test(runtime): verify native resource distributor tick
Docs Deploy / Build and Deploy MkDocs (push) Successful in 39s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 39s
This commit is contained in:
4 files changed
+709
-6
No files matched your search
@@ -127,8 +127,12 @@ Rust `load_mission` принимает явный `queue_time_ms`, а не чи
|
||||
объектов в создаваемом мире Rust;
|
||||
это интеграционный выбор runtime, а не подтверждённая эквивалентность всех
|
||||
host construction phases. Не проверена другая лицензированная миссия с
|
||||
непустым `Init`, не восстановлены игровой tick Behavior и общий dispatcher
|
||||
остальных событий.
|
||||
непустым `Init`. Вызов `Behavior.dll!FUN_10019e80` (RVA `0x19e80`, SHA-256
|
||||
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`) исполнялся
|
||||
на синтетических интерфейсах, но runtime пока не вызывает его в игровом цикле:
|
||||
не доказаны создание реальных записей distributor и интерфейсов ArealMap,
|
||||
которые ему нужны. Остальной Behavior tick flow и общий dispatcher событий
|
||||
также не восстановлены.
|
||||
|
||||
Для `Handler(30)` отдельно подтверждены обратный вызов `iron3d.dll` RVA
|
||||
`0x611d0` (SHA-256 `d1dc4ea8535e2069b0a05c9e8be2724dd438ba44cca7a83007d8b2e50f9e35fa`),
|
||||
|
||||
@@ -400,10 +400,57 @@ callback-точку входа и порядок внутри неё, но не
|
||||
bundle, но остальные потребители исходного глобального RNG не моделируются.
|
||||
В Rust также нет значения аргумента в `AI+0x3fc`, recursion guard `AI+0x78`
|
||||
или одноразового latch `AI+0x8a8`. Поэтому это связанный начальный поднабор, а
|
||||
не полная активация SuperAI и не обновление Behavior на игровых тиках. При
|
||||
не полная активация SuperAI. Восстановленный ниже distributor tick проверен на
|
||||
исходной DLL, но пока не подключён к Rust игровому циклу: у runtime нет
|
||||
ArealMap-объектов и интерфейсов, которые нужны этому вызову. При
|
||||
ошибке ограниченного `Init` Rust отклоняет полную загрузку и сохраняет прежний
|
||||
мир и состояние генератора; такая политика отката принадлежит Rust loader-у.
|
||||
|
||||
В проверенной GOG-сборке `Behavior.dll` (SHA-256
|
||||
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`)
|
||||
`FUN_10019e80` (RVA `0x19e80`) — отдельный шаг Resource Distributor. При
|
||||
нулевом `dt` он сразу возвращается. Иначе он проходит список
|
||||
строк по семь DWORD в порядке регистрации дважды: первый проход собирает
|
||||
значения, второй заново ищет объекты и применяет результаты. Поэтому найденный
|
||||
при втором проходе объект или getter может вернуть уже другое значение. Для каждой
|
||||
строки вызывается ArealMap lookup; отсутствующий объект пропускается. Если
|
||||
младшие 16 бит результата vslot `+0x44` равны `0xfffe`, строка пропускается до
|
||||
запроса интерфейсов. Иначе исходный код запрашивает QI `0x16` и `0x204`, читает
|
||||
класс через vslot `+0x38`, а для классов мощности вызывает vslot `+0x68` с
|
||||
селектором `0x1005`, для двух классов руды — с `0x1002`. Остальные ветви
|
||||
используют интерфейс `0x204` и свойство `0x02000100` через vslots `+0x4c` и
|
||||
`+0x48`. В реализации этого свойства `FUN_10026990` (RVA `0x26990`) возвращает
|
||||
DWORD по смещению `+0x14` как `f32`; если выходной указатель не null, функция
|
||||
копирует в него DWORD по смещению `+0x08`. Парный setter `FUN_100269E0` (RVA
|
||||
`0x269e0`) записывает переданный `f32` в `+0x14`; только если третий аргумент
|
||||
не null, setter копирует указанный им DWORD в `+0x08`. При null старое значение
|
||||
`+0x08` сохраняется. Это нулевые индексы 5 и 2 вектора DWORD; значение `+0x08`
|
||||
пока не получило доменного имени. Идентификаторы и границы вызовов не
|
||||
задают полную семантику свойств.
|
||||
|
||||
Изолированный x86 oracle вызывает исходный tick с подменой только IAT-слота
|
||||
`GetSystemArealMap`; синтетические vtables записывают последовательность
|
||||
вызовов и ответы. Для смешанного примера два прохода нашли объекты в порядке
|
||||
`41,42,41,42`. Производитель руды с начальным значением `1` и скоростью `4`
|
||||
вернул при следующем чтении скорость `1`; получатель с начальным значением `6`
|
||||
получил `7`, а агрегаты distributor равны `[0,1,1,0.375,6]`. При запасе
|
||||
производителя `0.25` получатель стал `6.25`; в трёхстрочном примере вес
|
||||
получателя `0` не изменил значение `6`, а вес `0.25` дал `8 -> 9`. В примере
|
||||
мощности доступное значение `4` покрывает спрос `0.375`; при значении `0.125`
|
||||
selector `1` на интерфейсе получателя становится `2/3` (битовый образ
|
||||
`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`.
|
||||
|
||||
Для проверки использованы три x86 helper-а на синтетической памяти. Caller
|
||||
oracle вызывает оригинальные `iron3d.dll` entry points и проверяет преобразование
|
||||
TMA-координат в аргументы `CreateSuperAI`. AI oracle проходит оригинальный
|
||||
@@ -438,6 +485,27 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
exit $LASTEXITCODE
|
||||
```
|
||||
|
||||
Отдельный oracle distributor tick компилируется и запускается так:
|
||||
|
||||
```powershell
|
||||
$env:FPARKAN_ORIGINAL_BEHAVIOR_DLL = 'C:\GOG Games\Parkan - Iron Strategy\Behavior.dll'
|
||||
New-Item -ItemType Directory -Force -Path target\behavior-distributor-native-oracle | Out-Null
|
||||
& "$env:WINDIR\Microsoft.NET\Framework\v4.0.30319\csc.exe" /nologo /platform:x86 /out:target\behavior-distributor-native-oracle\BehaviorDistributorTickOracle.exe crates\fparkan-runtime\tests\behavior_distributor_tick_native_oracle.cs
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
& .\target\behavior-distributor-native-oracle\BehaviorDistributorTickOracle.exe
|
||||
exit $LASTEXITCODE
|
||||
```
|
||||
|
||||
Oracle проверяет SHA-256 оригинального `Behavior.dll`:
|
||||
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`. Harness
|
||||
вызывает функцию по RVA `0x19e80` при единственной подмене импортного
|
||||
слота `GetSystemArealMap`; машинный код tick не патчится. Проверяются нулевой
|
||||
шаг, обе ветви селекторов скорости, пропуск состояния `0xfffe`, missing lookup,
|
||||
динамическое повторное чтение `0x1002`, ограниченный запас руды, разные веса,
|
||||
достаточное и недостаточное значение мощности. Это синтетические ArealMap
|
||||
объекты; исполнение оригинальной функции проверяет векторы oracle, но не
|
||||
подменяет сравнение Rust и интеграционную проверку миссии в игре.
|
||||
|
||||
Caller oracle проверяет AutoDemo пару `(500.2857, 751.8217)`, четыре режима
|
||||
x87 на tie-векторах, границу `10000` и младший DWORD. AI oracle отдельно
|
||||
проверяет, что `FUN_10005c70(0xffffffff, 10001)` даёт поля `(2^32, 10001.0)`,
|
||||
|
||||
@@ -203,9 +203,37 @@ SuperAI для записей кланов, затем обновляет Behavi
|
||||
Это полезное начало сценария, но ещё не целая игровая жизнь AI. Текущий runtime
|
||||
не воспроизводит аргумент обратного вызова в `AI+0x3fc`, защиту от повторного
|
||||
входа `AI+0x78`, одноразовую метку `AI+0x8a8` или обновления Behavior на игровых
|
||||
тиках. Он также не подменяет очередь оригинала: вызывающая сторона сама
|
||||
передаёт время, а локальный генератор Rust пока учитывает только случайные
|
||||
числа этого восстановленного шага.
|
||||
тиках. При этом один конкретный шаг Behavior — распределение ресурсов — уже
|
||||
исполнен на исходной DLL с искусственными объектами; FParkan пока не вызывает
|
||||
его, потому что игровой мир Rust ещё не предоставляет нужные ресурсные
|
||||
интерфейсы. Runtime также не подменяет очередь оригинала: вызывающая сторона
|
||||
сама передаёт время, а локальный генератор Rust пока учитывает только случайные
|
||||
числа восстановленного стартового шага.
|
||||
|
||||
### Регулярная передача ресурсов
|
||||
|
||||
Resource Distributor работает со списком зарегистрированных объектов. За один
|
||||
шаг он сначала проходит список и собирает нужные значения, а затем проходит его
|
||||
ещё раз и применяет результат. Во втором проходе объект ищется заново, поэтому
|
||||
его состояние или ответ getter-а может измениться между чтением и записью.
|
||||
Нулевой временной шаг ничего не делает; некоторые состояния объекта также
|
||||
пропускают строку ещё до запросов ресурсных интерфейсов.
|
||||
|
||||
В синтетическом примере исходного кода два объекта с ключами `41` и `42`
|
||||
обрабатываются в порядке `41, 42, 41, 42`: производитель сначала сообщает
|
||||
скорость `4`, затем `1`, а получатель со значением `6` заканчивает шаг со
|
||||
значением `7`. Это показывает, почему одного результата чтения недостаточно:
|
||||
алгоритм снова получает величины при применении. Сценарий был запущен прямо из
|
||||
исходной `Behavior.dll`, но объекты и ответы интерфейсов заданы тестом; это ещё
|
||||
не воспроизведение реальной миссии.
|
||||
|
||||
Пока мы знаем точные границы интерфейсных запросов и некоторые ветви классов
|
||||
объектов, но не все доменные значения полей записи и не происхождение реальных
|
||||
строк списка. Загрузчик FParkan не должен создавать такие строки из похожих
|
||||
полей миссии: сначала нужно доказать путь регистрации и связать его с типами
|
||||
ресурсных объектов. Технические идентификаторы и численные векторы приведены
|
||||
в [приложении о сценарной машине](../appendices/script-vm.md), а оставшиеся
|
||||
вопросы перечислены в [границах знания](../appendices/knowledge-boundaries.md).
|
||||
|
||||
Первый часто встречающийся table entry с side effect — `Handler(2)` (176
|
||||
records в corpus). Он берёт active instruction, разрешает семь slots через
|
||||
|
||||
Reference in new issue
Block a user