Справочник API
Справочник W API
Заголовок раздела «Справочник W API»// Редьюсер узла — вызывать внутри Behaviors.collectW.reduce(state, pulse, nodeId, handlers) → newState
// Проверка стабильности — использовать в _isStableW.stable(nodes, pulse) → boolean
// Экспорт состояния мира в world.appW.export(Renkon, { node1, node2, ... }, isStable)
// Миксин самохостящихся часов — распространять в обработчикиW.localReflector(tickMsg, innerTickDelay) → handlersMixin
// Удалить инфраструктурные поля (_queue, _nextAt, _depth, _lt)W.getState(node) → { ...userFields }Контекст обработчика (ctx)
Заголовок раздела «Контекст обработчика (ctx)»ctx.wallTime // текущее логическое wallTime (= lt)ctx.logicalTime // текущее logicalTime (= lt)ctx.depth // текущая глубина обратной связи
ctx.future(delay, msg, payload) // запланировать при wallTime + delayctx.send(targetId, msg, payload) // межузловое через исходящий ящикctx.feedback(msg, payload, maxDepth) // отслеживаемый по глубине future при wallTime + _fbStepMsctx.futureInf(msg, payload) // fireAt = wallTime — перепланирует каждый дренажный проходctx.localReflector(tickMsg, delay) // самохостящийся шаг часов субтикаПримитивы ctx
Заголовок раздела «Примитивы ctx»| Примитив | Семантика |
|---|---|
ctx.future(delay, msg, payload) | Запланировать msg через delay логических тиков |
ctx.send(nodeId, msg, payload) | Межузловое сообщение через исходящий ящик с ограничением по evalGen |
ctx.feedback(msg, payload, maxDepth) | Отслеживаемый по глубине future при wallTime + _fbStepMs |
ctx.futureInf(msg, payload) | fireAt = wallTime — перепланирует каждый дренажный проход |
ctx.localReflector(tickMsg, delay, payload) | Самохостящийся шаг часов субтика с опциональной полезной нагрузкой |
ctx.future — Полезная нагрузка и защита от идемпотентности
Заголовок раздела «ctx.future — Полезная нагрузка и защита от идемпотентности»payload — любое простое сериализуемое значение. Обработчик получает его вторым аргументом p:
ctx.future(delay, "beat", { depth: 2, cycleId: s.cycleId });
beat: (s, p, ctx) => { if (p.cycleId !== s.cycleId) return s; // устаревший — игнорировать // p.depth, p.cycleId доступны здесь}Почему передавать cycleId? Субтик-future’ы срабатывают асинхронно внутри цикла дренажа. Без защиты устаревший future повреждает состояние.
ctx.localReflector — Самохостящиеся часы
Заголовок раздела «ctx.localReflector — Самохостящиеся часы»// Накапливающие фазу локальные часы...W.localReflector("tick", 0.05),
tick: (s, p, ctx) => { const phase = ((s.phase ?? 0) + 0.01) % 1; // продвинуть фазу каждый тик ctx.localReflector("tick", 0.05, { phase }); // передать фазу вперёд в полезной нагрузке return { ...s, phase };}Важно:
W.localReflectorопределяет обработчик__macro. Не определяйте__macroв том же объекте обработчиков — spread молча перезапишет один из них.
Субтактовое планирование
Заголовок раздела «Субтактовое планирование»Future’ы с delay < SUBTICK_MS = 1 дренируются в текущем пульсе:
future(0) → fireAt = wallTime → дренируется сейчасfuture(0.5) → fireAt = wallTime+0.5 → дренируется сейчас (0.5 < SUBTICK_MS=1)future(1) → fireAt = wallTime+1 → ждёт следующего тикаfuture(60) → fireAt = wallTime+60 → ждёт 60 тиковИнкрементальный __macro
Заголовок раздела «Инкрементальный __macro»__macro вызывается не более одного раза за logicalTime:
__macro: (s, p, ctx) => { if (s.started) return s; ctx.future(0, "startCycle", { cycleId: 1 }); return { ...s, started: true };}W.rng — Детерминированный ГПСЧ
Заголовок раздела «W.rng — Детерминированный ГПСЧ»ВНИМАНИЕ: никогда не используйте
Math.random()внутри узлов мира — он недетерминирован и вызовет рассинхронизацию участников.
W.rng.next() // → float в [0, 1)W.rng.nextInt(n) // → целое в [0, n)W.rng.seed(lt) // пересев от logicalTime (детерминированный сброс за цикл)W.rng.state() // → { s0, s1, s2, s3 } (снимок)W.rng.restore(state) // восстановить из снимкаЛокальный рефлектор как контроль скорости симуляции
Заголовок раздела «Локальный рефлектор как контроль скорости симуляции»Размер тика локального рефлектора напрямую определяет единицу вычислений:
внешний тик = граница сетевой синхронизации (каждые 50мс реального времени)внутренний тик = шаг интеграции симуляции (каждые 1/N логических единиц)innerTickDelay | Шагов за внешний тик | Эквивалентная скорость симуляции |
|---|---|---|
0.5 | 2 | 2× за сетевой тик |
0.1 | 10 | 10× за сетевой тик |
0.01 | 100 | 100× за сетевой тик |
0.001 | 1000 | 1000× (у пола float) |
Цикл обратной связи — ctx.feedback
Заголовок раздела «Цикл обратной связи — ctx.feedback»ctx.feedback("respond", { value, cycleId }, 64);ctx.feedback() планирует сообщение при wallTime + _fbStepMs и увеличивает счётчик глубины волны на 1. Если depth >= maxDepth — вызов молча игнорируется.
Диаграмма глубины волны с обратной связью
Заголовок раздела «Диаграмма глубины волны с обратной связью»Логическое время T │ пульс ├── глубина 0 estimator.__macro → ctx.future(0, "sendObserve") ├── глубина 0 estimator.sendObserve → ctx.send("corrector", "observe") ├── глубина 0 corrector.observe → ctx.feedback("respond") ├── глубина 1 corrector.respond → ctx.send("estimator", "refine") ├── глубина 1 estimator.refine → delta > ε → ctx.feedback("continueRefine") ├── глубина 2 ...цикл продолжается... └── глубина N delta < ε → нет ctx.feedback → очереди дренируются → стабильно