grammar mistakes + game description
This commit is contained in:
+75
-90
@@ -65,7 +65,7 @@
|
||||
% tables=false,
|
||||
% theorems,
|
||||
% sourcecodes,
|
||||
glossaries,
|
||||
% glossaries,
|
||||
index
|
||||
]{kidiplom}
|
||||
|
||||
@@ -702,7 +702,7 @@ Rozšířenou architekturu vidíme na obrázku \ref{fig:multi_player_game}. Hned
|
||||
|
||||
\begin{figure}
|
||||
\begin{center}
|
||||
\includegraphics[width=0.5\textwidth]{graphics/multi_player_game.pdf}
|
||||
\includegraphics[width=1\textwidth]{graphics/multi_player_game.pdf}
|
||||
\end{center}
|
||||
\caption{Architektura hry pro více hráčů}
|
||||
\label{fig:multi_player_game}
|
||||
@@ -810,17 +810,16 @@ Většinou nechceme používat middleware protokol, který na rozhraní použív
|
||||
|
||||
Hned na začátku jsme se rozhodli nepoužít JSON nebo XML, protože jsou to textové formáty a jejich forma je často větší než binárních formátů. Například číslo 1 000 000 se v JSON zakóduje do 7 bytů, i když binární reprezentace stejného čísla se vleze do 4 bytů. Rozdíl je téměř dvojnásobný. Každý atribut objektu se v JSONu reprezentuje řetězcem, namísto identifikačním číslem o 2 bytech, které by bylo menší. Výhody formátu jsou především liberalní zpracování, kdy se schémata dvou koncových bodů mohou i mírně lišit, ale pokud má zpráva všechny potřebné atributy, tak se strany domluví.
|
||||
|
||||
Binární formáty tuto problematiku také řeší, například číslováním atributů, ale stále není řešení tak volné, jako u textových formátů. Obecně hrozí, že v binárním formátu je atribut navíc, nemusí se druhé straně podařit zprávu deserializovat. Proto se někdy JSON používá jako záložní formát, kdy dvě strany provedou handshake a domluví se na formátu. Pokud zjistí, že jedna strana používá starší definici schématu, mohou použít záložní JSON.
|
||||
Binární formáty tuto problematiku také řeší, například číslováním atributů, ale stále není řešení tak volné, jako u textových formátů. Obecně hrozí, že když bude ve zprávě v binárním formátu atribut navíc, nemusí se druhé straně podařit zprávu deserializovat. Proto se někdy JSON používá jako záložní formát, kdy dvě strany provedou handshake a domluví se na formátu. Pokud zjistí, že jedna strana používá starší definici schématu, mohou použít záložní JSON.
|
||||
|
||||
Rozhodli jsme se začít velmi rozšířeným ProtoBuf formátem. Začneme definicí zpráv mezi serverem a klientem. Vytvořili jsme WorldServerMessages.proto soubor, do kterého budeme zprávy definovat. Ve zkratce formát \inlcpp{.proto} představíme. V kódu \ref{code:entity_spawn_message} vidíme definici zprávy, která říká, že si příjemce má do svého stavu přidat novou entitu. Obsahuje tři atributy: ID pro navázání budoucích zpráv o entitě, dále příznak, zda se jedná o hráče a jméno.
|
||||
|
||||
\begin{kicode}{cpp}{}{cpp}
|
||||
\begin{kicode}{cpp}{code:entity_spawn_message}{cpp}
|
||||
message EntitySpawnMessage {
|
||||
uint32 entity_id = 1;
|
||||
bool is_player = 2;
|
||||
string name = 3;
|
||||
}
|
||||
\label{code:entity_spawn_message}
|
||||
\end{kicode}
|
||||
|
||||
Jazyk vypadá podobně jako C. Nejprve použijeme klíčové slovo \inlcpp{message} a název typu. Do těla píšeme atributy oddělené středníkem. Všimněme si, že pro každý atribut musíme definovat jeho unikátní číslo. To slouží při identifikaci a pomáhá při úpravách definice, abychom měli kontrolu nad tím, pod jakým klíčem se hodnota serializuje.
|
||||
@@ -902,20 +901,20 @@ Objekt navázaného spojení obsahuje stavový stroj a postupně mezi stavy pře
|
||||
|
||||
O spolehlivost doručení se protokol stará posíláním \inlcpp{Ack} rámců s číslem paketu, který získal. Navíc ale rozlišujeme spolehlivost na základě jednotlivých rámců. Pokud paket neobsahuje žádný spolehlivý rámec, nemusí pro něj příjemce posílat \inlcpp{Ack} rámec. Protokol v tento moment neřeší spolehlivost pořadí paketů. V našem případě posíláme například akce z klienta na server a u každé definujeme číslo snímku. Server si udržuje nejvyšší číslo snímku, které dostal, a všechny zprávy z nižších snímků zahazuje. Stejně to dělá klient se snapshoty. Proto neřešíme pořadí na transportní vrstvě.
|
||||
|
||||
Spolehlivost řeší komponenta \inlcpp{ReliabilityUnit}, která si udržuje frontu rámců, které je potřeba odeslat, čas, kdy je potřeba rámec odeslat a jejich spolehlivost. Čas slouží pouze pro prioritu a málokdy se stane, že se rámec odešle právě v tento čas. Objekt \inlcpp{QuicrEndpoint} se při ťiku dotazuje \inlcpp{QuicrConnection} na další datagram, který chce odeslat. Ten pomocí \inlcpp{ReliabilityUnit} začne datagram skládat z paketů. Když přijde rámec na řadu a je spolehlivý, hned se umístí na konec fronty a nastaví se mu čas odeslání. Většinou aktuální čas s přičteným konstantním intervalem. Pokud je nespolehlivý, do fronty se nevrátí. Krátce představíme rozhraní \inlcpp{ReliabilityUnit} objektu.
|
||||
Spolehlivost řeší komponenta \inlcpp{ReliabilityUnit}, která si udržuje frontu rámců, které je potřeba odeslat, čas, kdy je potřeba rámec odeslat a jejich spolehlivost. Čas slouží pouze pro prioritu a málokdy se stane, že se rámec odešle právě v tento čas. Objekt \inlcpp{QuicrEndpoint} se při aktualizaci dotazuje \inlcpp{QuicrConnection} na další datagram, který chce odeslat. Ten pomocí \inlcpp{ReliabilityUnit} začne datagram skládat z paketů. Když přijde rámec na řadu a je spolehlivý, hned se umístí na konec fronty a nastaví se mu čas odeslání. Většinou aktuální čas s přičteným konstantním intervalem. Pokud je nespolehlivý, do fronty se nevrátí. Krátce představíme rozhraní \inlcpp{ReliabilityUnit} objektu.
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[\inlcpp{push\_reliable\_frame\_to\_send(deadline, frame)}] \hfill \\
|
||||
\item[\texttt{push\_reliable\_frame\_to\_send(deadline, frame)}] \hfill \\
|
||||
Uloží si zakódovaný rámec a nastaví si u něj čas odeslání. Jakmile bude po deadline, jednotka se bude snažit dostat rámec do dalšího datagramu. Rámec se ukládá v zakódované podobě tak, aby byla předpovídatelná jeho velikost a snadno se odhadovalo, jestli se do dalšího datagramu vleze.
|
||||
|
||||
\item[\inlcpp{peek/pop\_reliable\_frames\_to\_send(packet\_number)}] \hfill \\
|
||||
\item[\texttt{peek/pop\_reliable\_frames\_to\_send(packet\_number)}] \hfill \\
|
||||
Vrátí rámce, které je potřeba v tomto okamžiku odeslat. Číslo paketu \inlcpp{packet\_number} si jednotka přiřadí jako verifikátor doručení rámce, který metoda vrátí. To znamená, že když toto číslo přijde v \inlcpp{Ack} rámci, bude jednotka vědět, který rámec může odebrat. Zároveň může mít vícero vazeb čísla paketu na jeden rámec. Libovolné číslo paketu smaže provázaný rámec. Důvod je, aby mohlo být v oběhu více paketů se stejným rámcem.
|
||||
|
||||
\item[\inlcpp{push\_ack(packet\_number)}] \hfill \\
|
||||
\item[\texttt{push\_ack(packet\_number)}] \hfill \\
|
||||
Vloží do seznamu čísel paketů. Celý tento seznam se odešle v \inlcpp{Ack} rámci v dalším datagramu.
|
||||
|
||||
\item[\inlcpp{peek/pop\_acks\_to\_send()}] \hfill \\
|
||||
\item[\texttt{peek/pop\_acks\_to\_send()}] \hfill \\
|
||||
Vrátí seznam čísel paketů, které musí druhé straně oznámit jako doručené.
|
||||
|
||||
\end{description}
|
||||
@@ -982,8 +981,8 @@ Systémovou architekturu vidíme na obrázku \ref{fig:zone_cluster_architecture}
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=1\textwidth]{graphics/zone_server_architecture.pdf}
|
||||
\label{fig:zone_cluster_architecture}
|
||||
\caption{Architektura systému s zone clusterem}
|
||||
\label{fig:zone_cluster_architecture}
|
||||
\end{figure}
|
||||
|
||||
|
||||
@@ -991,17 +990,18 @@ Systémovou architekturu vidíme na obrázku \ref{fig:zone_cluster_architecture}
|
||||
|
||||
\subsection{Výsledná hra}
|
||||
|
||||
V této části výslednou hru popíšeme. Po spuštění klienta dostaneme možnost zadat IP adresu serveru. V tento moment je aplikace ve stavu \inlcpp{lobby}. Hlavní třída, která řídí chod včetně nekonečné smyčky, se nazývá \inlcpp{Runtime}. Ta se chová jako stavový stroj a umožňuje stavům vracet zprávy, podle kterých mezi stavy přechází. Po kliknutí na \uv{connect} se klient pokusí připojit na server.
|
||||
V této části výslednou hru popíšeme. Po spuštění klienta dostaneme možnost zadat IP adresu serveru. Obrazovku vidíme na obrázku \ref{fig:lobby_ui}. V tento moment je aplikace ve stavu \inlcpp{lobby}. Po připojení hra přejde do stavu \inlcpp{game}. Hlavní třída, která řídí chod stavového stroje se nazývá \inlcpp{Runtime}. Chová jako stavový stroj a umožňuje stavům vracet zprávy, podle kterých mezi stavy přechází.
|
||||
|
||||
V případě že uspěje, přejde \inlcpp{Runtime} do stavu \inlcpp{game}. V něm už vidíme stav hry a uživatelské rozhraní, které se skládá z oken. Okna je možné skládat do sebe. TODO: Otevírání různých oken pro přehlednost.
|
||||
Po kliknutí na \uv{connect} se klient pokusí připojit na server. V případě že uspěje, přejde \inlcpp{Runtime} do stavu \inlcpp{game}. V něm už vidíme stav hry a uživatelské rozhraní. Ve hře je možné se volně pohybovat pomocí kláves W, A, S a D. Další připojení hráči jsou vidět jako červené kostky. Pro zátěžové testování je možné použít program \inlcpp{tw_mock_client}, který umožňuje připojit a simulovat určitý počet entit specifikovaný CLI argumentem. Náš hráč se pohybuje téměř okamžitě po stisku klávesy. Ostatní hráče vidíme s odezvou, která závisí na síťových podmínkách.
|
||||
|
||||
V horní části okna vidíme panel s možností \uv{Windows}, která po rozkliknutí zobrazí okna, která lze otevřít. Příklad je okno pro síťové statistiky, které zobrazuje různé sbírané metriky, jako odchozí a příchozí množství bitů. Dalším užitečným oknem je seznam entit ve hře. Po rozkliknutí entity vidíme komponenty a informace k nim. Například transformační matici nebo interpolační buffer. Otevřená okno pro seznam entit a síťové statistiky vidíme na obrázku \ref{fig:ui_showcase}.
|
||||
|
||||
Po připojení je možné se volně pohybovat pomocí WASD. Do hry se může připojit více hráčů. Například je možné použít program \inlcpp{tw_mock_client}.
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=1\textwidth]{graphics/lobby_ui.png}
|
||||
\label{fig:lobby_ui}
|
||||
\caption{Uživatelské rozhraní v lobby}
|
||||
\label{fig:lobby_ui}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}
|
||||
@@ -1015,27 +1015,27 @@ Po připojení je možné se volně pohybovat pomocí WASD. Do hry se může př
|
||||
\newpage
|
||||
\section{Měřění}
|
||||
|
||||
V této kapitole popíšeme, jaké testy jsme provedli a jejich výsledky. Nejprve představíme jak samotné metriky můžeme sbírat. Implementovali jsme jednoduchý systém pro sběr metrik, který je schopný je různě agregovat do oken dlouhých například jednu sekundu. Data jsme různě vizualizovali ve webovém rozhraní, tak i přímo v aplikaci klienta.
|
||||
V této kapitole popíšeme, jaké testy jsme provedli a jejich výsledky. Nejprve představíme jak samotné metriky můžeme sbírat. Implementovali jsme jednoduchý systém pro sběr metrik, který je schopný je různě agregovat do oken dlouhých například jednu sekundu. Data jsme různě vizualizovali jak ve webovém rozhraní, tak i přímo v aplikaci klienta.
|
||||
|
||||
\subsection{Metriky}
|
||||
|
||||
Představíme metody měřění a metriky v systému, které jsme využili pro hledání potenciálních míst pro optimalizaci. Problém jde rozdělit na tří úlohy: jak metriky sbírat, jak je ukládat a jak je zobrazit. Pro sběr jsme definovali třídy \inlcpp{NetworkMetricsReporter}, který obsahuje metody pro přidávání nových vzorků a různé možnosti čtení daných metrik. Každá lze přečíst jako celá série s určitou historií. Příkladem metod jsou \inlcpp{report\_outbound} a \inlcpp{get\_outbound}.
|
||||
|
||||
Implementace si data ukládá pouze do paměti RAM a po ukončení programu jsou nenávratně ztracena. Druhý problém šel ale řešit i perzistentně. Existují databáze, které jsou pro rychlé vkládání vzorků s časovou známkou vhodné. Mezi využití takových databází patří logy nebo právě metriky. Náš výběr představíme.
|
||||
Implementace si data ukládá pouze do paměti RAM a po ukončení programu jsou nenávratně ztracena. Druhý problém lze řešit i perzistentně. Existují databáze, které jsou pro rychlé vkládání vzorků s časovou známkou vhodné. Mezi využití takových databází patří logy nebo právě metriky. Náš výběr představíme.
|
||||
|
||||
Nakonec zbývalo metriky zobrazit. Rozhodli jsme se použít webové rozhraní pro rychlou inspekci stavu. Umožní rychle se podívat na množství hráčů nebo velikost datového toku. Metriky, které sbírá klient, zobrazujeme přímo v klientovi. Obě řešení ukážeme.
|
||||
|
||||
\subsubsection{PostgreSQL}
|
||||
|
||||
Pro perzistentní úložiště jsme vybrali populární relační databázový server PostgreSQL. Rozhodli jsme se právě pro ten, protože je to otevřený a svobodný software, který je zdarma. Databázi si lze snadno nasadit lokálně, ať už daemoném nebo jako Docker kontejner. Skrze rozšíření jako TimescaleDB lze přidat optimalizace pro rychlé vkládání dat o metrikách.
|
||||
Pro perzistentní úložiště jsme vybrali populární relační databázový server PostgreSQL. Rozhodli jsme se tak, protože jde o otevřený a svobodný software, který je zdarma. Databázi si lze snadno nasadit lokálně, ať už daemoném nebo jako Docker kontejner. Skrze rozšíření jako TimescaleDB lze přidat optimalizace pro rychlé vkládání dat o metrikách.
|
||||
|
||||
\subsubsection{TimescaleDB}
|
||||
|
||||
Pro optimalizaci PostgreSQL pro časová data jsme zvolili TimescaleDB. Díky tomu, že se jedná o rozšíření, nemusíme spravovat jinou službu, než PostgreSQL. TimescaleDB umožňuje vytvářet \uv{hypertables}, které jsou rychlé vkládání optimalizované. Vytvořili jsme komponentu \inlcpp{TimescaleDbMetricsWriter}, která čte z \inlcpp{NetworkMetricsReporter} a postupně zapisuje metriky do databáze. Jedná se o konkrétní implementace pro TimescaleDB. Komponenty jsou tak oddělené a snadno lze přidat rozšíření pro další typy úložišť, jako CSV nebo jiný databázový server.
|
||||
Pro optimalizaci PostgreSQL pro časová data jsme zvolili TimescaleDB. Díky tomu, že se jedná o rozšíření, není třeba spravovat jinou službu, než PostgreSQL. TimescaleDB umožňuje vytvářet tzv. \uv{hypertables}, které jsou optimalizované pro rychlé vkládání dat s časovou známkou\cite{timescaledb}. Vytvořili jsme komponentu \inlcpp{TimescaleDbMetricsWriter}, která čte z \inlcpp{NetworkMetricsReporter} a postupně zapisuje metriky do databáze. Jedná se o konkrétní implementaci pro TimescaleDB. Komponenty jsou tak oddělené a snadno lze přidat rozšíření pro další typy úložišť, jako CSV nebo jiný databázový server.
|
||||
|
||||
\subsubsection{Grafana}
|
||||
|
||||
Grafana je služba, která skrze webovou stránku poskytuje rozhraní pro vizualizaci metrik. Lze nastavit svůj dashboard a v něm různé grafy. Pro naše účely jsme vytvořili grafy pro odchozí a příchozí množství bytů. Služba slouží spíše pro sledování stavu a neumožňuje pokročilejší analýzu. Na obrázku \ref{fig:grafana} vidíme tři grafy. Horní graf ukazuje více metrik najednou, konkrétně množství odchozích a příchozích dat a počet hráčů. V tomto konkrétním případě jsme připojili 300 hráčů. Vidíme, že množství příchozích a odchozích dat se zvýšil. Množství odchozích dat se zvýšil očekávaně daleko více.
|
||||
Grafana je služba, která skrze webovou stránku poskytuje rozhraní pro vizualizaci metrik. Lze nastavit vlastní dashboard a v něm různé grafy. Pro naše účely jsme vytvořili grafy pro odchozí a příchozí množství bytů. Služba slouží spíše pro sledování stavu a neumožňuje pokročilejší analýzu. Na obrázku \ref{fig:grafana} vidíme tři grafy. Horní graf ukazuje více metrik najednou, konkrétně množství odchozích a příchozích dat a počet hráčů. V tomto konkrétním případě jsme připojili 300 hráčů. Vidíme, že množství příchozích a odchozích dat se zvýšil. Množství odchozích dat se zvýšil očekávaně daleko více.
|
||||
|
||||
\begin{figure}
|
||||
\begin{center}
|
||||
@@ -1047,9 +1047,9 @@ Grafana je služba, která skrze webovou stránku poskytuje rozhraní pro vizual
|
||||
|
||||
\subsubsection{Tracy}
|
||||
|
||||
Pro měření a pokročilejší analýzu výkonu programu klienta nebo serveru používáme profiler Tracy. Program je zdarma, open-source, napsaný v C++ a pro vykreslování UI využívá ImGui. Poskytuje přesnost na nanosekundy, což je užitečné u serverů, na kterém může být miliony různých entity. Navíc umožňuje připojit se k programu, který měří, vzdáleně. Případně sbírat metriky do souboru, ten si ze serveru stáhnout a analyzovat později.
|
||||
Pro měření a pokročilejší analýzu výkonu programu klienta nebo serveru používáme profiler Tracy. Program je zdarma, open-source, napsaný v C++ a pro vykreslování UI využívá ImGui. Poskytuje přesnost na nanosekundy, což je užitečné u serverů, na kterém může být miliony různých entity. Navíc umožňuje připojit se k měřícímu programu vzdáleně. Případně je možné sbírat metriky do souboru, ten si ze serveru stáhnout a analyzovat později.
|
||||
|
||||
V kódu umožňuje definovat jednotlivé iterace řídící logiky zvané snímky. Následně umožňuje definovat tzv. \uv{zóny}, které následně měří. Zóna může být průběh funkce nebo její konkrétní část. Na obrázku \ref{fig:frame_in_tracy} vidíme, jak taková analýza vypadá. Vidíme právě jeden snímek. V něm máme označené části replikátoru a manažera zájmu. Také vidíme dole zóny.
|
||||
V kódu umožňuje definovat jednotlivé iterace řídící logiky zvané snímky. Následně umožňuje definovat tzv. \uv{zóny}, které poté měří. Zóna může být průběh funkce nebo její konkrétní část. Na obrázku \ref{fig:frame_in_tracy} vidíme, jak taková analýza vypadá. Vidíme právě jeden snímek. V něm máme označené části replikátoru a manažera zájmu. V dolní části také vidíme zóny.
|
||||
|
||||
\begin{figure}
|
||||
\begin{center}
|
||||
@@ -1081,7 +1081,7 @@ Přímo v hráčově klientské aplikaci jsme chtěli mít možnost vidět metri
|
||||
|
||||
\subsection{QUICr}
|
||||
|
||||
Protokol měl za cíl snížit odezvu na nespolehlivé síti a neblokovat zprávy. Vytvořili jsme test, který simuluje program hry. V testu jsou dvě vlákna, jedno pro klienta a druhé pro server. Oba mají svou vlastní smyčku, ve kterém iterují. Klient odesílá hodnotu derivace typu double. Server si udržuje stav hodnoty, nazvěme ji \uv{pozice}. Při obdržení derivace ji přičte k aktuálnímu stavu. Každý snímek server odesílá na klienta stav pozice, kterou si klient aplikuje.
|
||||
Protokol měl za cíl snížit odezvu na nespolehlivé síti a neblokovat zprávy. Vytvořili jsme test, který simuluje program hry. V testu jsou dvě vlákna, jedno pro klienta a druhé pro server. Oba mají svou vlastní smyčku, ve které iterují. Klient odesílá hodnotu derivace typu double. Server si udržuje stav hodnoty, nazvěme ji \uv{pozice}. Při obdržení derivace ji přičte k aktuálnímu stavu. V každém snímku odesílá server klientovi stav pozice, kterou si klient aplikuje.
|
||||
|
||||
Pro nasimulování nespolehlivosti sítě jsme využili Linuxový nástroj \uv{tc}. Ten obsahuje \uv{network emulator} s různými nastaveními spolehlivosti sítě. Například kolik procent paketů má ztratit, jaká má být odezva nebo jak moc náhodné bude pořadí paketů. Budeme simulovat odezvu 200 milisekund s rozptylem 20ms, ztrátu 3\% paketů a 1\% paketů bude duplikovaných. Celý příkaz pro nastavení můžeme vidět v příkladu \ref{kod:tc}. Vidíme, že používáme pouze rozhraní \inlcpp{lo}, tedy loopback, které se týká posílání mezi programy na lokální adrese.
|
||||
|
||||
@@ -1103,7 +1103,7 @@ Pro nasimulování nespolehlivosti sítě jsme využili Linuxový nástroj \uv{t
|
||||
\label{fig:latencycomparison}
|
||||
\end{figure}
|
||||
|
||||
V prvním testu jsme pouze měřili odezvu mezi odesláním vstupu od klienta pro snímek $n$ a získáním jeho integrovaného stavu ze serveru. Na obrázku \ref{fig:latencycomparison} vidíme, že TCP obsahuje skoky a QUICr je stabilnější.
|
||||
V prvním testu jsme měřili pouze odezvu mezi odesláním vstupu od klienta pro snímek $n$ a získáním jeho integrovaného stavu ze serveru. Na obrázku \ref{fig:latencycomparison} vidíme, že TCP obsahuje skoky a QUICr je stabilnější.
|
||||
|
||||
\begin{figure}
|
||||
\begin{center}
|
||||
@@ -1131,8 +1131,8 @@ Nakonec jsme integrovali protokol přímo do replikátoru. Vytvořili jsme simul
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=1\textwidth]{graphics/300playerstrace.png}
|
||||
\label{fig:tracyreplicator}
|
||||
\caption{Analýza replikátoru}
|
||||
\label{fig:tracyreplicator}
|
||||
\end{figure}
|
||||
|
||||
Test s 301 hráči odhalil nedostatečný výkon serveru. Pomocí Tracy jsme začali program analyzovat. Na obrázku \ref{fig:tracyreplicator} vidíme, že pouze aktualizace replikátoru trvá každý snímek kolem 100ms. Pro představu, pokud bychom chtěli, aby server integroval 20 krát za sekundu, museli bychom všechno, nejen aktualizaci replikátoru, stihnout do 50 milisekund. Proto je aktuální stav nepřijatelně dlouhá doba. Úzké hrdlo najednou nebyla síť, ale příprava smysluplných dat, kterými síť zatížíme. Soustředili jsme další optimalizace právě na replikátor.
|
||||
@@ -1146,15 +1146,15 @@ Replikátor pro každého klienta zjistil jeho zájem a pro každou entitu v ně
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=1\textwidth]{graphics/ecs_optimization_tracy_01.png}
|
||||
\label{fig:ecs_optimization_tracy_01}
|
||||
\caption{Analýza optimalizace replikátoru pro ECS}
|
||||
\label{fig:ecs_optimization_tracy_01}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=1\textwidth]{graphics/ecs_optimization_tracy_02.png}
|
||||
\label{fig:ecs_optimization_tracy_02}
|
||||
\caption{Analýza optimalizace replikátoru pro ECS}
|
||||
\label{fig:ecs_optimization_tracy_02}
|
||||
\end{figure}
|
||||
|
||||
\begin{table}
|
||||
@@ -1171,7 +1171,7 @@ Replikátor pro každého klienta zjistil jeho zájem a pro každou entitu v ně
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
Replikátor jsme začali analyzovat do větších detailů. Na obrázku \ref{fig:ecs_optimization_tracy_02} vidíme, které jeho části zabrali jakou dobu. Zjistili jsme, že problém byl samotný ProtoBuf. Formát je podobně jako JSON dělaný pro složité struktury objektů. V našem případě je ale zpráva pro snapshot světa primitivní. Rozhodli jsme udělat porovnání s FlatBuffers. V testu jsme simulovali 100 snímků, tak, abychom si mohli dopředu alokovat všechnu potřebnou paměť, kterou můžeme mezi snímky využívat. Dopředu si vytvoříme vektor pozic, které v testu budeme serializovat. Tímto zajistíme, že nebudeme měřit čas včetně alokace paměti. Stejně jako v replikátoru používáme architekturu orientovanou na ECS a procházíme všechny pozice, ty pak umisťujeme do alokovaných bloků paměti pro každého klienta a simulujeme tak skládání rámců, které můžeme odesílat. Všimli jsme si, že v případě primitivní zprávy, jako je snapshot stavu světa, by nám stačila klasická funkce ze standartní C knihovny zvaná \inlcpp{memcpy} a do testu jsme ji zařadili. Výsledky vidíme v tabulce \ref{t:serialization_comparison}.
|
||||
Replikátor jsme začali analyzovat do větších detailů. Na obrázku \ref{fig:ecs_optimization_tracy_02} vidíme, jak dlouho trvali jeho jednotlivé části. Zjistili jsme, že problém byl samotný ProtoBuf. Formát je podobně jako JSON dělaný pro složité struktury objektů. V našem případě je ale zpráva pro snapshot světa primitivní. Rozhodli jsme udělat porovnání s FlatBuffers. V testu jsme simulovali 100 snímků tak, abychom si mohli dopředu alokovat všechnu potřebnou paměť, kterou můžeme mezi snímky využívat. Dopředu si vytvoříme vektor pozic, které v testu budeme serializovat. Tímto zajistíme, že nebudeme měřit čas včetně alokace paměti. Stejně jako v replikátoru používáme architekturu orientovanou na ECS a procházíme všechny pozice. Ty pak umisťujeme do alokovaných bloků paměti pro každého klienta a simulujeme tak skládání rámců, které můžeme odesílat. Všimli jsme si, že v případě primitivní zprávy, jako je snapshot stavu světa, by nám stačila klasická funkce ze standartní C knihovny zvaná \inlcpp{memcpy} a do testu jsme ji zařadili. Výsledky vidíme v tabulce \ref{t:serialization_comparison}.
|
||||
|
||||
Zjistili jsme, že problémem byl ProtoBuf a FlatBuffers by nám nepomohl. Nepřekvapilo nás, že memcpy byl nejrychlejší. Rozhodli jsme se, že pro serializaci a deserializaci snapshotů světa budeme používat vlastní serializaci.
|
||||
|
||||
@@ -1183,9 +1183,9 @@ Zpráva obsahuje hlavičku, ve které je počet aktualizovaných entit a počet
|
||||
|
||||
\subsection{Optimalizace manažera zájmů}
|
||||
|
||||
Všimli jsme si, že replikátor trval každou iteraci kolem 60 milisekund. Nejprve jsme se rozhodli přidat datové struktury. Druhým cílem bylo zlepšit API, kterým se replikátor dotazuje, jestli je entita pro klienta zajímavá.
|
||||
Všimli jsme si, že replikátor trval každou iteraci kolem 60 milisekund. Nejprve jsme se rozhodli přidat datové struktury. Druhým cílem bylo zlepšit API, kterou se replikátor dotazuje, zda je entita pro klienta zajímavá.
|
||||
|
||||
Jedna s datových struktur, která má nízkou složitost pro dotaz na seznam entit ve vzdálenosti maximálně, je klasická mřížka. Charakteristika hráčů ve hrách je, že se hodně pohybují. Mřížka nepotřebuje žádný přepočet, ale pouze vložit do předem alokovaného bufferu, nebo z něj naopak odebrat.
|
||||
Jednou z datových struktur, která má nízkou složitost pro dotaz na seznam entit ve maximální vzdálenosti, je klasická mřížka. Charakteristikou hráčů ve hrách je, že se zpravidla často pohybují. Mřížka nepotřebuje žádný přepočet, ale pouze vložit do předem alokovaného bufferu, nebo z něj naopak odebrat.
|
||||
|
||||
\begin{table}
|
||||
\begin{center}
|
||||
@@ -1207,25 +1207,25 @@ Jedna s datových struktur, která má nízkou složitost pro dotaz na seznam en
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
Testovali jsme 4 implementace, z toho 3 různé datové struktury a naivní přístup. Simulovali jsme pohyb 100 000 různých entit po dobu 100 snímků. Všechny možnosti představíme. Naivní přístup využívá pouze Pythagorovu větu, aby získal vzdálenost dvou bodů. Jediná optimalizace je vynechat odmocninu a umocnit místo toho vzdálenost. Druhá a třetí implementace využívá mřížku a entity rozděluje do svých polí. První je fixní mřížka, která je vhodná pro světy, které nemění svou velikost, jako např. World of Warcraft. Naopak hashovací mřížka přiřazuje entity do polí pomocí hashe jejich pozice. Jsou tak ideální pro dynamické a potenciálně nekonečné světy, jako např. Minecraft. Poslední je quad tree. Jedná se o obdobu stromu, kde vrcholy mají právě 4 potomky nebo žádného potomka. V tabulce \ref{table:range_query_comparison} vidíme výsledky testu. Celkový čas je milisekundách. Ve třetím a čvrtém sloupci je změřená část vkládání a dotazování. Díky tomu vidíme, že Quad tree má rychlejší dotazování, ale delší vkládání. Nejrychlejší je fixní mřížka. Má ale vysokou paměťovou náročnost a při použití je tak nutné zvážit, jestli není hashovací mřížka vhodnější. Quad tree implementace se ukázala jako nevhodná. S přibývající hloubkou je navíc pomalejší. Ideální se ukázali varianty s fixní a hashovací mřížkou.
|
||||
Testovali jsme 4 implementace, z toho 3 různé datové struktury a naivní přístup. Simulovali jsme pohyb 100 000 různých entit po dobu 100 snímků. Všechny možnosti představíme. Naivní přístup využívá pouze Pythagorovu větu, aby získal vzdálenost dvou bodů. Jediná optimalizace je vynechat odmocninu a umocnit místo toho vzdálenost. Druhá a třetí implementace využívá mřížku a entity rozděluje do svých polí. První je fixní mřížka, která je vhodná pro světy, které nemění svou velikost, jako např. World of Warcraft. Naopak hashovací mřížka přiřazuje entity do polí pomocí hashe jejich pozice. Jsou tak ideální pro dynamické a potenciálně nekonečné světy, jako např. Minecraft. Poslední je quad tree. Jedná se o obdobu stromu, kde vrcholy mají právě 4 potomky nebo žádného potomka. V tabulce \ref{table:range_query_comparison} vidíme výsledky testu. Celkový čas je milisekundách. Ve třetím a čvrtém sloupci je změřená část vkládání a dotazování. Díky tomu vidíme, že Quad tree má rychlejší dotazování, ale delší vkládání. Nejrychlejší je fixní mřížka. Má ale vysokou paměťovou náročnost a při použití je tak nutné zvážit, jestli není hashovací mřížka vhodnější. Quad tree implementace se ukázala jako nevhodná. S přibývající hloubkou je navíc pomalejší. Ideální se ukázaly varianty s fixní a hashovací mřížkou.
|
||||
|
||||
\subsection{Peer-to-peer}
|
||||
|
||||
Chtěli jsme změřit i hru s modelem peer-to-peer. Zároveň jsme chtěli dokázat, že náš protokol QUICr pomáhá i v tomto modelu. Vytvořili jsme proto program, který simuluje jednoho účastníka peer-to-peer systému, tzv. \uv{peer}. Na začátku program čeká, až se připojí a autentizují ostatní peer. Proto jsme definovali číslo hráčů, v základu rovno 2.
|
||||
|
||||
Hry peer-to-peer fungují podobně jako v modelu klient-server. Každý účastník si udržuje svůj svět a chová se jako server. Tedy pro něj je zdroj pravdy právě jeho stav. Následně je hra rozdělena na iterace. V každé iteraci všichni odešlou všem svou akci, kterou učinili. Jakmile peer v iteraci $i$ získá akce od všech ostatních pro iteraci $i$, tak si je lokálně aplikuje na svůj stav. Poté se posune do iterace $i+1$ a proces se opakuje. Tento způsob má nevýhodu v tom, že hra je stejně rychlá jako nejpomalejší peer. Pokud někomu bude dlouho trvat, než odešle svou akci, všichni ostatní jsou nuceni na něj čekat. Je totiž třeba žádnou akci nevynechat, jinak dojde k desynchronizaci.
|
||||
Hry peer-to-peer fungují podobně jako v modelu klient-server. Každý účastník si udržuje svůj svět a chová se jako server. Tedy pro něj je zdroj pravdy právě jeho stav. Následně je hra rozdělena na iterace. V každé iteraci všichni odešlou všem svou akci, kterou učinili. Jakmile peer v iteraci $i$ získá akce od všech ostatních pro iteraci $i$, lokálně si je aplikuje na svůj stav. Poté se posune do iterace $i+1$ a proces se opakuje. Tento způsob má nevýhodu v tom, že hra je stejně rychlá jako nejpomalejší peer. Pokud někomu bude dlouho trvat, než odešle svou akci, všichni ostatní jsou nuceni na něj čekat. Je nutné nevynechat žádnou akci, protože jinak dojde k desynchronizaci.
|
||||
|
||||
Optimalizaci, kterou jsme implementovali, byl rollback. Ten umožňil, aby nebylo nutné čekat na všechny akce. Pokud peer $A$ v iteraci $i$ chybí akce pro peer $B$, tak peer $A$ zkusí udělat pro peer $B$ predikci. Pokud eventuélně akce od $B$ do $A$ pro iteraci $i$ dorazí a liší se od predikce, tak se peer $A$ vrátí v historii, akci změní a aplikuje znova.
|
||||
Optimalizací, kterou jsme implementovali, byl rollback. Ten umožňil, aby nebylo nutné čekat na všechny akce. Pokud peer $A$ v iteraci $i$ chybí akce pro peer $B$, tak peer $A$ zkusí udělat pro peer $B$ predikci. Pokud nakonec akce od $B$ do $A$ pro iteraci $i$ dorazí a liší se od predikce, tak se peer $A$ vrátí v historii, akci změní a aplikuje znova.
|
||||
|
||||
Zde je nutné, aby peer posílal ne pouze poslední akci, ale celou historii svých akcí. Představme si, že akce pro iteraci $i$ chybí, takže peer iteraci predikuje a přejde do $i+1$. Akce pro iteraci $i$ se ztratila a už nedorazí. Jakmile ale dorazí akce pro $i+1$, tak obsahuje i akci pro $i$. Peer si tak může historii vrátit, nasimulovat správně a tím se synchronizovat. Díky tomu je synchronizace daleko plynulejší.
|
||||
Zde je nutné, aby peer neposílal pouze poslední akci, ale celou historii svých akcí. Představme si, že akce pro iteraci $i$ chybí, takže peer iteraci predikuje a přejde do $i+1$. Akce pro iteraci $i$ se ztratila a už nedorazí. Jakmile ale dorazí akce pro $i+1$, tak obsahuje i akci pro $i$. Peer si tak může historii vrátit, nasimulovat správně a tím se synchronizovat. Díky tomu je synchronizace daleko plynulejší.
|
||||
|
||||
Účastníci nemusejí vždy posílat celou svou historii, ale pouze tu část, kterou druhá strana ještě nemá. To lze zjistit tak, že příjemce posílá zprávu o tom, které iterace už obdržel. Tento přístup sice stále iteruje rychlostí nejpomalejšího účastníka, ale alespoň řeší lehké záseky při ztrátě paketu. Používá se často v bojových hrách, kde je odezva hráčových vstupů kritická.
|
||||
|
||||
V testu jsou tedy dva hráči, každý má svou postavu s pozicí ve 3D prostoru. Výše zmíněným způsobem provedou $X$ iterací. Postupně v čase jsme pozice obou postav exportovali do CSV pro oba hráče. Udělali jsme dvě měření, jedno pro TCP a druhé pro QUICr. Na obrázku \ref{fig:peer_to_peer} vidíme naměřené hodnoty pozice postavy hráče 1 jak ji viděl hráč 2. V případě TCP hráč silně skákal, právě kvůli ahead-of-line blokování. To snižuje hratelnost. Protokol QUICr je plynulejší a tedy i hratelnější.
|
||||
V testu jsou tedy dva hráči, každý má svou postavu s pozicí ve 3D prostoru. Výše zmíněným způsobem provedou $X$ iterací. Postupně v čase jsme pozice obou postav exportovali do CSV pro oba hráče. Udělali jsme dvě měření, jedno pro TCP a druhé pro QUICr. Na obrázku \ref{fig:peer_to_peer} vidíme naměřené hodnoty pozice postavy hráče 1 jak ji viděl hráč 2. V případě TCP hráč silně skákal, právě kvůli ahead-of-line blokování. To znepříjemňuje hratelnost. Protokol QUICr je plynulejší a tedy i hratelnější.
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\includegraphics[width=0.5\textwidth]{graphics/peer_to_peer.pdf}
|
||||
\includegraphics[width=1\textwidth]{graphics/peer_to_peer.pdf}
|
||||
\caption{Porovnání TCP a QUICr v peer-to-peer}
|
||||
\label{fig:peer_to_peer}
|
||||
\end{figure}
|
||||
@@ -1250,34 +1250,29 @@ V testu jsou tedy dva hráči, každý má svou postavu s pozicí ve 3D prostoru
|
||||
\begin{kiconclusions}
|
||||
V práci jsme představili problematiku distribuovaných systémů a soustředili jsme se na konkrétní využití pro hry více hráčů. Popsali jsme různé modely a architektury nejen systému, ale i programů v něm. Jednotlivé atributy, které systémy nebo komunikace mohou mít, jsme identifikovali a přenesli na náš konkrétní případ.
|
||||
|
||||
Vytvořili jsme si vlastní herní engine, který umožňuje vytvářet hry pro více hráčů. Programy jsme skládali jako modulární monolity a popisovali jejich architekturu a implementaci. Ukázali jsme, že náš přístup umožnil snadno herní engine rozšiřovat. Pro distribuovaný systém jsme vytvořili hned dva protokoly: jeden v transportní a druhý v aplikační vrstvě. První protokol zrychluje tempo, jakým se zprávy dostanou ke zpracování, oproti TCP. Jinými slovy eliminuje ahead-of-line blokování. Implementovali jsme různé vlastnosti jako handshake a spolehlivost. Druhý protokol umožňuje zprávy rozdělovat podle typu. Na koncových bodech je tak možné pro typ definovat funkci, která každou příchozí zprávu tohoto typu zpracuje. Pro snadnější práci se zprávami jsme přidali serializaci objektů. To znamená, že ve vrstvě, kde implementujeme herní logiku, nepracujeme se zprávami jako n-ticemi bytů, ale objekty.
|
||||
Vytvořili jsme vlastní herní engine, který umožňuje vytvářet hry pro více hráčů. Programy jsme skládali jako modulární monolity a popisovali jejich architekturu a implementaci. Ukázali jsme, že náš přístup umožnil snadno herní engine rozšiřovat. Pro distribuovaný systém jsme vytvořili hned dva protokoly: jeden v transportní a druhý v aplikační vrstvě. První protokol zrychluje tempo, jakým se zprávy dostanou ke zpracování, oproti TCP. Jinými slovy eliminuje ahead-of-line blokování. Implementovali jsme různé vlastnosti jako handshake a spolehlivost. Druhý protokol umožňuje zprávy rozdělovat podle typu. Na koncových bodech je tak možné pro typ definovat funkci, která každou příchozí zprávu tohoto typu zpracuje. Pro snadnější práci se zprávami jsme přidali serializaci objektů. To znamená, že ve vrstvě, kde implementujeme herní logiku, nepracujeme se zprávami jako n-ticemi bytů, ale objekty.
|
||||
|
||||
Na konci jsme definovali, jaké metriky chceme měřit a jaké chceme optimalizovat. Představili jsme nástroje a možnosti měření, které jsme využili. Naše metriky se netýkali jen komunikace v síti, ale i výkonu jednolivých programů. Například jsme naměřili, že v případě stovek hráčů začala být problém serializace a obecně sestavení smysluplných zpráv, nikoliv samotné odesílání. Implementovali a popsali jsme naše řešení. Zároveň jsme dokázali, že implementované optimalizace jsou vhodné nejen pro model klient-server ale i peer-to-peer.
|
||||
Nakonec jsme definovali, jaké metriky chceme měřit a jaké chceme optimalizovat. Představili jsme nástroje a možnosti měření, které jsme využili. Naše metriky se netýkaly jen komunikace v síti, ale i výkonu jednolivých programů. Například jsme naměřili, že v případě stovek hráčů začala být problém serializace a obecně sestavení smysluplných zpráv, nikoliv samotné odesílání. Implementovali a popsali jsme naše řešení. Zároveň jsme dokázali, že implementované optimalizace jsou vhodné nejen pro model klient-server ale i peer-to-peer.
|
||||
|
||||
Do budoucna by bylo dobré dokončit další vlastnosti QUICr protokolu, jako například sekvenční číslo zprávy. Bylo by nutné se zamyslet, do jaké hloubky by pořadí zpráv měl řešit QUICr. Aplikace sama nejlépe ví, jestli musí čekat na předchozí zprávu nebo ne, jako v našem případě snapshoty ze serveru. Možnost je definovat předchůdce pro kompletní odstranění ahead-of-line blokování. Protokol také neimplementuje dynamické změny datového toku. Ten je nutné snížit, když je síť zatížená.\cite{quic}
|
||||
Do budoucna by bylo dobré dokončit další vlastnosti QUICr protokolu, jako například sekvenční číslo zprávy. Bylo by nutné se zamyslet, do jaké hloubky by pořadí zpráv měl řešit QUICr. Aplikace sama nejlépe ví, jestli musí čekat na předchozí zprávu nebo ne, jako v našem případě se snapshoty ze serveru. Možnost je definovat předchůdce pro kompletní odstranění ahead-of-line blokování. Protokol také neimplementuje dynamické změny datového toku. Ten je nutné snížit, jakmile je síť zatížená.
|
||||
\end{kiconclusions}
|
||||
|
||||
\begin{kiconclusions}[english]
|
||||
Thesis conclusions in \uv{English}.
|
||||
In this thesis, we introduced the topic of distributed systems and focused on their specific use for multiplayer games. We described various models and architectures, not only of the system but also of the programs within it. We identified the individual attributes that systems or communication can have and applied them to our specific case.
|
||||
|
||||
We built our own game engine that enables the creation of multiplayer games. We structured the programs as modular monoliths and described their architecture and implementation. We showed that our approach made it easy to extend the game engine. For the distributed system, we created two protocols: one at the transport layer and one at the application layer. The first protocol speeds up the rate at which messages reach processing compared to TCP. It eliminates ahead-of-line blocking. We implemented various features such as handshaking and reliability. The second protocol allows messages to be split by type. At the endpoints, it is thus possible to define a handler function for each type that processes every incoming message of that type. To make working with messages easier, we added object serialization, meaning that at the layer where we implement game logic, we work with objects rather than tuples of bytes.
|
||||
|
||||
Finally, we defined which metrics we wanted to measure and optimize. We presented the tools and measurement options we used. Our metrics concerned not only network communication but also the performance of the individual programs. For example, we measured that with hundreds of players, serialization and, more generally, assembling meaningful messages became a bottleneck, rather than the sending itself. We implemented and described our solution. We also demonstrated that the implemented optimizations are suitable not only for the client-server model but also for peer-to-peer.
|
||||
|
||||
For future work, it would be good to complete additional features of the QUICr protocol, such as message sequence numbers. It would be necessary to consider how deeply QUICr itself should handle message ordering — the application itself best knows whether it needs to wait for a previous message or not, as in our case with server snapshots. One option is to define a predecessor to fully eliminate ahead-of-line blocking. The protocol also does not implement dynamic flow rate adjustment, which would be necessary to reduce throughput when the network is congested.
|
||||
\end{kiconclusions}
|
||||
|
||||
%% Přílohy obsahu textu práce, za makrem \appendix.
|
||||
\appendix
|
||||
|
||||
\section{První příloha}
|
||||
Text první přílohy
|
||||
|
||||
\section{Druhá příloha}
|
||||
Text druhé přílohy
|
||||
|
||||
%% Obsah elektronických dat. Poslední příloha. Upravte podle vlastní
|
||||
%% práce!
|
||||
\section{Obsah elektronických dat} \label{sec:ObsahData}
|
||||
|
||||
Na samotném konci textu práce je uveden stručný popis obsahu
|
||||
elektronických dat odevzdaných v systému katedry informatiky spolu s
|
||||
textem. Tato data jsou nedílnou součástí práce a tvoří (datovou)
|
||||
přílohu textu práce. Povinné položky struktury dat jsou:
|
||||
Práce se skládá z textové a praktické části. Přílohy jsme rozdělili do třech adresářů, které popíšeme.
|
||||
|
||||
\begin{description}
|
||||
|
||||
@@ -1285,56 +1280,46 @@ přílohu textu práce. Povinné položky struktury dat jsou:
|
||||
Adresář s textem práce ve formátu PDF, vytvořený s~použitím
|
||||
závazného stylu KI PřF UP v~Olomouci pro závěrečné práce, včetně
|
||||
všech (textových) příloh, a~všechny soubory potřebné pro
|
||||
bezproblémové vytvoření PDF dokumentu textu (případně v~ZIP
|
||||
archivu), tj.~zdrojový text textu a příloh, vložené obrázky, apod.
|
||||
bezproblémové vytvoření PDF dokumentu textu.
|
||||
|
||||
\item[\texttt{README.*}] \hfill \\
|
||||
Textový soubor (s příponou např. \texttt{.txt}) s informacemi o
|
||||
opakovatelném způsobu použití ostatních dat práce -- typicky plně
|
||||
reprodukovatelný co nejúplnější funkční postup zprovoznění software
|
||||
vytvořeného v~rámci práce, tzn. jeho případné instalace/nasazení a
|
||||
spuštění, včetně uvedení všech požadavků pro bezproblémový provoz;
|
||||
za zprovoznění software se nepovažuje zpřístupnění (např. po
|
||||
Internetu) již někde zprovozněného software.
|
||||
\item[\texttt{README.md}] \hfill \\
|
||||
Textový soubor s informacemi o sestavení a spuštění programů vytvořených v rámci této práce.
|
||||
|
||||
\item[\texttt{*}] \hfill \\
|
||||
Adresáře a soubory s veškerými ostatními autorskými daty práce
|
||||
(případně v~ZIP archivu) -- typicky spustitelné a další soubory
|
||||
software vytvořeného v rámci práce potřebné pro bezproblémový provoz
|
||||
software, případně jeho instalační program, a kompletní zdrojové
|
||||
texty software a další data nutná pro plně reprodukovatelné korektní
|
||||
vytvoření spustitelných souborů.
|
||||
\item[\texttt{build/}] \hfill \\
|
||||
Adresář obsahuje už sestavené programy připravené pro použití. Obsahují tři spustitelné soubory pro server, klienta a testovacího klienta.
|
||||
|
||||
\item[\texttt{src/}] \hfill \\
|
||||
Adresář se všemi zdrojovými kódy, včetně externích knihoven, které programy využívají. Instrukce pro sestavení jsou v README.md v kořenovém adresáři.
|
||||
|
||||
\end{description}
|
||||
|
||||
Dále mohou data obsahovat například:
|
||||
|
||||
\begin{itemize}
|
||||
% \begin{itemize}
|
||||
|
||||
%\item[\texttt{data/}] \hfill \\
|
||||
\item
|
||||
ukázková a~testovací data použitá v~práci nebo pro potřeby posouzení
|
||||
práce v rámci její obhajoby,
|
||||
% \item
|
||||
% ukázková a~testovací data použitá v~práci nebo pro potřeby posouzení
|
||||
% práce v rámci její obhajoby,
|
||||
|
||||
%\item[\texttt{literature/}] \hfill \\
|
||||
\item
|
||||
položky bibliografie v elektronické podobě, příp.~jiná relevantní literatura
|
||||
a dokumentace vztahující se k~práci,
|
||||
% %\item[\texttt{literature/}] \hfill \\
|
||||
% \item
|
||||
% položky bibliografie v elektronické podobě, příp.~jiná relevantní literatura
|
||||
% a dokumentace vztahující se k~práci,
|
||||
|
||||
%\item[\texttt{install/}] \hfill \\
|
||||
\item
|
||||
cizí data (software) potřebná pro bezproblémové použití autorských
|
||||
dat práce (software), která nejsou standardní součástí
|
||||
předpokládaného (softwarového) vybavení uživatele.
|
||||
% %\item[\texttt{install/}] \hfill \\
|
||||
% \item
|
||||
% cizí data (software) potřebná pro bezproblémové použití autorských
|
||||
% dat práce (software), která nejsou standardní součástí
|
||||
% předpokládaného (softwarového) vybavení uživatele.
|
||||
|
||||
\end{itemize}
|
||||
% \end{itemize}
|
||||
|
||||
U~veškerých cizích obsažených materiálů jejich
|
||||
zahrnutí dovolují podmínky pro jejich veřejné šíření nebo přiložený souhlas
|
||||
držitele práv k užití. Pro všechny použité (a~citované) materiály,
|
||||
u~kterých toto není splněno a~nejsou tak obsaženy, je uveden
|
||||
jejich zdroj, např.~webová adresa, v~bibliografii nebo textu práce
|
||||
nebo souboru \texttt{README.*}.
|
||||
% U~veškerých cizích obsažených materiálů jejich
|
||||
% zahrnutí dovolují podmínky pro jejich veřejné šíření nebo přiložený souhlas
|
||||
% držitele práv k užití. Pro všechny použité (a~citované) materiály,
|
||||
% u~kterých toto není splněno a~nejsou tak obsaženy, je uveden
|
||||
% jejich zdroj, např.~webová adresa, v~bibliografii nebo textu práce
|
||||
% nebo souboru \texttt{README.*}.
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user