Разработка
Цель этой статьи - познакомить вас с DDNet разработкой, поскольку это игра с открытым исходным кодом, она полагается на случайных людей, готовых внести в нее свой вклад в свободное время.
Ваша среда разработки
Крайне рекомендуется установить среду Linux для начала программирования в DDNet по следующим причинам (на данный момент):
- Большинство участников DDNet на самом деле используют Linux для внесения вклада.
- Более простое управление пакетами, вы можете легко установить все необходимые библиотеки и начать вносить свой вклад.
- Эта статья пока не имеет версии для Windows и ориентирована на Linux.
Прежде всего, DDNet написан с использованием языка программирования C++, вам необходимо быть достаточно знакомым с ним, но вы также можете знать основы и научиться большему с его помощью.
Некоторые полезные ресурсы для изучения C++:
- learncpp.com
- cppreference.com
- Предпочтительная поисковая система
Исходный код DDNet управляется с помощью Git, системы контроля версий, важного инструмента для совместной работы с несколькими разработчиками.
Если в вашем дистрибутиве Linux еще нет git, обязательно установите его, например, в большинстве дистрибутивов на базе debian/ubuntu: sudo apt install git
.
Получение исходного кода
Исходный код находится на Github, вы можете получить исходный код путем клонирования без необходимости иметь учетную запись, но если вы хотите когда-либо внести свои изменения в официальный исходный код, то она вам понадобится.
Если вы не знакомы с git/github, вы можете изучить основы здесь: Hello World - Github.
Установка зависимостей
Если вы работаете на Linux, вы можете установить все необходимые зависимости, прочитав README на странице DDNet на github: https://github.com/ddnet/ddnet#dependencies-on-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
методы для их обработки являются нормой.- Большая часть кода ввода-вывода, форматирования и печати выполняется с использованием методов, предоставляемых system.h.
Расположение исходного кода
Теперь, когда вы можете создать DDNet, вы можете приступить к его редактированию.
Каталог src/base
Поскольку DDNet является кросс-платформенной игрой, для облегчения разработки необходим слой абстракции над ней, этот каталог содержит множество полезных функций для этого.
Каталог src/engine
Здесь находится игровой движок, он обрабатывает большинство вещей, не связанных с игровым процессом, таких как графика, звук, сеть и т.д.
Каталог src/game
Весь код, связанный с геймплеем, находится здесь, разделенный на клиентский и серверный.
Серверная сторона
Эта игра использует свою собственную иерархическую объектно-ориентированную систему сущностей, основным классом, от которого происходят все остальные сущности, является CEntity
located in src/game/server/entity.h
.
Эти сущности управляются игровым миром, расположенным здесь src/game/server/gameworld.h
.
К числу важных структур относятся:
- CCharacter: Представляет собой тии, который является живым, он создается при порождении тии и удаляется при его смерти. Информацию о том, как игрок сохраняется между смертями, см. в CPlayer.
Клиентская сторона
Клиентская сторона состоит из компонентов, это классы, которые наследуют CComponent
: Эти компоненты могут реализовывать виртуальные методы, такие как OnInit
, OnMessage
и т.д., чтобы обеспечить их функциональность.
Сетевое взаимодействие
Сетевой протокол в основном генерируется скриптами python, которые выводят код C++, например, datasrc/network.py
определяет все сетевые пакеты.
Кодовые конвенции
Продолжающаяся дискуссия о кодовых соглашениях находится здесь: ddnet#2945.
В настоящее время применяется следующее:
Indentation стиль
Allman style используется.
Этот стиль помещает скобку, связанную с управляющим оператором, на следующую строку с отступом на том же уровне, что и управляющий оператор. Утверждения внутри скобок отступают на следующий уровень.
while(x == y)
{
Something();
SomethingElse();
}
FinalThing();
Классы и структуры
Должен иметь префикс C
(по устаревшим причинам это игнорируется для структур в некоторых местах, например, в графическом коде) или I
для интерфейсов. Новая структура s должна иметь префикс
S
.
Пример:
class CCharacter : public CEntity
{
// ...
}
Перечисления и константы
Должен быть screaming snake case, например: MAX_PLAYERS
enum
{
FAKETUNE_FREEZE = 1,
FAKETUNE_SOLO = 2,
FAKETUNE_NOJUMP = 4,
FAKETUNE_NOCOLL = 8,
FAKETUNE_NOHOOK = 16,
FAKETUNE_JETPACK = 32,
FAKETUNE_NOHAMMER = 64,
};
Именование переменных
- Имена переменных содержат 3 части: классификатор, префикс и имя.
- Имена переменных должны начинаться с прописной буквы, если только они не имеют длину в 1 символ без какого-либо префикса или определителя, например:
i
,x
,y
. - Переменные могут иметь более 1 классификатора (или нуля) и более 1 префикса (или нуля).
Они расположены следующим образом: [qualifiers]_[prefixes][Name]
Квалификаторы
m
для переменных-членов:m_MyVariable
.s
для статических переменных:s_MyStaticVariable
,ms_MyStaticMemberVariable
.g
для глобальных переменных с внешней связью:gs_MyGlobalStaticVar
.
Префиксы
p
для указателей:pMyPointer
,m_pCharacter
,ppMyPointerToPointer
.a
для массивов:aMyArray
,aBuf
.v
дляstd::vector
s:vMyVector
,vpvMyVectorOfPointersToVectors
.fn
для функций:pfnMyCallback
,m_papfnMyPointerToArrayOfCallbacks
.
Общие фрагменты
Вот список кода, который вы можете часто встретить в кодовой базе:
Форматирование текста
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"), ...);
char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers"), NumServers, TotalServers);
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");
Внешние ресурсы
- Знакомство с исходным кодом игры DDraceNetwork от Ryozuki
- Соглашения о коде в DDraceNetwork от Ryozuki
- Реализация команды чата в DDraceNetwork от Ryozuki
- Auto generated docs
- Техническая документация по форматам файлов Teeworlds и сетевому протоколу
- Анатомия размораживания одного тика
- YouTube-учебник по программированию Teeworlds от ChillerDragon