Megapack: Jak Factorio rozwiązał problem z trybem wieloosobowym dla 200 graczy

Megapack: Jak Factorio rozwiązał problem z trybem wieloosobowym dla 200 graczy
W maju tego roku brałem udział jako zawodnik w Wydarzenia MMO KatherineOfSky. Zauważyłem, że gdy liczba graczy osiągnie określoną liczbę, co kilka minut niektórzy z nich „odpadają”. Na szczęście dla ciebie (ale nie dla mnie) byłem jednym z tych graczy, którzy się rozłączyli za każdym razem, nawet przy dobrym połączeniu. Potraktowałem to jako osobiste wyzwanie i zacząłem szukać przyczyn problemu. Po trzech tygodniach debugowania, testowania i poprawek błąd został w końcu naprawiony, ale nie było to wcale takie łatwe.

Problemy z grami wieloosobowymi są bardzo trudne do wyśledzenia. Zwykle występują one przy bardzo specyficznych parametrach sieci i bardzo specyficznych warunkach gry (w tym przypadku przy więcej niż 200 graczach). Nawet jeśli problem można odtworzyć, nie można go poprawnie zdiagnozować, ponieważ wstawienie punktów przerwania zatrzymuje grę, dezorientuje liczniki czasu i zwykle powoduje przekroczenie limitu czasu połączenia. Ale dzięki wytrwałości i wspaniałemu narzędziu tzw niezdarny Udało mi się dowiedzieć co się dzieje.

Krótko mówiąc, z powodu błędu i niekompletnej implementacji symulacji stanu opóźnienia, klient czasami znajdował się w sytuacji, w której musiał wysłać pakiet sieciowy składający się z działań gracza związanych z wyborem wejścia, obejmujących około 400 jednostek gry w jednym cyklu zegara ( nazywamy to „megapakietem”). Serwer musi wówczas nie tylko poprawnie odebrać wszystkie te działania wejściowe, ale także wysłać je do wszystkich innych klientów. Jeśli masz 200 klientów, szybko staje się to problemem. Łącze z serwerem szybko się zatyka, co prowadzi do utraty pakietów i kaskady pakietów żądanych ponownie. Opóźnienie akcji wejściowej powoduje wówczas, że jeszcze więcej klientów wysyła megapakiety, co powoduje, że lawina staje się jeszcze większa. Szczęśliwym klientom udaje się odzyskać siły, wszyscy inni odpadają.

Megapack: Jak Factorio rozwiązał problem z trybem wieloosobowym dla 200 graczy
Problem był dość zasadniczy i jego naprawienie zajęło mi 2 tygodnie. Jest to dość techniczne rozwiązanie, dlatego poniżej wyjaśnię ciekawe szczegóły techniczne. Ale najpierw musisz wiedzieć, że od wersji 0.17.54, wydanej 4 czerwca, w obliczu tymczasowych problemów z połączeniem, tryb wieloosobowy stał się stabilniejszy, a ukrywanie opóźnień stało się znacznie mniej bujne (mniejsze spowolnienie i teleportacja). Zmieniłem także sposób ukrywania opóźnienia w walce i mam nadzieję, że dzięki temu będzie ono trochę płynniejsze.

Mega pakiet dla wielu graczy – szczegóły techniczne

Mówiąc prościej, tryb wieloosobowy w grze działa w następujący sposób: wszyscy klienci symulują stan gry, odbierając i wysyłając tylko dane wejściowe gracza (zwane „akcjami wejściowymi”, Akcje wejściowe). Głównym zadaniem serwera jest transfer Akcje wejściowe i kontroluj, czy wszyscy klienci wykonują te same czynności w tym samym cyklu zegara. Więcej na ten temat przeczytacie w poście FFF-149.

Ponieważ serwer musi podejmować decyzje dotyczące tego, jakie akcje wykonać, działania gracza przebiegają mniej więcej według następującej ścieżki: akcja gracza -> klient gry -> sieć -> serwer -> sieć -> klient gry. Oznacza to, że akcja każdego gracza jest wykonywana dopiero po odbyciu podróży w obie strony przez sieć. Z tego powodu gra wydawałaby się strasznie powolna, dlatego niemal natychmiast po wprowadzeniu do gry trybu wieloosobowego wprowadzono mechanizm ukrywania opóźnień. Ukrywanie opóźnienia symuluje działania gracza bez uwzględnienia działań innych graczy i decyzji serwera.

Megapack: Jak Factorio rozwiązał problem z trybem wieloosobowym dla 200 graczy
Factorio ma stan gry stan gry to pełny stan karty, gracza, bytów i wszystkiego innego. Jest ona deterministycznie symulowana we wszystkich klientach na podstawie działań otrzymanych z serwera. Stan gry jest święty i jeśli kiedykolwiek zacznie różnić się od stanu serwera lub innego klienta, następuje desynchronizacja.

Ale stan gry mamy stan opóźnień Stan opóźnienia. Zawiera mały podzbiór stanu podstawowego. Stan opóźnienia nie jest święte i po prostu przedstawia obraz tego, jak będzie wyglądał stan gry w przyszłości, na podstawie informacji wejściowych od graczy Akcje wejściowe.

W tym celu przechowujemy kopię utworzonej Akcje wejściowe w kolejce opóźnień.

