Модель синхронизации
Varion использует server-authoritative модель. Сервер создаёт все сетевые сущности, выдаёт им network ID и остаётся единственным источником истины. Клиенты только отображают состояние и передают ввод своего игрока. Клиенты никогда не обмениваются состоянием напрямую — весь трафик идёт через сервер.
Эта страница объясняет модель автору ресурса: как сервер стримит сущности, что такое net-owner и его миграция, как изолировать миры через dimensions и когда использовать virtual entities вместо реальных игровых сущностей.
Сервер — источник истины
| Правило | Как это работает |
|---|---|
| Сервер создаёт сущности | Каждая сетевая сущность (player, vehicle, ped, object, virtual entity) и её network ID создаются на сервере. Клиент не может создать сетевую сущность. |
| Сервер — маршрутизатор | Сервер принимает состояние от владельца сущности и ретранслирует его нужным наблюдателям. Он же выдаёт stream-in/out и миграцию владельца. |
| Передаёт только net-owner | Состояние сущности принимается сервером только от её net-owner (или от любого, если сущность ownerless). Чужой пакет отбрасывается. |
| Нет client → client | Между клиентами нет прямого канала. Любое сообщение проходит через сервер, который решает, кому его отдать. |
| Remote-сущности server-authoritative | На клиенте remote-сущности read-only. entity.setPos/setRot/destroy для чужого игрока блокируются и возвращают false. |
Авторитетную логику (gameplay, инвентарь, экономику, права, перемещение) проверяйте на сервере — см. Обзор платформы, раздел Trust model.
Стриминг сущностей
Сервер решает, каких сущностей «видит» каждый игрок, через spatial hash grid:
- Мир делится на квадратные ячейки со стороной =
streamingDistance(по умолчанию 300 м). - Для каждого наблюдателя (observer) сервер проверяет его ячейку и восемь соседних (3×3).
- Сущность в пределах
streamingDistanceи в том же dimension → stream in. - Сущность вышла за радиус или сменила dimension → stream out.
Что получает автор сущности
При stream-in сервер отправляет keyframe (полное состояние) плюс stream-synced meta; при stream-out — уведомление. На клиенте это превращается в события:
| Событие (client) | Когда |
|---|---|
entityStreamIn / entityStreamOut | Сущность вошла/вышла из зоны стрима наблюдателя. Payload — сырые данные (entityId, entityType). |
gameEntityCreate / gameEntityDestroy | У сущности появился/пропал игровой handle (готова к нативам). |
baseObjectCreate / baseObjectRemove | Появился/удалён base object. |
worldObjectStreamIn / worldObjectStreamOut | Стрим мирового объекта (обёрнутая сущность). |
varion.on('entityStreamIn', (data) => {
varion.log(`stream in type=${data.entityType} id=${data.entityId}`);
});
varion.on('gameEntityCreate', (entity) => {
// handle готов — нативы можно вызывать на entity.scriptID
});entityStreamIn сообщает, что сервер начал реплицировать сущность; gameEntityCreate наступает позже — когда игра выдала локальный handle. Нативы вызывайте после gameEntityCreate, а не сразу на entityStreamIn.
Почему delta доходит только до streamed-in
Delta-обновления (позиция, поворот, здоровье, оружие и т. д.) сервер рассылает только тем наблюдателям, которые сейчас стримят сущность. В этом суть масштабируемости: игрок на другом конце карты не тратит трафик на сущности, которых не видит. Пока сущность не in-stream у наблюдателя, он не получает её delta — только keyframe в момент stream-in.
Лимиты стрима — streamingDistance и maxStreamedEntities (см. server.toml).
Net-owner и миграция
Net-owner — клиент, который симулирует сущность и передаёт её состояние серверу. Сервер принимает состояние только от net-owner (или от любого, если сущность ownerless) и ретранслирует его streamed-in наблюдателям.
- Каждый игрок — net-owner своего локального игрока.
- Vehicle: net-owner — тот, кто им управляет. Пустая машина владельца не имеет либо привязана к ближайшему игроку.
Когда мигрирует (vehicle)
Владельца машин сервер пересчитывает несколько раз в секунду (~6–10 Гц) по приоритету:
- Driver — игрок за рулём становится владельцем.
- Гистерезис — если водителя нет, текущий владелец сохраняется, пока он в пределах
migrationDistance(по умолчанию 150 м). - Ближайший — иначе владельцем становится ближайший игрок в радиусе
migrationDistance. - Ownerless — если рядом никого, машина «замораживается» (без владельца).
При смене владельца:
- Сервер вызывает событие
netOwnerChange(entity, owner, oldOwner)— см. События и RPC. - Клиент получает
netOwnerChange(entity, owner, previousOwner), но только когда локальный игрок получает или теряет владение.
// client
varion.on('netOwnerChange', (entity, owner, previousOwner) => {
if (owner && owner.id === varion.Player.local.id) {
// теперь эту сущность симулируем мы
}
});Owner vs non-owner
| Net-owner | Не-owner | |
|---|---|---|
| Состояние сущности | Ваш клиент — источник; сервер ретранслирует его наблюдателям | Приходит интерполированное remote-состояние |
| Мутация remote-сущности | — | Заблокирована: setPos/setRot/destroy чужого игрока → false (server-authoritative) |
Dimensions
Dimension — числовой слой изоляции. Сущности и игроки в разных dimension не видят друг друга: сервер фильтрует по dimension и стриминг, и ретрансляцию. Используйте для инстансов, интерьеров, мини-игр и отдельных лобби.
- Сервер: смена dimension игрока запускает немедленный пересчёт стрима (stream-out сущностей других dimension). Событие
playerDimensionChange(player, oldDimension, newDimension). - Клиент:
localDimension— dimension локального игрока изменился;playerDimensionChange— событие смены dimension. Точные сигнатуры — События и RPC.
// client
varion.on('playerDimensionChange', (player, oldDimension, newDimension) => {
varion.log(`${player.id}: ${oldDimension} -> ${newDimension}`);
});Virtual entities
Virtual entity — лёгкий синхронизируемый объект с позицией, но без игровой сущности: нет модели, коллизии и нативов. Сервер стримит их по тем же пространственным правилам, что и реальные сущности, но без затрат на создание игрового объекта.
| Берите virtual entity | Берите реальную сущность |
|---|---|
| Точки интереса, зоны, «якоря» стрима, синхронные данные с позицией, кастомная логика в радиусе | Нужен видимый/физический объект в мире (ped, vehicle, prop) |
| Тысячи дешёвых точек | Игровое взаимодействие через нативы |
Virtual entity создаёт и стримит сервер (server authority) — клиент их только читает; конструктора на клиенте нет.
Группы и cell key
Virtual entities объединяются в VirtualEntityGroup. Группа задаёт радиус стрима по умолчанию (maxStreamingDistance) и лимит одновременно стримингуемых сущностей (maxEntitiesInStream). Хелпер cellKey помогает автору сгруппировать позиции по ячейкам сетки.
// client
varion.on('virtualEntityStreamIn', (ve) => {
varion.log(`VE ${ve.id} group=${ve.groupId} dist=${ve.streamingDistance}`);
});
varion.on('virtualEntityPositionChange', (ve) => {
varion.log(`VE ${ve.id} -> ${ve.pos.x}, ${ve.pos.y}`);
});
const ve = varion.VirtualEntity.get(remoteId);
if (ve) varion.log(ve.isStreamedIn, ve.visible, ve.dimension);
const key = varion.VirtualEntityGroup.cellKey(0.0, 0.0, 100);API (client, read)
varion.VirtualEntity
| Член | Описание |
|---|---|
get(id) / getByRemoteID(id) | VE по remote ID или null |
all | Все известные VE |
streamedIn | Только in-stream VE |
Свойства экземпляра: id / remoteId, groupId, group, isStreamedIn / streamed, streamingDistance (per-entity override или радиус группы), visible, dimension, pos, rot, а также meta-методы как у обычной сущности (см. Metadata).
varion.VirtualEntityGroup
| Член | Описание |
|---|---|
get(id) | Группа по ID или null |
all | Все группы |
cellKey(x, y, cellSize = 100) | Grid-ключ "cx:cy" для группировки позиций |
Свойства группы: id, maxStreamingDistance (по умолчанию 400), maxEntitiesInStream (по умолчанию 128), streamedIn (in-stream VE этой группы).
События virtual entity
| Событие | Когда |
|---|---|
virtualEntityStreamIn / virtualEntityStreamOut | VE вошла/вышла из стрима |
virtualEntityPositionChange | Позиция VE в snapshot изменилась |
entityStreamIn / entityStreamOut | Общий контракт; для VE entityType = 9 |
См. также
- События и RPC — точные сигнатуры stream-, owner- и dimension-событий.
- server.toml —
streamingDistance,migrationDistance,maxStreamedEntities. - Metadata — synced / stream-synced / local meta.
- Streaming и Assets — стриминг файлов ресурсов, не сущностей.
Source backing
Страница основана на:
server/src/CServer.cpp(UpdateStreaming,UpdateVehicleOwnership,ApplyVehicleOwnerChange,RelayPlayerPresentation)include/varion.h(streamingDistance,migrationDistance,maxStreamedEntities)sdk/client-js-runtime/injected/world-bootstrap.js(VirtualEntity,VirtualEntityGroup)sdk/client-js-runtime/injected/entities-bootstrap.jsиevents-bootstrap.js(stream-, owner- и dimension-события)