Перейти к содержимому

Клиент и Reflex

У обычного модуля одна клиентская среда. Reflex-модуль добавляет отдельную серверную среду и транспорт между ними:

ввод/UI клиента ── намерение ──▶ решение Reflex
отображение ◀── состояние ── результат Reflex

Среды не разделяют переменные, живые объекты и обработчики. Вспомогательный код из src/shared/ может импортироваться обеими точками входа, но попадёт в каждую сборку отдельной копией. Через работающую границу клиента и Reflex проходят только сериализованные сообщения.

РаботаВладелецПричина
HUD, UXML, камеры, звук, подсказкиКлиентЭти API существуют там, где игра их отображает
Бинды модуля и локальное состояние кнопокКлиентInput actions доступны только клиенту
Линии и отметки в миреКлиентЭто отображение, а не серверные сущности
Решения, которые серверный API может наблюдать и применитьReflexИм не нужно доверять состоянию со слов клиента
Серверное время и повторяющиеся проверкиReflexИм не нужно ждать клиентские кадры и сетевой круг
Показ подтверждённого сервером результатаКлиентReflex передаёт данные, клиент выбирает отображение

Задайте вопрос: какая среда способна увидеть нужное состояние, применить изменение и проверить результат? Там и должно приниматься решение. Reflex работает на сервере, но его API намеренно ограничен и учитывает видимость; само расположение на сервере не делает любое решение авторитетным. Клиент может показать ожидание или предсказанный результат, но не должен становиться источником истины для состояния, которое Reflex способен применить.

Клиентскому модулю достаточно:

src/client/main.ts

Reflex-модуль добавляет:

src/server/main.ts
src/shared/messages.ts

Клиент импортирует @killscript/sdk/client, сервер — @killscript/sdk/server. Компилятор не разрешает клиенту импортировать серверную реализацию и наоборот. Обычные интерфейсы и типы сообщений храните в src/shared/.

Намерение → решение → подтверждённое состояние

Заголовок раздела «Намерение → решение → подтверждённое состояние»
// Клиент сообщает, чего попросил игрок.
controls.toggle.onPressed(() => {
coreNetwork.send("setEnabled", { enabled: !displayedEnabled });
});
coreNetwork.on("stateChanged", ({ enabled }) => {
displayedEnabled = enabled;
updateHud(enabled);
});
// Reflex владеет состоянием и подтверждает его.
let enabled = false;
coreNetwork.on("setEnabled", ({ enabled: requested }) => {
const local = Agents.GetLocalAgent();
enabled = local?.Stats.IsAlive === true && requested;
coreNetwork.send("stateChanged", {
enabled,
changedAtTick: Time.Tick,
});
});

Клиент не утверждает, что запрос выполнен, а обновляется из состояния, принятого Reflex. И наоборот: Reflex не должен спрашивать клиента о данных, которые уже доступны в серверных глобальных API.

Полное объявление протокола и пакетная отправка описаны в Типизированной сети.

// Клиентское отображение следует кадрам.
import { scheduler } from "@killscript/sdk/client";
scheduler.frame(updateCamera);
// Серверная симуляция следует тикам.
import { scheduler } from "@killscript/sdk/server";
scheduler.tick(updateServerState);

В открытом API нет клиентского OnTick и серверного OnFrame. Если отображению нужно заметить новый тик симуляции, сравнивайте Time.Tick внутри кадрового обработчика.