Synchronizacja klienta o niskim natężeniu ruchu z serwerem w MMO

22

Wdrażam MMO, gdzie gracz lata w kosmosie na swoim statku, kontrolując go za pomocą klawiszy strzałek i współpracując z innymi graczami.

Chcę go wdrożyć, aby gracz mógł uchylić się przed rakietą lub czymś innym, więc staram się przewidzieć cały stan gry po stronie klienta, używając tego samego algorytmu symulacji świata, co serwer. Ten świat gry jest napisany na C # i będzie wywoływany bezpośrednio w kliencie (napisany na Unity3D) i przez CLR na serwerze C ++ (pod Linuksem). Połączenie przez UDP.

Problem polega na tym, jak utrzymać na przykład 1000 graczy na jednej mapie (wyłączając wszystkie inne obiekty gry, moby ...): Powiedzmy, że:

  • synchronizuj serwer z klientami 50 razy na sekundę
  • wysłać do każdego klienta stany tylko tych obiektów gry (i graczy), które jest w stanie zobaczyć (w pewnym promieniu)
  • muszę wysłać 100 obiektów do każdego gracza w promieniu jego widoku
  • musi wysłać średnio 50 bajtów na obiekt gry (to id, x, y coords, obrót, stan ...)

więc musi mieć taką przepustowość sieci: 1000 (klientów) * 50 (razy na sekundę) * 100 (obiekty do wysłania do każdego odtwarzacza) * 50 (bajtów na obiekt) = 250 000 000 bajtów na sekundę! To niemożliwe!

Czy można jakoś zmniejszyć tę wartość? Na przykład pozwól klientom w pełni symulować ich światy gry (przez długi okres czasu) i wysyłać im tylko dane wejściowe innych klientów i synchronizować światy gier, powiedzmy co kilka sekund, ale spowoduje to dziwne problemy z desynchronizacją na smyczy z powodu obliczeń zmiennoprzecinkowych .

W każdym razie, w jaki sposób takie gry są programowane we wspólny sposób? Dziękuję Ci.

słowiański
źródło
1
Wysyłam tylko logiczne informacje o obiektach (pozycja świata, aktualny stan (który jest jednym bajtem) itd.) - bez grafiki.
Slav
1
@Slav: Fajnie! Całe to przesuwanie bitów przypomina mi dni programowania ASM.
Randolf Richardson,
1
Dlaczego nie „dziś”? :) Kiedy piszę na AS3, Java, Lua, C # i spotykam się z tak niską wydajnością, brakuje mi C ++ i pamiętam o ASM.
Slav
1
@Slav: Heheh, ostatnio niewiele zrobiłem ASM. Większość rzeczy dla mnie jest teraz w Javie i Perlu (głównie mod_perl2), ale naprawdę lubię te języki.
Randolf Richardson
2
@Slav napisałeś: „Kiedy piszę na AS3, Java, Lua, C # i spotykam się z tak niską wydajnością, brakuje mi C ++ i pamiętam o ASM”. Powinieneś nauczyć się poprawnie używać Lua i C #, być może wydajność okaże się mniej absurdalna. Poza tym narzekanie na (rzekomo) najszybszy język skryptowy jest w najlepszym razie osobliwy ... Czy to gra o analizie ludzkiego genomu w czasie rzeczywistym?
Raine,

Odpowiedzi:

20

Potrzebujesz tylko około 30 aktualizacji (lub nawet mniej, może 10 lub 20) na sekundę. interpolować pozycje ruchomych obiektów po stronie klienta. Zasadniczo dane należy wysyłać tylko wtedy, gdy NAPRAWDĘ są potrzebne. W WoW możesz otrzymywać więcej aktualizacji od graczy, z którymi jesteś w grupie, niż od graczy, którzy są w tej samej lokalizacji. Ponadto, jeśli inny gracz jest daleko od ciebie, nie otrzymujesz o nim tyle aktualizacji na sekundę.

Następnie wysyłaj tylko jedną kompletną migawkę każdemu graczowi, gdy się połączy. Następnie wysyłaj tylko zmiany obiektów gry. Jeśli nie nastąpiła żadna zmiana, nie wysyłaj jej.

Następnie wykorzystaj BitVectors lub jakkolwiek możesz je wywołać, aby zmniejszyć ilość niepotrzebnych danych! Przykład: Możesz także spróbować napisać liczbę zmiennoprzecinkową, używając tylko jednego bajtu (w zakresie od 0 do 1 lub -1 do 1), więc masz tylko 256 lub 128 różnych wartości. Ale dzięki interpolacjom gracz nie zauważy żadnych gwałtownych ruchów.

