

\part{Serverová část}

\section{Úvod}
Divergent Teritory je online multiplayerová hra určená pro velké množství hráčů. Dělí se na serverovou část a klientskou část,
jako samostatný prvek klientské části je administrátorské rozhraní, které je určitým speciálním klientem.
Server používá k uchovávání dat databázi Firebird. Klient používá knihovnu Qt a knihovnu OpenGL pro zobrazování grafiky.


\subsection{Hardwarové požadavky serveru}

\begin{center}
\begin{tabular}{|c|}
\hline
2 GB RAM\\
4 jádrový procesor\\
DVD mechanika\\
Windows 2000 a vyšší\\
Firebird, verze 2.0.6 a vyšší\\
disk 50 MB instalace, cca 2 GB herní databáze\\
\hline
\end{tabular}
\end{center}

\subsection{Instalace}

\section{Architektura serveru}
Server se skládá ze dvou částí: herního enginu a vlastní hry. Herní engine zajišťuje servis 
herní logice a herním datovým strukturám. Tento engine lze použít i pro další hry
tohoto typu. 

V současné době engine umožňuje spustit jednu instanci hry na každý proces, nicméně
lze ho snadno upravit tak, že bude možné spouštět více instancí hry v jednom procesu.
\lstset{language=C++}

%%%%%%%%%%%%%%%%%%%%%%%%%

Protože je Divergent Territory massive multiplayer online hra, bylo nutné navrhnout architekturu serveru tak, aby velký počet zaregistrovaných či online hráčů nepředstavoval výkonostní problém.


\subsection{Běh herních mechanismů}

Téměř všechny herní mechanismy probíhají (poté, co je uživatel nastaví) automaticky a v reálném čase; server tedy musí simulovat mnoho paralelních dějů zároveň.
Implementačně nejjednodušší by sice byla spojitá simulace, která by ale jednak příliš zatěžovala server při několika stovkách či tisích registrovaných hráčů a jednak by vytěžovala linku mezi serverem a aktuálně nalogovanými hráči, protože by jim bylo každý krok třeba posílat.
Všechny děje jsou tedy simulovány tak, že si server drží stav v nějakém čase, gradient změny stavu a spočítaný čas příští změny. Když tento čas nastane, server znovu přepočítá stav, nový gradient a určí čas následující změny a tak dále.
Tento způsob sice umožňuje serveru zvládnout potenciálně velké množství hráčů, avšak implementačně není úplně triviální v případě, že jednotlivé děje na sobě (někdy i cyklicky) závisí.


\subsection{Práce s daty}

Další věc, pro kterou bylo nutné architekturu serveru navrhnout, je rychlá práce s velkými objemy dat mnoha typů; některá data se často mění (základny a lodě hráčů, předměty), jiná se mění málo (uživatelské účty, výzkum), další jsou v rámci hry konstantní (hvězdy, planety).
Pro tento účel používá server tzv. cacheListy, což jsou objekty, které spravují všechna data. Každý typ dat má svůj cacheList (pevnosti, hráči, předměty, vesmírné objekty, ....). Kdykoliv herní engine potřebuje nějaký objekt, předá odpovídajícímu cacheListu ID objektu (to je jednoznačné a používá se i v databázi), aby jej cacheList mohl vrátit (pokud neexistuje, vrací NULL).
Uvnitř mohou cacheListy fungovat různě (jsou parametrizovatelné). Nejčastěji při startu serveru načtou všechna data z databáze do paměti, kde je uloží do vektoru, čímž je zajištěn přístup v lineárním čase. Lze je ale nastavit (případně velmi snadno přeprogramovat) na mnoho dalších typů chování, např.: data se načítají přímo z databáze a po použití se uvolní; do paměti se načítají data, která by mohla být pravděpodobně potřebná záhy (načtení pevností a předmětů hráče při jeho přihlášení); data se načtou do paměti ve chvíli, kdy jsou poprvé potřeba, pak tam zůstanou do vypnutí serveru; data se načtou, když je jich třeba, z listu se uvolní, když začne docházet paměť.
Koherenci dat v paměti a databázi mohou listy též řešit mnoha způsoby: po každém použití dat je rovnou propsat; jednou za čas zapsat všechna data z listu; zapisovat data z uvolňovaných objektů; jednou za čas zapsat pouze změněná data; nezapisovat do databáze nikdy (to se hodí např. u vesmírných objektů, které se nemění, jen se z nich čte).


\subsection{Řízení běhu}