Megapack: Jak Factorio rozwiązał problem z trybem wieloosobowym dla 200 graczy
Oznacza to, że na końcu procesu po stronie klienta obraz wygląda mniej więcej tak:

  1. Stosować Akcje wejściowe wszyscy gracze stan gry sposób, w jaki te działania wejściowe zostały odebrane z serwera.
  2. Usuwamy wszystko z kolejki opóźnień Akcje wejściowe, do których według serwera zostały już zastosowane stan gry.
  3. Usuwać Stan opóźnienia i zresetuj go, aby wyglądał dokładnie tak samo jak stan gry.
  4. Stosujemy wszystkie akcje z kolejki opóźnień do Stan opóźnienia.
  5. Na podstawie danych stan gry и Stan opóźnienia Renderujemy grę dla gracza.

Wszystko to powtarza się w każdym takcie.

Zbyt trudne? Nie relaksuj się, to nie wszystko. Aby zrekompensować zawodne połączenia internetowe, stworzyliśmy dwa mechanizmy:

  • Pominięte znaczniki: kiedy serwer tak zdecyduje Akcje wejściowe zostanie wykonany w rytmie gry, a następnie w przypadku jego nieotrzymania Akcje wejściowe jakiegoś gracza (na przykład z powodu zwiększonego opóźnienia), nie będzie czekać, ale poinformuje tego klienta „Nie wziąłem pod uwagę Twojego Akcje wejściowe, postaram się je dodać w następnym takcie.” Dzieje się tak, aby z powodu problemów z połączeniem (lub komputerem) jednego gracza aktualizacja mapy nie spowolniła się u wszystkich pozostałych. Warto to zauważyć Akcje wejściowe nie są ignorowane, ale po prostu odkładane na bok.
  • Pełne opóźnienie w obie strony: serwer próbuje odgadnąć, jakie jest opóźnienie w obie strony między klientem a serwerem dla każdego klienta. Jeśli to konieczne, co 5 sekund negocjuje z klientem nowe opóźnienie (w oparciu o zachowanie połączenia w przeszłości) i odpowiednio zwiększa lub zmniejsza opóźnienie w obie strony.

Same te mechanizmy są dość proste, ale gdy są używane razem (co często zdarza się przy problemach z połączeniem), logiką kodu staje się trudna do zarządzania i przy wielu przypadkach brzegowych. Dodatkowo, gdy te mechanizmy wchodzą w grę, serwer i kolejka opóźniająca muszą poprawnie implementować funkcję specjalną Wejście Akcja prawo ZatrzymajMovementInTheNextTick. Dzięki temu, jeśli pojawią się problemy z połączeniem, postać nie ucieknie sama (np. pod pociąg).

Teraz musimy wyjaśnić, jak działa wybór jednostek. Jeden z przekazywanych typów Wejście Akcja jest zmianą stanu wyboru podmiotu. Informuje wszystkich, nad którym bytem znajduje się gracz. Jak możesz sobie wyobrazić, jest to jedna z najczęstszych akcji wejściowych wysyłanych przez klientów, więc aby zaoszczędzić przepustowość, zoptymalizowaliśmy ją tak, aby zajmowała jak najmniej miejsca. Działa to tak, że po wybraniu każdego obiektu, zamiast przechowywać bezwzględne, bardzo precyzyjne współrzędne mapy, gra zapisuje względne przesunięcie o niskiej precyzji w stosunku do poprzedniego wyboru. Działa to dobrze, ponieważ zaznaczenia myszy są zwykle bardzo zbliżone do poprzedniego wyboru. Wiąże się to z dwoma ważnymi wymaganiami: Akcje wejściowe Nigdy nie należy ich pomijać i należy je wykonywać we właściwej kolejności. Wymagania te są spełnione stan gry. Ale od zadania Stan opóźnienia jeśli chodzi o „wyglądanie wystarczająco dobrze” dla gracza, nie są one usatysfakcjonowane w stanie opóźnienia. Stan opóźnienia nie bierze pod uwagę wiele przypadków Edge, związane z pomijaniem cykli zegara i zmieniającymi się opóźnieniami transmisji w obie strony.

Już można się domyślić, dokąd to zmierza. W końcu zaczynamy widzieć przyczyny problemu megapacka. Źródłem problemu jest to, na czym opiera się logika wyboru jednostek Stan opóźnienia, a stan ten nie zawsze zawiera prawidłowe informacje. Dlatego generowany jest megapakiet mniej więcej tak:

  1. Odtwarzacz ma problemy z połączeniem.
  2. W grę wchodzą mechanizmy pomijania cykli zegara i regulowania opóźnienia transmisji w obie strony.
  3. Kolejka stanu opóźnienia nie uwzględnia tych mechanizmów. Powoduje to, że niektóre czynności zostaną przedwcześnie usunięte lub wykonane w niewłaściwej kolejności, co skutkuje niepoprawnością Stan opóźnienia.
  4. Gracz ma problem z połączeniem i aby dogonić serwer symuluje aż 400 cykli.
  5. Przy każdym zaznaczeniu generowana jest nowa akcja zmieniająca wybór podmiotu i przygotowywana do wysłania na serwer.
  6. Klient wysyła na serwer mega-partię ponad 400 zmian w wyborze podmiotów (a przy innych działaniach: stany strzelania, stany chodzenia itp. również występował ten problem).
  7. Serwer otrzymuje 400 działań wejściowych. Ponieważ nie wolno pomijać żadnych akcji wejściowych, nakazuje wszystkim klientom wykonanie tych akcji i wysyła je przez sieć.

Ironią jest to, że mechanizm zaprojektowany w celu oszczędzania przepustowości zakończył się utworzeniem ogromnych pakietów sieciowych.

Rozwiązaliśmy ten problem, naprawiając wszystkie przypadki brzegowe obsługi aktualizacji i kolejek zaległości. Choć zajęło to sporo czasu, ostatecznie warto było to zrobić dobrze, zamiast polegać na szybkich hackach.

Źródło: www.habr.com

Dodaj komentarz