Sprawozdanie: RC
This commit is contained in:
parent
c792f9fa86
commit
f7328d3102
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user