Styl kodowania
Dokumentacja programistyczna OpenTTD
Linki zewnętrzne

OpenTTD GitHub
Wkład w OpenTTD - wytyczne
OpenTTD Doxygen

Informacje Ogólne

Styl kodowania
Kompilowanie OpenTTD
Debugowanie
Dodanie ustawienia
Dodanie funkcji squirrel
Zrozumienie obsługi SaveGame
Wyznaczenie wersji gry zapisanej
Wykonanie wydania OpenTTD

Języki i Ciągi znaków

Podręcznik stylu
Format plików lang
Użycie ciągów OpenTTD
Lista ciągów specjalnych

System Okna

Użycie systemu okna
Kody kolorów istniejących w OpenTTD
Dodawanie pola tekstowego
Zrozumienie widget focus system
Przewodnik stylu GUI

Tryb wieloosobowy

OpenTTD TCP protokół
OpenTTD UDP protokół
Debugowanie desynchronizacji
Rozwój Portu Administratora Serwera

Konsola (w grze)

Okno konsoli
Komendy konsoli
Zmienne konsoli
Używanie skryptu konsoli
Dodanie funkcji/komend do konsoli
Dodanie zmiennych do konsoli
Historia rozwoju Konsoli

Interfejsy API (ramy modowania)

Grafika i podobne (NewGRFy)
Środowisko AI (NoAI)
Framework GS (NoGO)

Inne odniesienia

Tablica map (siatka pozioma)
Pojazdy
Wyszukiwanie trasy
Przyspieszenie pociągu

Dlaczego ustawiony styl kodowania jest ważny
Ten projekt jest projektem typu open source. Aby open source działał zgodnie z przeznaczeniem, wiele osób powinno być w stanie zrozumieć kod. Oznacza to, że istnieje dobrze udokumentowany i jednolity przepływ kodu. Nie musi to wcale oznaczać, że współtwórca nie powinien pisać zoptymalizowanego, ale tajemniczego kodu - zawsze należy dbać o wydajność. Jednak inni programiści muszą zrozumieć kod, co oznacza skomplikowane części kodu muszą mieć dobrą dokumentację.

Dlaczego wymagamy, aby WSZYSTKO było udokumentowane
To, co dla niektórych jest proste, dla innych może wydawać się bardzo skomplikowane. Dokumentacja pomaga tym innym. Każdy powinien być w stanie poprawić kod. Ale głównym powodem jest to, że jeśli twórcy łatek chcą, aby ich łatki zostały dodane do wspólnej bazy kodu, kod musi zostać sprawdzony przez jednego lub więcej programistów. Dokumentacja znacznie ułatwia przeglądanie.

Contents

Styl kodowania dla OpenTTD

Funkcje

void ThisIsAFunction()
{
}

Zmienne

int     number         = 10;
Vehicle *u_first_wagon = v->next;
int     value          = v->value;

uint32 _global_variable = 3750;

static const char * const _global_array[] = {
	"first string",
	"second string",
	"another string",
	"last string followed by comma",
};

protected:
	char protected_array[10];

for (int i = 0;;);
bool is_maglev_train = true;
if (!is_maglev_train) DoSomething();
/* Unoptimized code:
 * foo is not touched inside the loop!
 */
for (uint8 i = 0; i < 100000; i++) {
	/* Do something */
	if (value_to_check == (foo * 4) % 5 + 6) DoSomething();
	/* Do something else */
}

/* Better:
 * The used value of foo is calculated outside the loop.
 */
const uint32 bar = (foo * 4) % 5 + 6;
for (uint8 i = 0; i < 100000; i++) {
	/* Do something */
	if (value_to_check == bar) DoSomething();
	/* Do something else */
}

Wyliczenia / stałe statyczne

enum DiagDirection {
	DIAGDIR_BEGIN = 0,
	DIAGDIR_NE  = 0,
	DIAGDIR_SE  = 1,
	DIAGDIR_SW  = 2,
	DIAGDIR_NW  = 3,
	DIAGDIR_END,
	INVALID_DIAGDIR = 0xFF,
	BROKEN_DIAGDIR = 0xFE,
};

enum {
	DEPOT_SERVICE       = (1 << 0),
	DEPOT_MASS_SEND     = (1 << 1),
	DEPOT_DONT_CANCEL   = (1 << 2),
	DEPOT_LOCATE_HANGAR = (1 << 3),
};

DiagDirection enterdir = DIAGDIR_NE;

Przykład:

