Архитектура Reflex
Reflex-модуль добавляет к обычному клиентскому main.lua серверную точку входа server.lua. Обе части принадлежат одному модулю, но выполняются независимо и получают разные API.
ReflexModule/├── module.json # IsReflex: true└── scripts/ ├── main.lua # клиент └── server.lua # серверный контекст игрокаОтветственность частей
Заголовок раздела «Ответственность частей»main.lua
Заголовок раздела «main.lua»- читает
InputActions; - строит ImGui и UXML;
- использует локальные камеры, звук и графику;
- хранит данные в
Storage; - отправляет пользовательские намерения и настройки серверу.
server.lua
Заголовок раздела «server.lua»- выполняет работу через
Scheduler:OnTick(); - читает серверное состояние игровых объектов;
- вызывает методы, доступные только Reflex server;
- проверяет сообщения клиента;
- отправляет клиенту только необходимые данные для отображения.
Граница Network
Заголовок раздела «Граница Network»Части не разделяют Lua-таблицы или глобальные переменные. Единственный прямой канал между ними — Network.
main.lua ── SendTable ──▶ server.luamain.lua ◀─ SendTable ── server.luaСообщение сериализуется, поэтому передаются только строки, числа, boolean и вложенные таблицы. Объекты API не пересылаются.
Не отправляйте решение через client
Заголовок раздела «Не отправляйте решение через client»Если решение можно принять по состоянию, доступному серверу, выполняйте весь цикл в server.lua:
server state → server decision → server action └→ client presentationСхема server state → client decision → server action добавляет сетевой круг. К моменту возвращения команды исходное состояние уже может измениться. Это особенно важно для движения, состояния игровых кнопок, серверного направления взгляда, целей прицеливания, стрельбы и другой логики, зависящей от тиков.
InputActions доступны только на клиенте. Поэтому пользовательский бинд может отправить серверу включение режима, выключение или новую настройку. После этого наблюдение, тайминг и применение поддерживаемых изменений через AgentInput и Aim должны оставаться на сервере. Не пересылайте команды каждый кадр, если достаточно одного сообщения о состоянии режима.
Клиенту отправляйте только результат, необходимый для UI, камеры, звука и другой локальной обратной связи. Сервер не должен ждать подтверждения клиента, если способен принять решение самостоятельно.
Жизненный цикл
Заголовок раздела «Жизненный цикл»- Пользователь включает и выбирает Reflex-модуль.
- В игровой сессии запускается клиентская часть.
- Сервер получает и запускает
server.luaдля этого игрока. - Обе стороны регистрируют callbacks и обмениваются сообщениями.
- При hot reload старый экземпляр заменяется новым.
Не рассчитывайте на порядок первых сообщений. Регистрируйте OnTableReceived() до SendTable() и при необходимости повторно отправляйте начальное состояние после короткой задержки.
Ограничение одного Reflex-модуля
Заголовок раздела «Ограничение одного Reflex-модуля»Одновременно активен только один Reflex-модуль. Это отдельное правило, не связанное с обычными тегами функциональности.
Контекст всегда важнее имени метода
Заголовок раздела «Контекст всегда важнее имени метода»| API | Client | Reflex server |
|---|---|---|
InputActions, Storage, Localization |
✅ | ❌ |
ImGui, UI, Cameras, Textures |
✅ | ❌ |
Scheduler:OnFrame() |
✅ | ❌ |
Scheduler:OnTick() |
❌ | ✅ |
Network, Time, Agents |
✅ | ✅ |
У отдельных объектов состав свойств также различается. Всегда проверяйте отметки контекста в справочнике.
Главный принцип
Заголовок раздела «Главный принцип»Клиент сообщает что хочет сделать пользователь, а сервер решает можно ли это делать и какой результат является действительным. Не переносите серверное решение на клиент только ради удобства интерфейса.
Дальше: протокол Network и серверный цикл.