Jacek Śliwerski (rzyjontko)
Longum iter est per preaecepta, breve et efficax per exempla!1Na początku pisania tego tekstu postanowiłem sobie, że trzymać się będę tego wspaniałego rzymskiego powiedzenia. W dalszej części artykułu rozwijać będziemy przykład jednej, konkretnej gry. Jako przykład przyjmiemy znaną chyba wszystkim rozgrywkę pochodzącą z filmu ,,Tron''. Polega ona na tym, że po zamkniętej przestrzeni poruszają się pojazdy zostawiające za sobą ,,ślad'', przez który nie można przejechać. Gra kończy się dla zawodnika, który wpadnie na okalającą ścianę, lub ślad pozostawiony przez jednego z graczy. ,,Tron'' ma kilka istotnych właściwości, które będziemy mieć cały czas na uwadze:
W środowisku programistów tworzących gry panuje powszechna opinia, że najgorszą decyzją dotyczącą istniejącego produktu jest dodanie do niego obsługi wielu graczy przez sieć. Dlaczego tak się dzieje? Programiści bardzo często nieświadomie korzystają z pewnych założeń, które nie są prawdziwe w grze sieciowej. Takie fragmenty programu trudno jest odnaleźć i usunąć, bez wprowadzania dodatkowych błędów.
Przyjrzyjmy się (hipotetycznej) grze ,,Tron'' przeznaczonej dla jednego gracza. Główna pętla programu składa się z następujących części:
Na pierwszy rzut oka rozszerzenie tego schematu jest bardzo proste.
Wystarczy przecież tylko dodać punkt:
3a. Program wysyła innym swój stan oraz odbiera stany gry innych graczy.
Niestety takie rozwiązanie ma wiele wad, które będziemy rozważać
w kolejnych punktach.
Ludzie zawsze próbowali oszukiwać i będą to robić aż po wsze czasy. Gracz, który oszukuje grę lokalną (zmieniając plik wykonywalny, pliki danych gry, a nawet zmieniając zawartość obszaru pamięci, w którym znajduje się gra) oszukuje tylko siebie. Uczestnik rozgrywki sieciowej oszukuje innych graczy. Zepsucie innym zabawy to jeszcze niewielka strata. Przypomnijmy sobie jednak, jak kończyła się porażka w filmie ,,Tron''. Gdyby grę rozszerzyć o istnienie świata, w którym gracze mogą ,,tracić życie'', to niewielkie oszustwo może mieć dużo dalej idące konsekwencje. Aby nie być gołosłownym to wspomnę grę ,,Diablo'', w której gracze sprzedawali swoje postacie za ciężkie pieniądze (prawdziwe, nie wirtualne!!).
Uczestnik gry sieciowej ma ułatwione zadanie. Nie musi grzebać w plikach binarnych, wystarczy dokładna obserwacja danych przesyłanych przez interfejs sieciowy, a następnie podmienianie ich na takie, które zagwarantują zwycięstwo.
Skoro stan gry ,,Tron'' można opisać zaledwie kilkoma współczynnikami, to kuszącym rozwiązaniem byłoby przesyłanie pozycji pojazdu do pozostałych graczy. Konsekwencje tego będą jednak tragiczne. Nieuczciwy zawodnik może napisać sobie prosty skrypt, który będzie analizował przychodzące pozycje pozostałych pojazdów, a następnie obliczał sobie pozycję, w której należy się ustawić, aby spowodować kolizję.
Jednym z rozwiązań jest zastosowanie protokołu typu ,,zamówienie - odpowiedź''. Klient, przed wykonaniem każdej akcji, wysyła do serwera zamówienie na wykonanie pewnej akcji. Serwer sprawdza poprawność otrzymanych danych i możliwość wykonania danej akcji, a następnie odsyła żądającemu potwierdzenie. Jednocześnie zmienia u siebie stan gry zgodnie z żądaną akcją i rozsyła pozostałym klientom informację o zmianie stanu gry.
Niestety to rozwiązanie jest nie do przyjęcia w grze ,,Tron''. Jak przekonamy się później, bardzo trudno jest utrzymywać spójny stan gry na wszystkich komputerach w sytuacji, kiedy pojazdy poruszają się z tak dużymi prędkościami. Dodatkowy etap potwierdzania może nałożyć dodatkowe opóźnienia w synchronizacji, a w efekcie powodować ,,skoki'' w obrazie generowanym przez program gry.
Zamiast więc stosować się do maksymy Józefa Stalina, zmodyfikujemy ją tak, by dla oszusta protokół wyglądął jak ,,zamówienie - odpowiedź'', a dla uczciwego gracza jak protokół informowania serwera o wykonaniu akcji. W tabeli 1 zestawiłem kroki, jakie podejmują uczciwe strony podczas gry.
A co zrobić z oszustem? Józef Stalin kazałby pewnie rozstrzelać, albo wysłać na roboty na Syberię. My jednak musimy być ostrożni. Możliwe jest, że dane przychodzące od klienta będą docierały z dużymi opóźnieniami. Serwer powinien potrafić ocenić odchylenie żądanej akcji od poprawnej. Jeśli odległość przekracza pewną ustaloną wartość, klient powinien zostać wyeliminowany z dlaszej gry, a kolejne jego żądania ignorowane. Jeśli jednak żądanie mieści się w granicy, to serwer powinien odesłać niezbędną korektę.
Do tej pory dość często posługiwałem się pojęciami serwera i klienta, nie przecyzując dokładnie, o co mi chodzi. Popularnym podejściem do tego zagadnienia jest przyjęcie, że jeden z komputerów uczestniczących w grze pełni rolę serwera (jest to tzw player hosted client - server). Ta jedna instancja gry musi umożliwiać innym klientom nawiżanie połączenia i rozsyłać odpowiednie pakiety do wszystkich uczestników rozgrywki. Takie podejście jest dosyć intuicyjne dla gracza, a przy okazji daje się łatwo zaimplementować. Ma jednak swoje wady. Należy pamiętać, że osoba, która uruchamia usługę serwera musi sobie zdawać sprawę ze zwiększonego obciążenia sieciowego.
Ponieważ kontrola poprawności ruchów odbywa się na serwerze, to rozwiązanie to pociąga za sobą niebezpieczeństwo malwersacji ze strony osoby prowadzącej usługi serwera. Jeśli wyniki gry miałyby mieć duże znaczenie dla graczy, to znacznie lepszym pomysłem jest uruchomienie dedykowanego serwera.
Jest jeszcze jeden poważny problem do rozważenia. Co robią przeciwnicy, kiedy nie dostajemy informacji o ich pozycji? Gra nie może zmieniać pozycji pojazdu innego gracza tylko w tych chwilach, kiedy otrzymuje odpowiednie dane. Powodowałoby to ,,skakanie'' tych pojazdów z miejsca na miejsce.
Na nasze szczęście z problemem tym uporała się już amerykańska armia, która opracowała odpowiednie protokoły do symulacji bitwy. Technikę likwidowania skutków opóźnień związanych z przesyłaniem danych przez sieć nazwano ,,dead reckoning'' i opracowano w ramach projektu DARPA Advanced Distribution Simulation (w skrócie DIS).
Program gry symuluje zachowanie wszystkich pojazdów (łącznie ze swoim). Jeśli znane są prędkość i przyspieszenie pojazdu, to można określać jego kolejne położenia w kolejnych chwilach gry. W przypadku własnego pojazdu należy jednak za każdym razem, czy właściwa pozycja nie różni się zbytnio od tej symulowanej. Zakłada się przy tym pewną granicę tolerancji. Jeśli pojazd przekroczył granicę tolerancji, to wysyła się poprawkę pozycji.
Podobno nie ma gier sieciowych, które korzystałyby z protokołu TCP. Wynika to głównie z tego, że w grach nie trzeba się martwić o dostarczenie każdego datagramu. Tym bardziej, że retransmitowany pakiet jest już bezużyteczny.
This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 -nonavigation -nosubdir multi.tex
The translation was initiated by rzyjontko on 2003-03-18