编辑
2024-08-01
项目学习
00
请注意,本文编写于 245 天前,最后修改于 27 天前,其中某些信息可能已经过时。

目录

项目说明
项目环境配置
基本配置
代码格式化脚本
运行单元测试
使用 AddressSanitizer + UndefinedBehaviourSanitizer
使用 VSCode 工作区
项目结构
贡献代码指南
开始贡献
Good first issue
PR 常用技巧
其他的话
调整心态
大胆去问

DDraceNetwork 是一款开源免费的多人合作平台跳跃游戏(其前身是 Teeworlds 的一个叫 DDRace 的模组),上限高游戏性强,是一个非常独特的合作游戏。本项目依赖众多好心的程序员进行维护,作为一个基本成熟稳定的 C++ 游戏项目,我们可以从这个项目中学习到很多开发相关的知识,甚至如果你有什么改进,也可以为原代码仓库提出 Issue 贡献 PR。

image.png

项目说明

项目环境配置

基本配置

Fork 仓库后克隆代码

bash
git clone --recursive git@github.com:YourUserName/ddnet.git

安装依赖

bash
sudo apt install build-essential cargo cmake git glslang-tools google-mock libavcodec-extra libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libcurl4-openssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpng-dev libsdl2-dev libsqlite3-dev libssl-dev libvulkan-dev libwavpack-dev libx264-dev python3 rustc spirv-tools

构建项目

bash
cmake -Bbuild cmake --build build

如果构建的客户端无法连接服务端,有一种可能是你的 TUN 模式没关。

代码格式化脚本

代码格式化检查,运行 scripts/fix_style.py 脚本。由于 Ubuntu 24.04 已经找不到 clang-format-10 的安装包,我们需要自行编译或通过其他途径安装:

  • 使用 pip 安装(官方推荐)

    A lot of the style offenses can be fixed automatically by running the fix script ./scripts/fix_style.py

    We use clang-format 10. If your package manager no longer provides this version, you can download it from https://pypi.org/project/clang-format/10.0.1.1/.

    由于执行检查脚本使用的是 python,我们可以通过 pip 安装。如果是用 conda 安装的,在执行 python 脚本时指定 python 环境,就可以顺利运行检查。

    bash
    pip install clang-format==10.0.1.1
  • 直接下载二进制文件

    https://aur.archlinux.org/packages/clang-format-static-bin

    下载后将二进制文件移动到 /usr/local/bin 目录下并重命令为 clang-format-10

    bash
    sudo mv /path/to/clang-format-10_linux-amd64 /usr/local/bin/ cd /usr/local/bin/ sudo chmod +x clang-format-10_linux-amd64 sudo mv clang-format-10_linux-amd64 clang-format-10
  • 通过源码编译(未尝试)

    bash
    git clone https://github.com/llvm/llvm-project.git cd llvm-project git checkout llvmorg-10.0.1 mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang ../llvm make -j$(nproc) sudo make install

    编译过程中可能出现部分报错:

    • std:numeric_limits 不是 std 成员。在相关文件中包含 #include <limits> 即可
    • uintptr_t 未声明,在相关文件中包含 #include <cstdint> 即可

运行单元测试

按项目自述文件操作

bash
sudo apt install libgtest-dev cd /usr/src/gtest sudo cmake CMakeLists.txt sudo make # copy or symlink libgtest.a and libgtest_main.a to your /usr/lib folder sudo cp lib/*.a /usr/lib

在我运行时,还需要安装 gmock

bash
sudo apt install libgmock-dev

使用 AddressSanitizer + UndefinedBehaviourSanitizer

这个博主配了但试不出来有什么东西,估计是没配置好吧

  • 安装 Clang

    bash
    sudo apt install clang
  • 编译命令

    CC=clang CXX=clang++ CXXFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer" CFLAGS="-fsanitize=address,undefined -fsanitize-recover=address,undefined -fno-omit-frame-pointer" cmake -DCMAKE_BUILD_TYPE=Debug . make
  • 运行命令

    bash
    UBSAN_OPTIONS=suppressions=./ubsan.supp:log_path=./SAN:print_stacktrace=1:halt_on_errors=0 ASAN_OPTIONS=log_path=./SAN:print_stacktrace=1:check_initialization_order=1:detect_leaks=1:halt_on_errors=0 LSAN_OPTIONS=suppressions=./lsan.supp ./DDNet
  • 结果检查

    运行后,检查生成的 SAN.* 文件以查看详细的问题报告。ASan + UBSan 能够发现比 Memcheck 更多的问题,尤其是在内存错误和未定义行为方面。