static const int MAXIMUM_STATIONS = 42;
/** Enum referring to the widgets of the build rail depot window */
enum BuildRailDepotWidgets {
	BRDW_CLOSEBOX = 0,
	BRDW_CAPTION,
	BRDW_BACKGROUND,
	BRDW_DEPOT_NE,
	BRDW_DEPOT_SE,
	BRDW_DEPOT_SW,
	BRDW_DEPOT_NW,
};
/* ... */
/** Widget definition of the build rail depot window */
static const Widget _build_depot_widgets[] = {
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5, STR_..},   // BRDW_CLOSEBOX
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_...,  STR_...},  // BRDW_CAPTION
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   121, 0x0,      STR_NULL}, // BRDW_BACKGROUND
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,      STR_..},   // BRDW_DEPOT_NE
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,      STR_..},   // BRDW_DEPOT_SE
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,      STR_..},   // BRDW_DEPOT_SW
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,      STR_..},   // BRDW_DEPOT_NW
{   WIDGETS_END},
};
enum SomeEnumeration {
	SE_BEGIN = 0,        ///< Begin of the enumeration, used for iterations
	SE_FOO = 0,          ///< Used for xy
	SE_BAR,              ///< Another value of the enumeration
	SE_SUB,              ///< Special case for z
	SE_END,              ///< End of the enumeration, used for iterations
};

Kontrola przepływu

if (a == b) {
	Foo();
} else {
	Bar();
}

if (very_large_checks &&
		spread_over_two_lines) {
	Foo();
}

if (a == b) Foo();

switch (a) {
	case 0: DoSomethingShort(); break;

	case 1:
		DoSomething();
		/* FALL THROUGH */

	case 2:
		DoMore();
		b = a;
		break;

	case 3: {
		int r = 2;

		DoEvenMore(a);
		/* FALL THROUGH */
	}

	case 4: {
		int q = 345;

		DoIt();
		break;
	}

	default:
		NOT_REACHED();
}

for (int i = 0; i < 10; i++) {
	Foo();
	Bar();
}

Klasy

class ThisIsAClass {
public:
	typedef Titem_   *ItemPtr;
private:
	static const int MAX_SIZE = 500;
	int              size;
	ItemPtr          *items;

public:
	explicit ThisIsAClass();
	~ThisIsAClass();

	int GetSize() { return this->size; }
	virtual void Method();
};

/*virtual*/ void Class::Method()
{
	this->size *= 2;
}

Szablony

Szablony są bardzo potężnym narzędziem C++, ale mogą łatwo pomylić początkujących. A zatem:

template <typename T, typename Tsomething, int N, byte Tnumber_of_something>
int Func();

Inne ważne zasady

Dokumentacja

Używamy Doxygen do automatycznego generowania dokumentacji z kodu źródłowego. Skanuje pliki źródłowe w poszukiwaniu rozpoznawalnych komentarzy.

Komentarze Doxygen zaczynają się od następującego stylu:

/**
///<

Użyj /** dla wieloliniowych bloków komentarzy. Użyj ///< dla komentarzy jednowierszowych dla zmiennych. Użyj //!< do komentarzy jednowierszowych w plikach .hpp NoAI. W przypadku bloków komentarzy w funkcji zawsze używaj /* */ lub //. Użyj // tylko jeśli komentarz znajduje się w tym samym wierszu co instrukcja, w przeciwnym razie użyj /* */.

Pliki

/** 
 * @file
 * This is the brief description.
 * This is the detailed description here and on the following lines.
 */
/File/en/Content.png
Uwaga!
Jeśli w pliku brakuje file comment block wtedy Doxygen NIE udokumentuje ŻADNYCH elementów w tym pliku!

Funkcje

/**
 * A brief explanation of what the function does and/or what purpose it serves.
 * Then follows a more detailed explanation of the function that can span multiple lines.
 *
 * @param foo Explanation of the foo parameter
 * @param bar Explanation of the bar parameter
 * @return The sum of foo and bar (@return can be omitted if the return type is void)
 *
 * @see SomeOtherFunc()
 * @see SOME_ENUM
 * 
 * @bug Some bug description
 * @bug Another bug description which continues in the next line
 *           and ends with the following blank line
 *
 * @todo Some to-do entry
 */
static int FooBar(int foo, int bar)
{
	return foo + bar;
}

Klasy

/**
 * A short description of the struct.
 * More detailed description of the its usage.
 *
 * @see [link to anything of interest]
 */
struct foo {
}

Polecenia strukturalne JavaDoc

Ta tabela pokazuje polecenia, których powinieneś używać z OpenTTD. Pełna lista to tutaj.

