DDraceNetwork 是一款开源免费的多人合作平台跳跃游戏(其前身是 Teeworlds 的一个叫 DDRace 的模组),上限高游戏性强,是一个非常独特的合作游戏。本项目依赖众多好心的程序员进行维护,作为一个基本成熟稳定的 C++ 游戏项目,我们可以从这个项目中学习到很多开发相关的知识,甚至如果你有什么改进,也可以为原代码仓库提出 Issue 贡献 PR。
克隆代码
bashgit clone --recursive git@github.com:ZerolAcqua/ddnet.git
安装依赖
bashsudo 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
构建项目
bashcmake -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 环境,就可以顺利运行检查。
bashpip install clang-format==10.0.1.1
通过源码编译
bashgit 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>
即可运行单元测试
按项目自述文件操作
bashsudo 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
bashsudo apt install libgmock-dev
使用 AddressSanitizer + UndefinedBehaviourSanitizer
安装 Clang
bashsudo 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
运行命令
bashUBSAN_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
的类:这些组件通过实现视觉方法来提供功能,例如 OnInit
,OnMessage
等等。
网络
网络协议几乎由 python 脚本生成并输出 c++ 代码,例如说,datasrc/network.py
定义了所有网络包。
贡献前的准备
在编写代码前,建议先开一个 issue 讨论想法,确保贡献符合 DDNet 项目的理念。常见的被拒绝贡献包括:
编程语言
DDNet 主要使用 C++,少量使用 Rust,Python 用于代码生成和工具,CMake 用于构建。平台特定代码可能使用 Java(Android)或 Objective-C++(macOS),但不接受其他语言的代码。
命名风格
变量、方法和类名使用大驼峰命名法(如 MaxLength
),避免单字母变量名。
使用匈牙利命名法,继承自 Teeworlds。使用前缀如 m_
(类成员)、g_
(全局变量)、p
(指针)等。但不是严格的匈牙利命名法。c_
常量、b
布尔值、i
整型不使用。
Class Prefix | Usage | Example |
---|---|---|
m_ | Class member | int m_Mode , CLine m_aLines[] |
g_ | Global member | CConfig g_Config |
s_ | Static variable | static EHistoryType s_HistoryType , static char *ms_apSkinNameVariables[NUM_DUMMIES] |
p | Both raw and smart pointers | char *pName , void **ppUserData , std::unique_ptr<IStorage> pStorage |
a | Fixed sized arrays and std::array s | float aWeaponInitialOffset[NUM_WEAPONS] , std::array<char, 12> aOriginalData |
v | Vectors (std::vector ) | std::vector<CLanguage> m_vLanguages |
Class Prefix | Usage | Example |
---|---|---|
C | Classes | class CTextCursor |
I | Interfaces | class IFavorites |
S | struct STextContainerUsages |
代码格式化:使用 clang-format 10 自动格式化代码。
约定
现代 C++ 实践:优先使用 nullptr
、true
/false
,避免 goto
、默认参数和方法重载。
避免在 if
语句中赋值:明确分离变量赋值和条件判断。
Getter 方法命名:避免使用 Get 前缀(如 MyVariable()
优于 GetMyVariable()
)。
类成员初始化:在声明时直接初始化类成员变量。
文件命名:使用小写字母和下划线分隔(如 foo_bar.cpp
)。
提交消息
提交消息应描述对玩家/用户的影响,而非技术细节,以便直接用于变更日志。
goog first issue 的作用是什么?
比如我找的这个 issue:https://github.com/ddnet/ddnet/issues/9583 是个很简单的工作,只需要把描述文本修改一下就行。
另外,这个网站可以帮助你查看流行开源项目中可能的 goog first issue。
参与代码贡献是一件令人振奋的事情,但中间也会遇到一些会让你感到受挫的事情,你需要以平常心看待它们。
项目维护者对你的 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 许可协议。转载请注明出处!