Sprawozdanie: final

This commit is contained in:
Kacper Donat 2018-04-17 21:39:50 +02:00
parent f7328d3102
commit 1e03a2929c

View File

@ -95,7 +95,7 @@ tylko i wyłącznie swój czołg.
\subsection{Przechowywany stan} \subsection{Przechowywany stan}
Aktualny stan rozgrywki $S$ jest determinowany przez aktualne obiekty na mapie. Każda aktualizacja ze strony klienta, Aktualny stan rozgrywki $S$ jest determinowany przez aktualne obiekty na mapie. Każda aktualizacja ze strony klienta,
wpływająca na stan $S$ powoduje zwiększenie licznika stanu o 1. Ze względu na trudność synchronizacji kolejnosci wpływająca na stan $S$ powoduje zwiększenie licznika stanu o 1. Ze względu na trudność synchronizacji kolejności
aktualizacji (np. stan 2 klienta A może być stanem 3 dla klienta B i odwrotnie) zdecydowano się, że każdy klient aktualizacji (np. stan 2 klienta A może być stanem 3 dla klienta B i odwrotnie) zdecydowano się, że każdy klient
przechowuje ilość odebranych aktualizacji stanu od każdego klienta osobno. przechowuje ilość odebranych aktualizacji stanu od każdego klienta osobno.
@ -114,12 +114,12 @@ Z założenia, rozgrywka ma odbywać się w czasie rzeczywistym zatem wykorzysta
wprowadzać niepotrzebne opóźnienia, wynikające z niepotrzebnych retransmisji pakietów. wprowadzać niepotrzebne opóźnienia, wynikające z niepotrzebnych retransmisji pakietów.
Komunikacja będzie odbywała się zatem za pomocą datagramów UDP, które pozwalają na szybszą - acz mniej dokładną Komunikacja będzie odbywała się zatem za pomocą datagramów UDP, które pozwalają na szybszą - acz mniej dokładną
transmisję informacji pomiędzy klientami. W wypadku normlanej gry jednak strata części pakietów jest jak transmisję informacji pomiędzy klientami. W wypadku normalnej gry jednak strata części pakietów jest jak
najbardziej akceptowalna i nie wpłynie na rozgrywkę w znaczący sposób. najbardziej akceptowalna i nie wpłynie na rozgrywkę w znaczący sposób.
W projekcie przyjęto architekturę klient-klient, tj. nie występuje żaden serwer stanowiący źródło prawdy dla rozgrywki. W projekcie przyjęto architekturę klient-klient, tj. nie występuje żaden serwer stanowiący źródło prawdy dla rozgrywki.
Zamiast tego jeden z klientów zawsze będzie miał status \textit{mastera}. Poprawnym stanem rozgrywki z definicji jest Zamiast tego jeden z klientów zawsze będzie miał status \textit{mastera}. Poprawnym stanem rozgrywki z definicji jest
stan symulacji \textit{mastera}. \textit{masterem} zawsze jest klient o najmniejszym \texttt{ClientId}, tj. klient, stan symulacji \textit{mastera}. \textit{Masterem} zawsze jest klient o najmniejszym \texttt{ClientId}, tj. klient,
który podłączył się najwcześniej. Takie podejście zapewnia rozwiązanie sytuacji, w której aktualny \textit{master} się który podłączył się najwcześniej. Takie podejście zapewnia rozwiązanie sytuacji, w której aktualny \textit{master} się
rozłączy bez konieczności przeprowadzania negocjacji. rozłączy bez konieczności przeprowadzania negocjacji.
@ -142,52 +142,96 @@ rozłączy bez konieczności przeprowadzania negocjacji.
\caption{Proponowana architektura sieciowa} \caption{Proponowana architektura sieciowa}
\end{figure} \end{figure}
Dodatkowo, przyjmuje się zasadę, że każdy klient jest odpowiedzialny za swoją część symulacji, dla której Dodatkowo, przyjmuje się zasadę, że każdy klient jest odpowiedzialny za swoją część symulacji, dla której stanowi źródło
stanowi źródło prawdy. W praktyce oznacza to, że każdy gracz wysyła tylko i wyłącznie informacje o zmianach rzeczy, na które on ma wpływ - np. prawdy. W praktyce oznacza to, że każdy gracz wysyła tylko i wyłącznie informacje o zmianach rzeczy, na które on ma
pozycję własnego czołgu, czy wystrzelenie pocisku. W gestii innych klientów jest przetworzyć te informację i wpływ - np. pozycję własnego czołgu, czy wystrzelenie pocisku. W gestii innych klientów jest przetworzyć te informacje
zsynchronizować się. Przewidziano również obiekty o zachowaniu deterministycznym, które każdy klient może aktualizować i zsynchronizować się. Przewidziano również obiekty o zachowaniu deterministycznym, które każdy klient może aktualizować
samodzielnie w ramach swojej symulacji. samodzielnie w ramach swojej symulacji.
Takie podejście zapewnia brak konfliktu o zasoby, ponieważ każdy obiekt jest modyfikowany wyłącznie przez jedną osobę, Takie podejście zapewnia brak konfliktu o zasoby, ponieważ każdy obiekt jest modyfikowany wyłącznie przez jedną osobę,
zatem nigdy nie nastąpi konflikt. Nie występuje również problem odczytywania zmienionych danych ponieważ wyświetlanie zatem nigdy nie nastąpi konflikt. Nie występuje również problem odczytywania zmienionych danych ponieważ wyświetlanie
oraz aktualizacja stanu gry odbywają się w ramach jednego wątku. oraz aktualizacja stanu gry odbywają się w ramach jednego wątku.
Ze względu na możliwość zgubienia niektórych pakietów, oraz chęć unikniecia skokowej aktualizacji rozgrywki po stronie Ze względu na możliwość zgubienia niektórych pakietów, oraz chęć uniknięcia skokowej aktualizacji rozgrywki po stronie
każdego klienta następuje predykcja zachowań innych graczy. W wypadku otrzymania pakietu aktualizujacego stan gracza, każdego klienta następuje predykcja zachowań innych graczy. W wypadku otrzymania pakietu aktualizującego stan gracza,
predykcja jest odrzucana i wykorzystane są otrzymane dane. predykcja jest odrzucana i wykorzystane są otrzymane dane.
Ze względu na niedoskonałość mechanizmu predykcji, predykcje nie mogą modyfikować stanu gry w sposób nieodwracalny, tj Ze względu na niedoskonałość mechanizmu predykcji, predykcje nie mogą modyfikować stanu gry w sposób nieodwracalny, tj
np. predykcja nie może usunąć obiektu z mapy - moze go natomiast oznaczyć jako prawdopodobnie usunięty, co za tym idzie np. predykcja nie może usunąć obiektu z mapy - może go natomiast oznaczyć jako prawdopodobnie usunięty, co za tym idzie
nie będzie on wyświetlany ani predykowany dalej (z punktu widzenia gracza zostanie usunięty) - w takiej sytuacji nie będzie on wyświetlany ani predykowany dalej (z punktu widzenia gracza zostanie usunięty) - w takiej sytuacji
przywrócenie obiektu w wypadku błędnej predykcji wciąż będzie możliwe. przywrócenie obiektu w wypadku błędnej predykcji wciąż będzie możliwe.
Każdy klient będzie obsługiwany przez 4 osobnych wątki, których kompetencje nie kolidują ze sobą: Każdy klient będzie obsługiwany przez 4 osobne wątki, których kompetencje nie kolidują ze sobą:
\begin{enumerate} \begin{enumerate}
\item Aktualizacja, predykcja oraz renderowanie stanu symulacji \label{thread:render} \item Aktualizacja, predykcja oraz renderowanie stanu symulacji \label{thread:render}
\item Input z sieci (obiór pakietów, kolejkowanie) \label{input:net} \item Input z sieci (obiór pakietów, kolejkowanie) \label{input:net}
\item Input użytkownika (odczyt klawiatury itd.) \label{input:user} \item Input użytkownika (odczyt klawiatury itd.) \label{input:user}
\item Output sieciowy (wysyłanie pakietów, retranmisje) \label{output:net} \item Output sieciowy (wysyłanie pakietów, retransmisje) \label{output:net}
\end{enumerate} \end{enumerate}
\begin{figure}[H]
\centering
\begin{tikzpicture}[node distance=1cm]
\node[label=below:{Wątek \ref{thread:render}}] (thread-render) {
\begin{tikzpicture}[every node/.style={align=center}, node distance=1cm]
\node[draw] (predict) {Przewidywania};
\node[draw, below=of predict] (update) {Aktualizacja};
\node[draw, below=of update] (render) {Wyświetlanie};
\draw[->] (predict.west) edge [bend right] (update.west)
(update.west) edge [bend right] (render.west)
(render.east) edge [bend right] (predict.east);
\end{tikzpicture}
};
\node[label=below:{Wątek \ref{input:net} i \ref{input:user}}, right=of thread-render] (thread-input) {
\begin{tikzpicture}[every node/.style={align=center}, node distance=1cm]
\node[draw] (udp) {Nasłuchiwanie UDP};
\draw[->] (udp) edge [loop above] (udp);
\node[draw, below=of udp] (user) {Nasłuchiwanie HID};
\draw[->] (user) edge [loop above] (user);
\end{tikzpicture}
};
\node[label=below:{Wątek \ref{output:net}}, left=of thread-render] (thread-output) {
\begin{tikzpicture}[every node/.style={align=center}, node distance=1cm]
\node[draw] (listen) {Wysyłka pakietów};
\draw[->] (listen) edge [loop above] (listen);
\end{tikzpicture}
};
\node[below=of thread-render] (queue-actions) {Kolejka akcji};
\node[above=of thread-render] (queue-output) {Kolejka wysyłki};
\draw[<->, double] (thread-render) edge (queue-actions)
(thread-input) edge (queue-actions);
\draw[<->, double]
(thread-output) edge (queue-output)
(thread-input) edge (queue-output);
\end{tikzpicture}
\end{figure}
Wątki \ref{input:net} oraz \ref{input:user} będą umieszczały akcje do wykonania w jednej kolejce akcji, zatem kolejka ta Wątki \ref{input:net} oraz \ref{input:user} będą umieszczały akcje do wykonania w jednej kolejce akcji, zatem kolejka ta
musi być \textit{thread-safe} i udostępniać atomowe metody \texttt{enqueue} i \texttt{dequeue}, gwarantujące że stan musi być \textit{thread-safe} i udostępniać atomowe metody \texttt{enqueue} i \texttt{dequeue}, gwarantujące że stan
kolejki nie zostanie uszkodzony, tj. żadna akcja nie zostanie wykonana 2-krotnie ani żadna nie zostanie zgubiona. kolejki nie zostanie uszkodzony, tj. żadna akcja nie zostanie wykonana 2-krotnie ani żadna nie zostanie zgubiona.
Zostanie to osiągnięte poprzez ustawienie Mutexa\footnote{https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx} Zostanie to osiągnięte poprzez ustawienie Mutexa\footnote{https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx}
w metodach. w metodach.
Zakładamy, że akcje użytkownika muszą zostać zostać umieszczone na kolejce akcji natychmiastowo, zatem nie Zakładamy, że akcje użytkownika muszą zostać umieszczone na kolejce akcji natychmiastowo, zatem nie ma potrzeby
ma potrzeby synchronizacji z siecią. Co za tym idzie, dostęp do kolejki pakietów (potrzebnej ze względu na możliwość synchronizacji z siecią. Co za tym idzie, dostęp do kolejki pakietów (potrzebnej ze względu na możliwość otrzymania
otrzymania pakietów w nieodpowiedniej kolejności) i jej obsługa będzie w całości po stronie wątku \ref{input:net} - pakietów w nieodpowiedniej kolejności) i jej obsługa będzie w całości po stronie wątku \ref{input:net} - czyli problem
czyli problem wyścigów nie następuje. wyścigów nie następuje.
Poprzez identyczną kolejkę będzie obsługiwana wysyłka pakietów, stworzona w celu skomunikowania wątków \ref{input:user} Poprzez identyczną kolejkę będzie obsługiwana wysyłka pakietów, stworzona w celu skomunikowania wątków \ref{input:user}
oraz \ref{output:net}. Dodatkowo w wątku \ref{output:net} będzie działać samodzielna kolejka odpowiadająca za oraz \ref{output:net}. Dodatkowo w wątku \ref{output:net} będzie działać samodzielna kolejka odpowiadająca za
retransmisję pakietów. retransmisję pakietów.
Niektóre pakiety zmieniają stan rozgrywki (np. śmierć, wystrzelenie pocisku), a co za tym idzie muszą zostać Niektóre pakiety zmieniają stan rozgrywki (np. śmierć, wystrzelenie pocisku), a co za tym idzie muszą zostać
przetworzone przez wszystkich klientów w zbliżonej kolejnosci i w miarę jednakowym czasie (nie ma możliwości przetworzone przez wszystkich klientów w zbliżonej kolejności i w miarę jednakowym czasie (nie ma możliwości
zagwarntowania idealnej synchronizacji). W tym celu wprowadzony zostanie system numerowania kolejnych pakietów a każdy zagwarantowania idealnej synchronizacji). W tym celu wprowadzony zostanie system numerowania kolejnych pakietów, a każdy
pakiet, będzie zawierał informację na temat stanu od którego "zależy" - tj. numeru ostatniej aktualizacji. pakiet będzie zawierał informację na temat stanu od którego ,,zależy'' - tj. numeru ostatniej aktualizacji.
\subsection{Protokół} \subsection{Protokół}
Protokół opiera się o wysyłanie datagramów UDP między wszystkimi klientami. Ze względu na specyfikę UDP (brak gwarancji Protokół opiera się o wysyłanie datagramów UDP między wszystkimi klientami. Ze względu na specyfikę UDP (brak gwarancji
@ -236,9 +280,11 @@ ustawiony na 1 nie zachodzi potrzeba retransmisji - czyli są to pakiety mało i
Przykładem takiego pakietu może być aktualizacja pozycji gracza - nie ma potrzeby retransmisji ponieważ prawdopodobnie i Przykładem takiego pakietu może być aktualizacja pozycji gracza - nie ma potrzeby retransmisji ponieważ prawdopodobnie i
tak w drodze jest następny pakiet z nowszą pozycją. tak w drodze jest następny pakiet z nowszą pozycją.
W celu zapewnienia poprawności transmisji, każdy pakiet poprzedzany jest 32-bitową sumą kontrolną \texttt{CRC32}, W celu zapewnienia poprawności transmisji, każdy pakiet poprzedzany jest 16-bitową sumą kontrolną \texttt{CRC16},
pozwalającą upewnić się, że dane zostały odebrane poprawnie. Suma kontrolna liczona jest ze wszystkich składowych pozwalającą upewnić się, że dane zostały odebrane poprawnie. Suma kontrolna liczona jest ze wszystkich składowych
pakietu poczynając od \texttt{Type} włącznie. pakietu poczynając od \texttt{Type} włącznie. Dodatkowo, jeżeli pakiet potrzebuje dodatkowych danych prócz preambuły to
dane te zawierają własną sumę kontrolną \texttt{CRC16}. Rozgraniczenie to wprowadzono, aby uniknąć sytuacji, w której w
preambule zostałoby uszkodzone pole \texttt{Size} uniemożliwiając pobranie całego pakietu poprawnie.
W wypadku niezgodności przesłanej sumy kontrolnej z sumą wyliczoną po stronie odbierającego, odbierający wysyła pakiet W wypadku niezgodności przesłanej sumy kontrolnej z sumą wyliczoną po stronie odbierającego, odbierający wysyła pakiet
\texttt{RET = 0x81}. Dla pakietów mało ważnych nie zachodzi potrzeba retransmisji. \texttt{RET = 0x81}. Dla pakietów mało ważnych nie zachodzi potrzeba retransmisji.
@ -247,7 +293,7 @@ Aby upewnić się, że transmisja ważnego pakietu się powiodła, każdy klient
(\texttt{Type = 0x80}). W wypadku, gdyby odpowiedź \texttt{ACK} nie dotarła w ciągu $t\ \si{ms}$ pakiet zostaje (\texttt{Type = 0x80}). W wypadku, gdyby odpowiedź \texttt{ACK} nie dotarła w ciągu $t\ \si{ms}$ pakiet zostaje
wysłany ponownie. wysłany ponownie.
W przypadku wielokrotnego otrzymania pakietu o tym samym \texttt{PacketId} (np. w wypadku automatycznej retramsnisji), W przypadku wielokrotnego otrzymania pakietu o tym samym \texttt{PacketId} (np. w wypadku automatycznej retransmisji),
każdorazowo należy potwierdzić jego otrzymanie odpowiedzią \texttt{ACK} jednak akcja związana z tym pakietem powinna każdorazowo należy potwierdzić jego otrzymanie odpowiedzią \texttt{ACK} jednak akcja związana z tym pakietem powinna
zostać wykonana tylko raz. zostać wykonana tylko raz.
@ -286,10 +332,13 @@ Stąd przyjęto, że do realizacji lobby zostanie wykorzystany protokół TCP. Z
pośrednika do zestawiania połączeń, dokładny opis działania zdecydowano się pominąć. pośrednika do zestawiania połączeń, dokładny opis działania zdecydowano się pominąć.
Ponieważ protokół UDP nie zapewnia obsługi połączeń, każdy klient musi wysłać do wszystkich innych pakiet Ponieważ protokół UDP nie zapewnia obsługi połączeń, każdy klient musi wysłać do wszystkich innych pakiet
\texttt{CONNECT = 0x01}, którego odebranie zgodnie z opisem powyżej (najstarszy bit = 0) musi zostać potwierdzonye. \texttt{CONNECT = 0x01}, którego odebranie zgodnie z opisem powyżej (najstarszy bit = 0) musi zostać potwierdzone.
Po udanym nawiazaniu połączenia ze wszystkimi klientami, zostaje utworzony czołg gracza, a informacja o tym zostaje Po udanym nawiązaniu połączenia ze wszystkimi klientami, zostaje utworzony czołg gracza, a informacja o tym zostaje
przesłana do pozostałych graczy. przesłana do pozostałych graczy.
Dokładny opis pakietów służących do aktualizacji stanu rozgrywki (np. aktualizacja pozycji gracza) pomijamy, ponieważ
nie ma związku z problemem synchronizacji klientów.
\section{Potencjalne zmiany} \section{Potencjalne zmiany}
Założono, że pakiety będą dochodziły \textit{wystarczająco szybko} aby desynchronizacja związana z opóźnieniem w ruchu Założono, że pakiety będą dochodziły \textit{wystarczająco szybko} aby desynchronizacja związana z opóźnieniem w ruchu
sieciowym nie stanowiła problemu. Jednak należy przeprowadzić testy, czy faktycznie jest to dobre założenie. W wypadku, sieciowym nie stanowiła problemu. Jednak należy przeprowadzić testy, czy faktycznie jest to dobre założenie. W wypadku,
@ -299,7 +348,7 @@ opóźnienia odebrania pakietu względem przesłania - np. poprzez wprowadzenie
Synchronizację zegara można zapewnić w sposób analogiczny do działania protokołu Synchronizację zegara można zapewnić w sposób analogiczny do działania protokołu
SNTP\footnote{https://en.wikipedia.org/wiki/Network\_Time\_Protocol}, przy czym synchronizacja następowałaby do zegara SNTP\footnote{https://en.wikipedia.org/wiki/Network\_Time\_Protocol}, przy czym synchronizacja następowałaby do zegara
\textit{mastera}. W takim wypadku do pakietów moglibyśmy dołączyć informację na temat momentu w rozgrywce, w którym \textit{mastera}. W takim wypadku do pakietów moglibyśmy dołączyć informację na temat momentu w rozgrywce, w którym
pakiet został wysłany i odpowiednio uwzględniać opóźnienie (np. przesuwając obiekt o odpowiednia odległość względem pakiet został wysłany i odpowiednio uwzględniać opóźnienie (np. przesuwając obiekt o odpowiednią odległość względem
opóźnienia i dostępnych wektorów prędkości oraz przyspieszenia). opóźnienia i dostępnych wektorów prędkości oraz przyspieszenia).