2026-06-22 01:58:51 +04:00
|
|
|
|
# VII. Руководство по полной реализации
|
|
|
|
|
|
|
|
|
|
|
|
Этот том описывает инженерный путь к совместимому движку FParkan. Он опирается
|
|
|
|
|
|
на доказанные форматы и runtime-контракты, но не требует повторять физическое
|
|
|
|
|
|
деление оригинала на пятнадцать DLL. Повторить нужно наблюдаемое поведение:
|
|
|
|
|
|
форматы, имена, fallback, object IDs, порядок событий, численную политику,
|
|
|
|
|
|
границы кадра, сохранения и воспроизводимость прохождения.
|
|
|
|
|
|
|
|
|
|
|
|
Предложенные ниже modules, handles, snapshots, queues и scheduler phases являются
|
|
|
|
|
|
целевой архитектурой новой реализации, а не восстановленным внутренним layout
|
|
|
|
|
|
оригинального Iron3D. Главная практическая цель: запускаться из неизменённого
|
|
|
|
|
|
оригинального каталога игры, проходить corpus gates для демоверсии, Части 1 и
|
|
|
|
|
|
Части 2, а затем измеримо двигаться от archive compatibility к полной игровой
|
|
|
|
|
|
совместимости.
|
|
|
|
|
|
|
|
|
|
|
|
## Целевая архитектура
|
|
|
|
|
|
|
|
|
|
|
|
Практичная форма новой реализации -- модульный монолит с узкими интерфейсами и
|
|
|
|
|
|
отдельными platform adapters. Внутренние границы должны соответствовать ролям
|
|
|
|
|
|
Iron3D, а не обязательно его DLL. Это упрощает перенос на современные платформы
|
|
|
|
|
|
и оставляет возможность поддерживать разные compatibility profiles для разных
|
|
|
|
|
|
сборок данных.
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
application запуск, окно, конфигурация, shutdown
|
|
|
|
|
|
platform filesystem, clocks, input, threads, dynamic libraries
|
|
|
|
|
|
resources NRes, RsLi, paths, archives, cache and diagnostics
|
|
|
|
|
|
assets MSH, WEAR, MAT0, Texm, FXID and auxiliary formats
|
|
|
|
|
|
mission TMA, unit DAT, prototype graph, scenario data
|
|
|
|
|
|
world ObjectId, queue, lifecycle, time, messages, mirrors
|
|
|
|
|
|
terrain Land.msh, Land.map, surface and spatial queries
|
|
|
|
|
|
navigation areals, graph search, corridors
|
|
|
|
|
|
behavior unit state machines, target and path requests
|
|
|
|
|
|
physics control systems, collision proxies and contacts
|
|
|
|
|
|
animation pose sampling, hierarchy and blending
|
|
|
|
|
|
audio sample cache, sources, listener and buses
|
2026-06-23 22:05:16 +04:00
|
|
|
|
render immutable frame contracts and modern backend
|
2026-06-22 01:58:51 +04:00
|
|
|
|
network game message schema plus transport adapters
|
|
|
|
|
|
tools validators, extractors, viewers, captures and editors
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Каждый модуль зависит от нижележащих интерфейсов, а не от concrete managers.
|
|
|
|
|
|
Behavior видит `INavigation` и `IPhysicsCommandSink`, но не включает headers
|
|
|
|
|
|
renderer-а. Render получает immutable snapshot, а не mutable world. Network
|
|
|
|
|
|
receive не меняет мир напрямую: validated messages попадают в очередь следующей
|
|
|
|
|
|
calculation boundary.
|
|
|
|
|
|
|
|
|
|
|
|
### Центральные идентичности
|
|
|
|
|
|
|
|
|
|
|
|
Resource identity хранит и исходное написание, и нормализованный ASCII-key для
|
|
|
|
|
|
поиска:
|
|
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
|
struct ResourceKey {
|
|
|
|
|
|
NormalizedRelativePath archive;
|
|
|
|
|
|
FixedAsciiName name;
|
|
|
|
|
|
uint32_t type_id;
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Normalization сохраняет исходную строку для diagnostics и roundtrip, а отдельный
|
|
|
|
|
|
ASCII-casefold key используется только для lookup. Эта граница важна для
|
|
|
|
|
|
архивов [NRes](../reference/nres.md), таблиц [RsLi](../reference/rsli.md),
|
|
|
|
|
|
prototype references и fallback-путей материалов.
|
|
|
|
|
|
|
|
|
|
|
|
Object identity разделяет внутреннюю защиту от dangling references и исходную
|
|
|
|
|
|
сетевую/script-семантику:
|
|
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
|
struct ObjectHandle { uint32_t generation; uint32_t slot; };
|
|
|
|
|
|
struct OriginalObjectId { uint32_t raw; };
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`ObjectHandle` нужен для безопасного внутреннего владения, deferred deletion и
|
|
|
|
|
|
weak references. `OriginalObjectId` сохраняет наблюдаемую семантику исходной
|
|
|
|
|
|
игры: scripts, mirrors, network messages и savegame references должны видеть
|
|
|
|
|
|
логический ID, а не адрес объекта или номер slot в новом allocator-е.
|
|
|
|
|
|
|
|
|
|
|
|
Frame snapshot отделяет simulation от render. Simulation пишет mutable state;
|
|
|
|
|
|
renderer читает опубликованное состояние или строго ограниченную фазу
|
|
|
|
|
|
`in_render`. Deferred deletion применяется между фазами, а не во время traversal.
|
|
|
|
|
|
Командный контур renderer-а должен сверяться с [описанием кадра](../reference/render-frame.md)
|
|
|
|
|
|
до pixel comparison.
|
|
|
|
|
|
|
|
|
|
|
|
### Владение ресурсами
|
|
|
|
|
|
|
|
|
|
|
|
Ресурс проходит несколько уровней:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
ArchiveHandle -> EntryView -> DecodedBlob -> ParsedAsset -> RuntimeResource
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`EntryView` ссылается на metadata архива, `DecodedBlob` владеет подготовленными
|
|
|
|
|
|
bytes, `ParsedAsset` является CPU-представлением, `RuntimeResource` может
|
|
|
|
|
|
дополнительно владеть GPU/audio objects. Eviction верхнего уровня не закрывает
|
|
|
|
|
|
архив, если он ещё нужен другому entry. Ссылки идут вниз только через явные
|
|
|
|
|
|
handles.
|
|
|
|
|
|
|
|
|
|
|
|
Для shared objects допустимы reference counting или generation handles.
|
|
|
|
|
|
Intrusive refcount нужен только в ABI-shim; внутренний современный код
|
|
|
|
|
|
предпочтительно держит понятное владение и weak handles. Архивы, decoded blobs,
|
|
|
|
|
|
CPU assets и GPU resources имеют отдельные бюджеты и отдельные diagnostics.
|
|
|
|
|
|
|
|
|
|
|
|
### Backend adapters
|
|
|
|
|
|
|
2026-06-23 22:05:16 +04:00
|
|
|
|
Render, audio, input и network получают отдельные adapters. Compatibility state
|
|
|
|
|
|
живёт вне Vulkan, D3D11 или Metal backend; DirectPlay compatibility живёт
|
2026-06-22 01:58:51 +04:00
|
|
|
|
отдельно от modern transport. Так можно заменить платформу, не меняя форматы,
|
|
|
|
|
|
игровую семантику и regression corpus.
|
|
|
|
|
|
|
|
|
|
|
|
Backend adapter не должен быть местом, где исправляются данные. Если
|
|
|
|
|
|
[MSH](../reference/msh.md), [MAT0](../reference/materials.md) или
|
|
|
|
|
|
[Texm](../reference/texm.md) требуют fallback, это фиксируется в asset/runtime
|
|
|
|
|
|
слое и попадает в trace. Backend получает уже выбранные resources, states и
|
|
|
|
|
|
draw items.
|
|
|
|
|
|
|
|
|
|
|
|
### Scheduler phases
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
collect_platform_events
|
|
|
|
|
|
build_input_snapshot
|
|
|
|
|
|
advance_game_clock
|
|
|
|
|
|
calculate_world_queue
|
|
|
|
|
|
apply_deferred_operations
|
|
|
|
|
|
update_navigation_physics_animation_fx
|
|
|
|
|
|
publish_render_snapshot
|
|
|
|
|
|
render_world
|
|
|
|
|
|
render_ui
|
|
|
|
|
|
end_frame_callbacks
|
|
|
|
|
|
maintenance_and_eviction
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Фазы имеют стабильный порядок и запрещённые операции. Registry mutation
|
|
|
|
|
|
запрещена во время world traversal, GPU upload не изменяет simulation state, а
|
|
|
|
|
|
maintenance не влияет на gameplay. Script timers, material animation и FX
|
|
|
|
|
|
lifetime относятся к game time, если обратное не доказано.
|
|
|
|
|
|
|
|
|
|
|
|
Сначала реализуется однопоточный эталон. Параллелизм добавляется только внутри
|
|
|
|
|
|
фаз с детерминированным merge: decoding независимых assets, culling chunks или
|
|
|
|
|
|
подготовка immutable draw items. Это снижает риск скрытых race conditions и
|
|
|
|
|
|
расхождений replay.
|
|
|
|
|
|
|
|
|
|
|
|
### Структурированные ошибки
|
|
|
|
|
|
|
|
|
|
|
|
Каждая ошибка должна содержать фазу, путь, archive entry, object/prototype key,
|
|
|
|
|
|
offset и цепочку причины.
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
MissionLoadError
|
|
|
|
|
|
mission: Campaign.00/Mission.02
|
|
|
|
|
|
object: 17
|
|
|
|
|
|
resource_name: UNITS/.../unit.dat
|
|
|
|
|
|
component: e_tur_...
|
|
|
|
|
|
prototype: objects.rlb::e_tur_...
|
|
|
|
|
|
cause: model archive missing
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Логическое отсутствие необязательного lightmap, отсутствующий entry в архиве,
|
|
|
|
|
|
неизвестное opaque поле, выход ссылки за диапазон и повреждённый offset имеют
|
|
|
|
|
|
разный severity и разные способы исправления. Ошибка данных должна быть
|
|
|
|
|
|
actionable chain, а не строка вида `failed to load resource`.
|
|
|
|
|
|
|
|
|
|
|
|
## Порядок работ
|
|
|
|
|
|
|
|
|
|
|
|
Движок строится от данных к поведению и от детерминированных CPU-компонентов к
|
|
|
|
|
|
аппаратным. Каждый этап заканчивается исполняемым инструментом и тестовым
|
|
|
|
|
|
критерием. Нельзя начинать полноценный gameplay, пока ресурсный граф и
|
|
|
|
|
|
model/material path не дают воспроизводимый результат.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 0. Corpus harness
|
|
|
|
|
|
|
|
|
|
|
|
- индексировать оригинальный каталог и вычислить hashes;
|
|
|
|
|
|
- реализовать bounded binary cursor и structured diagnostics;
|
|
|
|
|
|
- создать CLI для массового запуска parser-ов;
|
|
|
|
|
|
- сохранять JSON-отчёт с counts, variants, warnings и failures;
|
|
|
|
|
|
- зафиксировать демоверсию, Часть 1 и Часть 2 как независимые baselines.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: повторный запуск на каждом неизменённом каталоге даёт идентичный
|
|
|
|
|
|
отчёт. Любой parser умеет завершиться контролируемой ошибкой с offset и
|
|
|
|
|
|
контекстом, а не crash или allocation по непроверенному count.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 1. Архивы и пути
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать strict/lossless [NRes](../reference/nres.md) reader/writer;
|
|
|
|
|
|
- реализовать [RsLi](../reference/rsli.md) mapping, table transform, lookup,
|
|
|
|
|
|
LZSS и Deflate;
|
|
|
|
|
|
- добавить адаптивный decoder для методов `0x080` и `0x0A0`;
|
|
|
|
|
|
- воспроизвести overlay и известные compatibility quirks;
|
|
|
|
|
|
- реализовать archive-handle cache и ASCII name policy.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: неизменённые архивы проходят byte-identical roundtrip; поиск всех
|
|
|
|
|
|
имён совпадает с каталогом; malformed corpus отклоняется без выхода за память.
|
|
|
|
|
|
NRes с ненулевым unindexed region обязательно остаётся regression case.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 2. Граф ресурсов
|
|
|
|
|
|
|
|
|
|
|
|
- разобрать `objects.rlb` и unit DAT;
|
|
|
|
|
|
- построить resolver прямой MSH, рекурсивного parent prototype через
|
|
|
|
|
|
`objects.rlb` и отдельного BASE payload;
|
|
|
|
|
|
- реализовать dependency graph с reachability от миссии;
|
|
|
|
|
|
- добавить parsers CTPT, NDPR и остальных служебных форматов в lossless-режиме;
|
|
|
|
|
|
- создать инспектор прототипа, показывающий все связанные ресурсы.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: 201 demo-объект раскрывается в 501 прототип. Затем все миссии
|
|
|
|
|
|
Частей 1 и 2 дают 4 701 и 5 845 prototype requests без failures. Недостижимые
|
|
|
|
|
|
отсутствующие ресурсы отмечаются отдельно от критических ошибок в reachable
|
|
|
|
|
|
graph.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 3. Статический asset viewer
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать [MSH](../reference/msh.md) core streams, slots и batches;
|
|
|
|
|
|
- декодировать Texm во все подтверждённые pixel formats;
|
|
|
|
|
|
- разобрать WEAR и [MAT0](../reference/materials.md) с точными fallback;
|
|
|
|
|
|
- построить современный renderer compatibility layer;
|
|
|
|
|
|
- добавить wireframe, normals, bounds, LOD/group и material debug views.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: открываются 435/511 моделей, 518/631 textures и 905/1 127 materials
|
|
|
|
|
|
Частей 1/2; batch/index bounds не нарушаются; viewer показывает корректно
|
|
|
|
|
|
текстурированную статическую модель из исходного архива. Красивый viewer всё ещё
|
|
|
|
|
|
означает только asset compatibility, а не готовую игру.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 4. Анимация и эффекты
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать MSH type 8/type 19 sampling и hierarchy;
|
|
|
|
|
|
- добавить x87-compatible reference path для чувствительных формул;
|
|
|
|
|
|
- реализовать material phase animation;
|
|
|
|
|
|
- разобрать FXID header/commands и runtime instances;
|
|
|
|
|
|
- сначала поддержать все opcodes, встречающиеся в корпусе, сохраняя raw body;
|
|
|
|
|
|
- добавить deterministic RNG stream и effect capture.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: frame-by-frame poses совпадают с golden reference своей части; все
|
|
|
|
|
|
923/1 065 FXID создаются без parser errors; перезапуск одинакового effect seed
|
|
|
|
|
|
даёт идентичный список emitted primitives.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 5. Карта и мир
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать `Land.msh` и corrected `TerrainFace28` layout;
|
|
|
|
|
|
- построить terrain rendering и CPU surface queries;
|
|
|
|
|
|
- реализовать `Land.map`, cell grid и graph links;
|
|
|
|
|
|
- визуализировать areals и найденные маршруты;
|
|
|
|
|
|
- разобрать [TMA](../reference/tma.md) и выполнять staged mission loading;
|
|
|
|
|
|
- создать World3D queue, ObjectId и deferred deletion.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: 65 карт и 60 TMA Частей 1 и 2 загружаются до EOF; все areal links
|
|
|
|
|
|
валидны; objects появляются в правильных transforms; мир выдерживает расчётные
|
|
|
|
|
|
шаги без рендера.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 6. Gameplay controllers
|
|
|
|
|
|
|
|
|
|
|
|
- подключить input snapshot и camera controller;
|
|
|
|
|
|
- реализовать navigation corridor, Behavior state machine и Wizard boundary;
|
|
|
|
|
|
- создать physical controller и collision manager;
|
|
|
|
|
|
- загрузить control resources в lossless typed model;
|
|
|
|
|
|
- внедрить game time, pause, event queue и end-of-frame callbacks;
|
|
|
|
|
|
- подключить AI layer и symbol/event layer сценариев.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: юнит получает цель, строит маршрут, движется по terrain, реагирует
|
|
|
|
|
|
на collision и исполняет базовые миссионные события в детерминированном replay.
|
|
|
|
|
|
На этом этапе вводится differential branch для изменённых `AniMesh`, `Control` и
|
|
|
|
|
|
`Effect`; неизменённые DLL используют общий reference path.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 7. Полный кадр, звук и UI
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать render phases, sorting, lighting, shadows и atmosphere;
|
|
|
|
|
|
- подключить 3D listener, sample cache, FX sounds и mission audio;
|
|
|
|
|
|
- воспроизвести shell/UI loading и post-world pass;
|
|
|
|
|
|
- добавить frame capture до UI и после UI;
|
|
|
|
|
|
- зафиксировать capability fallback profiles.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: миссия визуально и звуково проходима; каждый draw и sound event
|
|
|
|
|
|
имеет trace; одинаковый replay создаёт одинаковые command lists. На этом этапе
|
|
|
|
|
|
вводится differential branch для `iron3d` и `services`.
|
|
|
|
|
|
|
|
|
|
|
|
### Этап 8. Сеть, сохранения и динамическая совместимость
|
|
|
|
|
|
|
|
|
|
|
|
- реализовать modern transport над versioned game-message schema;
|
|
|
|
|
|
- отдельно исследовать DirectPlay wire и `netZipData` для native compatibility;
|
|
|
|
|
|
- добавить mirrors, ownership transfer и disconnect cleanup;
|
|
|
|
|
|
- восстановить save/campaign state и dispatcher;
|
|
|
|
|
|
- выполнить динамические captures оригинала для render states, script VM и
|
|
|
|
|
|
physics edge cases.
|
|
|
|
|
|
|
|
|
|
|
|
Готовность: одиночная кампания запускается из оригинального каталога,
|
|
|
|
|
|
сохраняется и продолжается; multiplayer replay согласован между peers; full
|
|
|
|
|
|
corpus не создаёт новых parser variants без явной регистрации.
|
|
|
|
|
|
|
|
|
|
|
|
## Тестовый контур
|
|
|
|
|
|
|
|
|
|
|
|
Совместимость нельзя подтвердить одним screenshot. Нужны тесты на уровне bytes,
|
|
|
|
|
|
структур, ссылок, simulation state, команд renderer-а и конечного изображения.
|
|
|
|
|
|
Каждый слой локализует свой класс ошибки.
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
unit tests
|
|
|
|
|
|
-> parser/property tests
|
|
|
|
|
|
-> corpus validation
|
|
|
|
|
|
-> cross-resource integration
|
|
|
|
|
|
-> deterministic simulation replay
|
|
|
|
|
|
-> render/audio command captures
|
|
|
|
|
|
-> pixel and gameplay parity
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Failure верхнего уровня всегда должен позволять спуститься к меньшему тесту и
|
|
|
|
|
|
понять причину.
|
|
|
|
|
|
|
|
|
|
|
|
### Unit, property и fuzz tests
|
|
|
|
|
|
|
|
|
|
|
|
Для каждого binary primitive проверяются little-endian чтение, bounded strings,
|
|
|
|
|
|
checked arithmetic и cursor boundaries. Для структур -- минимальный размер,
|
|
|
|
|
|
максимальные counts, пустые arrays, нулевые варианты и редкие branches.
|
|
|
|
|
|
|
|
|
|
|
|
Property tests генерируют случайные корректные NRes/RsLi/WEAR records,
|
|
|
|
|
|
выполняют encode -> decode и сравнивают семантику. Fuzz tests изменяют длины,
|
|
|
|
|
|
offsets, counts и termination bytes и требуют контролируемой ошибки без crash и
|
|
|
|
|
|
чрезмерного выделения памяти.
|
|
|
|
|
|
|
|
|
|
|
|
Критические алгоритмы имеют отдельные vectors: ASCII casefold, NRes permutation
|
|
|
|
|
|
search, RsLi byte transform, LZSS backreferences, quaternion shortest path,
|
|
|
|
|
|
matrix composition и terrain mask remap.
|
|
|
|
|
|
|
|
|
|
|
|
### Corpus validation
|
|
|
|
|
|
|
|
|
|
|
|
Каждый файл оригинального каталога проходит parser своего семейства. Отчёт
|
|
|
|
|
|
содержит hash, variant, counts, warnings, errors и точный offset сбоя. Baseline
|
|
|
|
|
|
демоверсии:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
MSH 435
|
|
|
|
|
|
MAT0 905
|
|
|
|
|
|
Texm 518
|
|
|
|
|
|
FXID 923
|
|
|
|
|
|
WEAR 457
|
|
|
|
|
|
Land.msh 6
|
|
|
|
|
|
Land.map 6
|
|
|
|
|
|
TMA 6
|
|
|
|
|
|
unit DAT 425
|
|
|
|
|
|
errors 0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Изменение parser-а принимается только если baseline остаётся стабильной либо
|
|
|
|
|
|
новый variant зарегистрирован с образцом и объяснением. Warnings должны быть
|
|
|
|
|
|
именованными: «неизвестное opaque поле» не равно «выход ссылки за диапазон».
|
|
|
|
|
|
|
|
|
|
|
|
### Cross-resource integration
|
|
|
|
|
|
|
|
|
|
|
|
Интеграционный тест начинается с миссии и проходит весь dependency graph:
|
|
|
|
|
|
object -> prototype -> MSH -> WEAR -> MAT0 -> Texm/lightmap/FXID. Он не
|
|
|
|
|
|
ограничивается тем, что файлы существуют: material slot должен указывать на
|
|
|
|
|
|
допустимый MAT0, phase -- на допустимую texture, model batch -- на существующий
|
|
|
|
|
|
WEAR index.
|
|
|
|
|
|
|
|
|
|
|
|
Demo mission total: 201 objects -> 501 prototypes -> 501 object MSH/WEAR.
|
|
|
|
|
|
Чистый object graph даёт 3 873 material slots и 5 049 texture requests; после
|
|
|
|
|
|
включения environment WEAR итог равен 3 879 material slots, 5 067 textures и
|
|
|
|
|
|
18 lightmaps, failures 0. Такой тест ловит ошибки casefold, suffix, fallback и
|
|
|
|
|
|
путей, которые отдельный parser не замечает.
|
|
|
|
|
|
|
|
|
|
|
|
Для каждого отсутствующего узла отчёт хранит полный parent chain, чтобы
|
|
|
|
|
|
различать broken global archive и реально достижимый mission failure.
|
|
|
|
|
|
|
|
|
|
|
|
### Deterministic simulation replay
|
|
|
|
|
|
|
|
|
|
|
|
Записывается начальная миссия, seed, input events, network messages и значения
|
|
|
|
|
|
внешних часов. На контрольных ticks сохраняется canonical state hash:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
sorted ObjectId list
|
|
|
|
|
|
transforms and velocities
|
|
|
|
|
|
critical properties and owners
|
|
|
|
|
|
AI/behavior state IDs
|
|
|
|
|
|
active effect state
|
|
|
|
|
|
game clock and RNG states
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Pointer addresses, allocator order и GPU handles в hash не входят. Два запуска с
|
|
|
|
|
|
одинаковым log должны давать одинаковый state hash на каждом checkpoint. Первое
|
|
|
|
|
|
расхождение гораздо информативнее финального разного результата миссии.
|
|
|
|
|
|
|
|
|
|
|
|
### Render command parity
|
|
|
|
|
|
|
|
|
|
|
|
До pixel comparison сравнивается command list:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
camera matrices and viewport
|
|
|
|
|
|
visible ObjectIds
|
|
|
|
|
|
render phase and stable order
|
|
|
|
|
|
model/node/slot/batch IDs
|
|
|
|
|
|
material phase and texture handles
|
|
|
|
|
|
legacy pipeline states
|
|
|
|
|
|
index ranges and transforms
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Если command lists совпадают, но pixels различаются, проблема находится в
|
|
|
|
|
|
shader/backend, sampling или численной точности. Если command lists уже
|
|
|
|
|
|
различаются, pixel diff лишь скрывает более раннюю ошибку.
|
|
|
|
|
|
|
|
|
|
|
|
Golden captures следует хранить отдельно для статической модели, анимации,
|
|
|
|
|
|
terrain, transparent FX, shadows, lightmap и atmosphere.
|
|
|
|
|
|
|
|
|
|
|
|
### Pixel, audio и network tests
|
|
|
|
|
|
|
|
|
|
|
|
Pixel tests используют фиксированное разрешение, camera, device profile, seed и
|
|
|
|
|
|
timeline. Сравниваются exact pixels для CPU/reference path и tolerance metrics
|
|
|
|
|
|
для GPU path, но tolerance не должна скрывать переставленные прозрачные
|
|
|
|
|
|
primitives.
|
|
|
|
|
|
|
|
|
|
|
|
Audio tests сравнивают список sound events, sample IDs, positions, loop flags и
|
|
|
|
|
|
gains; waveform зависит от mixer/device и является вторичным уровнем. Network
|
|
|
|
|
|
tests воспроизводят captured message sequences, проверяют mirrors, ownership и
|
|
|
|
|
|
disconnect. Для native DirectPlay compatibility дополнительно нужен packet-level
|
|
|
|
|
|
corpus.
|
|
|
|
|
|
|
|
|
|
|
|
## Regression baselines
|
|
|
|
|
|
|
|
|
|
|
|
Corpus validation формирует три независимых отчёта: демоверсия, Часть 1 и
|
|
|
|
|
|
Часть 2. Каждый сохраняет manifest файлов, hashes executable/DLL, variants,
|
|
|
|
|
|
warnings, global archive health и mission reachability.
|
|
|
|
|
|
|
|
|
|
|
|
Ключевые corpus gates:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
NRes: 120 файлов / 6 804 entries и 134 / 8 171 для Частей 1/2
|
|
|
|
|
|
TMA: 29 миссий / 864 objects / 28 extras и 31 / 885 / 41
|
|
|
|
|
|
MSH: 435 и 511 моделей
|
|
|
|
|
|
MAT0: 905 и 1 127 материалов
|
|
|
|
|
|
Texm: 518 и 631 текстура
|
|
|
|
|
|
FXID: 923 и 1 065 эффектов
|
|
|
|
|
|
full reachability: 4 701 и 5 845 prototype requests, failures 0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Расширенные mission-reachability totals:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Часть 1: 29 TMA, 864 objects, 4 701 prototypes,
|
|
|
|
|
|
36 954 materials, 48 806 textures, 139 lightmaps, failures 0
|
|
|
|
|
|
Часть 2: 31 TMA, 885 objects, 5 845 prototypes,
|
|
|
|
|
|
50 888 materials, 68 603 textures, 214 lightmaps, failures 0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Обязательные regression cases:
|
|
|
|
|
|
|
|
|
|
|
|
- NRes с ненулевым unindexed region;
|
|
|
|
|
|
- prototype inheritance через `objects.rlb`;
|
|
|
|
|
|
- unit DAT `description[32]` без NUL;
|
|
|
|
|
|
- TMA epilogue и `extra_count` 0--4;
|
|
|
|
|
|
- empty SWAV entry;
|
|
|
|
|
|
- stale save-slot metadata без payload;
|
|
|
|
|
|
- build-scoped RVA lookup.
|
|
|
|
|
|
|
|
|
|
|
|
Byte-identical asset comparison выполняется только внутри одного корпуса. Между
|
|
|
|
|
|
Частями 1 и 2 сравниваются semantic invariants и decoded representation,
|
|
|
|
|
|
поскольку многие assets пересобраны.
|
|
|
|
|
|
|
|
|
|
|
|
## Точность, скорость и повторяемость
|
|
|
|
|
|
|
|
|
|
|
|
Совместимый движок должен быть корректным, повторяемым и достаточно быстрым.
|
|
|
|
|
|
Эти свойства нельзя получать одним и тем же приёмом. Сначала создаётся простой
|
|
|
|
|
|
эталонный путь, затем он измеряется и оптимизируется без изменения результата.
|
|
|
|
|
|
|
|
|
|
|
|
Главные источники расхождений: x87 extended precision, преобразование float в
|
|
|
|
|
|
integer, порядок операций, старые SIMD implementations, нестабильная сортировка,
|
|
|
|
|
|
RNG и использование разных часов.
|
|
|
|
|
|
|
|
|
|
|
|
### x87 и округление
|
|
|
|
|
|
|
|
|
|
|
|
Оригинальный x86-код мог хранить промежуточные значения в 80-битных регистрах
|
|
|
|
|
|
x87, а в память записывать 32-битный float. Современный compiler чаще использует
|
|
|
|
|
|
SSE с округлением после каждой операции. Различие заметно на границах animation
|
|
|
|
|
|
frame, culling plane и collision threshold.
|
|
|
|
|
|
|
|
|
|
|
|
Для критических формул нужен reference mode:
|
|
|
|
|
|
|
|
|
|
|
|
- фиксированный порядок операций без reassociation;
|
|
|
|
|
|
- запрещённый fast-math;
|
|
|
|
|
|
- явные преобразования и проверенный режим округления;
|
|
|
|
|
|
- тесты возле half-integer и epsilon boundaries;
|
|
|
|
|
|
- при необходимости extended intermediate через `long double` на проверенной
|
|
|
|
|
|
платформе.
|
|
|
|
|
|
|
|
|
|
|
|
Не требуется эмулировать x87 во всём движке. Нужно локализовать функции, где
|
|
|
|
|
|
малое отличие меняет дискретное решение, и держать для них scalar reference path.
|
|
|
|
|
|
|
|
|
|
|
|
### RNG как часть состояния
|
|
|
|
|
|
|
|
|
|
|
|
FX, atmosphere и, вероятно, AI используют случайные значения. Один глобальный
|
|
|
|
|
|
RNG легко расходится, если новая реализация запрашивает дополнительное число для
|
|
|
|
|
|
визуальной оптимизации. Для трассировки полезны именованные streams:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
world/gameplay RNG
|
|
|
|
|
|
AI/script RNG
|
|
|
|
|
|
FX instance RNG
|
|
|
|
|
|
atmosphere RNG
|
|
|
|
|
|
non-deterministic cosmetic RNG
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Для native parity может потребоваться один общий алгоритм и точная sequence. До
|
|
|
|
|
|
подтверждения capture каждый stream хранит seed и счётчик вызовов в trace.
|
|
|
|
|
|
Cosmetic stream не входит в simulation hash.
|
|
|
|
|
|
|
|
|
|
|
|
### Стабильный порядок
|
|
|
|
|
|
|
|
|
|
|
|
Коллекции не должны зависеть от адресов, unordered containers или порядка
|
|
|
|
|
|
завершения worker threads. Для объектов, collision pairs, opaque/transparent
|
|
|
|
|
|
draws и network messages задаются явные stable keys:
|
|
|
|
|
|
|
|
|
|
|
|
- objects -- queue insertion sequence или OriginalObjectId;
|
|
|
|
|
|
- collision pairs -- упорядоченная пара IDs;
|
|
|
|
|
|
- opaque draws -- phase, pipeline key, material, stable insertion ID;
|
|
|
|
|
|
- transparent draws -- layer, quantized distance, stable insertion ID;
|
|
|
|
|
|
- network messages -- sequence и sender.
|
|
|
|
|
|
|
|
|
|
|
|
Даже когда математический результат коммутативен, side effects, cache accesses и
|
|
|
|
|
|
RNG делают порядок наблюдаемым.
|
|
|
|
|
|
|
|
|
|
|
|
### Часы и fixed-step
|
|
|
|
|
|
|
|
|
|
|
|
Monotonic platform clock хранится отдельно от game clock. Pause и time scaling
|
|
|
|
|
|
применяются к game clock. Simulation работает с фиксированным или точно
|
|
|
|
|
|
воспроизводимым шагом, а render может интерполировать presentation state, не
|
|
|
|
|
|
изменяя authoritative world.
|
|
|
|
|
|
|
|
|
|
|
|
Maintenance timers кэшей используют реальные часы или отдельную подтверждённую
|
|
|
|
|
|
шкалу; их срабатывание не должно менять gameplay. При перегрузке лучше выполнить
|
|
|
|
|
|
ограниченное число simulation steps и явно зафиксировать dropped presentation
|
|
|
|
|
|
frames, чем передать огромный `dt` в AI/physics.
|
|
|
|
|
|
|
|
|
|
|
|
### Оптимизация без потери эталона
|
|
|
|
|
|
|
|
|
|
|
|
1. Сохранить scalar reference implementation.
|
|
|
|
|
|
2. Добавить profiler counters на decoding, culling, sorting, animation, upload
|
|
|
|
|
|
и draw.
|
|
|
|
|
|
3. Оптимизировать только измеренный bottleneck.
|
|
|
|
|
|
4. Сравнить SIMD/parallel результат с reference на полном corpus.
|
|
|
|
|
|
5. Оставить runtime switch для отключения оптимизации при диагностике.
|
|
|
|
|
|
|
|
|
|
|
|
`g_FastProc` удобно моделировать как таблицу function objects: все slots сначала
|
|
|
|
|
|
указывают на scalar path, затем безопасные slots заменяются SIMD-вариантами
|
|
|
|
|
|
после self-test на старте.
|
|
|
|
|
|
|
|
|
|
|
|
### Кэш и память
|
|
|
|
|
|
|
|
|
|
|
|
Архивы, decoded blobs, CPU assets и GPU resources имеют отдельные budgets.
|
|
|
|
|
|
Eviction разрешена только для объектов с нулевым external refcount и после
|
|
|
|
|
|
безопасной frame fence. Original delayed cleanup порядка десятков секунд можно
|
|
|
|
|
|
воспроизвести policy-параметрами, не сканируя все entries каждый кадр.
|
|
|
|
|
|
|
|
|
|
|
|
Основные показатели: число открытых архивов, decoded bytes, resident
|
|
|
|
|
|
textures/lightmaps, models, active FX, draw items и deferred-delete size. Любой
|
|
|
|
|
|
неограниченно растущий счётчик является regression. Производительность считается
|
|
|
|
|
|
достаточной только после корректности: стабильные 60 FPS с неверным LOD или
|
|
|
|
|
|
пропущенными эффектами не являются успехом.
|
|
|
|
|
|
|
|
|
|
|
|
## Release gates
|
|
|
|
|
|
|
|
|
|
|
|
Версия не выпускается, если:
|
|
|
|
|
|
|
|
|
|
|
|
- появился новый corpus error;
|
|
|
|
|
|
- изменился byte roundtrip неизменённых ресурсов;
|
|
|
|
|
|
- dependency graph получил failure в достижимом пути;
|
|
|
|
|
|
- deterministic replay расходится;
|
|
|
|
|
|
- command capture изменился без ожидаемого changelog;
|
|
|
|
|
|
- parser допускает allocation по непроверенному count;
|
|
|
|
|
|
- новая оптимизация не имеет scalar reference comparison.
|
|
|
|
|
|
|
|
|
|
|
|
Каждое исправление регистрирует минимальный regression asset или synthetic
|
|
|
|
|
|
vector. Если новый behavior намеренно отличается от предыдущего, изменение
|
|
|
|
|
|
должно иметь compatibility profile, corpus sample и объяснение, почему старый
|
|
|
|
|
|
baseline был неполным или неверным.
|
|
|
|
|
|
|
|
|
|
|
|
## Уровни совместимости
|
|
|
|
|
|
|
|
|
|
|
|
Слово «совместимый» используется только с уровнем:
|
|
|
|
|
|
|
|
|
|
|
|
1. **Archive-compatible** -- открывает и сохраняет контейнеры.
|
|
|
|
|
|
2. **Asset-compatible** -- декодирует модели, материалы, текстуры и эффекты.
|
|
|
|
|
|
3. **Mission-compatible** -- загружает карту и создаёт все объекты.
|
|
|
|
|
|
4. **Runtime-compatible** -- исполняет время, события, поведение и физику.
|
|
|
|
|
|
5. **Presentation-compatible** -- воспроизводит рендер и звук.
|
|
|
|
|
|
6. **Game-compatible** -- позволяет пройти миссии, сохраняться и продолжать.
|
|
|
|
|
|
7. **Native-interoperable** -- взаимодействует с оригинальной сетью и внешним
|
|
|
|
|
|
ABI.
|
|
|
|
|
|
|
|
|
|
|
|
Viewer с красивой моделью находится только на втором уровне.
|
|
|
|
|
|
|
|
|
|
|
|
### Обязательные критерии запуска и данных
|
|
|
|
|
|
|
|
|
|
|
|
- приложение запускается из неизменённого оригинального каталога;
|
|
|
|
|
|
- относительные пути, регистр и legacy encodings разрешаются по исходным
|
|
|
|
|
|
правилам;
|
|
|
|
|
|
- все требуемые NRes/RsLi открываются без предварительной конвертации;
|
|
|
|
|
|
- parsers проверяют границы и не используют неопределённые bytes как указатели;
|
|
|
|
|
|
- неизвестные поля сохраняются lossless;
|
|
|
|
|
|
- все mission-reachable prototype, model, material, texture, lightmap и effect
|
|
|
|
|
|
references разрешаются;
|
|
|
|
|
|
- отсутствие необязательного ресурса следует документированному fallback, а не
|
|
|
|
|
|
случайному default.
|
|
|
|
|
|
|
|
|
|
|
|
### Обязательные критерии мира
|
|
|
|
|
|
|
|
|
|
|
|
- TMA разбирается до точного EOF;
|
|
|
|
|
|
- `Land.msh` и `Land.map` создают корректную поверхность и areal graph;
|
|
|
|
|
|
- ObjectId, owner и mirror semantics устойчивы;
|
|
|
|
|
|
- queue traversal и deferred deletion безопасны;
|
|
|
|
|
|
- pause, game time и simulation steps повторяемы;
|
|
|
|
|
|
- AI/Behavior/Wizard/Control взаимодействуют через заданные границы;
|
|
|
|
|
|
- collision и navigation не подменяют друг друга;
|
|
|
|
|
|
- script events используют logical IDs и переживают удаление объектов;
|
|
|
|
|
|
- deterministic replay совпадает на контрольных ticks.
|
|
|
|
|
|
|
|
|
|
|
|
### Обязательные критерии presentation
|
|
|
|
|
|
|
|
|
|
|
|
- static и animated MSH используют правильные slots, batches и transforms;
|
|
|
|
|
|
- WEAR/MAT0/Texm fallback и phase timing совпадают;
|
|
|
|
|
|
- mip-skip, palettes, Page atlases и lightmaps работают;
|
|
|
|
|
|
- render phases, depth/cull/blend state и transparent order подтверждены
|
|
|
|
|
|
captures;
|
|
|
|
|
|
- FXID commands и RNG дают устойчивый результат;
|
|
|
|
|
|
- camera и 3D sound listener синхронизированы;
|
|
|
|
|
|
- atmosphere, тени, солнце и flares не являются декоративными заглушками;
|
|
|
|
|
|
- UI и world rendering имеют правильную границу;
|
|
|
|
|
|
- golden command captures стабильны, pixel parity измеряется на фиксированных
|
|
|
|
|
|
сценах.
|
|
|
|
|
|
|
|
|
|
|
|
### Обязательные критерии полной игры
|
|
|
|
|
|
|
|
|
|
|
|
- все доступные миссии стартуют, завершаются и корректно сообщают
|
|
|
|
|
|
success/failure;
|
|
|
|
|
|
- campaign dispatcher сохраняет прогресс;
|
|
|
|
|
|
- savegame восстанавливает world, script, AI, RNG и clocks, а не только
|
|
|
|
|
|
placement;
|
|
|
|
|
|
- input remapping, pause, camera modes, sound и настройки работают из UI;
|
|
|
|
|
|
- длительный прогон не накапливает objects, resources или audio sources;
|
|
|
|
|
|
- ошибки данных показывают actionable chain;
|
|
|
|
|
|
- производительность приемлема без отключения подсистем;
|
|
|
|
|
|
- демоверсия, Часть 1 и Часть 2 проходят один и тот же тестовый контур с
|
|
|
|
|
|
раздельными manifests и эталонами.
|
|
|
|
|
|
|
|
|
|
|
|
### Native interoperability
|
|
|
|
|
|
|
|
|
|
|
|
Самый строгий уровень дополнительно требует совпадения x86 ABI экспортов, vtable
|
|
|
|
|
|
slots и calling conventions для подключаемых оригинальных модулей, а также
|
|
|
|
|
|
DirectPlay wire/framing и compression. Этот уровень независим от возможности
|
|
|
|
|
|
играть в новом standalone runtime.
|
|
|
|
|
|
|
|
|
|
|
|
Проект может честно заявлять game compatibility без native DLL/network
|
|
|
|
|
|
interoperability, но это должно быть явно указано. Аналогично pixel-perfect режим
|
|
|
|
|
|
может быть отдельным compatibility profile поверх функционально корректного
|
|
|
|
|
|
renderer-а.
|
|
|
|
|
|
|
|
|
|
|
|
### Совместимость нескольких наборов данных
|
|
|
|
|
|
|
|
|
|
|
|
Критерий полной совместимости применяется отдельно к демоверсии, Части 1 и
|
|
|
|
|
|
Части 2. Прохождение одного набора не позволяет заявлять поддержку остальных.
|
|
|
|
|
|
|
|
|
|
|
|
Обязательное различие:
|
|
|
|
|
|
|
|
|
|
|
|
- **format compatibility** -- один parser принимает все три набора;
|
|
|
|
|
|
- **content compatibility** -- конкретная миссия разрешает весь reachable graph;
|
|
|
|
|
|
- **behavior compatibility** -- runtime совпадает с соответствующей сборкой
|
|
|
|
|
|
изменённых DLL;
|
|
|
|
|
|
- **cross-version support** -- один новый движок выбирает корректные данные и
|
|
|
|
|
|
defaults по fingerprint установки.
|
|
|
|
|
|
|
|
|
|
|
|
Content fingerprint включает hashes executable/DLL и manifest ключевых архивов.
|
|
|
|
|
|
Он не используется для запрета модификаций, но выбирает compatibility profile и
|
|
|
|
|
|
делает отклонение диагностируемым.
|
|
|
|
|
|
|
|
|
|
|
|
## Definition of done
|
|
|
|
|
|
|
|
|
|
|
|
Полное документирование и реализация считаются завершёнными только когда каждый
|
|
|
|
|
|
критерий связан с главой спецификации, executable test и хотя бы одним
|
|
|
|
|
|
corpus/golden case. Утверждение без проверяемого критерия остаётся
|
|
|
|
|
|
исследовательской заметкой, а не контрактом.
|