From 1d20509b052c0b3e2db2a8bd86756f007041ab21 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Thu, 29 Oct 2026 19:43:09 +0400 Subject: feat(runtime): support class-9 and default Control budgets --- docs/appendices/knowledge-boundaries.md | 26 +++++---- docs/appendices/script-vm.md | 100 +++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 25 deletions(-) (limited to 'docs/appendices') diff --git a/docs/appendices/knowledge-boundaries.md b/docs/appendices/knowledge-boundaries.md index dcc14fe..626469f 100644 --- a/docs/appendices/knowledge-boundaries.md +++ b/docs/appendices/knowledge-boundaries.md @@ -360,13 +360,16 @@ API `distribute_loaded_mission_control_provider_amount` переносит Три API работают только с CPU-проекцией и не подключены к GPU/window path или игровому Control clock. Отдельный -`advance_loaded_mission_control_budget` переносит `0x2d340` для factory -callbacks `15/19` и current descriptor tags `15/19` (категории `0/1`); ставки сохраняются -в текущих полях `+0x5dc/+0x5e0`. Он выполняется транзакционно, а неизвестный -callback, provider или диапазон числа возвращает ошибку без частичного +`advance_loaded_mission_control_budget` переносит `0x2d340` для фабричных +callbacks `9`, `15`, `19` и общей default-пары; category lookup использует +текущий descriptor tag из подтверждённого префикса `0..=34`. Ставки сохраняются +в `+0x5dc/+0x5e0`. Шаг выполняется транзакционно, а неподдержанный +специализированный callback, provider или диапазон числа возвращает ошибку без частичного изменения. Тест с нулевым знаменателем генератора подтверждает откат после уже выполненного изменения потребителем. Пустая таблица всё равно записывает в оба поля отрицательный ноль; -неактивный callback не требует provider lookup. Gate `+0x618` изначально +неактивный callback не требует provider lookup. Class-9 constructor начинает +шесть значений `+0x98..+0xac` с `1.0` и шесть дельт `+0xb0..+0xc4` с нуля; +`INTO` сохраняет эти runtime-массивы. Gate `+0x618` изначально выключен; событие `7` задаёт его по правилу `gate = (arg == 2)` и вызывает `0x2dc20`, только если gate изменился. События Control в Rust пока не исполняются, поэтому ресурсный шаг вызывается явно и не подключён к часам. @@ -418,13 +421,16 @@ rate, изменение provider и собственного множителя подтверждают текущие ставки `+0x5dc/+0x5e0`, включая значения `0.2`/`0.4` у потребителя tag 15, пустой результат `-0.0` и невырожденную пару исходных устройств tag 19/tag 15 при `dt = 0.375`; Rust-тест повторяет поля этой пары -на синтетической таблице. Реализация принимает только описанные factory и -category tags; остальные callbacks и состояния остаются явными ошибками. +на синтетической таблице. Rust переносит class-9 rate/update и шестизначную +adjustment `0x25a90`, общую default callback-пару `0x21860/0x21890`, а также +category map только для current descriptor tags `0..=34`. Специализированные +factory callbacks вне этого набора остаются явными ошибками. Лицензированный `min_l_n1.dat` содержит исходные factory/current tags `[19, 19, 9, 15, 12, 12, 13, 13, 26, 25, 21]`. Публичный API оставляет всю -таблицу без изменений при не конечном интервале и при первом неподдержанном -current tag `9`; полный бюджетный проход для этой реальной таблицы поэтому не -проверен. Успешные бюджетные native-векторы используют синтетические +таблицу без изменений при не конечном интервале. Для конечного интервала +предварительная проверка распознаёт категории, но находит неподдержанный +factory tag `26` и отклоняет вызов до вычисления ставок и обновлений; полный +бюджетный проход для этой реальной таблицы поэтому не проверен. Успешные бюджетные native-векторы используют синтетические NDP/Node38-строки и подтверждают локальные переходы и порядок вызовов, а не полный Control tick на миссии. Прямые oracle-вызовы `0x11220` для одиночной строки используют `updateEffects = false`, что исключает ветвь Control FX и diff --git a/docs/appendices/script-vm.md b/docs/appendices/script-vm.md index a362b73..5fb0cd0 100644 --- a/docs/appendices/script-vm.md +++ b/docs/appendices/script-vm.md @@ -792,21 +792,23 @@ provider getter и изменение количества не подменен Rust предоставляет явный CPU-вызов `advance_loaded_mission_control_budget(engine, handle, dt)`. Он переносит -диспетчер и callbacks исходных factory tags `15` и `19`, если текущий -descriptor tag равен `15` или `19` (native categories `0` или `1`). Rates `+0x5dc/+0x5e0` -сохраняются отдельно. Вызов работает на копии полного состояния: неизвестный -descriptor, factory callback, provider или численный диапазон возвращает -ошибку без публикации частичных изменений. Вызов ещё не включён в Control clock; -нативный oracle также проверяет пустую таблицу с отрицательным нулём в обеих -выходных ставках и невырожденную пару исходных устройств tag 19/tag 15 при -`dt = 0.375`. Этот парный вектор проверяет изменения amount, processed amount, -накопителя, доли, обоих множителей и ставок `+0x5dc/+0x5e0` по битам. Rust-тест -повторяет его на синтетической provider-таблице. Для лицензированного -`min_l_n1.dat` отдельный тест подтверждает состав factory/current tags и то, что -первый неподдержанный current tag `9` отклоняет весь шаг без изменений; полного -успешного бюджетного прохода для реальной миссионной таблицы пока нет. -Rust также отклоняет нулевой знаменатель обновления tag 19; тест вызывает эту -ошибку после изменения provider потребителем и подтверждает откат всей таблицы. +исходные callbacks factory tags `9`, `15`, `19` и общий default callback, а +категории выбирает по текущему descriptor tag. Native map подтверждён только +для префикса тегов `0..=34`; неизвестная категория, специализированный +callback, provider или численный диапазон возвращает ошибку без публикации +частичных изменений. Ставки `+0x5dc/+0x5e0` сохраняются отдельно, а сам вызов +ещё не включён в Control clock. + +Нативный oracle проверяет пустую таблицу с отрицательным нулём в обеих выходных +ставках, невырожденную пару исходных устройств tag 19/tag 15 при `dt = 0.375` +и tag 9 с шестью значениями и шестью накопителями изменений. Rust-тесты +повторяют их по битам, включая положительное и отрицательное обновления tag 9. +В лицензированном `min_l_n1.dat` предварительная проверка распознаёт категории, +но находит неподдержанный factory tag `26` и отклоняет бюджет до расчёта ставок +или вызова устройств; транзакционный тест подтверждает, что исходная таблица +не меняется. Полный бюджетный проход на реальной миссии пока не выполнен. Rust также отклоняет нулевой знаменатель +обновления tag 19; тест вызывает эту ошибку после изменения provider +потребителем и подтверждает откат всей таблицы. #### Генератор tag 19 в том же бюджетном проходе @@ -846,6 +848,74 @@ tag `15`: смысл изменения этого поля зависит от Поддержанные Rust-векторы не означают, что любой Control descriptor или полный миссионный цикл уже работает. +#### Класс 9: ставка и шесть изменяемых значений + +У `Control.dll` SHA-256 +`bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e` +класс `9` использует constructor RVA `0x25460`, vtable `0x3c938`, rate +`0x25700`, update `0x257b0` и adjustment `0x25a90`. Constructor начинает шесть +значений в `+0x98..+0xac` с `1.0`, а шесть накопителей в `+0xb0..+0xc4` — с +нуля. Текущий `INTO` заменяет descriptor и общие коэффициенты, сохраняя эти +runtime-массивы. + +Для rate `q` — текущее значение provider, `B` — descriptor `+0x20`, `C0..C2` — +коэффициенты, а `scale` — class-9 поле `+0x11c`. Сначала отдельно округляются +`G0 = C0 * scale`, сумма шести значений, и свободная величина `6 - sum`; затем +вычисляются доступная часть `(6 - sum) * G0` и запрос `(C1 * q) * dt`. Rate +равна `-(dt * B) - C2 * min(доступная часть, запрос)`. В update множитель +`+0x4c` сначала получает allocation, затем вычисляется +`S = -(rate(dt) * allocation) - (dt * B)`. При `C2 > 0` изменение равно +`S / C2`; иначе его знак выбирает `±((C1 * q) * dt)`. Если `+0x618` не +приостанавливает обновление, а `G0 > 0`, изменение делится на `G0 * 6` и +передаётся `0x25a90`. + +Adjustment умножает упорядоченную сумму шести значений на единственную +binary32-константу `0x3e2aaaab` — приблизительное `1/6`. Дополнительного деления +на шесть нет. При положительном изменении native ограничивает фактор сверху +единицей и приближает каждое значение к `1`; при отрицательном — уменьшает +каждое пропорционально ему. Каждый фактический шаг также прибавляется к своему +накопителю. Oracle запускает исходные entry points на оригинальном Control.dll +при x87 control word `0x007f`: + +| Вектор | Вход | Rate / multiplier | Шесть выходных значений | Шесть выходных дельт | +|---|---|---|---|---| +| Уменьшение | `q=.4, dt=.5, B=.2, C0=10, scale=1, C1=.6, C2=2`, allocation `.5`; все значения `1`, дельты `0` | `0xbdcccccd`, `0x3f000000` | `0x3f7fe4b1` во всех строках | `0xb9da740e` во всех строках | +| Смешанный набор | `q=.4, dt=.5, B=.2, C0=.75, scale=1.25, C1=.6, C2=2`, allocation `1`; значения `[0,.25,.5,.75,1,.9]`, дельты `[.1,-.2,0,.3,-.4,.5]` | `0xbeae147b`, `0x3f800000` | `[3d49a637,3e92e795,3f064d32,3f432699,3f800000,3f67a90a]` | `[3e18cff4,be26fda3,3cc9a637,3e9fe6cc,becccccd,3f0142a4]` | + +Отдельный прямой вызов `0x25a90` для значения `[0,1,1,1,1,1]`, нулевых +накопителей и добавки `.1` записывает `0x3f19999c` в первую пару value/delta; +остальные пять строк остаются неизменными. Этот vector фиксирует умножение на +сохранённую константу, а не вычисление `sum / 6`. Rust хранит оба массива +отдельно и сохраняет их при descriptor override; ошибки численного диапазона +не публикуют частичный результат. + +#### Общие default budget callbacks и категории + +Ветка фабрики Control RVA `0x2d4b0` направляет обычные классы в constructor +`0x20800`; он устанавливает vtable `0x3c448` и word `+0x50 = 5`. Rate и update +этого семейства — исходные `0x21860` и `0x21890`: первая возвращает +`-(dt * descriptor[+0x20])`, второй записывает allocation в `+0x4c` после +общей проверки активности provider. Фабричные теги `12`, `13` и `25` идут по +ветке default; tag `21` явно выбирает ту же пару. У tags `4`, `8` и `27` свои +vtable/constructor, но их rate/update entries указывают на ту же пару. Native +oracle проверяет factory routing тегов `12/13`, vtable pointers и прямой +вызов rate/update общей пары. Rust повторяет формулу и сохраняет исходные +word-50 defaults, в частности `0x200` у тега `4` и ноль у тега `27`. + +Descriptor category lookup `0x2c3a0` возвращает следующие значения для +проверенного префикса `0..=34`: + +```text +[0,4,4,0,2,3,0,0,2,5,5,0,0,0,1,0,3,2,0,1,3,5,4,0,4,0,3,5,0,0,4,0,1,2,4] +``` + +Именно текущий descriptor выбирает категорию; callback остаётся у исходного +factory-класса. В Rust неизвестный текущий тег за концом префикса и +специализированный factory callback не подменяются общей формулой. На +лицензированном Unit DAT полный проход доходит до factory `26` и возвращает +ошибку без частичного изменения таблицы. Ни этот CPU-вызов, ни локальные +векторы не означают включения бюджетного шага в Control clock. + #### Активация и начальные часы Для Control класса `4` oracle вызывает исходный конструктор RVA `0x31490` -- cgit v1.2.3