Celý běh serveru je řízen událostmi ze dvou front. První je časová osa, druhou tvoří requesty uživatelů.
Časová osa obsahuje události, které do ní přidá server, když spočítá, že někdy v budoucnu dojde ke změně nějakého objektu (na osu přidá event s odpovídajícím časem a ID objektu). Nabíjí-li například loď energii do štítů a server zjistí, že za minutu budou štíty plně nabité, přidá na časovou osu událost s časem "nyní + 1 minuta", kterou za minutu vyzvedne a přenastaví rozvod energie v lodi, štíty se přestanou nabíjet (změní-li se něco, co událost přiblíží či oddálí, je tato událost na časové ose přemístěna).
Požadavky hráčů (z klientské části aplikace) předvídat nelze, proto se vyřizují v okamžiku, kdy přijdou. Vlákno serveru tedy stále čeká, dokud buď nepřijde požadavek po síti, nebo dokud nepřišel správný čas pro aktivaci události na časové ose.
Obrázek znázorňuje, jak jsou spolu propojené jednotlivé části serveru. Engine je řízen událostmi a požadavky, data získává z cacheListů, které komunikují s databází.

\begin{figure}[H]
\begin{center}
	\includegraphics[keepaspectratio=true, scale=0.5]{srvarc.eps}
	\caption{Schéma architektury serveru.}
\end{center}					
\end{figure}

\subsection{Více světů}

Server je navržen tak, aby na něm mohlo v jednu chvíli běžet více herních světů zároveň; každý by pak měl svou databázi a port, na kterém by poslouchal.
Všechny cacheListy i časová osa patří do tříd kontextu. Aktuální kontext je schován pod ukazatelem actx, do kterého se při každém přijetí události z časové osy nebo požadavku klienta přiřadí kontext, do kterého tento klient či událost patří.
Krom (neurčeného počtu) aktivních kontextů je na serveru ještě kontext globální, který obsahuje síťový server, a další věci společné pro všechny kontexty.
Svět je na provozování více světů naráz sice připraven a navržen, ale nikoliv otestován; je možné, že by pro zprovoznění této možnosti bylo třeba několik dalších zásahů.


\subsection{Paralelismus}

Zpracovávání požadavků a tvorba odpovědí, tedy operace s řetězci s vysokou časovou náročností, probíhá plně paralelně, neboť se jedná o operace, které se nijak neovlivňují a jejich paralelní zpracování může server výrazně zrychlit.
Aritmeticko-logické výpočty herní logiky či fyziky, které jsou méně náročné a jejich paralelizace by byla velmi složitá, probíhají v jednom vlákně.
V případě více herních světů by běžela tvorba zpráv opět ve velkém počtu vláken a aritmeticko-logické výpočty v jednom vláknu pro každý svět.
Architektura serveru potenciálně umožňuje ukládání dat z cacheListů do databáze ve zvláštním vlákně, čímž by bylo možné výkon ještě navýšit (nicméně takové vlákno zatím implementováno není).


\subsection{Protokol}

Server komunikuje s klienty asynchronně a bezstavově přes sockety. Chce-li klient data, vygeneruje žádost, server mu pak zpět pošle data, což není nijak odlišeno od toho, kdy na serveru dojde ke změně, o které je třeba informovat klienta.
Klient přijímá od serveru data, která buď zahazuje (pokud se hráč zrovna dívá na jinou obrazovku), nebo zobrazí (pokud jsou zrovna relevantní); z tohoto pravidla existuje několik výjimek (kvůli optimalizaci). Klient nerozlišuje, zda si data vyžádal, nebo mu byla poslána jen tak.


\subsection{Čas}

Vzhledem k tomu, že je na serveru implementována relativně komplexní fyzika (gravitace, působení sil, zrychlování objektů, některé důsledky Keplerových zákonů, ....), je nepraktické, aby herní čas běžel rychlostí skutečného času (trvalo by moc dlouho, než by se ve hře cokoliv stalo); běží tedy rychleji (defaultně 30x rychleji, což lze změnit přepsáním jediné konstanty).
Herní čas se vždy po spuštění serveru namapuje na reálný, k čemuž stačí dvojice odpovídajících časů a poměr rychlosti plynutí času ve hře a v reálném světě. Vzhledem k tomu, že je toto mapování velmi rychlé, je při vypnutí serveru (či náročnějších operacích jako je třeba generování většího vesmíru) snadné herní čas zastavit.
Posunutí času vpřed nezpůsobuje větší problémy až na to, že server posunutý do budoucnosti začne zpracovávat všechny události na časové ose, které už se staly, avšak nebyly zpracovány; bude i přidávat nové, z těchto událostí vyplývající, které pak zpracuje také, až na ně přijde řada, čímž se dostane přibližně (až na nějaké detaily) do takového stavu, v jakém by byl, kdyby čas uplynul standardní cestou (každopádně bude výsledný stav konzistentní).
Posunutí času zpět je nemožné; server se nejen dostane do velmi nekonzistentního stavu, ale navíc přestane fungovat, dokud se čas nevrátí přibližně do bodu, odkud byl vrácen (nicméně rozhodně plynule nenaváže - pro každý objekt ve hře začne čas plynout jindy, což způsobí další nekonzistence). Toto se týká pouze vracení herního času; návrat reálného času (napříkla přechod z letního na zimní čas) nevadí, je-li herní čas odpovídajícím způsobem přemapován.

