Protokół gry RTS

18

Myślałem o grze RTS dla wielu graczy. Częścią, której nie mogę się obejść, jest synchronizacja ruchu jednostek. Jeśli przesunę jednostkę A w celu wykrycia XY, muszę przekazać ją z powrotem do serwera, który przekaże drugi klient.

Jestem ciekawy, jak wyglądałaby komunikacja. Czy po prostu poinformowałbyś serwer, że przenoszę jednostkę A do XY z JZ? Może zamiast tego musisz komunikować koordynację ruchu przez koordynację? Jaka jest najbardziej efektywna metodologia komunikowania przepływu jednostek między klientami?

EDYTOWAĆ

To jest nowe pytanie od stackoverflow . Odkryłem, że ta strona była prawdopodobnie lepszym miejscem na pytanie.

Jedna z lepszych odpowiedzi z tego postu:

Zakładam, że zamierzasz użyć paradygmatu sieci klient-serwer? W takim przypadku nie można ufać klientom, że poradzą sobie z faktycznym pozycjonowaniem jednostek, należy przekazać to zadanie serwerowi. Następnie bierzesz listę poleceń od każdego klienta za każdym tikiem i obliczasz ruch każdej jednostki, po zakończeniu tej czynności, następnie zaznaczasz pozycję każdej jednostki odpowiednią dla każdego klienta (na podstawie całej mapy lub na podstawie widoku) i ponownie rozpocznij proces.

Darthg8r
źródło
2
Odpowiedź naprawdę zależy od metody, której chcesz użyć. Klient - klient lub klient - serwer. Klienta do serwera jest łatwiejsze, ale wymaga to trustable serwer
Cem Kalyoncu

Odpowiedzi:

25

Nie chcesz synchronizować pozycji wszystkich jednostek z serwera do każdego klienta; zajmie to znacznie więcej przepustowości niż potrzebujesz. Musiałbyś również mieć do czynienia z interpolacją / ekstrapolacją pozycji jednostek itp. Prawie żaden profesjonalny klient / serwer nie korzysta z usług RTS!

Zamiast tego chcesz wysyłać tylko polecenia graczy. Zamiast przesuwać jednostki natychmiast po kliknięciu przez gracza, ustawisz w kolejce polecenie ruchu, które ma być wykonane w pewnym momencie w przyszłości - zwykle tylko kilka klatek. Każdy wysyła swoje polecenia do wszystkich. Kilka klatek później wszyscy wykonują wszystkie polecenia, a ponieważ gra jest deterministyczna, wszyscy widzą ten sam wynik.

Minusem jest to, że każdy gracz jest tak wolny jak najwolniejszy gracz - jeśli ktoś pozostaje w tyle w wysyłaniu poleceń, wszyscy muszą zwolnić i czekać, aż nadrobi zaległości (w Starcraft 2 to jest „XXX spowalnia grę „ okno dialogowe).


W rzeczywistości zwykle wykonuje się jeszcze jedną rzecz: całkowicie wyeliminować serwer . Niech każdy klient wyśle ​​swoje polecenia do każdego innego klienta. Zmniejsza to opóźnienie (zamiast polecenia wysyłanego od ciebie -> serwer -> przeciwnik, po prostu idzie od ciebie -> przeciwnik) i ułatwia kodowanie, ponieważ nie musisz już kodować osobnego serwera. Ten rodzaj architektury nazywa się peer-to-peer (P2P).

Minusem jest to, że teraz potrzebujesz sposobu rozwiązywania konfliktów, ale ponieważ polecenia większości graczy są niezależne od siebie w większości RTS, zazwyczaj nie stanowi to większego problemu. Ponadto nie skaluje się dobrze - za każdym razem, gdy dodajesz nowego gracza, każdy gracz musi wysłać mu swoje polecenia. Nie będziesz robić MMO RTS za pomocą P2P.


Ta konfiguracja (wysyłanie tylko poleceń za pomocą P2P) jest sposobem działania większości RTS, w tym Starcraft, C&C i AoE, i jest jedynym sposobem, w jaki AoE może obsługiwać 1500 jednostek przy połączeniu 28,8 kb / s .

(obraz sieci w AoE)

