diff --git a/sprawozdanie/sprawozdanie.tex b/sprawozdanie/sprawozdanie.tex index 819cee8..bf3ef81 100644 --- a/sprawozdanie/sprawozdanie.tex +++ b/sprawozdanie/sprawozdanie.tex @@ -93,20 +93,29 @@ Gracze poruszają się po wspólnej planszy czołgami strzelając do siebie i starając się zniszczyć. Każdy gracz kontroluje tylko i wyłącznie swój czołg. +\subsection{Przechowywany stan} +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 +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. + +Dodatkowo, każdy obiekt na mapie zawiera swój własny stan, zależny od typu obiektu. Przykładowo dla czołgu może to być +pozycja (\texttt{vec2}), kąt (\texttt{double}) oraz ich pierwsza oraz druga pochodna (w celach predykcji). + \section{Wykorzystane technologie} \begin{itemize} - \item C\# - \item SFML.net - \item System.Threading - \item System.Net.Sockets + \item SFML.net - obsługa grafiki oraz wejścia od użytkownika + \item System.Threading - wątki + \item System.Net.Sockets - obsługa sieci \end{itemize} \section{Proponowana Architektura} -Z założenia, rozgrywka ma odbywać się w czasie rzeczywistym zatem wykorzystanie protokołu \textit{TCP} mogłoby -wprowadzać niepotrzebne opóźnienia wynikające z niepotrzebnych retransmisji pakietów. Komunikacja będzie odbywała się za -pomocą datagramów UDP, które pozwalają na szybszą - acz mniej dokładną transmisję informacji pomiędzy klientami. W -wypadku normlanej rozgrywki jednak strata części pakietów jest jak najbardziej akceptowalna i nie wpłynie na rozgrywkę w -znaczący sposób. +Z założenia, rozgrywka ma odbywać się w czasie rzeczywistym zatem wykorzystanie protokołu TCP mogłoby +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ą +transmisję informacji pomiędzy klientami. W wypadku normlanej gry jednak strata części pakietów jest jak +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. Zamiast tego jeden z klientów zawsze będzie miał status \textit{mastera}. Poprawnym stanem rozgrywki z definicji jest @@ -114,56 +123,6 @@ stan symulacji \textit{mastera}. \textit{masterem} zawsze jest klient o najmniej 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. -Dodatkowo, przyjmuje się zasadę, że każdy klient jest w 100\% odpowiedzialny za swoją część symulacji, dla której -stanowi źródło prawdy. tj. każdy gracz wysyła tylko i wyłącznie informacje o zmianach rzeczy, na które on ma wpływ - np. -pozycję własnego czołgu, czy wystrzelenie pocisku. W gestii innych klientów jest przetworzyć te informację i -zsynchronizować się. Przewidziano również obiekty o zachowaniu deterministycznym, które każdy klient może aktualizować -samodzielnie w ramach swojej symulacji. - -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 -oraz aktualizacja stanu gry odbywają się w ramach jednego wątku. - -Każdy klient będzie obsługiwany przez 4 osobnych wątki, których kompetencje nie kolidują ze sobą: -\begin{enumerate} - \item Aktualizacja i renderowanie stanu symulacji \label{thread:render} - \item Input z sieci (obiór pakietów, kolejkowanie) \label{input:net} - \item Input użytkownika (odczyt klawiatury itd.) \label{input:user} - \item Output sieciowy (wysyłanie pakietów, retranmisje) \label{output:net} -\end{enumerate} - -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 -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} -w metodach. - -Zakładamy, że akcje użytkownika muszą zostać zostać umieszczone na kolejce akcji natychmiastowo, zatem nie -ma potrzeby synchronizacji z siecią. Co za tym idzie, dostęp do kolejki pakietów (potrzebnej ze względu na możliwość -otrzymania 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 wyścigów nie następuje. - -Poprzez identyczną kolejkę będzie obsługiwana wysyłka pakietów będzie obsługiwana przez identyczną kolejkę 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 retransmisję pakietów. - -Niektóre pakiety zmieniają stan rozgrywki (np. śmierć, wystrzelenie pocisku), a co za tym idzie muszą zostać -przetworzone przez wszystkich klientów w jednakowej kolejnosci 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 -pakiet, będzie zawierał informację na temat stanu od którego "zależy" - tj. numeru ostatniej aktualizacji. W wypadku -gdyby doszły pakiety konkurujące ze sobą (tj. o tym samym numerze pakietu, oraz zależące od tego samego stanu) wygrywa -ten, który przyszedł pierwszy ponieważ są one od siebie niezależne. - -\subsection{Protokół} -Protokół opiera się o wysyłanie datagramów UDP między wszystkimi klientami. Ze względu na specyfikę UDP (brak gwarancji -dotarcia pakietów, brak gwarancji kolejności, brak gwarancji poprawności) przyjmuje się następujące założenia: -\begin{itemize} - \item nie wszystkie pakiety są ważne, niektóre można pominąć - \item wszystkie pakiety aby zostać przetworzone muszą dojść w całości poprawnie - \item pakiety są numerowane - \item pakiety mogą wymagać konkretnego poziomu stanu gry -\end{itemize} - \begin{figure}[H] \centering @@ -183,6 +142,63 @@ dotarcia pakietów, brak gwarancji kolejności, brak gwarancji poprawności) prz \caption{Proponowana architektura sieciowa} \end{figure} +Dodatkowo, przyjmuje się zasadę, że każdy klient jest odpowiedzialny za swoją część symulacji, dla której +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. +pozycję własnego czołgu, czy wystrzelenie pocisku. W gestii innych klientów jest przetworzyć te informację i +zsynchronizować się. Przewidziano również obiekty o zachowaniu deterministycznym, które każdy klient może aktualizować +samodzielnie w ramach swojej symulacji. + +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 +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 +każdego klienta następuje predykcja zachowań innych graczy. W wypadku otrzymania pakietu aktualizujacego stan gracza, +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 +np. predykcja nie może usunąć obiektu z mapy - moze 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 +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ą: +\begin{enumerate} + \item Aktualizacja, predykcja oraz renderowanie stanu symulacji \label{thread:render} + \item Input z sieci (obiór pakietów, kolejkowanie) \label{input:net} + \item Input użytkownika (odczyt klawiatury itd.) \label{input:user} + \item Output sieciowy (wysyłanie pakietów, retranmisje) \label{output:net} +\end{enumerate} + +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 +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} +w metodach. + +Zakładamy, że akcje użytkownika muszą zostać zostać umieszczone na kolejce akcji natychmiastowo, zatem nie +ma potrzeby synchronizacji z siecią. Co za tym idzie, dostęp do kolejki pakietów (potrzebnej ze względu na możliwość +otrzymania 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 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} +oraz \ref{output:net}. Dodatkowo w wątku \ref{output:net} będzie działać samodzielna kolejka odpowiadająca za +retransmisję pakietów. + +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 +zagwarntowania 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. + +\subsection{Protokół} +Protokół opiera się o wysyłanie datagramów UDP między wszystkimi klientami. Ze względu na specyfikę UDP (brak gwarancji +dotarcia pakietów, brak gwarancji kolejności, brak gwarancji poprawności) przyjmuje się następujące założenia: +\begin{itemize} + \item nie wszystkie pakiety są ważne, niektóre można pominąć + \item wszystkie pakiety aby zostać przetworzone muszą dojść w całości poprawnie + \item pakiety są numerowane + \item pakiety mogą wymagać konkretnego poziomu stanu gry +\end{itemize} + Ze względu na konieczność szybkiego przesyłania informacji pomiędzy klientami, zaprojektowany protokół jest protokołem binarnym, o konstrukcji pakietu widocznej poniżej. @@ -253,6 +269,13 @@ wzgledu na to, że \texttt{ACK} nie jest bezpośrednio związany z rozgrywką, w \caption{Pakiet \texttt{ACK}} \end{figure} +W momencie kiedy po $n$-tej retransmisji pakietu z rzędu nie otrzymamy potwierdzenia \texttt{ACK} uznajemy, że nastąpiło +zerwanie połączenia ze strony tego gracza - dodatkowo, jeżeli jakimś cudem wszyscy pozostali gracze stracili połączenie +zakładamy, że to problem jest po naszej stronie. + +W celu sprawdzenia poprawności połączenia z danym graczem można również wysłać pakiet \texttt{PING = 0x00}, za poprawny +\textit{pong} uznajemy pakiet \texttt{ACK}. + \subsection{Zestawienie połączeń} Ze względu na to, że port na którym nasłuchują klienci nie jest deterministyczny istnieje konieczność wprowadzania scentralizowanego \textit{lobby} pozwalającego na wymianę tych informacji pomiędzy klientami. Jedynym zadaniem @@ -262,8 +285,22 @@ działania. Stąd przyjęto, że do realizacji lobby zostanie wykorzystany protokół TCP. Ze względu na to, że lobby stanowi jedynie pośrednika do zestawiania połączeń, dokładny opis działania zdecydowano się pominąć. -\section{Rozwiązania} -O nie w tym wypadku też ciężko +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. +Po udanym nawiazaniu połączenia ze wszystkimi klientami, zostaje utworzony czołg gracza, a informacja o tym zostaje +przesłana do pozostałych graczy. + +\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 +sieciowym nie stanowiła problemu. Jednak należy przeprowadzić testy, czy faktycznie jest to dobre założenie. W wypadku, +gdyby jednak desynchronizacja związana z opóźnieniami stanowiła problem należy wprowadzić system pozwalający uwzględniać +opóźnienia odebrania pakietu względem przesłania - np. poprzez wprowadzenie wspólnego, synchronizowanego zegara gry. + +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 +\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 +opóźnienia i dostępnych wektorów prędkości oraz przyspieszenia). \end{document}