diff options
| author | Valentin Popov <valentin@popov.link> | 2026-10-29 17:24:43 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-10-29 17:24:43 +0300 |
| commit | d8e86e4f33241d4c0cbb236ecbd32f71798ce9ed (patch) | |
| tree | 6318667b6880f26de54dd3a8dcd6e89f6eae6af7 /docs | |
| parent | be4031cb54d9bd88d1bad9cf993d27f914c767fa (diff) | |
| download | fparkan-d8e86e4f33241d4c0cbb236ecbd32f71798ce9ed.tar.xz fparkan-d8e86e4f33241d4c0cbb236ecbd32f71798ce9ed.zip | |
feat(runtime): distribute Control provider amounts
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/appendices/knowledge-boundaries.md | 42 | ||||
| -rw-r--r-- | docs/appendices/script-vm.md | 65 | ||||
| -rw-r--r-- | docs/tomes/06-behavior.md | 17 |
3 files changed, 100 insertions, 24 deletions
diff --git a/docs/appendices/knowledge-boundaries.md b/docs/appendices/knowledge-boundaries.md index e8bdb42..9869581 100644 --- a/docs/appendices/knowledge-boundaries.md +++ b/docs/appendices/knowledge-boundaries.md @@ -338,17 +338,37 @@ MSH-часть проверки подтверждает начальный сн остаётся статическим выводом по RVA `AniMesh.dll+0xa460`, а последующие изменения в полном Control/animation tick-е не проверялись. -Для provider-строки Rust сохраняет исходные и текущие amount/type-1 поля -отдельно, а `processed_amount_bits` соответствует native `+0x0c`. Инициализатор -`Control.dll+0xf940` кладёт результат selector `3` в текущее и обработанное -количество; при подтверждённых единичных множителях это raw `max_volume` NDP. -Прямой API `apply_loaded_mission_control_node_delta` меняет amount, долю, флаг -истощения, aggregate и dirty, применяя публикацию `0x200` к CPU-проекции -соответствующего узла модели. Исходный `AniMesh.dll+0x5750` подтверждён в -связке с `Control+0x10f30`: меняется только поле узла `+0x124`, а стадия и -соседние узлы сохраняются. Эта проекция ещё не подключена к GPU/window path -и сама delta-операция не выполняет пересчёт стадий, видимости, FX или время -Control. Обработанное количество при ней остаётся прежним. Отдельный +Для provider-строки Rust хранит amount `+0x08`, накопитель `+0x10`, обработанное +количество `+0x0c`, долю `+0x14`, стадию `+0x18` и flags `+0x28` раздельно. +Инициализатор `Control.dll+0xf940` кладёт результат selector `3` в текущее и +обработанное количество; при подтверждённых единичных множителях это raw +`max_volume` NDP. Таблица также хранит capacity aggregate `+0x58c`, текущий +aggregate `+0x590`, dirty `+0x598` и начальный gate `+0x618`. + +Прямой API `apply_loaded_mission_control_node_delta` сохраняет native четвёртый +аргумент `0`: меняет amount, долю, флаг истощения, aggregate и dirty, оставляя +`+0x10` нетронутым. Публикация `0x200` отражается в CPU-проекции соответствующего +узла модели по правилу исходного обработчика; стадия и соседние узлы не меняются. +Обработанное количество при этой прямой операции остаётся прежним. +API `distribute_loaded_mission_control_provider_amount` переносит +`Control+0x10ba0`: proportional mode делит добавку по свободной ёмкости, а +вычитание распределяет по текущим количествам; sequential заполняет строки +вперёд, снимает с конца и использует исходный запрос даже если общая ёмкость +округлилась вниз. Row updater вызывается с четвёртым аргументом `1`, поэтому +`+0x10` получает фактическую разницу amount. После распределения обязателен +`0x11130(root, true)`. + +Оба API работают только с CPU-проекцией и не подключены к GPU/window path или +игровому Control clock. Gate `+0x618` изначально выключен; событие `7` задаёт его +по правилу `gate = (arg == 2)` и вызывает `0x2dc20`, только если gate изменился. +События Control в Rust пока не исполняются. +Capacity aggregate выведен при подтверждённых единичных множителях Unit DAT; +изменение масштабов `0x9ee0`/`0x9f90` не реализовано. Из-за округления `+0x590` +может стать больше сохранённого `+0x58c`; распределение сохраняет этот signed +capacity по native формуле, не вводя отдельного запрета. Если итоговый stage walk +нуждается в неизвестном состоянии, depletion lifecycle, FX или host callback, +распределение сообщает ошибку и не публикует ни одной частичной записи. +Отдельный `update_loaded_mission_control_node_stage` реализует наследование parent ratio и stage, ceiling-арифметику, CPU visible stage и копию processed amount. Отдельная `propagate_loaded_mission_control_node_stage` переносит `0x11130`: diff --git a/docs/appendices/script-vm.md b/docs/appendices/script-vm.md index 60d15e1..b658348 100644 --- a/docs/appendices/script-vm.md +++ b/docs/appendices/script-vm.md @@ -624,12 +624,25 @@ oracle вызывает оригинальные `FUN_10010ba0` (RVA `0x10ba0`), `0xdc40`. Пропорциональный режим проверен на максимумах `[30, 60, 50]`, текущих значениях `[10, 20, 0]` и добавке `22`, результат `[14, 28, 10]`; отдельный вектор подтверждает последовательное заполнение вперёд и вычитание с конца. -Проверены ограничение строк, снятие признака истощения при пополнении и порядок -обратных вызовов. При x87 control word `0x007f` для `[2^24, 2]` с запросом `-1` -получены значения `[16777214, 1.99999976]` и итог `16777216`: округление -вычисленного изменения даёт `-2`. Oracle задаёт внешнюю публикацию результата -обратным вызовом; provider строки не имеют родительских узлов и не проходят -отдельный жизненный цикл истощения корневого объекта. +Проверены ограничение строк, пропуск истощённых записей, их пополнение с флагом +revive и порядок обратных вызовов. Все распределительные вызовы используют +четвёртый аргумент `1` в `0x10f30`: фактическое изменение строки прибавляется к +её полю `+0x10`. Для исходных значений `[2^24, 2]` и запроса `-1` получены +`[16777214, 1.99999976]`, общий итог `16777216` и накопленные разницы +`[-2, -2.3841858e-7]`. Проверка также подтверждает, что последовательный режим +несёт исходный запрос между строками: сумма свободных мест `[16777216, 1]` +округляется до `16777216`, однако запрос `33554432` заполняет обе строки. +Oracle задаёт внешнюю публикацию результата обратным вызовом; provider-строки +не имеют родительских узлов и не проходят отдельный жизненный цикл истощения +корневого объекта. + +Агрегаты тоже округляются независимо. Начальная ёмкость `+0x58c = 33554436` +для максимумов `[33554432, 3]` не меняется при обновлениях одной строки +`-1, -1, -1, +3`, но текущий итог `+0x590` округляется до `33554440`. +Следующая добавка `1` с revive использует signed capacity `-4`, выдаёт ratio `1` +и сохраняет уже полные строки. Native oracle и Rust повторяют эту достижимую +последовательность; отрицательный результат разности сам по себе не является +ошибкой состояния. Для `FUN_10012a40` (RVA `0x12a40`) нативный вектор задаёт класс `4`, интервал `2`, коэффициент `+0x1a4` равный `1` и текущее значение `10.000001`; результат @@ -654,14 +667,16 @@ oracle вызывает оригинальные `FUN_10010ba0` (RVA `0x10ba0`), хосте класса `10`, как описано ниже. Источник входного времени и порядок его доставки до обработчика установлены статически; Rust ещё не вызывает этот бюджетный цикл; отдельно перенесён прямой путь -изменения одной строки `0x10f30`, описанный ниже. +изменения одной строки `0x10f30` и явный шаг распределения `0x10ba0`, описанные +ниже. Ни один из них пока не подключён к игровому Control clock. #### Прямое изменение одной строки в Rust `apply_loaded_mission_control_node_delta(engine, handle, key, delta)` моделирует `0x10f30` с нулевым четвёртым аргументом: накопление анимационного изменения -`+0x10` и detached-ветвь сюда не входят. Максимум читается из исходной NDP -строки при подтверждённых единичных множителях Unit DAT. Сначала складываются +`+0x10` не выполняется, а текущее значение поля сохраняется; detached-ветвь +объекта тоже не моделируется. Максимум читается из исходной NDP строки при +подтверждённых единичных множителях Unit DAT. Сначала складываются `f32` amount и delta, результат ограничивается `0..maximum`; при изменении native `+0x590` получает `max(0, total + (new - old))`, `+0x14` — долю `new / maximum`, а `+0x598` становится dirty. Флаг `+0x28 & 0x10` отмечает @@ -711,6 +726,38 @@ Native oracle отдельно подтверждает сохранение `+0 native записывает биты `+0.0`, но сравнение количеств считает их равными: доля, флаги, итог и callback остаются прежними. Этот случай сохранён и в Rust. +#### Распределение provider-таблицы в Rust + +`distribute_loaded_mission_control_provider_amount` переносит общий шаг +`Control.dll+0x10ba0`. Режим proportional распределяет добавку по свободной +ёмкости, а снятие — по текущему количеству строк; sequential заполняет их с +первой и снимает ресурс с последней. Для sequential native сохраняет исходную +величину запроса, даже когда сумма свободных мест округляется вниз. +`revive_exhausted` включает строки с флагом истощения `+0x28 & 0x10` при добавке. +Rust хранит максимум общей таблицы `+0x58c` отдельно от текущей суммы `+0x590`; +для загруженных Unit DAT эти значения рассчитаны при подтверждённых единичных +масштабах. Их разность может стать отрицательной из-за независимого округления +изменений текущего агрегата; распределитель сохраняет такой signed capacity по +формуле оригинала. Обновления масштабов `0x9ee0` и `0x9f90` в Rust не реализованы. + +Native row updater вызывается здесь с четвёртым аргументом `1`, поэтому +изменение `(new amount - old amount)` накапливается в provider `+0x10`. +`apply_loaded_mission_control_node_delta` сохраняет прежний четвёртый аргумент +`0` и оставляет этот накопитель нетронутым. После всех строк `0x10ba0` +обязательно запускает `0x11130(root, update_effects=true)`. Rust переносит этот +проход на копии таблицы: если ему нужны неизвестное состояние, depletion +lifecycle, FX или host callback, весь шаг завершается ошибкой без частичных +amount, accumulator, aggregate или stage изменений. + +Поле `+0x618` изначально выключено; обработчик события `7` задаёт gate по +правилу `gate = (arg == 2)` и вызывает пересчёт `0x2dc20`, только если gate +изменился. Проекция сохраняет начальное состояние и уважает установленную +suspension-метку. События Control пока не исполняются, +поэтому это API остаётся явным CPU-шагом и не подключено к игровому clock. +Тесты Rust повторяют пропорциональный вектор, накопитель `+0x10`, округление +`2^24`, sequential capacity boundary, достижимый negative capacity и откат при +unsupported FX/lifecycle. + #### Бюджет и обновление устройства tag 15 Для той же сборки `Control.dll` oracle исполняет оригинальный диспетчер diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md index 777caf1..452f577 100644 --- a/docs/tomes/06-behavior.md +++ b/docs/tomes/06-behavior.md @@ -995,7 +995,13 @@ clock Control. добавку либо пропорционально свободному месту, либо по очереди: при максимумах `[30, 60, 50]`, текущих значениях `[10, 20, 0]` и добавке `22` первый способ даёт `[14, 28, 10]`. При вычитании последовательный способ начинает с последней -строки. Так одна таблица описывает и текущее значение, и то, как оно меняется. +строки. Пропорциональное вычитание делит запрос по текущим количествам. +У каждой изменившейся строки есть отдельный накопитель изменений количества: он +получает фактически применённую разницу, а не исходный запрос. +Большие суммы округляются в двоичном формате. Поэтому после нескольких +маленьких изменений общий запас иногда оказывается чуть больше первоначальной +ёмкости; оригинал продолжает считать с этой отрицательной разностью, а не +исправляет её отдельным правилом. У строк есть и связи с родительскими узлами. Нативный `Control.dll` сначала обновляет запрошенную строку через `0x11220`, затем `0x11130` ищет её детей @@ -1040,8 +1046,10 @@ clock Control. Для точного повтора важен порядок округления. При исходных значениях `[2^24, 2]` и запросе `-1` оригинал сначала складывает общий итог с изменением, затем вычитает прежний итог. Из-за округления фактическое изменение получается `-2`. -Нативная проверка подтвердила это поведение; общий распределитель -`0x10ba0` в Rust ещё не перенесён, хотя прямая добавка к одной строке уже доступна. +Rust переносит этот порядок, сохраняет native накопитель каждой строки и +запускает обязательный проход стадий с effects. Если ему нужны ещё не +реализованные FX или lifecycle-вызовы, таблица остаётся без изменений. Эти +операции обновляют только CPU-снимок и пока не подключены к игровым часам Control. Отдельный путь объекта класса `4` умножает интервал на контактный коэффициент и передаёт изменение строке. В проверенном примере коэффициент `1`, интервал `2`, @@ -1058,7 +1066,8 @@ Control заново разыгрывает срок обновления, даÐ Принудительный шаг тоже расходует RNG; неактивный объект синхронизирует часы без такого расхода. Это локальные native проверки, а не работающий Rust Control: запросы Rust `0xe00` и `0x1002` читают текущее состояние строк, -которое пока меняет только прямой API, а `0x1005` — загруженную ставку профиля. +которое меняют явные API одной строки и распределения, а `0x1005` — загруженную +ставку профиля. Точные нативные векторы и открытые границы описаны в [приложении](../appendices/script-vm.md#изменение-provider-состояния-control). На x87 control word `0x007f` каждое умножение округляет мантиссу до 24 бит, |