%%%%%%%%%%%%%%%%%%%%%%%%%%



\subsection{Sítě a vlákna}
Nejnižší vrstva enginu hry zajišťuje abstrakci vláken, socketů a synchronizačních primitiv nad systémovými funkcemi.
Umožňuje tak přizpůsobivost kódu v případě, že se Windows API změní, nebo v případě, že hra bude migrována
na jiný operační systém. Abstrakce umožňuje používat obdobné objekty reprezentující
jednotlivá vlákna, sockety a synchronizační primitiva, jaké jsou k dispozici například v Javě či C\#.

\subsubsection{Vlákna}

Abstrakce vláken je zajištěna objektem \dspFuncName{thread}. Do toho objektu přidáme potomka
třídy \dspFuncName{runnable}, kam dopíšeme příslušnou metodu operátoru, provádějící námi požadovanou funkčnost.
Vlákno můžeme spustit hned, jak ho vytvoříme s pomocí konstruktoru (v tom případě nastavíme proměnnou \dspFuncName{suspended} na hodnotu false).
V opačném případě ho můžeme spustit později metodou \dspFuncName{start}. Metoda \dspFuncName{isAlive} indikuje, zda je vlákno spuštěné, nebo ne.
Na každé spuštěné vlákno vždy (někdy v budoucnosti) musíme zavolat úspěšně metodu \dspFuncName{join} a tím vlákno zničit a uvolnit jeho prostředky.
Metody \dspFuncName{resume} a \dspFuncName{suspend} spouštějí resp. pozastavují vlákno, ale důrazně se doporučuje je nepoužívat,
protože mohou vlákno pozastavit v jakémkoliv okamžiku, například když provádí zápis do souboru.
Z tohoto důvodu je vhodnější používat k zastavování resp. opětovného spuštění vláken synchronizační primitiva a funkci
\dspFuncName{sleepCurrentThread}, která uspí vlákno na určitý čas.

\begin{lstlisting}
	class runnable 
	{
	public:
		virtual void operator()(thread* thd) = 0;
	};

	class thread 
	{
	public:
		thread(runnable* runObj, bool suspended);
		void start(void);
		void join(void);
		bool join(DWORD miliseconds);
		void resume(void);
		void suspend(void);
		bool isAlive(void);
	private:
		HANDLE threadHandle_;
	};
\end{lstlisting}

\subsubsection{Zámky}

Abstrakce zámků je zajištěna objektem \dspFuncName{mutex}. Mutex je nejjednodušší synchronizační
primitivum pro vzájemné vyloučení. Funkce \dspFuncName{lock} zamkne kritickou sekci. Funkce \dspFuncName{unlock}
ji odemkne. Varianta funkce \dspFuncName{lock} umožňuje zkusit kritickou sekci uzamknout a pokud
se to nezdaří do určité doby, vrátí hodnotu false, pokud se povede, vrátí hodnotu true.

\begin{lstlisting}
	class mutex 
	{
	private:
		HANDLE mutexHandle_;
	public:
		mutex();
		~mutex();
		void lock(void);
		bool lock(DWORD miliseconds);
		void unlock(void);
	};
\end{lstlisting}

\subsubsection{Semafory}

Semafory jsou v jistém smyslu zobecnění zámků. Každý semafor má číselnou proměnnou.
Pokud je tato proměnná kladná, zavoláním funkce \dspFuncName{down} se odečte hodnota 1 a 
proces vejde do kritické sekce. V opačném případě se volání zablokuje. Funkce \dspFuncName{up}
přičte k proměnné hodnotu 1 a tím odblokuje případné procesy čekající na semaforu.
Funkce \dspFuncName{up} existuje i v časově omezené variantě - je možné zadat časový interval,
po který se čeká na vstup do kritické sekce, pokud v tomto intervalu se podaří procesu
vstoupit do kritické sekce, tak vrátí funkce hodnotu true, v opačném případě false.

\begin{lstlisting}
	class semaphore
	{
	private:
		HANDLE semaphoreHandle_;
	public:
		semaphore(int initcnt, int maxcnt);
		~semaphore();
		void up(void);
		void down(void);
		bool down(DWORD miliseconds);
	};
\end{lstlisting}

\subsubsection{Sockety}
Sockety se dělí na "klientský" a "serverový". Zatímco serverový pouze přijímá spojení,
klientský slouží k oboustranné komunikaci. Klientský socket má metody \dspFuncName{sendData} a \dspFuncName{recvData},
které slouží posílání a přijímání dat. Pokud nastane při přenosu chyba, je vyhozena výjimka.
Klient se umí připojit k TCP/IP serveru pomocí metody \dspFuncName{connectToHost}, kde se zadá jméno hosta a port.
Při úspěchu vrátí hodnotu true, false jinak. K uzavření spojení slouží metoda \dspFuncName{close}.

