Помощь по обмену сообщениями между клиентом и сервером в сети

10

Я делаю физическую игру с быстрым темпом - настольный хоккей. С двумя молотками и одной шайбой. Игра работает на iphone / ipad, и я делаю многопользовательскую часть через GameCenter.

Так работает сетевая система. Клиент, которому назначено совпадение, будет считаться сервером, а тот, который принимает запрос на совпадение, является клиентом.

На «сервере» запущена физика, и ответ мгновенен, а на клиенте также запущена физика, поэтому он выглядит гладко между обменом сообщениями. Что я делаю в качестве сервера, так это то, что я отправляю клиенту мою скорость шайбы и мою позицию, и клиент регулирует свою скорость / позицию шайбы, связанную с сервером, чтобы поддерживать его синхронизацию. В противном случае физика десинхронизируется и облажается.

Когда сетевая задержка хорошая, ниже 100 мс, результаты довольно хорошие, я получил плавную играбельную игру на стороне клиента, и странное поведение минимально. Проблема возникает, когда задержка составляет от 150 до 200 мс. В этом случае случается, что моя клиентская шайба уже достигла края и перевернутого направления, но получает сообщение о задержке от сервера и немного отступает назад, вызывая странное чувство поведения мяча.

Я прочитал несколько вещей об этом:

Пример сети понг

Нажмите Пример синхронизации

Википедия о синхронизации часов

Итак, как я могу решить это? Насколько я прочитал, лучший вариант, который у меня есть, это выполнить синхронизацию часов на сервере / клиенте с отметкой времени, чтобы, когда я получаю сообщения о задержке, связанные с моими часами, я просто игнорировал их и позволял моделированию клиентов выполнять работа. Ребята, вы согласны с этим? И так как я посылаю ненадежные данные (UDP), я могу получить задержанные сообщения или сообщения не в порядке.

Если это лучший подход, как мне реализовать синхронизацию часов. Я прочитал шаги о том, как, но я не совсем понял это.

Это говорит о том, что:

  1. Клиент маркирует текущее местное время в пакете «запрос времени» и отправляет его на сервер.
  2. При получении сервером сервер помечает серверное время и возвращает
  3. После получения клиентом клиент вычитает текущее время из отправленного времени и делит на два для вычисления задержки. Он вычитает текущее время из времени сервера, чтобы определить дельту времени клиент-сервер, и добавляет половину задержки, чтобы получить правильную дельту часов. (Пока этот algothim очень похож на SNTP)
  4. Клиент повторяет шаги с 1 по 3 пять или более раз, каждый раз останавливаясь на несколько секунд. В промежуточный период может быть разрешен другой трафик, но его следует минимизировать для достижения наилучших результатов. Результаты приема пакетов накапливаются и сортируются в порядке наименьшей задержки на наибольшую задержку. Медианная задержка определяется путем выбора выборки средней точки из этого упорядоченного списка.
  5. Все образцы выше приблизительно 1 стандартного отклонения от медианы отбрасываются, а остальные образцы усредняются с использованием среднего арифметического.

Следуя этому примеру, я бы получил это:

Давайте представим, что игра загружена, а время моего клиента равно 0, поэтому я отправляю на сервер, что мое время равно 0.

Сообщение занимает 150 мс, чтобы добраться до сервера, но часы сервера уже запущены и опережают клиента на 1 секунду. Когда сервер получит сообщение, время будет: 1.15 и отправит это время клиенту, хорошо? Давайте представим, что наше отставание постоянно на 150 мс.

Теперь клиент получает время 1,15 и вычитает текущее время из отправленного времени и делит на два, чтобы вычислить задержку. Что составляет: 0,3 - 0 = 0,3 / 2 -> 150 мс.

Он вычитает текущее время из времени сервера, чтобы определить дельту времени клиент-сервер, и добавляет половину задержки, чтобы получить правильную дельту часов:
Время клиента: 0,3 Время сервера 1,15
0,3 - 1,15 = .85 + задержка (.15) = 1

Как это синхронизировано? Что мне не хватает?

Это мой первый опыт многопользовательской игры и работы в сети, поэтому я немного запутался.

Спасибо.

