9.0 KiB
9.0 KiB
Godot Best Practices for OpenWC
Статус
Это нормативные правила применения Godot в OpenWC. Они дополняют общую архитектуру и основаны на официальных Godot practices с учётом большого streaming world, native parsers и встроенного Editor tooling.
Nodes только на границах
- Domain, packet codecs, validators, coordinate math, build graph и canonical content не должны быть Nodes.
- Node/Control используются для lifecycle, scene composition, input, rendering/audio presentation и Editor UI.
- Большой объём однородных данных хранится в typed objects/packed arrays/resources, а не в тысячах служебных Nodes.
- Scene не ищет глобально dependency по имени; parent composition передаёт contract/reference явно.
Scene organization
- Каждая scene имеет одну ответственность и может запускаться в isolation с test doubles.
- Parent-child означает ownership: удаление parent логично удаляет child. Иначе системы являются siblings/services.
- Scene наружу публикует небольшой API/signals, а внутренние NodePaths не становятся публичным контрактом.
- Runtime, render sandbox, preview и capture scenes собираются из общих presenters/services.
- Generated world preview не сохраняется как authoring source.
Dependency injection и signals
- Dependencies передаются composition root, initializer или exported interface reference.
- Signals используются для notification; commands/intents вызываются через явные методы/bus contracts.
- Signal connections снимаются симметрично при unload/plugin disable.
- Не использовать SceneTree groups как скрытый service locator для core architecture.
Autoload policy
Autoload разрешён только для действительно process-wide lifecycle:
- composition/bootstrap coordinator;
- глобальная immutable build/version information;
- crash/diagnostic sink, если ему нужен process lifetime.
Gameplay state, network session, renderer world, editor session, caches конкретного проекта и audio emitters не должны становиться неявными глобальными managers. Они создаются/освобождаются владельцем режима.
Resources and data
- Resource подходит для immutable/imported descriptors и editor-inspectable configuration.
- Runtime mutable authoritative state не хранится в shared Resource, который может быть случайно разделён сценами.
- Resource schema имеет version и migration, если сохраняется пользователем.
- Original extracted corpus и bulk caches не следует заставлять Godot массово импортировать без необходимости; применять
.gdignore, custom import staging или native repository. - Importer проверяет malformed input и записывает dependency/profile metadata.
Background work
- Blocking
load()не используется в frame-critical streaming path; применяются threaded load/jobs. - Worker выполняет file I/O, parsing, decompression, grouping и pure data generation.
- SceneTree mutation, Node attach и большинство GPU/RID finalization выполняются main thread budget.
- Один Resource не модифицируется несколькими workers.
- GDScript container resize/mutation между threads защищается mutex или заменяется message passing.
- Все worker tasks ожидаются/очищаются; session cancellation не оставляет orphan work.
Rendering performance
- Сначала измерять SceneTree/CPU/GPU bottleneck, затем переходить к RenderingServer.
- Repeated static M2 группировать spatial MultiMesh cells; один MultiMesh на весь мир ухудшает culling.
- Large transform buffers готовить линейно/в native code и отправлять батчем.
- Visibility ranges/HLOD, occlusion и automatic mesh LOD использовать совместно по профилю.
- Unique materials/textures минимизировать; descriptor/resource counts являются budget metric.
- RenderingServer RIDs имеют явного владельца и освобождаются в deterministic shutdown test.
- Shader/material profiles разделяют Blizzlike и Enhanced; runtime не компилирует тяжёлые варианты при пересечении ADT boundary.
EditorPlugin lifecycle
- Любая регистрация в
_enter_tree()имеет соответствующее удаление в_exit_tree(). - Docks, inspectors, importers, gizmos, debugger plugins, signals и jobs снимаются при disable/reload.
- Editor UI не пишет файлы/SQL напрямую — только создаёт application commands.
- Mutation интегрирована с EditorUndoRedoManager или общей command history.
- Preview nodes считаются disposable; stable content IDs не равны instance IDs.
- Долгая операция имеет progress, cancellation и staging output.
Editor imports and inspectors
- Custom import plugin применяется для формата, которому действительно нужен first-class Godot Resource lifecycle.
- Один extension/type имеет стабильный importer name и version.
- Platform/profile variants фиксируются явно.
- Custom inspectors редактируют canonical data через commands и показывают field-level validation.
- Gizmos не владеют сущностью и создают одну undoable logical operation на drag/stroke.
UI
- Control tree не читает network packets и не ищет world nodes.
- View models immutable на frame/update boundary.
- Layout использует anchors/containers/theme tokens, а не hardcoded pixel positions вне fidelity backend.
- FrameXML compatibility layout изолирован от modern Godot-native screens.
- Input consumption и focus/modal context тестируются сценами.
Profiling and diagnostics
- Использовать Godot profiler для script/physics и Visual Profiler для render CPU/GPU.
- Headless/CLI runs сохраняют JSON metrics; GPU runs фиксируют backend/device/driver/resolution.
- Performance changes сравниваются cold/warm и p50/p95/p99, а не одним FPS.
- GPU validation, memory tracking и RenderDoc workflow документируются для shader/backend defects.
- Custom monitors публикуют streaming queues, cache hits, entities, materials, RIDs и job latency.
Tests
- Pure logic тестируется без SceneTree.
- Scene runner используется только для lifecycle/input/signals/layout/presentation.
- EditorPlugin имеет enable/disable/reload и undo/save/reload tests.
- Headless является обязательным CI path; visual tests запускаются отдельно на GPU runner.
- GdUnit4 рассматривается как кандидат для GDScript/scene tests; native tests остаются отдельным target.