\begin{lstlisting}
	class clientSocket
	{
	private:
		SOCKET socketHandle_;
	public:
		clientSocket(void);
		clientSocket(SOCKET socketHandle);
		bool connectToHost
			(const char* hostName, int port);
		int sendData(const char* data, int size);
		int recvData(char* buf, int size);
		void close(void);
	};
\end{lstlisting}

Serverový socket má metodu \dspFuncName{listenPort}, která slouží k poslouchání na daném portu, tj. čekání na příchozí spojení.
K přijetí spojení slouží metoda \dspFuncName{acceptConnection}, která je blokující. K uzavření socketu (a ukončení poslouchání na daném portu)
slouží metoda \dspFuncName{close}.

\begin{lstlisting}
	class serverSocket
	{
	private:
		SOCKET socketHandle_;
	public:
		serverSocket(void);
		void listenPort(int port);
		void listenPort
			(int port, int maxConnections);
		clientSocket* acceptConnection(void);
		void close(void);
	};
\end{lstlisting}

Uvedená implementace je nízkoúrovňová, je možné například clientSocket použít k připojení na TCP/IP server, který nepochází z tohoto projektu.
Před použitím těchto objektů je nutné zavolat inicializační funkci \dspFuncName{socketsStart}.


\subsection{Funkce datum/čas}

Herní engine používá svou vlastní reprezentaci datumu/času tak, aby bylo možné 
jednotlivá data sčítat/odečítat a šly s nimi provádět různé další operace.
Datum/čas je reprezentován v plovoucí řádové čárce datovým typem double; konstanta 1.0 odpovídá jednomu dni.
Třída \dspFuncName{datetime} obsahuje mnoho funkcí na přidávání/odečítání času, dále časoměrné funkce a také operátory,
takže časy lze standardně sčítat operátorem +. Metoda \dspFuncName{toString} převede datum/čas na textový řetězec.

\begin{lstlisting}
class datetime
{
private:
	double datetime_;
public:
	datetime(void);
	datetime(double datetime);

	datetime& operator=(const datetime& other);

	const datetime operator+
		(const datetime& other) const;
	const datetime operator-
		(const datetime& other) const;

	const datetime operator*
		(const double other) const;
	const datetime operator*
		(const unsigned long long other) const;
	const double operator/
		(const datetime& other) const;
	const datetime operator/
		(const double other) const;
	const datetime operator/
		(const unsigned long long other) const;

	datetime& operator+=(const datetime& other);
	datetime& operator-=(const datetime& other);
	datetime& operator*=(const datetime& other);

	bool operator==(const datetime& other) const;
	bool operator!=(const datetime& other) const;

	bool operator<(const datetime& other) const;
	bool operator>(const datetime& other) const;
	bool operator<=(const datetime& other) const;
	bool operator>=(const datetime& other) const;

	operator double() const;

	static const datetime nanosecond(void);
	static const datetime microsecond(void);
	static const datetime millisecond(void);
	static const datetime second(void);
	static const datetime minute(void);
	static const datetime hour(void);
	static const datetime day(void);
	static const datetime month(void);
	static const datetime year(void);

	datetime& incYear(int years);
	datetime& incMonth(int months);
	datetime& incDay(int days);
	datetime& incHour(int hours);
	datetime& incMinute(int minutes);
	datetime& incSecond(int seconds);
	datetime& incMillisecond(int milliseconds);

	datetime& decYear(int years);
	datetime& decMonth(int months);
	datetime& decDay(int days);
	datetime& decHour(int hours);
	datetime& decMinute(int minutes);
	datetime& decSecond(int seconds);
	datetime& decMillisecond(int milliseconds);

      std :: string toString(void);
};
\end{lstlisting}

Dále jsou k dispozici různé funkce pro kódování/dekódování času.

\begin{lstlisting}
datetime encodeDate(int year, int month, int day);
datetime encodeTime(int hours, int minutes, 
	int seconds, int milliseconds);
datetime encodeDateTime(int year, int month, int day, 
	int hours, int minutes, 
	int seconds, int milliseconds);
datetime encodeSeconds(unsigned long long seconds);

void decodeTime(const datetime& t, 
	int& hours, int& minutes, 
	int& seconds, int& milliseconds);
void decodeDate(const datetime& t, int& year, 
	int& month, int& day);
void decodeDateTime(const datetime& t, int& year, 
	int& month, int& day, 
	int& hours, int& minutes, 
	int& seconds, int& milliseconds);
void decodeTime(const datetime* t, 
	int* hours, int* minutes, 
	int* seconds, int* milliseconds);
void decodeDate(const datetime* t, int* year, 
	int* month, int* day);
void decodeDateTime(const datetime* t, int* year, 
	int* month, int* day, 
	int* hours, int* minutes, 
	int* seconds, int* milliseconds);

bool isLeapYear(int year);
\end{lstlisting}

