Jak utrzymać synchronizację struktury danych w sieci?


12

Kontekst

W grze, nad którą pracuję (rodzaj graficznej przygody typu „wskaż i kliknij”), prawie wszystkim, co dzieje się w świecie gry, kontroluje menedżer akcji o strukturze przypominającej:

wprowadź opis zdjęcia tutaj

Na przykład jeśli wynik badania obiektu sprawi, że postać przywita się, pójdzie trochę, a następnie usiądzie, po prostu spawnuję następujący kod:

var actionGroup = actionManager.CreateGroup();
actionGroup.Add(new TalkAction("Guybrush", "Hello there!");
actionGroup.Add(new WalkAction("Guybrush", new Vector2(300, 300));
actionGroup.Add(new SetAnimationAction("Guybrush", "Sit"));

Spowoduje to utworzenie nowej grupy akcji (cała linia na powyższym obrazku) i dodanie jej do menedżera. Wszystkie grupy są wykonywane równolegle, ale działania w obrębie każdej grupy są powiązane ze sobą, dzięki czemu druga zaczyna się dopiero po zakończeniu pierwszej. Po zakończeniu ostatniej akcji w grupie grupa zostaje zniszczona.

Problem

Teraz muszę powielić te informacje w sieci, aby w sesji dla wielu graczy wszyscy gracze zobaczyli to samo. Serializacja poszczególnych działań nie stanowi problemu. Ale jestem absolutnym początkującym, jeśli chodzi o networking i mam kilka pytań. Myślę, że dla uproszczenia w tej dyskusji możemy wyodrębnić komponent menedżera akcji jako po prostu:

var actionManager = new List<List<string>>();

Jak powinienem kontynuować synchronizację zawartości powyższej struktury danych między wszystkimi odtwarzaczami?

Oprócz podstawowego pytania mam również kilka innych związanych z tym obaw (tj. Wszystkie możliwe implikacje tego samego problemu powyżej):

  • Jeśli korzystam z architektury serwer / klient (gdy jeden z graczy działa zarówno jako serwer, jak i klient), a jeden z klientów stworzył grupę działań, czy powinien dodać je bezpośrednio do menedżera, czy tylko wysłać zapytanie na serwerze, który z kolei zleci każdemu klientowi dodanie tej grupy ?
  • Co z utratą pakietów i tym podobne? Ta gra jest deterministyczna, ale myślę, że każda rozbieżność w sekwencji działań wykonywanych u klienta może prowadzić do niespójnych stanów na świecie. Jak uchronić się przed tego rodzaju problemem ?
  • Co się stanie, jeśli dodam zbyt wiele działań na raz, czy nie spowoduje to problemów z połączeniem? W jakiś sposób to złagodzić?

5
Obowiązkowe „przeczytaj ten pierwszy” link gafferongames.com/networking-for-game-programmers, który nie jest zbyt techniczny, mimo że ma trochę kodu, ale jest trudny i dość dobrze wyjaśnia twoje opcje. Ponadto zapewni Ci równe szanse wszystkim osobom, które przeczytały ten link i jest to szczęśliwe miejsce.
Patrick Hughes,

@PatrickHughes Dzięki za link! Na pewno to wszystko przeczytam.
David Gouveia,

Istnieje kilka pakietów, które zarządzają tego rodzaju rzeczami dla Ciebie. Nie wiem, jak wydajna lub szybka, ale NowJS ( nowjs.com ) jest ciekawym przykładem. Z punktu widzenia programistów po prostu masz wspólne struktury danych między klientem a serwerem. Zmień jedną, drugą zmianę.
Tim Holt,

Odpowiedzi:


9

Jeśli korzystam z architektury serwer / klient (gdy jeden z graczy działa zarówno jako serwer, jak i klient), a jeden z klientów stworzył grupę działań, czy powinien dodać je bezpośrednio do menedżera, czy tylko wysłać zapytanie na serwerze, który z kolei zleci każdemu klientowi dodanie tej grupy?

W tym przypadku masz trzy opcje, z których dwie już wspomniałeś. Wybór opcji zależy od wielu czynników, które tylko Ty możesz być najlepszym oceniającym:

1: Klient odradza grupę działań, dodaje je bezpośrednio, a następnie wysyła na serwer. Serwer akceptuje to bez żadnych kontroli

* Korzyści z tego podejścia polegają na tym, że jeśli chodzi o klienta, jego własne działania dają mu natychmiastową informację zwrotną niezależnie od opóźnienia sieci. Wadą jest to, że wiadomości klienta są akceptowane przez serwer jako fakt (którym mogą nie być) *

2: Klient wysyła żądanie do serwera, który z kolei wysyła żądanie do wszystkich, w tym do klienta, który je wysłał.

Zaletą tego podejścia jest to, że serwer kontroluje teraz wszystko, co dzieje się w grze, co łagodzi większość problemów związanych z nielegalną aktywnością (tutaj nielegalna może obejmować od przycinania ściany przez klienta do wykonywania ruchu, który jest teraz nielegalny, ponieważ serwer ma więcej informacji, których klient nie miał, gdy wykonał określony ruch)

3: Klient odradza grupę działań, dodaje je bezpośrednio, a następnie wysyła na serwer. Serwer przetwarza żądanie, przeprowadza własne kontrole działań, aby upewnić się, że nie są one nielegalne, a następnie transmituje ruch do wszystkich graczy, w tym klienta.

Wykorzystuje to, co najlepsze zarówno z 1, jak i 2, kosztem nieco więcej wymagań dotyczących przepustowości. Korzyścią jest natychmiastowa informacja zwrotna dla klienta na temat własnych ruchów, a jeśli ruchy, które wykonuje klient, są nielegalne, serwer podejmuje odpowiednie działania (zdecydowanie koryguje klienta).

Co z utratą pakietów i tym podobne? Ta gra jest deterministyczna, ale myślę, że każda rozbieżność w sekwencji działań wykonywanych u klienta może prowadzić do niespójnych stanów na świecie. Jak uchronić się przed tego rodzaju problemem?

Utrata pakietów i ekstremalne opóźnienie to trudny problem do rozwiązania . W celu rozwiązania problemu stosuje się różne rozwiązania (żadne z nich nie jest idealne), w zależności od rodzaju gry (np. Liczenie). Zakładam, że gra nie musi synchronizować fizyki.

Jedną z pierwszych rzeczy, które powinieneś zrobić, jest oznaczanie czasu wszystkimi ruchami. Twój system powinien następnie wziąć to pod uwagę i odpowiednio odtworzyć ruchy (być może serwer ponosi tę odpowiedzialność, a następnie odmówi nielegalnych ruchów). Wdrażając ten system, załóż 0 opóźnień (testuj lokalnie).

Opóźnienie 0 nie jest oczywiście dobrym założeniem, nawet w sieciach lokalnych, więc teraz, gdy wszystkie ruchy szanują czas, w którym czasie ufasz? Właśnie tutaj pojawia się kwestia synchronizacji czasu. Wiele artykułów / artykułów online pomoże ci rozwiązać ten problem.

Co się stanie, jeśli dodam zbyt wiele działań na raz, czy nie spowoduje to problemów z połączeniem? W jakiś sposób to złagodzić?

Najpierw oczywiste rzeczy. Nigdy nie wysyłaj ciągów ani serializowanych obiektów. Nie wysyłaj wiadomości tak szybko, jak sama gra się aktualizuje. Wysyłaj je w kawałkach (np. 10 razy na sekundę). Wystąpi błąd (zwłaszcza błąd zaokrąglania), że serwer / klient będzie musiał od czasu do czasu naprawić błędy (więc co sekundę serwer wysyła wszystko [wszystko = wszystkie dane, które są ważne, aby utrzymać poprawność działania gry stan], do którego następnie klient przyciąga i / lub bierze pod uwagę, aby poprawić swój stan).EDYCJA: Jeden z moich kolegów wspomniał, że jeśli używasz UDP [co powinieneś, jeśli masz dużo danych], to nie ma porządku sieciowego. Wysyłanie więcej niż 10 pakietów na sekundę zwiększa zmiany pakietów wychodzących z zamówienia. Zobaczę, czy mogę przytoczyć to twierdzenie. Jeśli chodzi o same pakiety poza kolejnością, możesz je odrzucić lub dodać do kolejki wiadomości / przenoszenia w systemie, która została zaprojektowana do obsługi zmian w przeszłości w celu skorygowania obecnego stanu

Powinieneś mieć system przesyłania wiadomości, który polega na czymś, co zajmuje bardzo mało miejsca. Jeśli musisz wysłać coś, co może mieć tylko dwie wartości, wyślij tylko jeden bit. Jeśli wiesz, że możesz wykonać tylko 256 ruchów, wyślij znak bez znaku. Istnieją różne sztuczki, które pozwalają na jak najściślej upakowanie wiadomości.

Twój system komunikacyjny najprawdopodobniej wykorzysta wiele wyliczeń, aby umożliwić łatwe wydobycie ruchów z przychodzącej wiadomości (sugerowałbym rzucić okiem na RakNet i przykłady w nim, jeśli nie rozumiesz, co mam na myśli).

Jestem pewien, że już wiesz, że nie możesz naprawić problemów, które pojawiają się przy dużym opóźnieniu i utracie pakietów, ale możesz zmniejszyć ich efekty. Powodzenia!

EDYCJA: Powyższy komentarz Patricka Hughesa z linkiem sprawia, że ​​ta odpowiedź jest w najlepszym razie niekompletna i specyficzna dla pytania OP. Zamiast tego zdecydowanie zalecam przeczytanie powiązanych tematów


Bardzo dziękuję za szczegółową odpowiedź, która w zasadzie obejmuje wszystkie moje obawy. Tylko jedno pytanie dotyczące części, w której mówisz so every second, the server sends everything ... which the client then snaps to. Czy mogę po prostu wysłać jednocześnie dużą ilość takich informacji? Jaki jest rozsądny maksymalny rozmiar?
David Gouveia,

Rozsądnym maksimum będzie liczba, która nie wprowadza lagów:) ... Wiem, że nie szukałeś tej odpowiedzi, ale nie chcę zapisywać numeru, ponieważ nie znam twojej gry.
Samaursa,

Nie ma też twardej zasady, że powinieneś wysłać jedną dużą porcję, podałem to tylko jako przykład.
Samaursa,

@Samaursa - nie zgadzam się z komentarzem „Jeśli używasz UDP [co powinieneś, jeśli masz dużo danych]”. Ponieważ są one nowe w programowaniu sieciowym, TCP zajmuje się większością trudnych rzeczy, kosztem znacznie wolniejszego. W ich przypadku używałbym TCP, dopóki nie stał się wąskim gardłem. W tym momencie lepiej zrozumieliby, w jaki sposób ich aplikacja korzysta z sieci, więc wdrożenie UDP byłoby łatwiejsze.
Chris

@Chris: Nie będę się zgadzać :) ... decyzja pomiędzy użyciem TCP i UDP jest jak (imo) decyzja pomiędzy wyborem Pythona i C ++ do programowania. Teoretycznie może to zabrzmieć dobrze, ale w praktyce trudno będzie po prostu przełączyć się (ponownie, to jest imo).
Samaursa,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.