開發

From DDraceNetwork
Revision as of 05:40, 30 July 2023 by Darkh (talk | contribs) (Created page with "<syntaxhighlight lang="cpp"> int i = 2; dbg_msg("wikiprint", "Hello from the ddnet wiki: %d", i); </syntaxhighlight>")

本文皆在教您如何對 DDNet 開發 ,因為這是一款開源遊戲(原始碼公開),它依賴於一些善良的人們在空餘時間的貢獻。


開發環境

截至目前,基於以下原因,我們強烈推薦使用Linux系統進行DDNet開發:

  • 大多數DDNet貢獻者使用Linux,使用Linux與之前的開發者交流起來更加方便。
  • 軟件包管理更簡單,您可以輕鬆地安裝所有需要的庫。
  • 本文專注於Linux,還沒有涉及Windows。

首先,DDNet由 C++ 語言編寫,開發者需要對它相當熟悉,但您也可以在只有基礎知識的情況下慢慢學習深入。

學習C++的一些有用資源:

DDNet的原始碼使用Git託管,這是一個版本控制系統,也是與多人協作開發的重要工具。

如果您的Linux發行版中還沒有git,請安裝它,例如在大多數基於debian/ubuntu的發行版中,您可以打開終端,輸入:sudo-apt-install git


獲取原始碼

原始碼位於Github,您沒有帳戶的情況下可以通過Clone獲得原始碼,但如果想更改官方原始碼,則需要登錄賬號。

如果您不熟悉git/github,可以在這裏學習基礎知識: Hello World - Github


安裝依賴項

如果您在Linux上,您可以通過閱讀DDNet github頁面上的自述文件來安裝所有需要的依賴項:https://github.com/ddnet/ddnet#dependencies-在linux上--macos

對於Arch Linux:

控制台輸入 sudo pacman -S --needed base-devel cmake curl ffmpeg freetype2 git glew glslang gmock libnotify libpng opusfile python rust sdl2 spirv-tools sqlite vulkan-headers vulkan-icd-loader wavpack x264

對於Debian:

控制台輸入 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 python rustc spirv-tools4

編譯DDNet

我們使用CMake來控制編譯進程,如果你安裝完成所有的依賴項,之後只需要簡單輸入這些命令(確保你在DDNet文件夾下):

mkdir build
cd build
cmake ..
make -j$(nproc)


基本信息

以下是一些基本信息:

  • 目前,原始碼是使用C++17標準編譯的,但您會看到代碼的許多部分更像C,因為只有大多數新代碼使用C++17。
  • std::string很少使用,字符數組加system.h方法才是常態。
  • 大多數輸入輸出代碼、格式化和Print都是使用system.h提供的方法完成的。


原始碼佈局

現在你可以構建DDNet並開始編輯它了。


src/base 目錄

由於DDNet是一個跨平台遊戲,需要一個抽象層來簡化開發,因此該目錄包含許多有用的函數來處理這一問題。


src/engine 目錄

遊戲引擎所在位置,它處理大多數與玩法無關的東西,比如圖形、聲音、網絡等。


src/game 目錄

所有玩法的代碼所在位置,分為客戶端和伺服器。


伺服器端

這個遊戲使用自己的面向對象的實體分級系統,所有其他實體源於主類CEntity,位於src/game/server/e ntity.h目錄。

遊戲世界在src/game/server/gameworld.h路徑下,管理這些實體。

一些重要實體有:

  • CCharacter: 代表一個活着的Tee , 當一個Tee生成時會被實例化,當它死亡時會被刪除。有關玩家在死亡之間的信息,請參閱CPlayer


客戶端

客戶端由許多組件構成,都是繼承CComponent的類:這些組件通過實現視覺方法來提供功能,例如OnInitOnMessage等等。


網絡

網絡協議幾乎由python腳本生成並輸出c++代碼,例如說,datasrc/network.py定義了所有網絡包。


代碼規範

代碼規範的討論還在持續:ddnet#2945

目前已經應用了下面的部分:


縮進格式

採用Allman 風格

此風格將控制語句關聯的大括號放在下一行並且縮進到相同位置,內部語句則縮進到下一級。

while(x == y)
{
    Something();
    SomethingElse();
}

FinalThing();

類和結構體

必須帶有前綴C(由於歷史遺留原因有一些結構體沒有遵循,例如圖形代碼部分)或者代表接口的I。新的結構體需要帶前綴S

示例:

class CCharacter : public CEntity
{
    // ...
}


枚舉和常量

應該採用大蛇式命名法(所有單詞大寫並且用下劃線間隔),例如MAX_PLAYERS

enum
{
	FAKETUNE_FREEZE = 1,
	FAKETUNE_SOLO = 2,
	FAKETUNE_NOJUMP = 4,
	FAKETUNE_NOCOLL = 8,
	FAKETUNE_NOHOOK = 16,
	FAKETUNE_JETPACK = 32,
	FAKETUNE_NOHAMMER = 64,
};


變量命名

  • 變量命名有三個部分:修飾和前綴、名字。
  • 變量命名應該以大寫字母開始,除非是單字符例如:ixy
  • 變量可以有多於一個的修飾或前綴,也可以沒有。

排列方式:[修饰]_[前缀][名字]


修飾

  • 成員變量mm_MyVariable
  • 靜態變量ss_MyStaticVariablems_MyStaticMemberVariable
  • 帶外部連接的全局變量ggs_MyGlobalStaticVar


前綴

  • p for pointers: pMyPointer, m_pCharacter, ppMyPointerToPointer.
  • a for arrays: aMyArray, aBuf.
  • v for std::vectors: vMyVector, vpvMyVectorOfPointersToVectors.
  • fn for functions: pfnMyCallback, m_papfnMyPointerToArrayOfCallbacks.


Common snippets

下面列出經常出現在代碼庫的代碼:


格式文本

char aBuf[128];
str_format(aBuf, sizeof(aBuf), "number: %d", 2);

參閱printf 文檔查看格式提示符列表。

全玩家迭代

for(int i = 0; i < MAX_CLIENTS; i++)
{
    // Server-side
    CPlayer *pPlayer = GameServer()->m_apPlayers[i];
}


輸出到遊戲控制台

Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "wikiprint", "Hello from the ddnet wiki!");


輸出調試結果

int i = 2;
dbg_msg("wikiprint", "Hello from the ddnet wiki: %d", i);

Translating text in the client

Localize can be used in the game client to get the translation for a specific string from the language file selected by the user.
DoButton(..., Localize("Connect"), ...);
The string can also contain format specifiers. The translated string must contain the same format specifiers.
char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers"), NumServers, TotalServers);
A script is used to scan the code for calls to Localize and collect the strings to update the translation files. For this reason, the call to Localize must not contain any other code, or the script cannot determine the text correctly.
// Do NOT do this:
const char *pStr = Localize(Team == TEAM_RED ? "Red team" : "Blue team");
</div>

<div lang="en" dir="ltr" class="mw-content-ltr">
// Do this instead:
const char *pStr = Team == TEAM_RED ? Localize("Red team") : Localize("Blue team");

外部資料