Usprawnienia obliczania pingu

5.02.2019
Przez: Twórcy Fortnite
Witam wszystkich! Jestem Bart Hawthorne, starszy inżynier sieciowy w zespole Epica ds. silnika gry i chciałbym dziś porozmawiać o naszej metodzie obliczania wyświetlanej wartości pingu w Fortnite, a także o nowej metodzie wprowadzonej w wersji 7.30, jej wadach i naszych planach zwiększenia dokładności podawania tej informacji.

Na początek pokrótce wyjaśnię kilka terminów, których będę często używać w tym artykule, na potrzeby nieznających ich graczy:

Klient i serwer: Każda konsola, urządzenie mobilne czy komputer PC lub Mac jest klientem gry i klienci ci łączą się z dedykowanym serwerem, który prowadzi grę. Serwer ten przechowuje ogólny stan gry (położenie postaci graczy, stan kręgu burzy, aktywne skrzynie skarbów oraz mnóstwo innych rzeczy). Serwer jest również odpowiedzialny za aktualizację klientów gry, gdy stan gry się zmienia.

Pakiet: Pakiety to porcje danych wysyłane przez sieć od klienta do serwera lub odwrotnie. W ten sposób klienci i serwery gry porozumiewają się między sobą. W Fortnite (oraz ogólnie w UE4) wykorzystujemy te pakiety w metodzie obliczania pingu.

Klatka: Na serwerze termin „klatka” oznacza jednokrotne wykonanie wszystkich zadań serwera – sprawdzenie odbioru nowych pakietów, aktualizację stanu gry i wysyłkę aktualizacji do klientów. U klienta gry „klatka” oznacza jedną aktualizację obrazu na ekranie. Liczba klatek na sekundę (ang. FPS, frames per second) określa, ile klatek serwer lub klient gry przetwarza w każdej sekundzie. Jeżeli więc mówimy, że klient gry działa w trybie 60 klatek/sek., oznacza to, że w każdej sekundzie dochodzi do 60 takich aktualizacji.

Teraz przejdźmy do omawiania pingu!

Stara metoda obliczania pakietów pingu

Dla większości ludzi „ping” oznacza ilość czasu, jaką zajmuje klientowi wysłanie pakietu do serwera i otrzymanie odpowiedzi lub odwrotnie. Ten czas nazywamy również RTT (ang. round-trip time – czas podróży w obie strony). Zapewnia to również dane interesujące większość graczy, czyli ile czasu trwa otrzymanie reakcji serwera i ile czasu potrwa przesłanie ich reakcji do serwera. Im wyższa jest wartość pingu, tym bardziej prawdopodobne jest, że w grze wystąpią zakłócenia w rodzaju teleportacji graczy, niespodziewanych likwidacji bądź opóźnień w używaniu skrzyń i innych obiektów.

Wartość pingu podajemy w milisekundach – im mniej, tym lepiej. Jeśli mieszkacie tuż obok centrum danych, w którym znajdują się nasze serwery, możecie nawet mieć ping jednocyfrowy! Z kolei jeśli łączycie się z drugiego końca świata, wasz ping może wynosić 100 ms lub więcej.

Jednak komunikacja serwera z klientem jest bardziej złożona, niż wskazuje na to sam czas przesyłania pakietów tam i z powrotem. W Fortnite na ogół sprawdzamy obecność nowych pakietów raz na klatkę tuż po rozpoczęciu tej klatki, zarówno na serwerze, jak i w kliencie. Oznacza to, że jeśli pakiet nadejdzie zaraz po takim sprawdzeniu, może dojść do opóźnienia prawie całej klatki między nadejściem pakietu i odczytaniem go. Co więcej, samo odczytanie pakietu i aktualizacja stanu gry zgodnie z jego zawartością również wymaga czasu.

Oto schemat, który pomoże zobrazować sytuację:


Czarne strzałki klienta i serwera oznaczają czas. W tym przypadku klient i serwer są w pełnej synchronizacji, tj. działają z identyczną liczbą klatek/sek. Czarne linie pionowe oznaczają początek każdej klatki, niebieskie znaczki wskazują moment, w którym klient lub serwer sprawdza obecność nowych pakietów do przetworzenia, a pomarańczowe linie to moment wysłania pakietu. Fioletowe linie przerywane to pakiety przesyłane przez internet.

Oto kolejność zdarzeń:

  1. Klient gry wysyła pakiet do serwera
  2. Serwer otrzymuje pakiet nadesłany przez klienta.
  3. Serwer sprawdza, czy nadeszły nowe pakiety – dopiero wtedy serwer może zobaczyć pakiet nadesłany przez klienta. Zauważcie, że ten pakiet siedzi bezczynnie przez prawie całą klatkę, gdyż nadszedł on zaraz po tym, jak serwer sprawdził obecność nowych pakietów.
  4. Serwer wysyła do klienta potwierdzenie odbioru pakietu.
  5. Odpowiedź serwera dociera do klienta.
  6. Klient czyta odpowiedź serwera.
Chcemy tylko wyświetlać RTT oznaczony zielonymi strzałkami i odjąć czas oznaczony czerwonymi strzałkami. Staramy się jak najwierniej to odwzorować – gdy wysyłamy pakiet z serwera do klienta, zawiera on czas klatki serwera, więc dla danego pakietu odejmujemy ten czas, a także czas klatki klienta do momentu przetworzenia pakietu. Metoda obliczania pingu wygląda więc następująco:
Ping = czas podróży pakietu do serwera + czas podróży pakietu do klienta - czas klatki serwera - aktualny czas klatki klienta

