\documentclass[]{article} \usepackage[T1]{fontenc} \usepackage{polski} \usepackage[utf8]{inputenc} \usepackage[margin=1.25in]{geometry} \usepackage{alltt} \usepackage{titling} \usepackage{pdfpages} \usepackage{float} \usepackage{amsmath} \usepackage{booktabs} \usepackage{tabularx} \usepackage{amstext} \usepackage{pgfplots} \usepackage{tikz} \usepackage{xspace} \usepackage{enumerate} \usepackage{lmodern} \usepackage{amsfonts} \usepackage{mathtools} \usepackage{alphalph} \usepackage{algorithm} \usepackage{algpseudocode} \usepackage{wrapfig} \usepackage[polish]{babel} \usepackage{braket} \usepackage{subcaption} \usepackage{multirow} \usepackage{etoolbox} \usepackage{color} \pgfplotsset{compat=1.15} \usepgfplotslibrary{groupplots} \usepgfplotslibrary{external} \usetikzlibrary{decorations.pathmorphing, arrows.meta, positioning} \usetikzlibrary{shapes.geometric, arrows, intersections} \newcommand{\areyousure}[1]{{\color{red}\textbf{???} #1 \textbf{???}}} \newcommand{\todo}[1]{{\color{blue}\textbf{TODO:} #1}} \makeatletter \newcommand{\member}[2]{% \ifcsdef{last}{\node[packet, right=of \last, text width=(#2*4mm)]}{\node[packet, text width=(#2*4mm)]} (#1) {\texttt{#1}};% \node[below=of #1] {#2}; \def\last{#1}% }% \newenvironment{packet}{% \begin{tikzpicture}[packet/.style={draw, auto, align=center, minimum height=7mm}, node distance=0]% }{% \end{tikzpicture}% \let\last\undefined } \makeatother % opening \title{Przetwarzanie Rozproszone - Projekt} \author{\protect\begin{tabular}{ll|l} Weronika Czarnecka & \textit{165600} & \multirow{4}{*}{\shortstack[l]{Informatyka 2016/2017 \\ Semestr IV, gr. 1}} \tabularnewline Anna Piliczewska & \textit{165436} & \tabularnewline Maciej Borzyszkowski & \textit{165407} & \tabularnewline Kacper Donat & \textit{165581} & % \protect\end{tabular}} \floatname{algorithm}{Program} \begin{document} \maketitle \section{Zasady rozgrywki} 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. \section{Wykorzystane technologie} \begin{itemize} \item C\# \item SFML.net \item System.Threading \item System.Net.Sockets \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. 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 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ę 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ę. Takie podejście zapewnia brak konfliktu o zasoby, ponieważ każdy obiekt jest modyfikowany wyłącznie przez jedną osobę (a co za tym idzie, jeden wątek) zatem nigdy nie nastąpi wyścig. \todo{Modyfikacja w trakcie odczytu? Ma znaczenie?} \areyousure{Przewidziano również wystąpowanie obiektów, które nie posiadają właściciela, a ich zachowanie jest w pełni deterministyczne zatem nie wymaga jawnej synchronizacji pomiędzy klientami.} Każdy klient będzi obsługiwany przez 5 osobnych wątki, których kompetencje nie kolidują ze sobą: \begin{enumerate} \item Renderowanie stanu symulacji \item Aktualizacja stanu (na podstawie kolejki akcji) \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. \areyousure{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 \item wszystkie pakiety aby zostać przetworzone muszą dojść w całości poprawnie \item pakiety są numerowane \item przez ważne pakiety rozumiemy te, które zmieniaja stan mapy \item pakiety mogą wymagać konkretnego poziomu stanu gry \item pakiety zawierają timestamp aby w razie kolizji ustalić pierwszeństwo \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. \begin{figure}[H] \center \begin{packet} \member{CRC32}{4} \member{Type}{1} \member{Size}{2} \member{ClientId}{2} \member{State}{4} \member{PacketId}{4} \node[packet, dotted, right=of PacketId, text width=6cm] (Data) {Packet Data ...}; \end{packet} \caption{Budowa pakietu} \end{figure} \begin{table}[H] \centering \begin{tabular}{l|l|l} \textbf{Właściwość} & \textbf{Typ} & \textbf{Opis} \\ \hline \texttt{CRC32} & \texttt{int32} & Suma kontrolna danych w pakiecie \\ \texttt{Type} & \texttt{uint8} & Rodzaj wysyłanego pakietu \\ \texttt{Size} & \texttt{uint16} & Rozmiar wysłanych danych, razem z pierwszym bajtem \\ \texttt{ClientId} & \texttt{uint16} & Identyfikator klienta \\ \texttt{State} & \texttt{uint32} & Wymagany stan gry do przetworzenia pakietu \\ \texttt{PacketId} & \texttt{uint32} & Timestamp oryginalnego wysłania pakietu \end{tabular} \end{table} Dla ułatwienia implementacji przyjmuje się, że dla pakietów, których najstarszy bit typu (\texttt{Type \& 0xC0}) jest ustawiony na 1 nie zachodzi potrzeba retransmisji - czyli są to pakiety mało istotne bądź szybko przedawniające się. 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ą. Aby upewnić się, że transmisja ważnego pakietu się powiodła, każdy klient musi zwrócić \section{Rozwiązania} O nie w tym wypadku też ciężko \end{document}