使用 VSCode 工作区

这个工作区配置挺折腾,中间没配好我真的非常沮丧……

在调试过程中,我发现一旦客户端代码命中断点,就会导致鼠标被游戏捕获无法释放的问题。在 dc 中询问后,Jupstar 大佬给我了一个解决方案。

注意

修改桌面环境之后我的系统会经常报错,虽然不影响使用,但修改之前请考虑!

  • other/vscode/README.md 的说明,下载相应的二进制文件和安装相应的包(其中 clang-format 可按前文所述安装)

    sudo apt install clangd clang-tidy lldb rustup mold ninja-build xdotool
  • 按照自述文件先按 Ctrl+Shift+P 输入 Scan for kits 扫描工具箱,再按 Ctrl+Shift+P 并选择 Select a kit,然后选 [DDNet] 开头的工具。

  • 如果 cmake 生成时出现 rustup 相关报错,请执行命令:

    bash
    rustup default stable
  • 查看一下当前显示是什么桌面环境,如果使用的不是 X11 的桌面环境,需要修改设置

    查看桌面环境:

    bash
    echo $XDG_SESSION_TYPE

    修改设置:

    bash
    sudo vim /etc/gdm3/custom.conf

    把该文件中这一行的注释状态解除:

    diff
    - # WaylandEnable=false + WaylandEnable=false

    然后重启即可切换。

  • vscode 调试时需要从“运行与调试”中进入,不要点击 vscode 底部的 Cmake 调试按钮,否则走的不是工作区中定义的调试配置。

项目结构

  • src/base 目录

    由于DDNet是一个跨平台游戏,需要一个抽象层来简化开发,因此该目录包含许多有用的函数来处理这一问题。

  • src/engine 目录

    游戏引擎所在位置,它处理大多数与玩法无关的东西,比如图形、声音、网络等。

  • src/game 目录

    所有玩法的代码所在位置,分为客户端和服务器。

    • 服务器端

      • 这个游戏使用自己的面向对象的实体分级系统,所有其他实体源于主类 CEntity,位于 src/game/server/entity.h 目录。

      • 游戏世界在 src/game/server/gameworld.h 路径下,管理这些实体。

      • 一些重要实体有:

        • CCharacter:代表一个活着的 Tee, 当一个 Tee 生成时会被实例化,当它死亡时会被删除。
        • CPlayer:包含了其他没有关联 Tee 的信息(昵称、国籍等)。有关玩家在死亡之间的信息,请参阅此项。
    • 客户端

      客户端由许多组件构成,都是在 src/game/client/component.h 中定义的继承 CComponent 的类:这些组件通过实现视觉方法来提供功能,例如 OnInitOnMessage 等等。

    • 网络

      网络协议几乎由 python 脚本生成并输出 c++ 代码,例如说,datasrc/network.py 定义了所有网络包。

贡献代码指南

  • 贡献前的准备

    在编写代码前,建议先开一个 issue 讨论想法,确保贡献符合 DDNet 项目的理念。常见的被拒绝贡献包括:

    • 影响游戏玩法的 dummy 扩展
    • 破坏向后兼容性的改动(如网络协议、文件格式或游戏玩法)。
  • 编程语言

    DDNet 主要使用 C++,少量使用 Rust,Python 用于代码生成和工具,CMake 用于构建。平台特定代码可能使用 Java(Android)或 Objective-C++(macOS),但不接受其他语言的代码。

  • 代码风格

    命名规则:变量、方法和类名使用大驼峰命名法(如 MaxLength),避免单字母变量名。

    匈牙利命名法:继承自 Teeworlds,使用前缀如 m_(类成员)、g_(全局变量)、p(指针)等。

    现代 C++ 实践:优先使用 nullptrtrue/false,避免 goto、默认参数和方法重载。

    代码格式化:使用 clang-format 10 自动格式化代码。

  • 最佳实践

    避免在 if 语句中赋值:明确分离变量赋值和条件判断。

    Getter 方法命名:避免使用 Get 前缀(如 MyVariable() 优于 GetMyVariable())。

    类成员初始化:在声明时直接初始化类成员变量。

    文件命名:使用小写字母和下划线分隔(如 foo_bar.cpp)。

  • 提交消息

    提交消息应描述对玩家/用户的影响,而非技术细节,以便直接用于变更日志。

开始贡献

Good first issue