Existují také funkce vracející určitý počet časových jednotek mezi dvěma daty.
Funkce \dspFuncName{datediff} spočítá časový rozdíl mezi dvěma daty.

\begin{lstlisting}
datetime datediff(const datetime& d1, const datetime& d2);

long long yearsBetween		
	(const datetime& d1, const datetime& d2);
long long monthsBetween		
	(const datetime& d1, const datetime& d2);
long long daysBetween		
	(const datetime& d1, const datetime& d2);
long long hoursBetween		
	(const datetime& d1, const datetime& d2);
long long minutesBetween	
	(const datetime& d1, const datetime& d2);
long long secondsBetween	
	(const datetime& d1, const datetime& d2);
long long millisecondsBetween
    (const datetime& d1, const datetime& d2);

long long yearsBegunBetween     
	(const datetime& d1, const datetime& d2);
long long monthsBegunBetween    
	(const datetime& d1, const datetime& d2);
long long daysBegunBetween      
	(const datetime& d1, const datetime& d2);
long long hoursBegunBetween     
	(const datetime& d1, const datetime& d2);
long long minutesBegunBetween   
	(const datetime& d1, const datetime& d2);
long long secondsBegunBetween   
	(const datetime& d1, const datetime& d2);
long long millisecondsBegunBetween
    (const datetime& d1, const datetime& d2);
\end{lstlisting}

Nakonec funkce \dspFuncName{now} vrací aktuální čas.

\begin{lstlisting}
datetime now(void);
\end{lstlisting}

\subsection{Databázové připojení}

Databázové připojení využívá knihovny IBPP\footnote{\url{www.ibpp.org}} k připojení k databázovému serveru Firebird.
Metoda \dspFuncName{connect} se pokusí připojit k databázovému serveru se zadanými parametry, pokud se to nezdaří, vrátí hodnotu false, jinak true.
Metoda \dspFuncName{commit} potvrdí aktuální transakci, tj. všechny změny databáze provedené od posledního zavolání metody \dspFuncName{commit}, případně \dspFuncName{connect},
pokud byla databáze právě připojena. Metoda \dspFuncName{disconnect} pak odpojí databázi.


\begin{lstlisting}
class dbClient
{
private:
	IBPP :: Database db_;
	IBPP :: Transaction tr_;
	
public:
	bool connect(const std :: string& server, 
		const std :: string& database, 
		const std :: string& username, 
		const std :: string& password);
	void commit(void);
	void disconnect(void);
};
\end{lstlisting}

Do této třídy pak lze dopisovat jednotlivé dotazy pro tu určitou hru, která je právě vyvíjena, popřípadě
lze napsat potomka této třídy. Tato komponenta je stále ještě nízkoúrovňová, k abstrakci a správě databázových objektů
slouží objekt "cache list" popsaný v následující sekci.

\subsection{Cache objektů}

Tato komponenta představuje abstrakci nad databází, dokáže spravovat databázové záznamy tak,
jako by to byly objekty v operační paměti. S její pomocí lze záznamy ukládat, vytvářet, mazat a načítat.
Jelikož je to abstrakce nad databází, lze ji specializovat a například některé zdroje dat vyčlenit - například statická data
lze spravovat jako speciální cache list, který vůbec nespolupracuje s databází, ale má záznamy uložené přímo v kódu. 

Jako základní objekt abstrakce nad záznamy v databázi můžeme považovat třídu \dspFuncName{dataListObjectClass}.
Jednotlivé specializované záznamy pak od této třídy dědí a implementují její virtuální funkce.
Tento objekt má také položku \dspFuncName{ID\_}, která jednoznačně určuje daný záznam v databázi. 

\begin{lstlisting}
class dataListObjectClass
{
protected:
	dtIdentifier   ID_;
public:
    dataListObjectClass();
    virtual       ~dataListObjectClass();

    void           setID    (unsigned long ID);
    unsigned long  getID    () const;

    virtual void   DB_Load  (  unsigned long ID);
    virtual void   DB_Save  (  unsigned long ID);
    virtual void   DB_Insert(  unsigned long ID);

    virtual void   DB_TLoad      ();
    virtual void   DB_TSave      ();
    virtual void   DB_TInsert    ();

    virtual void   UpdateEvents  ();               
};
\end{lstlisting}

K abstrakci tabulky slouží třída \dspFuncName{cacheListClass}. Tato třída zajišťuje správu
databázových záznamů a jejich reprezentaci v operační paměti. Uživatel (programátor herní logiky)
se tak nemusí starat o správu objektů.

\begin{lstlisting}
class cacheListClass
{
public:
    virtual ~cacheListClass();

    virtual dataListObjectClass * operator []  
		(unsigned long position          );

    virtual dtIdentifier            insertItem            
		(dataListObjectClass * newItem);
    virtual void                    deleteItem            
		(dtIdentifier          id     );
    virtual dataListObjectClass *   access                
		(dtIdentifier          id     );
    virtual void                    synchronize           
		(void                         );

