Jak działa przewidywanie po stronie klienta?

33

Przeczytałem Valve + Gafferon i setki stron z Google, ale z jakiegokolwiek powodu nie mogę się oprzeć prognozom klientów.

Według mnie podstawowym problemem jest:

  • Klient A wysyła dane wejściowe o T0
  • Serwer otrzymuje dane wejściowe o godzinie T1
  • Wszyscy klienci otrzymują zmianę o T2

Na T2Jednak użycie przewidywania klienta, klient A jest teraz w odpowiedniej pozycji do T4.

W jaki sposób upewniasz się, że klient A, przewidując, że serwer zaakceptuje żądanie przeniesienia, nie wyprzedzi serwera? Oczywiście przez cały czas, gdy są do przodu, powoduje to powrót do miejsca, w którym serwer ich ostatnio widział. Po wszystkich poprawkach, które próbowałem, nadal jest to zauważalne po zatrzymaniu, ponieważ serwer zatrzymuje się za tobą

Chris Evans
źródło

Odpowiedzi:

35

Napisałem na ten temat szereg artykułów . Opiera się na tych samych pomysłach, które przeczytałeś gdzie indziej, ale wyjaśnił w bardzo szczegółowy i (mam nadzieję) dostępny sposób.

W szczególności artykuł o prognozach po stronie klienta jest ten .

ggambett
źródło
Doskonałe artykuły :-) Chciałbym zobaczyć czwartą część serii. Jako mała sugestia, link do następnej części na końcu każdego z artykułów z pewnością poprawiłby nawigację.
LUB Mapper
5
@ORMapper - w końcu napisałem czwarty artykuł! gabrielgambetta.com/fpm4.html
ggambett
Wyrazy uznania dla twojej serii artykułów :-) Bardzo pomocna, dzięki :-)
LUB Mapper
Wszystkie artykuły (które mogłem znaleźć), które mówią o rekonstrukcji przeszłości za pomocą przechowywanych migawek, biorą za przykład strzelanie. Czy dotyczy to również ruchu? Mogę sobie wyobrazić, że resymulacja ruchu może prowadzić do dużych różnic dla innych graczy, jeśli mogą się ze sobą zderzyć. Powiedzmy, że dwóch graczy porusza się przeciwko sobie, a jeden z nich przestaje się oddalać o kilka „kroków” od punktu, który byłby punktem kolizji. Komendy zatrzymania są spóźnione z powodu opóźnienia, więc jeśli ponownie zasymulujemy świat, obaj gracze będą na bardzo różnych pozycjach
Lope
To interesujące pytanie. Niestety nie mam ostatecznej odpowiedzi. Myślę, że to zależy od tego, jak krytyczne są ruchy w grze; wpadasz na kogoś innego i nic się nie dzieje? W takim przypadku serwer prawdopodobnie nie dba o to, jest to postrzegane jako błąd przewidywania (wszyscy widzieliśmy, że dzieje się to w punktach dławikowych, prawda?). Czy zabijasz drugiego gracza przez kontakt? W takim przypadku poprawne wykonanie jest o wiele ważniejsze i może być warte powtórzenia. Zauważ, że w pewnym momencie musisz odrzucić niektóre pakiety jako „za stare”, w przeciwnym razie w dowolnym momencie możesz ponownie zasymulować od t = 0.
ggambett,
4

Nie zaimplementowałem tego (więc mogą pojawić się problemy, których nie widzę od razu), ale pomyślałem, że spróbuję pomóc.

Oto, co się wydarzyło:

Klient A wysyła dane wejściowe w T0

Serwer otrzymuje dane wejściowe w T1

Wszyscy klienci otrzymują zmianę w T2

Jednak w T2, używając prognozy klienta, klient A jest teraz w pozycji odpowiedniej dla T4.

Prawdopodobnie przydałoby się myśleć o czasie serwera. Jest (prawdopodobnie) bardzo podobny do działania interpolacji .

Każde polecenie jest wysyłane z czasem serwera. Ten czas serwera jest ustalany na początku meczu, sprawdzając tik serwera, kompensując czas pingowania. Na kliencie masz własną lokalną liczbę tików, a każde wysyłane polecenie jest konwertowane na tiki serwera (jest to prosta operacja odejmowania)

Ponadto klient zawsze renderuje „w przeszłości”. Zakładasz więc, że świat, który widzi klient, jest, powiedzmy, o 100 ms krótszy niż rzeczywisty czas serwera.

Przeformułujmy więc twój przykład z czasem serwera (oznaczonym przez S).

Klient wysyła dane wejściowe w czasie T0 z czasem serwera S0 (domyślam się, że tak naprawdę „reprezentacja czasu serwera przez klienta minus czas interpolacji”). Klient nie czeka na odpowiedź z serwera i natychmiast się przenosi.

Serwer otrzymuje dane wejściowe w T1. Serwer ustala wiarygodną pozycję klienta w czasie serwera S0 podaną przez klienta. Wysyła to do klienta.