Oto kilka dodatkowych wskazówek dotyczących pisania RTS P2P:

  • Z oczywistych względów ta konfiguracja może działać tylko wtedy, gdy gra używa określonego czasu - nie chcesz, aby wyniki obliczeń zależały od liczby klatek na sekundę! W przypadku większości rzeczy łatwiej jest pracować ze stałym krokiem, więc nie powinno to stanowić problemu.
  • Aby to zadziałało, wyniki każdego polecenia muszą być całkowicie deterministyczne .
    • Zazwyczaj jest to dość łatwe, jeśli ograniczysz się do jednego systemu (takiego jak 32-bitowy system Windows) i zmusisz wszystkich klientów do korzystania z tego samego pliku wykonywalnego: upewnij się, że generatory liczb losowych mają to samo ziarno i zawsze są wywoływane w tej samej kolejności; zachowaj szczególną ostrożność podczas iteracji po nieuporządkowanych kolekcjach ; itp.
    • Jest to niezwykle trudne, jeśli planujesz umożliwić grę na różnych platformach lub (jak to często bywa w Linuksie) pozwolić klientom na samodzielne skompilowanie kodu. Nie tylko są różne biblioteki systemowe prawie gwarantowane stosować różne implementacje rand(), cos()etc, ale prawie wszystko zmiennoprzecinkową matematyka jest wykluczone (patrz tutaj , tutaj i tutaj ) ! W takim przypadku lepiej będzie użyć klienta-serwera.
  • Będziesz chcesz wysłać wszystkie pozycje jednostkowe raz na jakiś czas, przynajmniej podczas debugowania, aby wykryć utratę synchronizacji błędy (które, zaufaj mi, to będzie mieć). Niezależnie od tego, czy utrzymasz to w ostatecznej wersji gry, zsynchronizuję przynajmniej niektóre jednostki (lub użyję pewnego rodzaju sumy kontrolnej), aby wykryć próbę włamania.
BlueRaja - Danny Pflughoeft
źródło
Dobry post Niewielka rzecz do dodania, nawet te same kompilatory optymalizują, debugowanie / wydanie i inne flagi mogą zmienić wynik. Bądź ostrożny!
Peter Ølsted
14

Zrobiłem RTS w sieci TCP, w którym sam przekazałem polecenia, a nie ich wyniki . Na przykład gracz wydaje rozkaz ruchu. Jeśli zamówienie przeniesienia jest ważne według tego klienta, jest wysyłane na serwer. Serwer następnie odsyła go z powrotem do wszystkich klientów, którzy go sprawdzają i wykonują.

Więc wszystkie komputery klienckie same uruchamiają grę, kod serwera przyjmuje wiadomości i odsyła je z powrotem do wszystkich klientów. Jeśli klient wyda polecenie przeniesienia, nie zacznie go wykonywać, dopóki nie zostanie odebrane z serwera.

Serwer wysyła również „tik”, pod którym należy wykonać polecenie, czyli kilka tyknięć przed „bieżącym” tikiem. W ten sposób wszystkie polecenia mogą być wykonywane na tym samym „tykanie” na wszystkich komputerach.

Jedną z zalet tej metody jest to, że sprawdzanie poprawności polecenia nie zależy od pojedynczego komputera klienckiego. Jeśli przejdę wyniki ruchu, być może uda mi się go zhakować, aby szybciej przenieść moje jednostki. Wszyscy klienci muszą wykonać to samo polecenie, a jeśli jedna maszyna wykona je inaczej, będzie to oczywiste.

Sprawdzanie poprawności polecenia po stronie klienta przed wysłaniem go do serwera nie jest konieczne, ale teoretycznie oszczędza ruch sieciowy. Użyłem tego samego kodu sprawdzania poprawności, aby poinformować interfejs użytkownika, że ​​przeniesienie było możliwe, więc nie wymagało pisania dodatkowego kodu.

Co do tego, jak mogą wyglądać wiadomości. Nie interesowała mnie ultra wydajność, ponieważ była to moja pierwsza gra sieciowa. Polecenia przekazałem jako ciągi znaków. Polecenia będą sformatowane w następujący sposób:"<player_id>:<command>:<parameters>"

Na dobry przykład, polecenie ruch może wyglądać następująco: "3:move:522:100:200". Oznacza to, że Gracz 3chce movepołączyć się 522z ( 100, 200).

Serwer przekazuje polecenia do wszystkich klientów, w tym jeden, który wysłał go z numerem kleszcza załączonym tak: "153238:3:move:522:100:200".

Następnie wszyscy klienci wykonaliby to polecenie, gdy zostanie wykonany tyk 153238.

Philip
źródło
Dodałem trochę więcej informacji do pytania. Odpowiedź SO wydaje się być sprzeczna z tym, co powiedziałeś, i chciałbym omówić najdrobniejsze szczegóły.
Darthg8r
Tak, to inny sposób na zrobienie tego, ale wydaje mi się, że przekazanie tak dużej części stanu gry byłoby więcej pracy niż tylko poleceń. Moja gra była na tyle prosta, że ​​wszystko można uruchomić na każdym komputerze klienckim. W przypadku MMO lub czegoś takiego jak Minecraft nie masz całej symulacji uruchomionej po stronie klienta, więc przekazujesz tylko informacje istotne dla każdego klienta indywidualnie.
Philip