Синхронизация
Синхронизация состояний относится к синхронизации значений, таких как целые числа, числа с плавающей запятой, строки и логические значения, принадлежащие скриптам.
Синхронизация состояния осуществляется с сервера на удаленные клиенты. У локального клиента нет сериализованных данных. Ему это не нужно, потому что он разделяет сцену с сервером. Однако hook'и SyncVar вызываются на локальных клиентах.
Данные не синхронизируются в обратном направлении - от клиентов к серверу. Чтобы синхронизировать данные, вам нужно использовать [Command].
SyncVars SyncVars - это переменные в скриптах, наследуемые от NetworkBehaviour, которые синхронизируются с сервера на клиенты.
SyncEvents (Устарело) SyncEvents - это сетевые события, подобные событиям ClientRpc, но вместо вызова функции для игрового объекта они запускают события. ВАЖНО: это было удалено в версии 18.0.0, смотрите здесь Issue для получения более подробной информации.
SyncLists SyncList содержат list'ы значений и синхронизируют данные с сервера на клиенты.
SyncDictionary SyncDictionary - это ассоциативный массив, содержащий неупорядоченный список пар ключ-значение.
SyncHashSet Неупорядоченный набор значений, которые не повторяются.
SyncSortedSet Отсортированный набор значений, которые не повторяются.
Sync To Owner
Часто бывает так, что вы не хотите, чтобы некоторые данные игрока были видны другим игрокам. В инспекторе измените "Network Sync Mode" с "Observers" (по умолчанию) на "Owner" чтобы сообщить Mirror о необходимости синхронизации данных только с клиентом-владельцем.
Например, предположим, что вы создаете систему инвентаря. Предположим, игроки A, B и C находятся в одной и той же области. Всего во всей сети будет 12 объектов:
У клиента A на карте есть игрок A (он сам), игрок B и игрок C
У клиента B на карте есть игрок A, игрок B (он сам) и игрок C
У клиента C на карте есть игрок A, игрок B и игрок C (он сам)
На сервере есть игрок A, Игрок B, игрок C
у каждого из них есть компонент инвентаря
Предположим, игрок А подбирает какую-то добычу. Сервер добавляет добычу в инвентарь игрока A, который будет иметь SyncLists из предметов.
По умолчанию Mirror теперь должен синхронизировать инвентарь игрока A везде, это означает что он отправит сообщения об обновлении клиенту A, клиенту B и клиенту C, потому что у всех них есть копия игрока A. Это расточительно, клиенту B и клиенту C не нужно знать об инвентаре игрока A, они никогда не увидят этого на экране. Это также проблема безопасности, кто-то может взломать клиент и отобразить инвентарь других людей и использовать его в своих интересах.
Если вы установите для параметра "Network Sync Mode" в компоненте инвентаря значение "Owner", то инвентарь игрока A будет синхронизирован только с клиентом A.
Теперь предположим, что вместо 3 человек у вас в районе 50 человек, и один из них собирает добычу. Это означает, что вместо отправки 50 сообщений 50 разным клиентам вы отправите только 1. Это может оказать большое влияние на пропускную способность вашей игры.
Другие типичные варианты использования включают квесты, руку игрока в карточной игре, навыки, опыт или любые другие данные, которыми вам не нужно делиться с другими игроками.
Расширенная синхронизация состояний
В большинстве случаев использования SyncVars достаточно для того, чтобы ваши игровые скрипты сериализовали свое состояние для клиентов. Однако в некоторых случаях вам может потребоваться более сложный код сериализации. Эта страница актуальна только для продвинутых разработчиков, которым нужны индивидуальные решения для синхронизации, выходящие за рамки обычной функции Mirror SyncVar.
Пользовательские функции сериализации
Чтобы выполнить свою собственную пользовательскую сериализацию, вы можете реализовать виртуальные функции из NetworkBehaviour, которые будут использоваться для сериализации SyncVar. Этими функциями являются:
Используйте флаг initialState
чтобы различать, когда игровой объект сериализуется в первый раз, и когда могут быть отправлены дополнительные обновления. При первой отправке игрового объекта клиенту он должен содержать полный снимок состояния, но последующие обновления могут сэкономить на пропускной способности, включая только постепенные изменения.
Функция OnSerialize
должно возвращать значение true, указывающее на то, что обновление должно быть отправлено. Если он возвращает значение true, то биты dirty для этого скрипта устанавливаются равными нулю. Если он возвращает значение false, то биты dirty не изменяются. Это позволяет накапливать множество изменений в сценарии с течением времени и отправлять их, когда система будет готова, вместо каждого кадра.
Функция OnSerialize
вызывается только для initialState
или когда NetworkBehaviour
является dirty. NetworkBehaviour
будет dirty только в том случае, если SyncVar
или SyncObject
(например SyncList
) изменились с момента последнего вызова OnSerialize. После того, как данные были отправлены, NetworkBehaviour
больше не будет dirty до следующего syncInterval
(установите в инспекторе). NetworkBehaviour
также может быть помечен как dirty при ручном вызове SetDirtyBit
(это не позволяет обойти ограничение syncInterval).
Хоть это и работает, обычно лучше позволить Mirror генерировать эти методы и предоставлять пользовательские сериализаторы для вашего специфичного поля.
Поток сериализации
Игровой объект с прикрепленным компонентом Network Identity может содержать несколько скриптов, унаследованных от NetworkBehaviour
. Процесс сериализации этих игровых объектов выглядит следующим образом:
На сервере:
Каждый
NetworkBehaviour
имеет dirty mask. Эта маска доступна внутриOnSerialize
какsyncVarDirtyBits
Каждому SyncVar в скрипте
NetworkBehaviour
назначается бит в dirty mask.Изменение значения SyncVars приводит к тому, что бит для этого SyncVar устанавливается в dirty mask
В качестве альтернативы, вызывается
SetDirtyBit
который непосредственно записывает бит в dirty maskИгровые объекты с NetworkIdentity проверяются на сервере как часть цикла обновления
Если какой нибудь
NetworkBehaviour
уNetworkIdentity
является dirty, тогда пакетUpdateVars
будет создан для данного игрового объектаПакет
UpdateVars
заполняется вызовомOnSerialize
на каждыйNetworkBehaviour
на игровом объектеNetworkBehaviours
которые не являются dirty, запишут ноль в пакет для их dirty битовNetworkBehaviours
которые являются dirty, записывают свою dirty mask, затем значения для измененных синхронизаторовЕсли
OnSerialize
возвращает true уNetworkBehaviour
, dirty mask будет сброшена для данногоNetworkBehaviour
, таким образом, он не будет отправляться снова до тех пор, пока его значение не изменится.пакет
UpdateVars
отправляется готовым клиентам, которые наблюдают за игровым объектом
На клиенте:
UpdateVars packet
будет получен для игрового объектаФункция
OnDeserialize
будет вызвана для каждого скриптаNetworkBehaviour
на игровом объектеКаждый скрипт
NetworkBehaviour
на игровом объекте прочитает dirty mask.Если dirty mask для
NetworkBehaviour
является нулевой, функцияOnDeserialize
вернется, больше ничего не читаяЕсли dirty mask не является нулевым значением, тогда функция
OnDeserialize
прочитает значение для SyncVar которые соответствуют установленным dirty битамЕсли есть функции SyncVar hook, они вызываются со значением, считанным из потока.
Итак, для этого скрипта:
В следующем примере показан код, сгенерированный Mirror для функции SerializeSyncVars
которая вызывается внутри NetworkBehaviour.OnSerialize
:
В следующем примере показан код, сгенерированный Mirror для функции DeserializeSyncVars
которая вызывается внутри NetworkBehaviour.OnDeserialize
:
Если NetworkBehaviour
имеет базовый класс, который также имеет функции сериализации, функции базового класса также должны вызываться.
Обратите внимание, что пакеты UpdateVar
обновления состояния, созданные для игровых объектов, могут быть объединены в буферах перед отправкой клиенту, поэтому один пакет транспортного уровня может содержать обновления для нескольких игровых объектов.
Last updated