aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-23 11:02:27 +0300
committerValentin Popov <valentin@popov.link>2026-10-23 11:02:27 +0300
commit7c909ea67ba7f27e11d5b33b8df9f6569da7eb7b (patch)
tree344ca595b3fcd34eb20671b65ad86146813d7873 /docs
parent83db371742ad3865bf4dc534ae38429c07a936d3 (diff)
downloadfparkan-7c909ea67ba7f27e11d5b33b8df9f6569da7eb7b.tar.xz
fparkan-7c909ea67ba7f27e11d5b33b8df9f6569da7eb7b.zip
feat(runtime): advance loaded mission resources
Diffstat (limited to 'docs')
-rw-r--r--docs/appendices/knowledge-boundaries.md25
-rw-r--r--docs/appendices/script-vm.md91
-rw-r--r--docs/tomes/06-behavior.md44
3 files changed, 125 insertions, 35 deletions
diff --git a/docs/appendices/knowledge-boundaries.md b/docs/appendices/knowledge-boundaries.md
index 3744d84..b9bbf27 100644
--- a/docs/appendices/knowledge-boundaries.md
+++ b/docs/appendices/knowledge-boundaries.md
@@ -325,17 +325,28 @@ sidecar недоступен, запрос `0xe00` сообщает это отÐ
Rust query обращается к загруженному объекту и поддерживает `0xe00` только для
текущего descriptor tag `26` с factory tag `26`; другие активные typed tags и
-factory paths остаются явными границами. Snapshot показывает состояние сразу
-после native-конструкторов, но он не обновляется при Control tick-е. Native
-`FUN_10010f30` (RVA `0x10f30`) изменяет и ограничивает provider amount, а
-связанные callbacks переносят его между строками; их общий переход в Rust ещё
-не реализован. Поэтому вызов нельзя считать живым ресурсным симулятором.
+factory paths остаются явными границами. Этот запрос возвращает начальный
+снимок provider amount: Native `FUN_10010f30` (RVA `0x10f30`) изменяет и
+ограничивает количество, а связанные callbacks переносят его между строками;
+этот переход в Rust ещё не реализован.
+
+Отдельный API `advance_loaded_mission_resources` принимает явное время очереди
+и обновляет представленные поля `CurrentOre`, строк распределителя и Control
+selector `1`. Его граница — статические снимки профиля и provider-значений:
+динамическое распространение Control amount, смена профилей, special
+object-state gate и жизненный цикл созданных/удалённых объектов здесь не
+моделируются. Если обязательный запрос позднего объекта не поддержан,
+транзакция возвращает ошибку и не применяет подготовленные изменения. API не
+подключён к игровому циклу; AutoDemo-тест подтверждает связь настоящих профилей
+с этим шагом, но не полный паритет миссии. Отдельный aggregate getter Behavior
+RVA `0xa490` в oracle не исполнен.
+
Behavior selectors `0x1002` и `0x1005` используют значения выбранного
профиля из `behpsp.res`, разобранного до создания нового мира. `0x1002` складывает
только текущие descriptor tag `26` запросы `0xe00`, затем применяет
`Transfer_Ore_OffBoard`; `0x1005` возвращает `Transfer_Power_Out`. Это начальный
-снимок: он не отражает последующие изменения provider-строк, Control tick-а или
-профиля. Полная загрузка сохраняет результат у `ObjectHandle` и возвращает
+снимок профиля и provider-строк. Полная загрузка сохраняет результат у
+`ObjectHandle` и возвращает
ошибку, если известный код класса не имеет корректной записи; класс,
отсутствующий в таблице выбора, сохраняет начальные значения конструктора.
Preview и объекты без полного Unit DAT не получают профильное состояние.
diff --git a/docs/appendices/script-vm.md b/docs/appendices/script-vm.md
index c15b208..99c448f 100644
--- a/docs/appendices/script-vm.md
+++ b/docs/appendices/script-vm.md
@@ -373,10 +373,17 @@ nearest-even `FISTP qword`, младший DWORD, преобразование D
deadline `+0x14` по формуле `время + 3*64 + jitter`, где `jitter` лежит в
`0..63` и получен одним шагом глобального MSVCRT LCG. В этой ветви она также
обнуляет пять слов `+0x30..+0x40`. При повторном вызове до deadline ненулевой
-`+0x10` оставляет deadline прежним, не расходует новое случайное число и не
-очищает эти слова. Поведение после истечения deadline этим опытом не проверено.
-Если само время очереди равно нулю, `+0x10` остаётся нулевым, и повторный вызов
-снова проходит ветвь начальной подготовки.
+`+0x10` оставляет срок прежним, не расходует новое случайное число и не
+очищает эти слова. Если само время очереди равно нулю, `+0x10` остаётся
+нулевым, и повторный вызов снова проходит ветвь начальной подготовки. После
+срока scheduler продолжает при нулевом deadline или при беззнаковом
+`now > deadline`; равенство ненулевому deadline ещё ждёт. Он считает
+`u32(now - first_time)` с переполнением по модулю `2^32`, переводит миллисекунды в секунды через x87
+`FILD`/`FMUL`/`FSTP` и планирует следующий срок от текущего времени. Шаг
+интервала равен `3 * 64 + jitter`: один шаг LCG `state = state * 214013 +
+2531011` выдаёт значение `random = (state >> 16) & 0x7fff`, а
+`jitter = ((random & 0xff) * 64) >> 8`. Native oracle проверяет
+равенство и проход срока, обёртку и большие интервалы с округлением времени.
Путь host callback в проверенной паре DLL теперь статически известен. В
`iron3d.dll` функция `FUN_1005ff40` (RVA `0x5ff40`) сначала вызывает
@@ -400,9 +407,11 @@ callback-точку входа и порядок внутри неё, но не
bundle, но остальные потребители исходного глобального RNG не моделируются.
В Rust также нет значения аргумента в `AI+0x3fc`, recursion guard `AI+0x78`
или одноразового latch `AI+0x8a8`. Поэтому это связанный начальный поднабор, а
-не полная активация SuperAI. Восстановленный ниже distributor tick проверен на
-исходной DLL, но пока не подключён к Rust игровому циклу: у runtime нет
-ArealMap-объектов и интерфейсов, которые нужны этому вызову. При
+не полная активация SuperAI. После полной загрузки узкая Rust-реализация
+вызывается явно через `advance_loaded_mission_resources(engine, queue_time_ms)`;
+runtime не читает системную очередь, а приложение пока не вызывает API из
+игрового цикла. Шаг использует разрешённые handles и представленные snapshots
+профилей и Control, поэтому не воспроизводит отсутствующие в них callbacks. При
ошибке ограниченного `Init` Rust отклоняет полную загрузку и сохраняет прежний
мир и состояние генератора; такая политика отката принадлежит Rust loader-у.
@@ -428,9 +437,33 @@ DWORD по смещению `+0x14` как `f32`; если выходной ук
пока не получило доменного имени. Идентификаторы и границы вызовов не
задают полную семантику свойств.
-Изолированный x86 oracle вызывает исходный tick с подменой только IAT-слота
-`GetSystemArealMap`; синтетические vtables записывают последовательность
-вызовов и ответы. Для смешанного примера два прохода нашли объекты в порядке
+Раздача считает доступный за этот шаг объём `A`, полный спрос `D` и взвешенный
+спрос `W = Σ(demand × share)`. Из них выводятся коэффициенты приоритетной доли
+`P`, остатка `R` и расхода `C`. В таблице ниже показан исходный порядок
+проверок: он важен, когда нестандартные доли дают `W > D`.
+
+| Первое совпавшее условие (при `A > 0`, `D > 0`) | `P` | `R` | `C` |
+| --- | ---: | ---: | ---: |
+| `A ≤ W` | `A / W` | `0` | `1` |
+| `W < A < D` | `1` | `(A − W) / (D − W)` | `1` |
+| `A ≥ D` | `1` | `1` | `D / A` |
+
+При нулевом доступном количестве или спросе native path не выделяет
+положительное значение. Строка с долей `share` получает коэффициент
+`(1 − share) × R + share × P`, который сочетается с её спросом или доступной
+вместимостью. `C` задаёт долю отбора руды у источников; для источников энергии
+этот коэффициент вычисляется, но native-код не применяет его к производству.
+У объектов в ветви приёма руды (не источников руды) native-код ограничивает `CurrentOre`:
+если `MaximumOre − CurrentOre < 0.01`, значение становится `MaximumOre`.
+Сравнение строгое: при разнице ровно `0.01` значение не меняется.
+В x86-коде это константа `0x3c23d70a`; сравнение и округление проверены при
+игровом x87 control word `0x007f`.
+
+Изолированный x86 oracle вызывает исходный scheduler RVA `0x1adb0`, который
+сам вызывает исходный tick RVA `0x19e80`. Для синтетического времени он
+подменяет IAT-слот `GetQueue`; tick подменяет `GetSystemArealMap`, а синтетические
+vtables записывают последовательность вызовов и ответы. Для смешанного примера
+два прохода нашли объекты в порядке
`41,42,41,42`. Производитель руды с начальным значением `1` и скоростью `4`
вернул при следующем чтении скорость `1`; получатель с начальным значением `6`
получил `7`, а агрегаты distributor равны `[0,1,1,0.375,6]`. При запасе
@@ -441,11 +474,13 @@ selector `1` на интерфейсе получателя становится
`0x3f2aaaab`). Эти векторы проверяют арифметику исходной функции, но не
распределение реальной карты.
-Полная загрузка Rust теперь собирает этот список из runtime `ObjectHandle` и
-сохраняет веса вместе с пятью исходными DWORD хвоста каждой записи. Членство
-строится только для полного режима; preview остаётся статическим. Это пока не
-даёт ресурсных полей и интерфейсов ArealMap для `FUN_10019e80`, а
-`step_headless` по-прежнему шагает только `World`.
+Полная загрузка Rust собирает этот список из runtime `ObjectHandle` и сохраняет
+веса и поля строк, а также начальные ore-свойства объектов. Членство строится
+только для полного режима; preview остаётся статическим. Явный API
+`advance_loaded_mission_resources` использует очередь времени, расписание,
+профили и представленные Control-таблицы, чтобы исполнить этот ограниченный
+шаг над загруженным состоянием. `step_headless` по-прежнему шагает только
+`World`.
### Добавление и нормализация долей
@@ -520,17 +555,21 @@ exit $LASTEXITCODE
Oracle проверяет SHA-256 оригинального `Behavior.dll`:
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`. Harness
-явно устанавливает x87 CW `0x007f` для вызовов Add (`0x1aac0`) и normalize
-(`0x1ac90`), проверяет CW до и после каждого вызова и восстанавливает прежнее
-значение перед tick suite. Для этих двух методов подменяется только bounded
-вставка синтетического vector; их машинный код не патчится. Для tick helper
-подменяет только IAT-слот `GetSystemArealMap` и вызывает RVA `0x19e80`.
-Проверяются нулевой
-шаг, обе ветви селекторов скорости, пропуск состояния `0xfffe`, missing lookup,
-динамическое повторное чтение `0x1002`, ограниченный запас руды, разные веса,
-достаточное и недостаточное значение мощности. Это синтетические ArealMap
-объекты; исполнение оригинальной функции проверяет векторы oracle, но не
-подменяет сравнение Rust и интеграционную проверку миссии в игре.
+явно устанавливает x87 CW `0x007f` для Add (`0x1aac0`), normalize (`0x1ac90`)
+и scheduler/tick (`0x1adb0`/`0x19e80`), проверяя слово перед набором вызовов
+и восстанавливая прежнее значение после каждого набора. Для Add и normalize
+подменяется только bounded вставка синтетического vector; их машинный код не
+патчится. Scheduler и tick тоже исполняются оригинальными: подменяются лишь
+IAT-слоты `GetQueue` и `GetSystemArealMap`, а synthetic vtables задают часы и
+ответы объектов. Проверяются равенство и проход срока, переполнение времени
+и точные `f32`-биты elapsed seconds (`1 → 0x3a83126f`, `1001 → 0x3f8020c5`,
+`16777217 → 0x46831270`, `16777219 → 0x46831271`, `u32::MAX → 0x4a83126f`).
+Tick-векторы проверяют нулевой шаг, обе ветви селекторов скорости, пропуск
+состояния `0xfffe`, отсутствующий объект, повторное чтение `0x1002`, ограниченный
+запас руды, порог `0.01`, ветви дефицита/частичного/избыточного предложения и
+равенство спросу, разные веса и достаточную/недостаточную мощность. Это
+синтетические ArealMap объекты: oracle исполняет оригинальные scheduler и tick,
+но не сравнивает паритет Rust и не заменяет проверку полной миссии в игре.
Caller oracle проверяет AutoDemo пару `(500.2857, 751.8217)`, четыре режима
x87 на tie-векторах, границу `10000` и младший DWORD. AI oracle отдельно
diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md
index bcb7320..b3dd8d0 100644
--- a/docs/tomes/06-behavior.md
+++ b/docs/tomes/06-behavior.md
@@ -326,8 +326,48 @@ Resource Distributor работает со списком зарегистрир
записях при игровом x87 control word `0x007f`: проверены добавление, повторный
ключ, закреплённая запись, нулевая сумма и округление. Это позволяет Rust
сохранять начальный список, но пока не подтверждает веса реальных объектов в
-живой миссии. Для регулярного шага по-прежнему не хватает интерфейсов ArealMap.
-Остальные ограничения собраны в [границах знания](../appendices/knowledge-boundaries.md).
+живой миссии.
+
+Узкая часть регулярного шага уже связана с загруженным состоянием движка.
+`advance_loaded_mission_resources(engine, queue_time_ms)` принимает явную
+метку времени общей 32-битной очереди. Для каждого видимого Behavior clan он
+проверяет срок запуска, выполняет два прохода по списку и сохраняет пять
+итогов: доступную энергию и руду, буфер руды, потребление энергии и потребление
+руды. В обоих проходах объекты разрешаются заново по исходному TMA ID. Первый
+проход измеряет поставки и потребность, второй повторно читает представленные
+профильные и Control-значения перед применением выделения. Нативный код также
+запрашивает интерфейсы во втором проходе, но динамические getter-callbacks в
+этом снимке Rust не воспроизводятся.
+
+Планировщик использует беззнаковое сравнение времени: равенство ненулевому
+сроку ещё не запускает шаг, а нулевой срок не задерживает его. Переполнение
+вычитания даёт 32-битную длительность. Затем
+длительность переводится в секунды с округлением, подтверждённым x87-путём
+оригинальной DLL. Нативный oracle проверяет его на длинных интервалах, включая
+значения около границы точного целого `f32`, а также на равенстве и строгом
+проходе срока. Формула запуска, native vectors и команда воспроизведения
+описаны в технической главе [script VM](../appendices/script-vm.md).
+
+Шаг работает только с уже созданными полными объектами миссии и снимками тех
+профилей и Control-таблиц, чьи запросы и setters представлены в runtime. Он
+обновляет загруженные `CurrentOre`, поля строк распределителя и поддержанный
+Control selector `1`; неподдержанное свойство, запрос Control или невозможное
+арифметическое значение возвращает ошибку. Обновления нескольких кланов, их
+расписания и RNG фиксируются одной транзакцией: если обязательный запрос
+позднего клана не разрешён, ранее подготовленные изменения остаются
+неприменёнными. Приложение пока не вызывает
+этот API из игрового цикла, а динамические callbacks, которые переносят
+количество между Control provider-строками, ещё не моделируются. Поэтому это
+исполняемый загруженный resource step, а не полный симулятор миссии.
+
+Для настоящих ресурсов AutoDemo тест проходит несколько временных точек:
+генераторы `ObjectId 6` и `ObjectId 7` читают `Transfer_Power_Out = 10` из
+`prof_generator.var`; после шага в 500 мс каждый сообщает 5 единиц доступной
+энергии. Это подтверждает связь загруженного профиля с runtime-расчётом, но не
+сравнение полной миссии с оригинальной игрой. Native oracle сверяет исходные
+планировщик и распределитель GOG `Behavior.dll`; техническая процедура и
+проверяемые векторы описаны в [script VM](../appendices/script-vm.md). Остальные ограничения собраны в
+[границах знания](../appendices/knowledge-boundaries.md).
Первый часто встречающийся table entry с side effect — `Handler(2)` (176
records в corpus). Он берёт active instruction, разрешает семь slots через