fix(runtime): model distributor share normalization
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s
This commit is contained in:
5 files changed
+516
-42
No files matched your search
@@ -157,13 +157,17 @@ Behavior (`Behavior.dll`, RVA `0x3600`) вызывает `World3D!stdGetGameMode
|
||||
подтверждает только состав списков, не точную эквивалентность всей загрузки или
|
||||
регулярного шага Behavior.
|
||||
|
||||
Нативный `Behavior.dll!Add` (RVA `0x1aac0`) создаёт для каждого участника запись
|
||||
размером `0x1c` с начальной долей `1 / count`, а `normalize` (RVA `0x1ac90`)
|
||||
перенормирует прежние доли после добавления. Rust пока хранит только членство:
|
||||
поля записи, доли и их изменение ещё не исполняются. Не восстановлено и
|
||||
соединение с реальными интерфейсами ArealMap, необходимыми для регулярного
|
||||
вызова `FUN_10019e80`; остальной Behavior tick flow и общий dispatcher событий
|
||||
также остаются открытыми.
|
||||
Нативный `Behavior.dll!Add` (RVA `0x1aac0`) и `normalize` (RVA `0x1ac90`)
|
||||
проверены прямыми x86-вызовами на синтетических записях при x87 control word
|
||||
`0x007f`, измеренном также у игры на входах обоих методов. Rust повторяет
|
||||
начальные доли и нормализацию отдельными операциями `f32`, а повторную запись
|
||||
сопоставляет по `ObjectHandle`, меняя только долю существующего участника.
|
||||
Пять DWORD после доли хранятся сырыми; ненулевое первое слово закрепляет вес,
|
||||
смысл четырёх следующих не установлен. Лицензированный AutoDemo-тест проверяет
|
||||
список TMA-объектов и получившиеся начальные доли по кланам, но это не сравнение
|
||||
живых весов миссии с полями исходных runtime-записей. Не восстановлено соединение
|
||||
с реальными интерфейсами ArealMap, нужными `FUN_10019e80`; полный Behavior tick
|
||||
flow и общий dispatcher событий также остаются открытыми.
|
||||
|
||||
Для `Handler(30)` отдельно подтверждены обратный вызов `iron3d.dll` RVA
|
||||
`0x611d0` (SHA-256 `d1dc4ea8535e2069b0a05c9e8be2724dd438ba44cca7a83007d8b2e50f9e35fa`),
|
||||
|
||||
@@ -441,15 +441,36 @@ selector `1` на интерфейсе получателя становится
|
||||
`0x3f2aaaab`). Эти векторы проверяют арифметику исходной функции, но не
|
||||
распределение реальной карты.
|
||||
|
||||
Текущие типы Rust не дают честно наполнить этот список: мир хранит
|
||||
идентификаторы и преобразования объектов, но не нужные tick ресурсные поля;
|
||||
`MissionObjectDraft.properties` сохраняет сырые слова без связи с QI
|
||||
`0x16`/`0x204` или свойством `0x02000100`. Методы добавления и перенастройки
|
||||
строк `FUN_1001a9a0` и `FUN_1001aac0` доступны через entries vtable
|
||||
`0x10059794` по смещениям `+0x0c` и `+0x10`; кто именно вызывает эти методы и
|
||||
какие значения передаёт, пока не установлено. TMA-anchors и сырые preview
|
||||
drafts не используются вместо неизвестной регистрации. `step_headless` по-прежнему
|
||||
шагает только `World`.
|
||||
Полная загрузка Rust теперь собирает этот список из runtime `ObjectHandle` и
|
||||
сохраняет веса вместе с пятью исходными DWORD хвоста каждой записи. Членство
|
||||
строится только для полного режима; preview остаётся статическим. Это пока не
|
||||
даёт ресурсных полей и интерфейсов ArealMap для `FUN_10019e80`, а
|
||||
`step_headless` по-прежнему шагает только `World`.
|
||||
|
||||
### Добавление и нормализация долей
|
||||
|
||||
В этой же GOG `Behavior.dll` (SHA-256
|
||||
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`)
|
||||
`FUN_1001aac0` (RVA `0x1aac0`) добавляет запись размером `0x1c`. Её слово по
|
||||
`+0x04` — доля `f32`, а слова `+0x08..+0x18` копируются как сырой хвост.
|
||||
Новая запись начинается с доли `1 / count` и нулевого хвоста. При повторном
|
||||
ключе запись не вставляется снова: меняется только её доля, остальные пять
|
||||
DWORD сохраняются. После каждого вызова выполняется `FUN_1001ac90` (RVA
|
||||
`0x1ac90`). Она считает долю целевого ключа и записей с ненулевым первым словом
|
||||
хвоста фиксированной. Если сумма остальных долей положительна, остаток от
|
||||
единицы распределяется пропорционально; при нулевой или отрицательной сумме он
|
||||
делится поровну между изменяемыми записями. Проверенный ветвящийся путь также
|
||||
направляет unordered-сумму в пропорциональную ветвь, но стандартный набор этих
|
||||
тестов содержит конечные значения.
|
||||
|
||||
Инструкции Add и normalize выполнялись при x87 control word `0x007f`, то есть
|
||||
с 24-битной точностью мантиссы; такой же CW измерен у оригинальной игры на
|
||||
входах обоих методов. Поэтому Rust повторяет их в порядке списка отдельными
|
||||
операциями `f32`, без раннего перехода в `f64`. Native x86 oracle проверяет
|
||||
три последовательных Add, повторный ключ с sentinel-хвостом, фиксированную
|
||||
запись, нулевую сумму и пограничный вектор округления. В последнем доли имеют
|
||||
биты `[0x3f000000, 0x3f000000, 0x33000000]`. Эти сценарии сверяют локальную
|
||||
арифметику на стандартных конечных данных, а не весь игровый запуск.
|
||||
|
||||
Для проверки использованы три x86 helper-а на синтетической памяти. Caller
|
||||
oracle вызывает оригинальные `iron3d.dll` entry points и проверяет преобразование
|
||||
@@ -485,7 +506,8 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
exit $LASTEXITCODE
|
||||
```
|
||||
|
||||
Отдельный oracle distributor tick компилируется и запускается так:
|
||||
Native oracle Add, normalize и distributor tick компилируется и запускается
|
||||
так:
|
||||
|
||||
```powershell
|
||||
$env:FPARKAN_ORIGINAL_BEHAVIOR_DLL = 'C:\GOG Games\Parkan - Iron Strategy\Behavior.dll'
|
||||
@@ -498,8 +520,12 @@ exit $LASTEXITCODE
|
||||
|
||||
Oracle проверяет SHA-256 оригинального `Behavior.dll`:
|
||||
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`. Harness
|
||||
вызывает функцию по RVA `0x19e80` при единственной подмене импортного
|
||||
слота `GetSystemArealMap`; машинный код tick не патчится. Проверяются нулевой
|
||||
явно устанавливает x87 CW `0x007f` для вызовов Add (`0x1aac0`) и normalize
|
||||
(`0x1ac90`), проверяет CW до и после каждого вызова и восстанавливает прежнее
|
||||
значение перед tick suite. Для этих двух методов подменяется только bounded
|
||||
вставка синтетического vector; их машинный код не патчится. Для tick helper
|
||||
подменяет только IAT-слот `GetSystemArealMap` и вызывает RVA `0x19e80`.
|
||||
Проверяются нулевой
|
||||
шаг, обе ветви селекторов скорости, пропуск состояния `0xfffe`, missing lookup,
|
||||
динамическое повторное чтение `0x1002`, ограниченный запас руды, разные веса,
|
||||
достаточное и недостаточное значение мощности. Это синтетические ArealMap
|
||||
|
||||
+20
-10
@@ -207,7 +207,9 @@ SuperAI для записей кланов, затем обновляет Behavi
|
||||
исполнен на исходной DLL с искусственными объектами. Rust пока не вызывает
|
||||
регулярный шаг: игровой мир ещё не предоставляет нужные ресурсные интерфейсы.
|
||||
При полной загрузке Rust уже восстанавливает членство объектов в клановых
|
||||
списках Behavior, но сами записи распределителя и их доли пока не моделирует.
|
||||
списках Behavior и задаёт их начальные доли. Остальные слова записи хранятся
|
||||
сырыми: семантика только первого из пяти слов хвоста установлена для
|
||||
нормализации.
|
||||
Runtime также не подменяет очередь оригинала: вызывающая сторона сама передаёт
|
||||
время, а локальный генератор Rust пока учитывает только случайные числа
|
||||
восстановленного стартового шага.
|
||||
@@ -241,10 +243,17 @@ Rust-тест повторно читает лицензированную ми
|
||||
TMA-записями и проверяет реальные `ObjectHandle`, но помечен `#[ignore]`, чтобы
|
||||
не требовать оригинальных ресурсов от остальных разработчиков.
|
||||
|
||||
Это пока только перечень участников. Оригинальный `Behavior.dll!Add` создаёт для
|
||||
каждого участника запись размером `0x1c` с долей `1 / count`, после чего
|
||||
нормирует прежние доли. Rust ещё не хранит эти поля и не исполняет их регулярный
|
||||
обмен ресурсами. Поля не выводятся из TMA-соседнего слова или `OwnerId`.
|
||||
Список распределителя хранит не только участников, но и долю каждого. При
|
||||
добавлении двух объектов оба получают по половине общего веса. Если добавить
|
||||
третий, движок оставляет долю новой записи около одной трети, а оставшийся вес
|
||||
делит между прежними изменяемыми записями. Помеченная запись сохраняет свой вес,
|
||||
как закреплённая часть общего набора. Повторное добавление того же объекта не
|
||||
создаёт вторую строку и оставляет остальные слова записи нетронутыми.
|
||||
|
||||
Rust хранит этот небольшой начальный список с долями. Он не смешивает selector
|
||||
из TMA, владельца мирового объекта и внутренний ключ Behavior: связь в Rust
|
||||
опирается на уже созданный `ObjectHandle`. Регулярный обмен ресурсами пока
|
||||
остаётся отдельным этапом, потому что для него нужны интерфейсы игровых объектов.
|
||||
|
||||
### Регулярная передача ресурсов
|
||||
|
||||
@@ -263,11 +272,12 @@ Resource Distributor работает со списком зарегистрир
|
||||
исходной `Behavior.dll`, но объекты и ответы интерфейсов заданы тестом; это ещё
|
||||
не воспроизведение реальной миссии.
|
||||
|
||||
Членство списка связано с реальным путём регистрации, но выполнение этой
|
||||
миссии не сравнивалось с тем, как исходная DLL заполняет поля записи `0x1c` и
|
||||
нормирует доли. Rust пока не хранит эти поля, а игровому миру ещё не хватает
|
||||
ресурсных интерфейсов ArealMap для регулярного шага. Открытые вопросы и границы
|
||||
проверки приведены в [границах знания](../appendices/knowledge-boundaries.md).
|
||||
Для правила долей есть отдельная проверка исходной `Behavior.dll` на синтетических
|
||||
записях при игровом x87 control word `0x007f`: проверены добавление, повторный
|
||||
ключ, закреплённая запись, нулевая сумма и округление. Это позволяет Rust
|
||||
сохранять начальный список, но пока не подтверждает веса реальных объектов в
|
||||
живой миссии. Для регулярного шага по-прежнему не хватает интерфейсов ArealMap.
|
||||
Остальные ограничения собраны в [границах знания](../appendices/knowledge-boundaries.md).
|
||||
|
||||
Первый часто встречающийся table entry с side effect — `Handler(2)` (176
|
||||
records в corpus). Он берёт active instruction, разрешает семь slots через
|
||||
|
||||
Reference in new issue
Block a user