    virtual void  DB_Initialize                
		(         void                   );

    template<typename T>
    T* getItem (unsigned long position);
    void          getObjectIds                 
		(std :: deque<dtIdentifier>& ids );
    virtual void  updateEvents                 
		(void                            );
	
	size_t getSize();
};
\end{lstlisting}

Každý záznam má své jednoznačné ID. Pomocí hranatého operátoru lze získat z cache listu příslušný záznam, případně
hodnotu NULL, pokud daný záznam neexistuje. Obdobnou, resp. stejnou funkcionalitu má funkce \dspFuncName{access}.
K přidávání prvků slouží metoda \dspFuncName{insertItem}, v okamžiku volání příslušná instance cache listu přebírá zodpovědnost
za tento objekt, tj. v případě potřeby ho dealokuje. K mazání prvků slouží metoda \dspFuncName{deleteItem}, opět, smazaný objekt
není po té nutné dealokovat, neboť to udělá cache list sám. Aby se usnadnilo přetypování a také pro typovou čistotu,
šablonová funkce \dspFuncName{getItem} vrací objekt příslušného typu přetypovaný pomocí \dspFuncName{dynamic\_cast}.
Funkce \dspFuncName{getObjectIds} dokáže zjistit ID všech objektů, které jsou v dané tabulce reprezentované cache listem.
Virtuální metoda \dspFuncName{updateEvents} slouží k inicializaci objektů při prvním načtení, může, nebo také nemusí být využita.
Nakonec funkce \dspFuncName{getSize} vrátí potenciální počet prvků v cache listu a metoda \dspFuncName{DB\_Initialize} inicializuje
cache list před prvním použitím (synchronizuje ho s databází).

Cache list však změny nezapíše okamžitě do databáze. Příslušné změny v objektech, jejich smazání a přidávání si pamatuje a k zápisu
aktuálního stavu do databáze slouží metoda \dspFuncName{synchronize}. Je to tak lepší proto, že server pak běží mnohem rychleji,
neboť nepotřebuje neustále zapisovat změny v databázi na disk. Tento přístup je horší pro bezpečnost dat, protože v databázi jsou uložena starší
data a při pádu serveru se tedy obnoví pouze starší data. To ale u online her nevadí, neboť je časté, že se třeba vezme záloha z minulého dne.
Více by to vadilo například v nějakém platebním systému. Vnitřně má každý záznam u sebe údaj, který určuje platnost dat jak v databázi, tak v operační paměti.
Záznam může mít u sebe následující příznak:
\begin{itemize}
\item \dspFuncName{DATA\_IS\_EMPTY}: Data nejsou ani v databázi, ani v paměti, záznam neexistuje.
\item \dspFuncName{DATA\_IN\_DATABASE\_ONLY}: Platný záznam je pouze v databázi, bude nutné ho načíst.
\item \dspFuncName{DATA\_IN\_MEMORY\_ONLY}: Platný záznam je pouze v paměti, bude nutné ho vložit do databáze. 
\item \dspFuncName{DATA\_IN\_DATABASE\_INVALID}: Záznam je neplatný a je v databázi, je ho z ní nutné smazat.
\item \dspFuncName{DATA\_SYNCHRONIZED}: Data záznamu jsou jak v databázi, tak v operační paměti, jsou oboje platná a identická.
\item \dspFuncName{DATA\_DESYNCHRONIZED}: Platný záznam je v operační paměti, v databázi jsou zastaralá data, která budou muset být aktualizována.
\end{itemize}

\subsection{Kontext hry}

Kontext hry obsahuje všechny herní datové struktury a další položky nutné pro běh enginu. Z důvodu potenciální možnosti mít spuštěné
více instancí hry najednou, je kontext rozdělen na globální část \dspFuncName{global\_context} a lokální část \dspFuncName{context}. Zatímco
globální část je společná pro všechny instance hry, každá instance hry má svůj lokální kontext. Engine hry si sám nastavuje příslušný kontext
podle dané instance hry. Lokální kontext je možné nastavit pomocí funkce \dspFuncName{setContextById}, zjistit identifikátor nastaveného lokálního
kontextu lze funkcí \dspFuncName{getActualContextID} a konečně získat ukazatel na lokální kontext (podle daného identifikátoru) lze funkcí 
\dspFuncName{getContextById}. V současné podobě lze mít spuštěnu pouze jednu instanci hry, příslušný lokální kontext je v proměnné \dspFuncName{actx},
globální kontext je pak v proměnné \dspFuncName{gctx}.

\begin{lstlisting}
class global_context 
{
public:
    /* server */
    server srv;

    /* engine hry */
    engine eng;

    void closeClient(id clientID);
    void sendToClient(id clientID, char* message);

    void startEngine(void);
    void stopEngine(void);