Komenda Akcja Przykład
@attention Rozpoczyna akapit, w którym można wprowadzić wiadomość wymagającą uwagi. Akapit zostanie wcięty. @attention Whales crossing!
@brief Rozpoczyna akapit, który służy jako krótki opis. W przypadku klas i plików krótki opis zostanie wykorzystany na listach i na początku strony z dokumentacją. W przypadku członków klasy i plików krótki opis zostanie umieszczony w deklaracji członka i poprzedzony szczegółowym opisem. Krótki opis może obejmować kilka wierszy (choć zaleca się, aby był krótki!). @brief This is the brief description.
@bug Rozpoczyna akapit, w którym można zgłosić jeden lub więcej błędów. Akapit zostanie wcięty. Wiele sąsiadujących poleceń @bug zostanie połączonych w jeden akapit. Opis każdego błędu rozpocznie się w nowej linii. Alternatywnie jedno polecenie @bug może wspomnieć o kilku błędach. @bug Memory leak in here?
@note Rozpoczyna akapit, w którym można wprowadzić notatkę. Akapit zostanie wcięty. @note Might be slow
@todo Rozpoczyna akapit, w którym opisano pozycję 'DO ZROBIENIA'. Opis doda również pozycję do osobnej listy TODO. Oba wystąpienia opisu zostaną powiązane. Każdy element na liście DO ZROBIENIA będzie poprzedzony nagłówkiem wskazującym pochodzenie elementu. @todo Better error checking
@warning Rozpoczyna akapit, w którym można wprowadzić jeden lub więcej komunikatów ostrzegawczych. Akapit zostanie wcięty. @warning Not thread safe!

Function related commands

@return Rozpoczyna opis wartości zwracanej dla funkcji. @return a character pointer
@param Uruchamia opis parametru dla parametru funkcji o nazwie <parameter-name>. Następnie następuje opis parametru. Istnienie tego parametru jest sprawdzane i wyświetla się ostrzeżenie, jeśli brakuje dokumentacji tego (lub dowolnego innego) parametru lub nie ma go w deklaracji funkcji lub definicji.

Komenda @param ma opcjonalny atrybut określający kierunek atrybutu. Możliwe wartości to "in" i "out".
@param n The number of bytes to copy
@param[out] dest The memory area to copy to.
@param[in] src The memory area to copy from.
@see Rozpoczyna akapit, w którym można podać jedno lub więcej odsyłaczy do klas, funkcji, metod, zmiennych, plików lub adresu URL. Dwie nazwy połączone przez :: lub # są rozumiane jako odnoszące się do klasy i jednego z jej członków. Można wybrać jedną z kilku przeciążonych metod lub konstruktorów, umieszczając w nawiasach listę typów argumentów po nazwie metody. Tutaj można znaleźć szczegółowe informacje na temat tej funkcji. @see OtherFunc()
@b Displays the following word using a bold font. Equivalent to <b>word</b>. To put multiple words in bold use <b>multiple words</b>. ...@b this and @b that...
@c / @p Wyświetla parametr <word> przy użyciu czcionki do pisania. Za pomocą tego polecenia można odwoływać się do parametrów funkcji składowej w bieżącym tekście. Aby mieć wiele słów czcionką do pisania, użyj fontu <tt>multiple words</tt>. ... the @p x and @p y coordinates are used to...
@arg / @li To polecenie ma jeden argument, który trwa do pierwszej pustej linii lub do napotkania kolejnego @arg / @li. Polecenia można użyć do wygenerowania prostej, nie zagnieżdżonej listy argumentów. Każdy argument powinien zaczynać się od komendy @arg / @li . @arg @c AlignLeft left alignment.
@arg @c AlignCenter center alignment.
@arg @c AlignRight right alignment
@n Wymusza nową linię. Równoważny i inspirowany funkcją printf. @n

Więcej na temat Doxygen i JavaDoc

Doxygen zna dwa różne rodzaje komentarzy:

Możesz pominąć jedno lub umieścić je w różnych miejscach, ale dla tego samego bytu dozwolony jest tylko jeden krótki i jeden szczegółowy opis.

Doxygen zna trzy tryby dokumentowania bytu:

Ten ostatni jest nieco trudniejszy w utrzymaniu, ponieważ prototyp encji, którą opisuje, jest przechowywany w kilku miejscach (np. Plik .h i plik z opisami). Oprócz tego, że kod jest łatwiejszy do odczytania, ułatwia także pominięcie ważnego kroku aktualizacji opisu bytu, jeśli został zmieniony - i wszyscy wiemy, dlaczego tak się nie stało ;)
Z tych powodów użyjemy tylko dwóch pierwszych schematów dokumentacji.

