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

目录

项目说明
项目环境配置
项目结构
贡献说明
为 DDNet 贡献代码的指南
Good first issue
调整心态

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

image.png

项目说明

项目环境配置

克隆代码

bash
git clone --recursive git@github.com:ZerolAcqua/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
  • 通过源码编译

    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 更多的问题,尤其是在内存错误和未定义行为方面。

项目结构

  • 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 定义了所有网络包。

贡献说明

为 DDNet 贡献代码的指南

  • 贡献前的准备

    在编写代码前,建议先开一个 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 的 review 可能比较严格

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

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

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

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

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

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

本文作者:Zerol Acqua

本文链接:

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