Поскольку в качестве примера вы использовали Quake3, я сосредоточусь на том, как там все делается. Первое, что вам нужно понять, это то, что «состояние игры» по отношению к играм клиент-сервер не относится ко всему внутреннему состоянию объекта, включая текущее состояние ИИ, функции коллизий, таймеры и т. д. Сервер игры на самом деле дает клиенту НАМНОГО меньше. Только положение объектов, ориентация, модель, кадр в анимации модели, скорость и физический тип. Последние два используются, чтобы сделать движение более плавным, позволяя клиенту имитировать баллистическое движение, но это все.
В каждом внутриигровом кадре, который происходит примерно 10 раз в секунду, сервер запускает физику, логику и таймеры для всех объектов в игре. Затем каждый объект вызывает функцию API для обновления своего нового положения, кадра и т. д., а также для обновления того, был ли он добавлен или удален в этом кадре (например, кадр удаляется из-за того, что он попал в стену). Вообще, в Quake 3 есть интересный баг на этот счет - если выстрел движется во время фазы физики, и попадает в стену, он удаляется, и удаляется единственное обновление, которое получает клиент, а не предыдущий полет в сторону стены, поэтому клиент видит, как выстрел исчезает в воздухе за 1/10 секунды до того, как он действительно попадает в стену.
С этой небольшой информацией об объекте довольно легко отличить новую информацию от старой. Кроме того, только объекты, которые фактически изменяются, вызывают API обновления, поэтому объекты, которые остаются прежними (например, стены или неактивные платформы), даже не должны выполнять такое сравнение. Кроме того, сервер может еще больше сэкономить на отправленной информации, не отправляя клиенту объекты, которые невидимы для клиента, пока они не появятся в поле зрения. Например, в Quake2 уровень разделен на области обзора, и одна область (и все объекты внутри нее) считается «вне поля зрения» другой, если все двери между ними закрыты.
Помните, что серверу не нужно, чтобы клиент имел полное игровое состояние, а только граф сцены, а для этого требуется гораздо более простая сериализация и полное отсутствие указателей (в Quake он фактически хранится в одном массиве статических размеров, который также ограничивает максимальное количество объектов в игре).
Кроме того, есть также данные пользовательского интерфейса для таких вещей, как здоровье игрока, боеприпасы и т. Д. Опять же, каждый игрок получает только свое здоровье и боеприпасы, отправленные ему, а не всем на сервере. У сервера нет причин делиться этими данными.
Обновление: чтобы убедиться, что я получаю самую точную информацию, я дважды проверил код. Это основано на Quake3, а не на Quake Live, поэтому некоторые вещи могут отличаться. Информация, отправляемая клиенту, инкапсулируется в единую структуру с именем snapshot_t
. Он содержит один playerState_t
для текущего игрока и массив из 256 entityState_t
для видимых игровых объектов, а также несколько дополнительных целых чисел и массив байтов, представляющий битовую маску "видимых областей".
entityState_t
, в свою очередь, состоит из 22 целых чисел, 4 векторов и 2 траекторий. «Траектория» — это структура данных, используемая для представления движения объекта в пространстве, когда с ним ничего не происходит, например. баллистическое движение или полет по прямой. Это 2 целых числа, 2 вектора и одно перечисление, которое концептуально можно хранить как небольшое целое число.
playerState_t
немного больше, содержит примерно 34 целых числа, примерно 8 целочисленных массивов размером от 2 до 16 каждый и 4 вектора. Он содержит все, от текущего кадра анимации оружия до инвентаря игрока и звука, который издает игрок.
Поскольку используемые структуры имеют предустановленные размеры и, ну, структуру, создание простой ручной функции «diff», сравнивающей каждый из членов для каждого, довольно просто. Однако, насколько я могу судить, entityState_t
и playerState_t
отправляются только полностью, а не частями. Единственное, что подвергается "дельте" - это то, какие сущности отправляются как часть массива сущностей в snapshot_t
.
В то время как снимок может содержать до 256 объектов, сама игра может содержать до 1024 объектов. Это означает, что только 25% объектов могут быть обновлены с точки зрения клиента в одном кадре (любое большее количество вызовет печально известную ошибку «переполнение пакета»). Сервер просто отслеживает, какие объекты имели значимое движение, и отправляет их. Это намного быстрее, чем выполнение фактического сравнения - просто отправка любого объекта, который вызывает «обновление» сам по себе и находится внутри битовой маски видимой области игрока. Но теоретически, написанный от руки diff для каждой структуры сделать не так уж и сложно.
Для здоровья команды, хотя Quake3, похоже, не делает этого, поэтому я могу только догадываться, как это делается в Quake Live. Есть два варианта: либо отправить все структуры playerState_t
, так как их максимум 64, либо добавить еще один массив в playerState_t
для сохранения HP команды, так как это будет только 64 целых числа. Последнее гораздо более вероятно.
Чтобы поддерживать синхронизацию массива объектов между клиентом и сервером, каждая сущность имеет индекс сущности от 0 до 1023 и отправляется как часть сообщения от сервера к клиенту. Когда клиент получает массив из 256 сущностей, он просматривает массив, считывает поле индекса из каждого и обновляет сущность по прочитанному индексу в своем локально сохраненном массиве сущностей.
person
SlugFiller
schedule
05.04.2015