Na potrzeby wyświetlanej wartości wyliczamy średnią wartość z pakietów z ostatnich kilku sekund, aby otrzymać bardziej stabilną wartość.

Jednak w praktyce nie działa to tak dokładnie, jak byśmy chcieli. Obliczenia te nie biorą pod uwagę czasu oczekiwania pakietów na ich przetworzenie, więc wyświetlana wartość często jest zbyt wysoka. Problem stanowi to, że większość platform nie daje możliwości określenia, kiedy dokładnie dany pakiet przybył, więc nie wiemy, ile dokładnie czasu należy odjąć.

Metoda obliczania pingu pakietów w wersji 7.30

W wersji 7.30 chcieliśmy spróbować czegoś nowego – a jeśli wykorzystamy tylko pakiety, którym droga od klienta do serwera i uzyskanie odpowiedzi zajmuje najmniej czasu? W teorii pakiety, które zajmują najmniej czasu, będą pakietami przetwarzanymi od razu, co pozwoli nam zignorować czas oczekiwania, gdyż będzie on znikomy. Ostatecznie wykorzystaliśmy do obliczania pingu najszybsze 25% pakietów, gdyż wydawało się nam to rozsądnym kompromisem między wyborem najszybszych pakietów i zebraniem wystarczającej ilości danych, by otrzymać realistyczną wartość.

Co poszło nie tak?

Po wypuszczeniu wersji 7.30 otrzymaliśmy mnóstwo zgłoszeń od graczy, którzy widzieli jednocyfrowy (czasem nawet zerowy!) ping w warunkach, w których ewidentnie powinien być on wyższy. Jeśli wierzyć tej wartości, wysyłalibyśmy pakiety z prędkością nadświetlną, a nasz silnik gry, choć znakomity, takich możliwości jeszcze nie posiada! A zatem coś ewidentnie poszło nie tak.

Problem dotyczy naszego sposobu obliczania pingu. Przedstawiam go ponownie w celu zilustrowania sytuacji:
Ping = czas podróży pakietu do serwera + czas podróży pakietu do klienta - czas klatki serwera - aktualny czas klatki klienta

Problem polega na odejmowaniu czasu klatki serwera. Jeśli bierzemy pod uwagę tylko najszybsze pakiety, odejmujemy zdecydowanie za dużo – jeśli pakiet zostanie przeczytany przez serwer zaraz po dotarciu na miejsce, przetworzenie jego zawartości i wysłanie odpowiedzi może zająć tylko kilka milisekund. Cała klatka serwera może trwać nawet 5 razy dłużej!

Oto schemat przedstawiający tę sytuację:



Tutaj klient ma dużo wyższą liczbę klatek/sek. od serwera. Ponieważ bierzemy pod uwagę tylko zielone pakiety, przychodzą one do serwera tuż przed sprawdzeniem ich obecności. W tym wypadku serwer działa szybciej od ustalonej liczby klatek/sek., więc na początku klatki, jeszcze przed sprawdzeniem obecności nowych pakietów, przez pewien czas czeka bezczynnie.
 



Czerwona linia oznacza odejmowaną liczbę klatek serwera/sek., lecz w tej sytuacji zamiast niej należałoby odjąć pomarańczową linię. Teraz odejmujemy zbyt dużo czasu, przez co u niektórych klientów gry ping jest równy 0.

Przy wykorzystaniu poprzedniej metody, biorącej pod uwagę szerszy wachlarz pakietów, nadal stanowi to problem. Bardziej prawdopodobne jest jednak uwzględnienie pakietów, które długo czekają na przetworzenie, więc mogą one zrównoważyć pakiety, przy których odjęliśmy zbyt dużo czasu.

Plany na przyszłość

W kolejnych wersjach gry planujemy wprowadzić pewne usprawnienia, które zwiększą dokładność podawania wartości pingu. Jak wspomniałem wcześniej:

„Problem stanowi to, że większość platform nie daje możliwości określenia, kiedy dokładnie dany pakiet przybył, więc nie wiemy, ile dokładnie czasu należy odjąć.”


Na szczęście Linux oferuje taką możliwość! A skoro nasze serwery pracują wyłącznie pod Linuxem, możemy zmienić sposób obliczania pingu na następujący:

Ping = czas podróży pakietu do serwera + czas podróży pakietu do klienta - czas między dotarciem pakietu do serwera i wysłaniem odpowiedzi - aktualny czas klatki klienta


W ten sposób omijamy konieczność zgadywania, poprzez odejmowanie czasu klatki serwera. Spodziewamy się, że zapewni nam to dużo dokładniejsze wartości, lecz metoda ta wymaga jeszcze wielu testów.

Badamy również możliwość częstszego sprawdzania nowych pakietów u klienta, co pozwoliłoby nam usunąć z obliczeń aktualny czas klatki klienta.

Mamy nadzieję, że te dwie zmiany spowodują znaczne zwiększenie dokładności wyświetlanego pingu.

Zakończenie

Mam nadzieję, że przydało wam się to wyjaśnienie naszej metody obliczania pingu i pracy nad jej usprawnieniem. Jeśli chodzi o podawanie tego rodzaju danych, ich dokładność ma ogromne znaczenie – podanie nieprawdziwej wartości może być gorsze, niż jej całkowity brak. Gracze muszą wiedzieć, kiedy spudłowali, a także czy opóźnienia gry są spowodowane kiepskim pingiem, co pozwoli im dostosować się do sytuacji.

Będę czytać wasze komentarze i w miarę możliwości chętnie odpowiem na zadane pytania. Dziękuję za uwagę!