goog first issue 的作用是什么?

  • 一般是开源仓库中比较简单的 issue,可能几行代码就能解决,甚至让人有一种捡漏的感觉,一般会由项目维护者选择简单易上手的、无关痛痒的问题,不亲自处理而是留待潜在的贡献者处理,可以吸纳新人参与到项目贡献中。
  • 对于没有什么提交经验的人,面对一个复杂的项目,这种简单的 issue 可以帮助参与者建立对流程的基本认识。对贡献者来说,自己提交的 pr 能被合并是非常有成就感的,尤其是首先提交 pr 的贡献者,看到自己的修改被合并还是很高兴的。

比如我找的这个 issue:https://github.com/ddnet/ddnet/issues/9583 是个很简单的工作,只需要把描述文本修改一下就行。

另外,这个网站可以帮助你查看流行开源项目中可能的 goog first issue

PR 常用技巧

  • 在 PR 中提及要解决的 issue,一般可以用 fix#xxxxclose#xxxx,这样 github 会自动渲染出链接的 issue 并会提示 PR 将关闭这个 issue

  • 想要验证其他人还未被合并的 PR?

    在你已经 Fork 了自己的仓库,并且克隆到本地的基础上,先要添加 ddnet 官方的上游仓库:

    bash
    git remote add upstream git@github.com:ddnet/ddnet.git

    然后,根据你要拉取的 PR 编号进行拉取,比如我想拉取 #9786 PR,我就从 upstream 拉取 pull/9786/head,然后把这个本地分支命名为 keb-pr:

    bash
    git fetch upstream pull/9786/head:keb-pr
  • 在 issue 中插入视频,直接拖入即可 `.mp4` 最佳,可直接渲染出播放窗口

  • 在 issue 中引用特定提交的代码。在 github 某个提交的代码浏览的界面里,点选行号(按住 shift 可以选择范围),点击菜单选择 Copy permalink 即可,以下链接会被 github 渲染成相应区域的代码块:

    https://github.com/ddnet/ddnet/blob/0aff2c75322a99a218fc6a8d659e8e77d62928e4/src/test/editor.cpp#L5-L8

其他的话

调整心态

参与代码贡献是一件令人振奋的事情,但中间也会遇到一些会让你感到受挫的事情,你需要以平常心看待它们。

  • 项目维护者对你的 PR 的 review 可能比较严格

    一般是因为你的提交存在一些问题,或者不规范的地方,如要求你压缩你的提交,修改提交信息等。正视它们,这些直率的建议也能让你进步的更快。

  • 社区的评价不一定都是中听的

    社区对你的贡献的评价也可能让你不舒服,尽管他们看起来并没有恶意。比如我贡献一个提交,想获取 dev 身份组时,会有人用 “commit: readme typo fix” 来打趣我(而且这种开源贡献笑话还挺常见的,时不时就会出现一次)。

    面对这种情况,还是要调整好心态,如果想要做长期的贡献还是从简单的 issue 开始的。跟进时间长了自然会做出有价值的提交。而且即使是简单的拼写修复提交,仓库的维护人员也一直欢迎和接纳,不要太受社区人员的影响。同时也需要自己提升心理承受能力。

  • 开源社区终究也是社区,你并不突出

    社区总有光彩夺目的人,你需要降低期待做好无人理睬的准备。在 Manim 幼儿园的时候就已经有这种无力感了,更别说外国社区,英语蹩脚没有熟人更会让你觉得自己是被无视了。DDNet 社区里虽然有几位国人版主和贡献者,但也都根本没有接触过,颇有一种孤军奋战的感觉。

大胆去问

在 github 上找了个几个月前的 issue,里面提到了了一个小 BUG,我的开发就从地图编辑器开始了。

随着慢慢深入源码,我可以发现代码中其实有很多可以改进的地方,不知不觉已经提了五个 PR 了。在这个过程中,可能会有自己的 PR 或者 issue 没有被仓库协作者看到的情况。你可以大胆地在 DC 群里去请求 review。当然遇到了编程相关的问题,也可以去问问 DC 里的人,如果有了解相关的代码的人,是会给予你帮助的。即使他们可能没有负责这部分的代码,他们也可以为你提供一些有价值的建议。

另外,这次有了五个 PR 之后,我也是鼓起勇气去 @ 了管理员,最后也是如愿以偿地得到了开发者的 role,所以还是得要大胆一点,只要你不犯大病礼貌一点,不会有什么惩罚的。

本文作者:Zerol Acqua

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!