Spójrz na to na przykład z LidgrenLibrary na temat kompresji danych: http://code.google.com/p/lidgren-network-gen3/wiki/Optimization

Dalej: Staraj się zmniejszać promień widzenia graczy podczas ruchu i przesyłaj tylko ważne informacje w tym czasie. Następnie, gdy przestaną zwiększać promień widzenia, ponownie. Możesz użyć przestrzennego systemu haszującego lub drzewa bsp, aby zmniejszyć obciążenie związane z wyszukiwaniem obiektów znajdujących się „w zasięgu”. To dobra lektura dla tematu: http://en.wikipedia.org/wiki/Collision_detection

Również kompresuj dane TYLKO SIEBIE wiesz o strukturze danych i czasowej spójności danych (które można i należy wykorzystać). Należy stosować ogólny algorytm, taki jak Bzip2, Deflate, cokolwiek, ale tylko jako ostatni etap kompresji!

Ponadto w przypadku informacji nieistotnych dla gry możesz również zastosować dodatkowe techniki P2P. Przykład: Gracz odtwarza animację „cześć” (tylko efekt graficzny). Gracz wysyła te informacje do serwera, ale serwer nie przekazuje informacji innym graczom. Zamiast tego ten niekrytyczny efekt jest wysyłany przez samego gracza do innych klientów w zasięgu.

EDYCJA (z powodu komentarza):

Dodatkowe metody zmniejszania średniej liczby bitów na sekundę dla każdego gracza:

  1. Napisałeś, że wysyłasz „Obiekt się nie zmienił”. Nie ma powodu, aby to robić. Jeśli martwisz się utratą pakietów (i z tego powodu zsynchronizujesz symulację), weź pod uwagę następujące kwestie: Przy każdym ustalonym czasie (np. 100, 200, 300, 400 ...) hasz stan symulacji i wyślij go na serwer . serwer potwierdza lub wysyła pełną migawkę wszystkich danych z powrotem.

  2. W przypadku rakiet, a nawet graczy możesz zastosować nie tylko interpolację, ale także ekstrapolację, aby uczynić symulację bardziej realistyczną. Przykład „Rakieta”: Zamiast aktualizować za pomocą wiadomości typu „Jest teraz w pozycji x”, po prostu wyślij wiadomość zawierającą następujące informacje: „Rakieta pojawiła się: pozycja (wektor), Czas (w którym etapie symulacji spawnowała się rakieta), prędkość ( wektor)". Nie musisz nawet uwzględniać obrotu, ponieważ końcówka zawsze będzie w kierunku „prędkości”.

  3. Połącz wiele poleceń w jedną wiadomość i nigdy nie wysyłaj wiadomości mniejszych niż 16-20 bajtów, ponieważ nagłówek udp będzie większy niż sama wiadomość. Nie wysyłaj również pakietów większych niż MTU twojego protokołu, ponieważ fragmentacja spowolni prędkość transmisji.

Riki
źródło
Och, dobrym pomysłem jest aktualizowanie niektórych obiektów częściej niż innych, używanie P2P, obniżanie dokładności liczb zmiennoprzecinkowych, wysyłanie tylko zmian (co nie jest dla mnie trywialne, ponieważ zamierzałem okresowo synchronizować obiekty, ale „obiekt się nie zmienił” to informacja zbyt). Po tych wszystkich modyfikacjach cały obraz wygląda bardziej realistycznie!
Slav
1
Wysyłanie powiadomień typu „obiekt się nie zmienił” może być użyteczną techniką do celów testowych, w których chcesz zobaczyć, jak gra się gra, gdy gracze są włączeni w czasie zajętości, ponieważ może to powodować wymagania dotyczące przetwarzania, a także sieci, ale istnieją jeszcze lepsze rozwiązania (takie jak utworzenie autonomicznego demona, który kontroluje rzeczywistą postać w grze, a następnie wielokrotne uruchamianie tego demona z różnych maszyn).
Randolf Richardson,
5

Oto dwa podejścia:

Po pierwsze:
przełącz się na fizykę deterministyczną, wysyłaj polecenia odtwarzacza, działania AI, obiekty pojawiające się w polu widzenia i wszystko, czego nie można ustalić po stronie klienta. Musi to obejmować non-polecenia, potwierdzenie, że do pewnego momentu nie mają zastosowania nic oprócz poleceń, które zostały wysłane i odebrane.