Doxygen obsługuje style komentarzy Qt i JavaDoc:

Obsługuje także więcej stylów komentarzy, ale te dwa są znormalizowane. W przypadku OTTD będziemy używać stylu JavaDoc. Jednym z powodów jest to, że ma funkcję, której nie oferuje styl Qt: bloki komentarzy w stylu JavaDoc automatycznie rozpoczną krótki opis, który kończy się na pierwszej kropce, po której następuje spacja lub nowy wiersz. Wszystko później będzie również częścią szczegółowego opisu.

Ogólna struktura komentarza w stylu JavaDoc to

/**
 * This is the brief description. And this sentence contains some further explanations that will appear in the detailed description only.
 */

i wynikowe opisy tego bloku byłyby:

Rozróżnienia między krótkim i szczegółowym opisem dokonuje kropka, po której następuje spacja/nowa~linia, więc jeśli chcesz użyć tego w krótkim opisie, musisz uciec spacji/nowej~linii:

/**
 * This is a brief description (e.g.\ using only a few words). Details go here.
 */

Jeśli robisz komentarz w jednym wierszu, użyj:

int i; ///< This is the description.

Również w bloku komentarza można dołączyć tak zwane polecenia strukturalne, które mówią Doxygenowi, co następuje. Zasadniczo ich obszar działania zaczyna się po słowie polecenia, a kończy po napotkaniu pustej linii lub innego polecenia. Również wiele wystąpień tego samego polecenia strukturalnego w bloku komentarza lub encji odsyłającej będzie zwykle dołączanych do danych wyjściowych dokumentacji.

Wiadomość Zatwierdzenia

Aby osiągnąć spójną całość i ułatwić pisanie dziennika zmian, oto kilka wskazówek dotyczących komunikatów zatwierdzania (commit). Na serwerze git znajduje się skrypt kontrolny (dostępny również dla klientów, patrz poniżej), aby upewnić się, że wszyscy przestrzegamy tych zasad.

Pierwszy wiersz wiadomości musi być zgodny:

<keyword>( #<issue>| <commit>(, (<keyword> #<issue>|<commit>))*)?: ([<section])? <Details>

Słowa kluczowe to:

Jeśli zatwierdzisz poprawkę dla issue, dodaj odpowiedni numer wydania w postaci #NNNN. Zrób to również, jeśli zaimplementujesz funkcję z pasującym wpisem.

W przypadku poprawek błędów, jeśli wiesz, jaką poprawkę wprowadzono (np. Regresję), należy również wspomnieć o tej poprawce tuż po prefiksie. Znalezienie powodującej problemy rewizji jest bardzo zachęcane backporting/branching/releases - o wiele łatwiej.

Aby dodatkowo ustrukturyzować dziennik zmian, możesz dodać sekcje. Przykładami są:

Dalsze wyjaśnienia, ogólne 'bitching' itp. Nie wchodzą w pierwszą linię. Użyj nowej linii dla nich.

Kompletne przykłady:

Inne wskazówki

Usuń końcowe białe znaki

Aby dowiedzieć się, czy/gdzie masz końcowe białe znaki, możesz użyć następującego polecenia (unix/bash):

grep -n -R --include "*.[ch]" '[ 	]$' * | grep --invert-match ".diff" | grep --invert-match ".patch"

Automatyczne usuwanie białych znaków jest również możliwe za pomocą następującego skryptu powłoki (pamiętaj, że sprawdza tylko .c, .cpp, .h, .hpp and .mm pliki):

#!/bin/sh
IFS='
'
for i in Makefile `find . -name \*.c -o -name \*.cpp -o -name \*.h -o -name \*.hpp -o -name \*.mm`
do
  (
    echo '%s/[ 	]\{1,\}$/'
    echo w
    echo q
  ) | ed $i 2> /dev/null > /dev/null
done

Zainstaluj 'haczyki' zatwierdzania git po stronie klienta

'Haki' (hooks) po stronie klienta wykonują różne kontrole, gdy zatwierdzasz zmiany lokalnie.

Zdobądź 'haki':

git clone https://github.com/OpenTTD/OpenTTD-git-hooks.git openttd_hooks

Zainstaluj te 'zaczepy', zakładając, że "openttd" jest folderem drzewa pracy:

cd openttd/.git/hooks
ln -s -t . ../../../openttd_hooks/hooks/*