feat(runtime): update live Control provider amounts
Docs Deploy / Build and Deploy MkDocs (push) Successful in 37s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 37s
This commit is contained in:
6 files changed
+522
-34
No files matched your search
@@ -334,21 +334,27 @@ sidecar недоступен, запрос `0xe00` сообщает это от
|
||||
проецируются из AniMesh по глобальному ordinal; точные правила, native ABI
|
||||
векторы и лицензированная root/`EXTO` fixture описаны в
|
||||
[техническом подразделе](script-vm.md#начальная-msh-часть-строки-provider).
|
||||
Проверки подтверждают только начальный снимок: сборка runtime parent-ов
|
||||
MSH-часть проверки подтверждает начальный снимок: сборка runtime parent-ов
|
||||
остаётся статическим выводом по RVA `AniMesh.dll+0xa460`, а последующие
|
||||
изменения в полном Control/animation tick-е не проверялись.
|
||||
|
||||
Для provider-строки Rust также хранит одно `initial_amount_bits`. Нативный
|
||||
`Control.dll` `FUN_1000f940` (RVA `0xf940`) кладёт результат selector `3` в
|
||||
текущее и обработанное значения строки; при подтверждённых единичных множителях
|
||||
это raw `max_volume` NDP. Последующее обновление меняет оба native поля, поэтому
|
||||
снимок не моделирует вместимость или изменяемый Control amount.
|
||||
Для 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` для
|
||||
узла. Он пока не доставляет её в AniMesh и не выполняет пересчёт стадий,
|
||||
видимости, FX или время Control. Поэтому обработанное количество остаётся
|
||||
прежним. Гибель root/critical узла и неизвестные flags/aggregate дают явную
|
||||
ошибку до записи; неподдержанные численные диапазоны тоже отклоняются.
|
||||
|
||||
Rust query обращается к загруженному объекту и поддерживает `0xe00` только для
|
||||
текущего descriptor tag `26` с factory tag `26`; другие активные typed tags и
|
||||
factory paths остаются явными границами. Этот запрос возвращает начальный
|
||||
type-1 provider value (`1.0`); дальнейшее изменение provider amount в Rust не
|
||||
реализовано.
|
||||
factory paths остаются явными границами. Запрос читает текущее type-1 значение.
|
||||
Licensed `min_l_n1.dat` подтверждает изменение `0xe00` и Behavior `0x1002` с
|
||||
`1.0` до `0.5` после явного уменьшения количества вдвое; это ещё не
|
||||
автоматический бюджетный цикл.
|
||||
Native oracle проверил изолированные пути распределения, обновления класса `4`,
|
||||
стадийную арифметику `0x11220`, обход дочерних provider-строк `0x11130` и
|
||||
visibility transition `0x11920` с исходными AniMesh callbacks. Отдельный
|
||||
@@ -389,7 +395,7 @@ World3D RVA `0x6bf0` вычисляет часы из `timeGetTime()` с игр
|
||||
RVA `0xbcf0` для остальных. Это подтверждает статическую цепочку вызовов, но не
|
||||
её исполнение на миссии: связывание интерфейсов, активация устройств и shapes,
|
||||
остальные ветви `0xbcf0` и
|
||||
изменение provider-снимков Rust остаются открытыми. Источник контактного коэффициента прослежен статически,
|
||||
автоматическое изменение provider через clock и бюджет Rust остаются открытыми. Источник контактного коэффициента прослежен статически,
|
||||
но не исполнен. Подробные векторы и статические границы приведены в
|
||||
[техническом подразделе о provider-состоянии Control](script-vm.md#изменение-provider-состояния-control).
|
||||
|
||||
|
||||
@@ -653,7 +653,41 @@ oracle вызывает оригинальные `FUN_10010ba0` (RVA `0x10ba0`),
|
||||
через `FUN_10012ce0`. Бюджетная часть активной ветви проверена на ограниченном
|
||||
хосте класса `10`, как описано ниже. Источник входного времени и порядок его
|
||||
доставки до обработчика установлены статически;
|
||||
Rust продолжает читать начальные provider snapshots и не моделирует эти изменения.
|
||||
Rust ещё не вызывает этот бюджетный цикл; отдельно перенесён прямой путь
|
||||
изменения одной строки `0x10f30`, описанный ниже.
|
||||
|
||||
#### Прямое изменение одной строки в Rust
|
||||
|
||||
`apply_loaded_mission_control_node_delta(engine, handle, key, delta)` моделирует
|
||||
`0x10f30` с нулевым четвёртым аргументом: накопление анимационного изменения
|
||||
`+0x10` и detached-ветвь сюда не входят. Максимум читается из исходной NDP
|
||||
строки при подтверждённых единичных множителях Unit DAT. Сначала складываются
|
||||
`f32` amount и delta, результат ограничивается `0..maximum`; при изменении
|
||||
native `+0x590` получает `max(0, total + (new - old))`, `+0x14` — долю
|
||||
`new / maximum`, а `+0x598` становится dirty. Флаг `+0x28 & 0x10` отмечает
|
||||
истощение и снимается при пополнении. Количество `+0x0c` этим методом не
|
||||
обновляется: native записывает его позже в `0x11220`. Конструктор владельца
|
||||
`0x6bf0` начинает с `+0x598 = 1`, а `0xf940` оставляет этот dirty-флаг:
|
||||
первый агрегатный проход нужен даже до новой добавки. Rust loader сохраняет
|
||||
это начальное значение.
|
||||
|
||||
Rust сохраняет начальные и текущие поля отдельно. Возвращаемая
|
||||
`ControlNodeProviderPublication` содержит ключ и значение для обязательного
|
||||
callback узла с selector `0x200`; автоматической доставки в AniMesh пока нет.
|
||||
Полный пересчёт стадии `0x11130`/`0x11220`, видимости и FX также не вызывается.
|
||||
Истощение корня или критического узла возвращает `UnsupportedNodeLifecycle`
|
||||
до записи: состояние владельца `+0x550`, отложенная гибель и сопутствующие
|
||||
callbacks ещё не реализованы. Отсутствие MSH flags, provider aggregate,
|
||||
неподдержанный численный диапазон или неверный handle тоже дают явную ошибку
|
||||
без частичных изменений.
|
||||
|
||||
Rust-тест повторяет native последовательность количества
|
||||
`40 → 60 → 50 → 100 → 0 → 10` при максимуме `100`, проверяет публикации,
|
||||
неизменное обработанное количество и новые результаты `0xe00`/`0x1002`.
|
||||
Native oracle отдельно подтверждает сохранение `+0x0c = 40` и снятие флага
|
||||
истощения при последнем пополнении. Для amount `-0.0` с отрицательной добавкой
|
||||
native записывает биты `+0.0`, но сравнение количеств считает их равными:
|
||||
доля, флаги, итог и callback остаются прежними. Этот случай сохранён и в Rust.
|
||||
|
||||
#### Бюджет и обновление устройства tag 15
|
||||
|
||||
@@ -972,8 +1006,8 @@ Runtime временно собирает mesh-состояния в поряд
|
||||
их с уже упорядоченными NDP provider-строками по общему ordinal. Избыточная NDP
|
||||
строка или отсутствующий MSH asset получают `initial_mesh_state = None`;
|
||||
совпадение по `(component_index, source_row_index)` не выполняется. Состояние
|
||||
также не меняет существующий `0xe00` query: он читает начальное type-1 поле
|
||||
`+0x14` provider-строки, равное `1.0` после нативного конструктора.
|
||||
не меняет правило `0xe00` query: он читает текущее type-1 поле `+0x14`
|
||||
provider-строки, которое после нативного конструктора равно `1.0`.
|
||||
|
||||
### Начальное количество строки provider
|
||||
|
||||
@@ -1005,15 +1039,17 @@ callback-и пришли в порядке `[0, 1, 2, 3]`. Отдельный п
|
||||
`0x8310` имеет ветку установки scale. AniMesh constructor (`AniMesh.dll`,
|
||||
SHA-256 `65659bf67f20e5be77d67a5b2f59968f61b94293143807fa59ca451c560c4f7e`,
|
||||
RVA `0x68a0`) читает три DLL scale globals, которые после LoadLibrary в oracle
|
||||
равны `1.0`. В Rust одна `initial_amount_bits` запись
|
||||
сохраняет это общее начальное количество; полный update, который меняет
|
||||
amount и обработанное предыдущее значение, пока не моделируется.
|
||||
равны `1.0`. В Rust `initial_amount_bits` сохраняет это общее начальное
|
||||
количество отдельно от текущего и последнего обработанного. Прямой delta API
|
||||
меняет только текущее количество; стадийный update пока не перенесён.
|
||||
|
||||
Licensed full-load regression `full_load_builds_effective_control_device_table_for_mine_unit_dat`
|
||||
проверяет значения настоящего `min_l_n1.dat`: корневой provider row `0` даёт
|
||||
`1.0`, а row `1` примонтированного `EXTO` (общий ordinal `22`) даёт `6000.0`.
|
||||
Проверка использует исходные CTL/NDP/MSH и не делает выводов о динамическом
|
||||
изменении этих строк.
|
||||
Проверка использует исходные CTL/NDP/MSH, затем явно уменьшает количество
|
||||
строки tag-26 устройства вдвое. Rust `0xe00` и `0x1002` дают `0.5`, и обычный
|
||||
шаг runtime сохраняет эту долю. Это вызов прямого API, а не полный живой
|
||||
Control tick или автоматическая публикация изменения в AniMesh.
|
||||
|
||||
Native-часть этих ABI проверок воспроизводит
|
||||
`behavior_distributor_tick_native_oracle.cs` на синтетически выделенной памяти
|
||||
|
||||
@@ -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`. По нему запускается представленный ресурсный планировщик
|
||||
|
||||
+42
-10
@@ -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<MissionControlNodeInitialMeshState>
|
||||
На той же начальной стадии 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 бит,
|
||||
|
||||
Reference in new issue
Block a user