Klient otrzymuje autorytatywną pozycję w T2 (nadal z wyznaczeniem czasu serwera S0). Klient śledzi przeszłe zdarzenia z przeszłości (prawdopodobnie tylko kolejkę wszystkich niepotwierdzonych prognoz).

Jeśli przewidywana pozycja / prędkość / cokolwiek, co serwer odsyła w S0, jest inne niż to, co klient zapisał w S0, klient jakoś sobie z tym poradzi. Albo przez przywrócenie odtwarzacza do poprzedniej pozycji, albo na powrót do poprzedniej informacji, albo może coś innego, o czym nie myślałem.

Tetrad
źródło
3
To wszystko jest poprawne, z wyjątkiem fragmentu dotyczącego renderowania klienta w przeszłości. W stosunku do serwera klient faktycznie renderuje w przyszłości! Serwer wie, że informacje, które posiada od każdego klienta, są stare i że od tego czasu każdy klient już się zmienił.
Kylotan
2

W rzeczywistości w github istnieje implementacja typu open source, która pokazuje, jak to się robi. Sprawdź Lance.gg

repozytorium github: https://github.com/lance-gg/lance

Kod prognozy klienta jest zaimplementowany w module o nazwie src/syncStrategies/ExtrapolateStrategy.js

Oprócz ekstrapolacji istnieją dwa pojęcia, o których nie wspomniałem wyżej:

  1. Gięcie przyrostowe. Zasadniczo zamiast stosowania korekcji serwera naraz, pozwalasz na zastosowanie delty w małych krokach. W ten sposób zdalne obiekty będą stopniowo dostosowywać swoje pozycje do pozycji serwera. Istnieje gięcie pozycji, gięcie prędkości, gięcie kątowe i gięcie prędkości kątowej. Możesz także chcieć różnych współczynników zginania dla różnych obiektów.
  2. Krok Ponowne uchwalenie. Fakt, że dane są w przeszłości, oznacza, że ​​możesz cofnąć czas do czasu danych serwera i zrestartować od tego momentu. Oczywiście nadal będziesz musiał pochylić się w kierunku nowo znalezionej pozycji, zamiast skakać do niej.
Gary Weiss
źródło
1

Klient A zawsze wyprzedza serwer - ale to nie ma znaczenia. Musisz tylko cofnąć klienta, jeśli serwer stwierdzi, że wystąpił problem z raportowaną pozycją, w którym to momencie klient ponownie uruchamia wszystkie zmiany, które wprowadził od czasu błędu z poprawionymi wartościami, aby doprowadzić go do stanu zgodnego z serwerem.

Aby to zrobić, klient musi zapamiętać niektóre z jego przeszłych stanów i poprzednich aktualizacji. Może to być tylko kilka prostych wartości, takich jak pozycja, prędkość, orientacja itp. Serwer okresowo wysyła potwierdzenie, że różne aktualizacje klienta były legalne, co oznacza, że ​​można je teraz zapomnieć. Jeśli jednak serwer zgłasza, że ​​aktualizacja była nieprawidłowa, stan klienta przywraca ten punkt i przyszłe zmiany są stosowane do tego zmodyfikowanego stanu.

Na dole artykułu Valve znajdują się dodatkowe linki, które warto przeczytać - jest to jeden z nich: https://developer.valvesoftware.com/wiki/Prediction

Kylotan
źródło
Tak, mam rację sądząc, że klient (at t=4) otrzymuje informacje o t=2, więc resetuje stan aby t=2następnie Ponowne wykonanie aktualizacji przynieść przedmioty od t=2do t=4?
George Duckett
Z jakiegoś powodu wciąż tego nie rozumiem. Serwer nie otrzymuje informacji o pozycji odtwarzacza, tylko dane wejściowe. Gracz porusza się więc od ostatniej pozycji, o której powiedział serwer. Dane wejściowe są stosowane. Serwer jest informowany. Serwer potwierdza dane wejściowe dla wszystkich. Zakładając, że wszystkie polecenia są akceptowane, serwer nadal będzie w tyle za klientem A - więc gdy klient A zatrzyma się, jego postać zatrzyma się natychmiast, a następnie wróci do lokalizacji serwerów, gdy otrzyma potwierdzenie zatrzymania.
Chris Evans
@GeorgeDuckett: tak (chociaż nie musi to być t = 4, może być za każdym razem, gdy wykryta zostanie rozbieżność, i może być dowolna liczba ponownych zastosowań).
Kylotan
@ChrisEvans: znany stan + zmiany oparte na danych wejściowych są w każdym razie równoważne stanowi wysyłania. Jeśli chodzi o przykład zatrzymania, to samo w sobie jest wejściem, a serwer nadal symuluje ruch, dopóki go nie otrzyma. Zakładając stałe opóźnienie, serwer zatrzyma odtwarzacz poruszający się dokładnie w tej samej pozycji, którą widział klient, gdy przestał się poruszać, ponieważ klient był przed serwerem. (W prawdziwym świecie opóźnienie jest różne, więc interpolujesz trochę, aby go wygładzić.)
Kylotan