    bool startGame(const std :: string& server, 
	const std :: string& database, 
		const std :: string& username, 
		const std :: string& password, 
		int port, int maxConnections);
    void stopGame();
};
\end{lstlisting}

Funkce \dspFuncName{startGame} spustí jednu instanci hry na základě parametrů předaných této funkci.
Spuštěnou hru je možné ukončit funkcí \dspFuncName{stopGame}. Funkce \dspFuncName{startEngine} resp. \dspFuncName{stopEngine} jsou 
vnitřní funkce sloužící k spuštění, resp. zastavení jedné instance hry. Funkce \dspFuncName{closeClient} odpojí klienta, pokud je připojen.
Klientovi je možné poslat data pomocí funkce \dspFuncName{sendToClient}, přičemž funkce se stane vlastníkem předané zprávy, není tedy nutné
řetězec odalokovat. Pokud klient s daným identifikátorem není připojen, pouze se zpráva odalokuje.

\begin{lstlisting}
class context 
{
public:
    /* databaze */
    dbClient db;

    /* cache listy */

    dv_time :: datetime           lastSynchronization;

    void initialize_cache_lists(void);
    void initialize_events(void);

    void synchronize(bool force = false);

    static const unsigned long index = 0;
};
\end{lstlisting}

Lokální kontext obsahuje všechny datové struktury, případně další položky, pro každou jednotlivou instanci hry.
Povinně obsahuje databázového klienta \dspFuncName{db}, datum a čas poslední synchronizace cache listů s diskem - \dspFuncName{lastSynchronization},
index instance hry v proměnné \dspFuncName{index} (zatím statická proměnná), funkci inicializující cache listy - \dspFuncName{initialize\_cache\_lists},
funkci inicializující jednotlivé objekty - \dspFuncName{initialize\_events} a konečně funkcí \dspFuncName{synchronize} synchronizující data v operační
paměti s daty v databázi na pevném disku.

\subsection{Chyby a logování}

K zaznamenávání logů slouží makro \dspFuncName{LOG}. V ladícím režimu zaznamená i funkci, soubor a řádku, na které bylo toto makro zavoláno.
Makro volá jednu z funkcí \dspFuncName{log} a \dspFuncName{rlog}, z nichž první je určena pro ostrou verzi serveru, zatímco druhá pro ladící verzi.
Funkce \dspFuncName{lnextline} slouží k odřádkování. Všechny výše zmíněné funkce jsou plně synchronizované a lze je tedy volat z libovolného vlákna.
Pokud chce uživatel napsat na konzoli něco sám, musí k synchronizaci použít funkce \dspFuncName{lockConsoleForWrite} a 
\dspFuncName{unlockConsoleForWrite}.

Z důvodu ladění server vypisuje některé informace automaticky, zatímco v ostré verzi se vypisují pouze chyby. Nastavení těchto ladících zpráv
se povoluje, případně zakazuje, pomocí následujících maker:

\begin{lstlisting}
#if defined(_DEBUG)
    /* verbose */

    /* globalni povoleni / zakazani ladicich zprav */
    #define DV_SERVER_VERBOSE

    #if defined(DV_SERVER_VERBOSE)
        #define DV_SERVER_VERBOSE_REQUEST                           /* zobrazovani requestu                                 */
        #define DV_SERVER_VERBOSE_LOGON                             /* zobrazovani prihlaseni                               */
     // #define DV_SERVER_VERBOSE_SENDED_DATA                       /* zobrazovani poslanych dat                            */
        #define DV_SERVER_VERBOSE_EVENTS                            /* zobrazovani zmen eventu                              */
        #define DV_SERVER_VERBOSE_MOBILITY_EVENT                    /* zobrazovani spusteni mobility eventu                 */
        #define DV_SERVER_VERBOSE_QGPLASMA_EVENT                    /* zobrazovani spusteni qgplasma eventu                 */
        #define DV_SERVER_VERBOSE_ENERGY_DISTRIBUTION_EVENT         /* zobrazovani spusteni energy distribution eventu      */
        #define DV_SERVER_VERBOSE_CONTAINERED_RESOURCES_EVENT       /* zobrazovani spusteni containered resource eventu     */
        #define DV_SERVER_VERBOSE_CHECK_CONSTRUCTION_EVENT          /* zobrazovani spusteni kontroly postaveni eventu       */
    #endif
#endif
\end{lstlisting}

Na zaznamenávání menších i kritických chyb slouží makra \dspFuncName{errExceptionError} a \dspFuncName{errLogicalError}. Zaznamenají soubor, funkci a řádku,
ve které chyba nastala a také chybovou hlášku. V ladící verzi je možné zapnout breakpoint na místě chyby v případě, že chyba nastane.

\subsection{Komunikační protokol}

