675 lines
38 KiB
Markdown
675 lines
38 KiB
Markdown
# 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
|
||
render legacy-state compatibility and modern backend
|
||
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
|
||
|
||
Render, audio, input и network получают отдельные adapters. Legacy compatibility
|
||
state живёт выше Vulkan, D3D11 или Metal backend; DirectPlay compatibility живёт
|
||
отдельно от 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. Утверждение без проверяемого критерия остаётся
|
||
исследовательской заметкой, а не контрактом.
|