Как сохранить синхронизацию структуры данных по сети?

12

контекст

В игре, над которой я работаю (своего рода графическое приключение типа «укажи и щелкни»), почти все, что происходит в игровом мире, управляется менеджером действий, который структурирован примерно так:

введите описание изображения здесь

Так, например, если в результате изучения объекта персонаж должен сказать привет, немного пройтись и затем сесть, я просто создаю следующий код:

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"));

Это создаст новую группу действий (целая строка на рисунке выше) и добавит ее в менеджер. Все группы выполняются параллельно, но действия внутри каждой группы связаны друг с другом, так что вторая начинается только после окончания первой. Когда завершается последнее действие в группе, группа уничтожается.

проблема

Теперь мне нужно скопировать эту информацию по сети, чтобы в многопользовательском сеансе все игроки видели одно и то же. Сериализация отдельных действий не является проблемой. Но я абсолютный новичок, когда дело доходит до сетей, и у меня есть несколько вопросов. Я думаю, для простоты в этом обсуждении мы можем абстрагировать компонент диспетчера действий от простого:

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

Как мне продолжать синхронизировать содержимое вышеупомянутой структуры данных между всеми игроками?

Помимо основного вопроса, у меня также есть несколько других проблем, связанных с этим (то есть все возможные последствия той же проблемы выше):

  • Если я использую архитектуру сервер / клиент (с одним из игроков, выступающим в роли как сервера, так и клиента), и один из клиентов породил группу действий, должен ли он добавить их непосредственно менеджеру или только отправить запрос на сервер, который, в свою очередь, прикажет каждому клиенту добавить эту группу ?
  • Как насчет потерь пакетов и тому подобного? Игра детерминированная, но я думаю, что любое несоответствие в последовательности действий, выполняемых в клиенте, может привести к противоречивым состояниям мира. Как мне защититься от подобных проблем ?
  • Что если я добавлю слишком много действий одновременно, не вызовет ли это проблем для соединения? Есть ли способ облегчить это?
Дэвид Гувея
источник
5
Обязательная ссылка «сначала прочти » gafferongames.com/networking-for-game-programmers, которая не очень техническая, несмотря на наличие некоторого кода, но многословна и довольно хорошо объясняет ваши варианты. Кроме того, это поставит вас в равное положение со всеми, кто читает эту ссылку, и это счастливое место.
Патрик Хьюз
@PatrickHughes Спасибо за ссылку! Я определенно буду читать все это.
Дэвид Гувейя
Есть несколько пакетов, которые управляют такими вещами для вас. Я не знаю, насколько это эффективно или быстро, но NowJS ( nowjs.com ) - интересный пример. С точки зрения разработчиков, вы просто разделяете структуры данных между клиентом и сервером. Измени одно, другое изменится.
Тим Холт

Ответы:

9

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

В этом случае у вас есть три варианта, два из которых вы уже упомянули. Выбор того, какой вариант вы выберете, зависит от множества факторов, из которых только вы можете быть лучшим судьей:

1: Клиент порождает группу действий, добавляет их напрямую, а затем отправляет на сервер. Сервер принимает это без каких-либо проверок

* Преимущества этого подхода состоят в том, что в отношении клиента его собственные действия дают им мгновенную обратную связь независимо от задержки в сети. Недостатком является то, что сообщения клиента принимаются сервером как факт (которым они могут не быть) *

2: Клиент отправляет запрос на сервер, который, в свою очередь, передает запрос всем, включая клиента, который отправил запрос.

Преимущества этого подхода состоят в том, что сервер теперь контролирует все, что происходит в игре, что устраняет большинство проблем с нелегальной деятельностью (здесь нелегальная может варьироваться от того, как клиент обрезает стену, до выполнения движения, которое теперь незаконно, потому что сервер имеет больше информации, которой у клиента не было, когда клиент сделал определенный ход)

3: Клиент порождает группу действий, добавляет их напрямую, а затем отправляет их на сервер. Сервер обрабатывает запрос, выполняет собственные проверки действий, чтобы убедиться, что они не являются незаконными, и затем передает ход всем игрокам, включая клиента.

Это занимает лучшее из 1 и 2 за счет чуть большего требования к пропускной способности. Преимуществами являются мгновенная обратная связь с клиентом для его собственных ходов, и если ходы, которые делает клиент, являются незаконными, сервер предпринимает соответствующие действия (принудительно исправляет клиента).

