From acdf635ffb3868eee4fa383303cac75fdeea8e23 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Thu, 29 Oct 2026 16:55:09 +0400 Subject: feat(runtime): update live Control provider amounts --- docs/tomes/02-architecture.md | 4 +++- docs/tomes/06-behavior.md | 52 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 11 deletions(-) (limited to 'docs/tomes') diff --git a/docs/tomes/02-architecture.md b/docs/tomes/02-architecture.md index 527d7b2..1eaedc7 100644 --- a/docs/tomes/02-architecture.md +++ b/docs/tomes/02-architecture.md @@ -361,7 +361,9 @@ CollObject, Control и PhysicalModel, пропуская отсутствующ значений сам. Таблицы, адреса и различие путей классов приведены в [техническом разборе](../appendices/script-vm.md#доставка-игровых-часов-в-control). Статическая цепочка ещё не доказывает исполнение активного Control update на -миссии. Rust пока сохраняет начальные provider-снимки и не изменяет их этим путём. +миссии. Rust сохраняет исходные и текущие provider-поля отдельно. Прямой delta +API меняет строку и возвращает публикацию узла, но описанный clock/budget-путь +пока не подключён. В текущем runtime `step_headless` и `frame` получают время очереди явным аргументом `u32`. По нему запускается представленный ресурсный планировщик diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md index eaa5148..aa2fa09 100644 --- a/docs/tomes/06-behavior.md +++ b/docs/tomes/06-behavior.md @@ -891,8 +891,9 @@ TMA и компоненты, но во временном VFS заменяет оригинальный профиль генератора — 10.0 и 0.0 соответственно. Для исходного tag-26 ряда получается `1.0` provider × `1.0` coefficient × `1.0` multiplier; профиль mine умножает эту сумму на `1.0`, поэтому selector `0x1002` возвращает -`1.0`. Эти запросы используют начальные provider-значения после конструкторов, -а не результаты живого игрового тика. +`1.0`. Эти числа описывают начальное состояние после конструкторов. +Сами запросы читают текущую долю provider; отдельное прямое изменение строки +уже может изменить ответ, хотя полный игровой Control tick ещё не исполняется. У Control есть и начальные сведения о модели узла, но их таблица не совпадает с NDP. Sidecar задаёт состав и порядок provider-строк, а поле type-1 `+0x14` @@ -920,12 +921,42 @@ initial_mesh_state: Option На той же начальной стадии Control вычисляет количество строки по NDP `max_volume`. Для загрузки Unit DAT проверенные множители равны `1.0`, поэтому количество совпадает с исходным значением NDP. Оригинал начинает с одинакового -текущего и последнего обработанного количества; Rust сохраняет его один раз как -`initial_amount_bits`. Это исходный снимок, а не неизменяемая вместимость: -позднее Control меняет оба количества. В лицензированном `min_l_n1.dat` +текущего и последнего обработанного количества. Rust оставляет исходное число +в `initial_amount_bits`, а текущее и обработанное количество хранит отдельно. +Вместимость при этом берётся из NDP; исходный снимок не заменяет её. В лицензированном `min_l_n1.dat` проверены, например, root row `0` со значением `1.0` и строка `EXTO` с local row `1` в общей позиции `22` со значением `6000.0`. +Для одной строки перенесён прямой путь `Control.dll+0x10f30` с выключенным +флагом накопления анимационного изменения. API +`apply_loaded_mission_control_node_delta` принимает живой объект, глобальный +ключ строки и добавку. Например, у строки с максимумом `100` и количеством +`40` добавка `20` даёт количество `60` и долю `0.6`. Переполнение ограничивается +максимумом, отрицательный результат — нулём; при нуле ставится флаг истощения +`0x10`, а пополнение снимает его. Общий итог изменяется именно на разницу +нового и прежнего количества: + +```rust +let amount = (previous + delta).clamp(0.0, maximum); +let total = (total + (amount - previous)).max(0.0); +let ratio = amount / maximum; +``` + +Это три отдельные операции над `f32`, с проверкой поддерживаемого численного +диапазона до записи состояния. Исходные `initial_*` поля сохраняются; +`processed_amount_bits` тоже не меняется, поскольку его обновляет другой +нативный проход — пересчёт стадий. API возвращает обязательную публикацию +`ControlNodeProviderPublication` для узла и selector `0x200`; сам callback +AniMesh и пересчёт стадий этим API не исполняются. Истощение корня либо узла +с критическим флагом `1` пока возвращает ошибку без изменения состояния: +эта ветвь требует полного lifecycle объекта, а не только нового количества. + +На исходном `min_l_n1.dat` прямое уменьшение количества строки ресурсного +устройства вдвое меняет ответы `0xe00` и Behavior `0x1002` с `1.0` на `0.5`. +Обычный последующий шаг runtime сохраняет это состояние. Это воспроизводимый +тест связки загрузки и запросов, но пока не автоматическое изменение через +clock Control. + После загрузки количество в строках может меняться. Контроллер распределяет добавку либо пропорционально свободному месту, либо по очереди: при максимумах `[30, 60, 50]`, текущих значениях `[10, 20, 0]` и добавке `22` первый способ @@ -946,14 +977,15 @@ Control пересчитывает дочерние строки в глубин `[102, 570]`. В проверенных векторах одинаковые данные при скрытии и повторном включении восстановили суммы; переходы с разными слотами — нет. Эти проверки исполняют исходные DLL на тестовых строках, но не являются запуском Control на -полной миссии; Rust пока сохраняет только начальное состояние provider-строк. +полной миссии; Rust пока не выполняет эти переходы стадий и видимости. +Прямой путь изменения количества остаётся отдельной операцией. Подробные границы приведены в [техническом приложении](../appendices/script-vm.md#родители-стадии-и-видимость-provider-узлов). Для точного повтора важен порядок округления. При исходных значениях `[2^24, 2]` и запросе `-1` оригинал сначала складывает общий итог с изменением, затем вычитает прежний итог. Из-за округления фактическое изменение получается `-2`. -Нативная проверка подтвердила это поведение; Rust пока использует начальный -снимок provider-строк и не исполняет их дальнейшее распределение. +Нативная проверка подтвердила это поведение; общий распределитель +`0x10ba0` в Rust ещё не перенесён, хотя прямая добавка к одной строке уже доступна. Отдельный путь объекта класса `4` умножает интервал на контактный коэффициент и передаёт изменение строке. В проверенном примере коэффициент `1`, интервал `2`, @@ -969,8 +1001,8 @@ Control заново разыгрывает срок обновления, да на `502`. Выбрать случайный срок один раз и ждать его было бы неверно. Принудительный шаг тоже расходует RNG; неактивный объект синхронизирует часы без такого расхода. Это локальные native проверки, а не работающий Rust -Control: запросы Rust `0xe00` и `0x1002` по-прежнему используют начальные -снимки provider-строк, а `0x1005` — загруженную ставку профиля. +Control: запросы Rust `0xe00` и `0x1002` читают текущее состояние строк, +которое пока меняет только прямой API, а `0x1005` — загруженную ставку профиля. Точные нативные векторы и открытые границы описаны в [приложении](../appendices/script-vm.md#изменение-provider-состояния-control). На x87 control word `0x007f` каждое умножение округляет мантиссу до 24 бит, -- cgit v1.2.3