gmemario
источник
Хороший вопрос. Не могли бы вы исправить: 0,3 - 1,15, чтобы быть 1,15 - 0,3?
Кьяран

Ответы:

12

Опубликованный алгоритм был верным, но в своем примере вы забыли о времени, которое требуется для того, чтобы серверный пакет добрался до клиента, поэтому:

Server time: 1
Client time: 0
Client sends 0 to server

... 150ms to get to server  (ping is 300! not 150ms in this case. Ping is round-trip)

Server time: 1.15
Client time: 0.15
Server receives packet and sends client 1.15

... 150ms to get back to client

Server time: 1.30
Client time: 0.30
Client receives 1.15 from server

Теперь, как вы можете видеть, если бы клиент изменил свои часы на 1.15, он бы отставал от сервера на 0.15, поэтому вам нужно настроить Ping (так называемое Round Trip Time [RTT]). Вот полный расчет времени дельты, сделанный за много шагов:

Server Time - Current Time + Ping / 2
= Server Time - Current Time + (Current Time - First Packet Time) / 2
= 1.15 (Perceived, not actual!) - 0.30 + (0.30 - 0.00) / 2
= 1.00

Это дает нам правильное время дельта 1,00 секунд

Джон Макдональд
источник
Я получаю его, поэтому мои клиентские часы равны 1,00, а сервер - 1,30. После того, как я получаю свое сообщение от сервера, мне нужно добавить пинг, чтобы проверить, является ли сообщение поздним или что-то еще? Другое дело, что, если задержка изменится, я должен постоянно делать эти вычисления?
gmemario 20.10.11
Дельта-время 1,00 с должно быть добавлено к текущим часам, чтобы время клиента равнялось времени сервера. Задержка всегда меняется, каждый пакет будет иметь немного отличающийся RTT. На коротком временном интервале, как в игре, я просто сделал бы синхронизацию времени только один раз, у клиента будет довольно хорошее предположение относительно того, каково время сервера, и оба часа должны двигаться вперед примерно с одинаковой скоростью. Единственное исключение будет, если вы получили пакет, который, кажется, из будущего. В этом случае есть 2 решения: 1) Выполните новую синхронизацию времени. 2) Продвиньте свои часы, чтобы соответствовать будущим часам
Джон Макдональд
Хорошо, давайте посмотрим на эту ситуацию, просто чтобы убедиться, что я понял: Время сервера: 1 с Время клиента: 0 с 150 мсек, чтобы добраться до сервера Время сервера: 1.15 с Время клиента: 0,15 с Сервер отправляет клиенту 1,15 с 200 мс, чтобы добраться до клиента Итак, мой пинг сейчас составляет 350 мс. Это верно? Время сервера: 1,35 Время клиента: 0,35 Выполнение вычислений: 1,15 - .35 + (0,35 - 0,00) / 2 Теперь мой deltaTime = 0,975 Если я добавлю время дельты 0,975 к своему .35, я получу: 1,325, что является своего рода рассинхронизацией. Это правильно, а?
gmemario 20.10.11
@Gilson, это правильно. Если задержка для двух пакетов разная (почти гарантированная), вычисление дельта-времени клиента не будет совершенным. У клиента никогда не будет совершенства. В алгоритме, который вы описали в вопросе, дельта-время рассчитывалось несколько раз, и был метод выбора и усреднения этих результатов. Многие методы временной синхронизации будут делать что-то подобное, но обычно они предназначены для критически важных для бизнеса и долгосрочных серверов, например для фондовых бирж. Точность, которую вы требуете для короткой игры, думаю, будет излишней.
Джон Макдональд
@ Gilson, пока клиент имеет разумную оценку времени сервера и обе часы движутся вперед примерно с одинаковой скоростью, вы не должны замечать никаких проблем. Когда клиент или сервер отправляют действие другой стороне, они отправляют свое местное время. На принимающей стороне сообщение всегда должно быть в прошлом и должно интерпретироваться как прошедшее событие. Это означает, что вам нужна вся информация в пакете, такая как местоположение, скорость (величина + направление) и время. Затем вы можете поместить шайбу тут же и переместить шайбу из прошлого в настоящее. Кстати, я в чате
Джон Макдональд,