Как насчет потерь пакетов и тому подобного? Игра детерминированная, но я думаю, что любое несоответствие в последовательности действий, выполняемых в клиенте, может привести к противоречивым состояниям мира. Как мне защититься от подобных проблем?

Потерю пакетов и экстремальное отставание трудно исправить . Для решения проблемы используются разные решения (ни одно из которых не является идеальным), в зависимости от типа игры (например, расчет замерзших). Я предполагаю, что игра не должна синхронизировать физику.

Одна из первых вещей, которые вы должны сделать, это отметить все ваши ходы. Ваша система должна принять это во внимание и соответствующим образом отыграть ходы (возможно, на сервере лежит эта ответственность, а затем запретить незаконные ходы). При реализации этой системы принимайте 0 задержек (тестируйте локально).

0 задержка - это, конечно, не очень хорошее предположение, даже в локальных сетях, так что теперь, когда все движутся, уважают время, какому времени вы доверяете? Вот где проблема синхронизации времени вступает в игру. Многие статьи / статьи онлайн помогут вам решить эту проблему.

Что если я добавлю слишком много действий одновременно, не вызовет ли это проблем для соединения? Есть ли способ облегчить это?

Сначала очевидные вещи. Никогда не отправляйте строки или сериализованные объекты. Не отправляйте сообщения так быстро, как сама игра обновляется. Отправляйте их кусками (например, 10 раз в секунду). Будет ошибка (особенно ошибка округления), что серверу / клиенту потребуется время от времени исправлять ошибки (поэтому каждую секунду сервер отправляет все [все = все данные, которые важны для поддержания правильной игры в вашей игре). состояние], к которому клиент затем привязывается и / или учитывает для исправления своего состояния).РЕДАКТИРОВАТЬ: Один из моих коллег упомянул, что если вы используете UDP [что вы должны, если у вас много данных], то нет порядка сети. Отправка более 10 пакетов в секунду увеличивает изменения пакетов, поступающих не по порядку. Я посмотрю, смогу ли я привести это утверждение. Что касается самих неупорядоченных пакетов, вы можете либо отбросить их, либо добавить их в очередь сообщений / перемещений вашей системы, которая предназначена для обработки изменений в прошлом с целью исправления текущего состояния.

У вас должна быть система обмена сообщениями, которая опирается на то, что занимает очень мало места. Если вам нужно отправить что-то, что может иметь только два значения, отправьте только один бит. Если вы знаете, что можете сделать только 256 ходов, отправьте неподписанный символ. Существуют различные хитрые трюки, чтобы максимально плотно упаковывать сообщения.

Ваша система обмена сообщениями, скорее всего, будет использовать множество перечислений, чтобы вы могли легко извлекать ходы из входящего сообщения (я бы посоветовал взглянуть на RakNet и примеры в нем, если вы не понимаете, что я имею в виду здесь).

Я уверен, что вы уже знаете, что вы не можете исправить проблемы, возникающие из-за высокой задержки и потери пакетов, но вы можете уменьшить их влияние. Удачи!

РЕДАКТИРОВАТЬ: комментарий Патрика Хьюза выше со ссылкой делает этот ответ неполным и специфическим для вопроса ОП в лучшем случае. Я бы настоятельно рекомендовал вместо этого прочитать связанные темы

Samaursa
источник
Большое спасибо за подробный ответ, который в значительной степени покрывает все мои проблемы. Только один вопрос относительно той части, где вы говорите so every second, the server sends everything ... which the client then snaps to. Могу ли я просто отправить большое количество информации сразу? Какой разумный максимальный размер?
Дэвид Гувейя
Разумным максимумом будет число, которое не вводит задержку :) Я знаю, что вы не искали этот ответ, но я не хочу записывать число, потому что я не знаком с вашей игрой.
Самаурса
Кроме того, нет строгого правила, что вы должны отправлять один большой кусок, я просто привел это в качестве примера.
Самаурса
@Samaursa - я не согласен с комментарием «Если вы используете UDP [что вам следует, если у вас много данных]». Так как они являются новичками в сетевом программировании, TCP позаботится о большинстве трудностей для вас за счет того, что это будет намного медленнее. В их случае я бы использовал TCP, пока он не стал узким местом. В этот момент у них будет лучшее понимание того, как их приложение использует сеть, так что реализация UDP будет проще.
Крис
@Chris: я не соглашусь :) ... решение между использованием TCP и UDP похоже на (imo) решение между выбором Python и C ++ для разработки. Теоретически это может звучать нормально, но на практике это будет трудно просто переключиться (опять же, это IMO).
Самаурса