開發

From DDraceNetwork
Revision as of 05:50, 30 July 2023 by Darkh (talk | contribs) (Created page with "=== 在客户端翻译文本 === <code>Localize</code> 用于在游戏客户端从用户选定的语言文件中获取特定字串的翻译。<syntaxhighlight lang="cpp"> DoButton(..., Localize("Connect"), ...); </syntaxhighlight>字符串可以包含格式说明符,翻译文本必须包含同样的格式说明符(这可能产生某些排序不一致的问题)。<syntaxhighlight lang="cpp"> char aBuf[128]; str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers")...")

本文皆在教您如何對 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);

在客戶端翻譯文本

Localize 用於在遊戲客戶端從用戶選定的語言文件中獲取特定字串的翻譯。

DoButton(..., Localize("Connect"), ...);

字符串可以包含格式說明符,翻譯文本必須包含同樣的格式說明符(這可能產生某些排序不一致的問題)。

char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers"), NumServers, TotalServers);

在調用Localize時依靠一個腳本來掃描識別調用代碼,然後收集字符串上傳翻譯文件。由於此原因,調用Localize 不能包含任何其他代碼,否則腳本無法正確識別文本。

// 请勿这样做:
const char *pStr = Localize(Team == TEAM_RED ? "Red team" : "Blue team");

// 应该这样做:
const char *pStr = Team == TEAM_RED ? Localize("Red team") : Localize("Blue team");

外部資料