Klient musi uruchomić dwie lub trzy jednoczesne symulacje.
1: Zatrzymuje się, gdy brakuje danych do następnego kroku.
2: Kontynuuj używanie zgadywania i podaj stan używany do renderowania. 3: Za każdym razem, gdy nr 1 zatrzymuje się, ta symulacja kopiuje stan nr 1, nadrabiaj bieżący czas i przejmuj numer 2, który jest następnie upuszczany.

Jeśli nadrabianie zaległości jest wystarczająco szybkie, możesz pominąć różnicę między nr 2 i nr 3 i po prostu natychmiast usunąć stare dane.

Po drugie:
nie używaj fizyki deterministycznej, rób to samo co powyżej, ale wysyłaj „pełne klatki” co kilka sekund. Możesz łatwo całkowicie pominąć przenoszenie tymczasowych rzeczy, takich jak pociski.

W obu przypadkach możesz być ostrożny, gdy klient przewiduje, że ktoś umrze, to trochę głupio patrzeć na niewybuch przeciwnika.

I +1 za wykonanie matematyki, zbyt wiele osób nie robi prostych oszacowań zużycia zasobów.

aaaaaaaaaaaa
źródło
2
Czy „fizyka deterministyczna” oznacza, że ​​nie mogę używać wartości zmiennoprzecinkowych ani różnych etapów symulacji? Zastanawiam się, czy krytyczna desynchronizacja może się zdarzyć, gdy np. Rakieta przebije się przez wieżę wroga na kliencie, ale trafi ją na serwerze (z powodu niedokładności zmiennoprzecinkowych), co spowoduje, że gracz będzie walczył z tą wieżą do następnego pakietu synchronizacji serwera (kilka sekund).
Slav
3
Oznacza liczby całkowite i stały krok czasu. Teoretycznie możesz wyśmiewać zmiennoprzecinkowe, ale zachowanie liczb całkowitych jest o wiele prostsze. Masz rację z przykładem brakującego pocisku, jeśli używasz fizyki niedeterministycznej, prawdopodobnie najlepiej pozwolić serwerowi w pełni poradzić sobie ze śmiercią i szybko przekazywać przypadki śmierci / zniszczenia.
aaaaaaaaaaaa
5

Najpierw kilka pytań.

Czy „rakiety lub coś innego” są inteligentne czy głupie? Jeśli są głupie, wszystko czego potrzebujesz to znacznik czasu ognia, pochodzenie i wektor do symulacji ich ścieżki. Jeśli są inteligentni, jak są inteligentni? Czy potrafisz obliczyć w czasie pożaru, że trafią lub nie trafią? Jeśli tak, możesz zasymulować całą ścieżkę na kliencie. („W T13 pocisk uderzy w statek, ponieważ gra straciła rzut unik / strzelec strzelił trafienie krytyczne.”)

Ogólnie jednak nie ma prawie żadnego powodu, aby: A) mieć częstotliwość taktowania 50 Hz, (większość strzelców ma 15-20 i mniej MMO). B) wysyłają pełny stan co klatkę. (Czy obrót pocisku w kosmosie ma w ogóle znaczenie? Czy może po prostu założyć, że jego „przód” jest zorientowany wzdłuż wektora, który leci?)

Poświęć czas na przewidywanie i interpolację, a zobaczysz, jak spada przepustowość. Projekt, nad którym pracowałem, miał częstotliwość aktualizacji 10 Hz, a reprezentacja stanu obiektu to chyba 14 bajtów. (Kompresuj wszystko, co możesz! Wydaje mi się, że użyliśmy 6 bitów do zdefiniowania obrotu wokół płaszczyzny x, a następnie kolejnych 6 bitów dla pochylenia powyżej / poniżej tej płaszczyzny, wyglądało to nie do odróżnienia od wysłania rzeczywistej macierzy obrotowej / czwartorzędu.)

Inną rzeczą, którą możesz zrobić, jest ustalanie priorytetów obiektów. Pokaż, może w odpowiednim zestawie znajduje się 100 obiektów, ale czy znasz jego widok frustum na serwerze? Jeśli coś nie jest w jego odczuciu, czy możesz upuścić częstotliwość aktualizacji o rząd wielkości?

Ogólna idea nie polega na stworzeniu idealnej symulacji na kliencie, to niemożliwe, chodzi o stworzenie fajnej gry, w której gracze nie zauważą, że nie jest to idealna symulacja.

Doug-W
źródło