aboutsummaryrefslogtreecommitdiff
path: root/docs/appendices/script-vm.md
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-20 05:35:38 +0300
committerValentin Popov <valentin@popov.link>2026-10-20 05:35:38 +0300
commite4645b1d6278a2f766dac99815d3938c42f61640 (patch)
treed9f07ef2c295484ed8a0f71e6ea24ba29e6dd332 /docs/appendices/script-vm.md
parent89a0d1da6180767dd20fccc47227580b83f9a874 (diff)
downloadfparkan-e4645b1d6278a2f766dac99815d3938c42f61640.tar.xz
fparkan-e4645b1d6278a2f766dac99815d3938c42f61640.zip
test(runtime): verify native resource distributor tick
Diffstat (limited to 'docs/appendices/script-vm.md')
-rw-r--r--docs/appendices/script-vm.md70
1 files changed, 69 insertions, 1 deletions
diff --git a/docs/appendices/script-vm.md b/docs/appendices/script-vm.md
index 9bb33db..4020ebb 100644
--- a/docs/appendices/script-vm.md
+++ b/docs/appendices/script-vm.md
@@ -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)`,