Komunikační protokol je čistě textový, je proto možné si ho přečíst. Na začátku zprávy je číslo s údajem, jak dlouhá je zpráva za tímto číslem.
Poté následuje hlavička s typem zprávy, popř. jménem požadavku zasílaný na server/klienta, následují parametry ukončené speciálním řetězcem.


K vytváření zpráv v tomto protokolu slouží třída \dspFuncName{protocolWriter}. Pokud chceme vytvořit zprávu, nejprve vytvoříme novou instanci této třídy.
Poté zavoláme funkci \dspFuncName{beginMessage} s požadovaným typem zprávy. Parametry lze zapisovat pomocí funkce \dspFuncName{writeValue}, lze takto zapisovat
různé číselné, řetězcové i boolean hodnoty. Nakonec zprávu ukončíme funkcí \dspFuncName{endMessage}, která nám vrátí konstantní ukazatel
na řetězec s vytvořenou textovou zprávou. Tento řetězec lze duplikovat pomocí funkce \dspFuncName{copyMessage}.

Vytvořenou textovou zprávu je možné zpětně rozparsovat pomocí třídy \dspFuncName{protocolReader}. Funkci \dspFuncName{translateMessage} předáme
ukazatel na řetězec s textovou zprávou a tato funkce nám vrátí instanci třídy \dspFuncName{message} reprezentující obsah dané zprávy.
Typ zprávy zjistíme pomocí funkce \dspFuncName{getType}, jméno žádosti dostaneme funkcí \dspFuncName{getRequestName},
druh odpovědi funkcí \dspFuncName{getAnswerKind} a konečně mapu parametrů funkcí \dspFuncName{getValues}.

\subsection{Časová osa}

V mnoha hrách jsou důležité asynchronně, na hráči nezávisle spouštěné různé typy událostí. U budovatelského typu her to může být například
postavení budovy. Ke správě těchto událostí slouží časová osa. Události je možné vytvářet, registrovat a asynchronně v určitý čas spouštět.
Nový typ události se vyrobí poděděním třídy \dspFuncName{triggerEventClass} a přepsáním příslušných virtuálních funkcí.
Funkce \dspFuncName{checkEvent} spustí příslušný kód, který má být proveden v určitý čas, uživatel ji musí podědit a napsat si do ní vlastní kód.
Obdobně funkce \dspFuncName{getEventName} slouží k definování jména události. Z hlediska vnitřní logiky události je nutné  si zaznamenávat
identifikátor události, který získáme pomocí funkce \dspFuncName{getEventName}. S pomocí tohoto identifikátoru lze aktualizovat čas spuštění události
tím, že se tato událost smaže a nahradí se nějakou bližší událostí stejné třídy. Funkce \dspFuncName{getServerID} slouží jako potenciální možnost
rozlišit eventy pro více instancí hry běžící na jednom serveru.

Události spravuje třída \dspFuncName{timeAxis}, ty události, které je nutné spustit, předá tato třída instanci enginu hry (viz. kapitola níže).
Pomocí funkce \dspFuncName{setEngine} nastavíme příslušný engine hry, kterému má časová osa zasílat události, které je nutné spustit. Funkcí
\dspFuncName{start} spustíme časovou osu, naopak funkcí \dspFuncName{stop} ji zastavíme. Novou událost vsuneme funkcí \dspFuncName{pushEventTrigger}.
Časová osa si sama zjistí, zda je událost aktuální, nebo existuje jiná, kterou je nutné spustit dříve. V takovém případě tuto událost smaže, v opačném
případě smaže starší událost a nahradí ji aktuální. Třídy událostí určuje proměnná \dspFuncName{lastTriggerID}, která určuje, se kterou událostí
se bude nová událost porovnávat. Pokud žádná taková událost neexistuje, je vhodné nastavit tuto proměnnou na nulu.


\subsection{Vlastní engine}

Engine hry tvoří jádro celé hry. Zpracovává požadavky od klientů a také asynchronní události, které mu dodá časová osa. Třídy požadavků jsou uloženy
v tabulce, kterou je před spuštěním hry nutné inicializovat funkcí \dspFuncName{init\_request\_table}. Samotný server lze spustit metodou
\dspFuncName{start} a zastavit metodou \dspFuncName{stop}. Pokud chceme provádět zásahy do datových struktur hry za běhu hry, je nutná synchronizace
pomocí metod \dspFuncName{beginWork} a \dspFuncName{endWork}. Požadavek klienta se zpracovává metodou \dspFuncName{processRequest}, nové požadavky
klienta lze přidat metodou \dspFuncName{addRequest}, zatímco asynchronní události určené ke spuštění se přidávají metodou \dspFuncName{addEvent}.
Nové události je možné registrovat metodou \dspFuncName{registerEvent}, která zavolá příslušnou metodu časové osy. Funkce \dspFuncName{isClientUpdateSuppressed},
\dspFuncName{suppressClientUpdates} a \dspFuncName{renewClientUpdates} slouží k potlačení zasílání aktualizací klientům.

