Compare commits
5 Commits
cb103e1fde
...
a85e416cb8
| Author | SHA1 | Date | |
|---|---|---|---|
| a85e416cb8 | |||
| c69abd634f | |||
| cd23a62be8 | |||
| 2ee647a220 | |||
| 60f1e363c0 |
@@ -950,6 +950,20 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
|||||||
- The ray height, 3x3 tile search and `2/5/10/20/40`-unit nearby fallback are
|
- The ray height, 3x3 tile search and `2/5/10/20/40`-unit nearby fallback are
|
||||||
preserved. No terrain geometry, cache, streaming or visual behavior changed.
|
preserved. No terrain geometry, cache, streaming or visual behavior changed.
|
||||||
|
|
||||||
|
## 2026-07-16 World Environment Snapshot Facade
|
||||||
|
|
||||||
|
- `WorldEnvironmentSnapshot` carries one immutable finite time-of-day value,
|
||||||
|
normalized to `[0, 24)` hours; invalid non-finite input is explicit.
|
||||||
|
- `WorldRenderFacade.apply_environment_snapshot()` delegates the snapshot to the
|
||||||
|
scene-owned `WowSkyController`. The facade owns no `Environment`, sun, skybox,
|
||||||
|
shader global or DBC sampling state.
|
||||||
|
- Checkpoint capture now submits its manifest time through the facade instead of
|
||||||
|
locating `WowSkyController` and mutating `use_system_time`, `time_speed` and
|
||||||
|
`fixed_time_hours` directly.
|
||||||
|
- Applying a snapshot performs the same three fixed-clock assignments as the old
|
||||||
|
capture path. Light/Area/Skybox DBC lookup, interpolation, fog, sun and shader
|
||||||
|
behavior remain unchanged; weather and indoor environment state are follow-up work.
|
||||||
|
|
||||||
## Practical Rule For Future Work
|
## Practical Rule For Future Work
|
||||||
|
|
||||||
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
|
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# M03-RND-FACADE-ENVIRONMENT-001 — Renderer environment snapshot boundary
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-FACADE-ENVIRONMENT-001:sindo-main-codex:2026-07-18 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-facade-environment`
|
||||||
|
- Lease expires UTC: 2026-07-18
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Add an immutable world-environment snapshot accepted by `WorldRenderFacade`,
|
||||||
|
delegate it to the existing DBC sky controller, and migrate deterministic render
|
||||||
|
checkpoint time setup away from direct sky-controller property mutation.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change Light/Area/Skybox DBC selection, interpolation, fog, sun or shader rules.
|
||||||
|
- Add weather, indoor lighting, new clock semantics or build-12340 parity claims.
|
||||||
|
- Extract the sky service or move its Environment/Node/RID ownership.
|
||||||
|
- Change runtime scene defaults, cache formats, streaming behavior or target status.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/environment/world_environment_snapshot.gd`,
|
||||||
|
`src/tools/verify_world_environment_snapshot.gd`, this claim
|
||||||
|
- Shared/hotspots: `src/render/world_render_facade.gd`,
|
||||||
|
`src/scenes/sky/wow_sky_controller.gd`, both runtime streaming scenes,
|
||||||
|
`src/tools/capture_render_checkpoints.gd`,
|
||||||
|
`src/tools/verify_world_render_facade.gd`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Public API: immutable `WorldEnvironmentSnapshot` and
|
||||||
|
`WorldRenderFacade.apply_environment_snapshot`
|
||||||
|
- Snapshot input: finite time-of-day hours normalized into one 24-hour day
|
||||||
|
- Adapter: explicit `world_environment_controller_path`; facade/controller retain
|
||||||
|
no ownership of caller snapshot
|
||||||
|
- Schema/cache/coordinate contracts and runtime defaults: unchanged
|
||||||
|
- Consumer: deterministic renderer checkpoint capture; future world-state adapter
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: integrated M03 facade/ground-query master `cb103e1`
|
||||||
|
- Blocks: completion of the facade environment checklist slice and later sky extraction
|
||||||
|
- External state: none for contract tests; visual comparison still needs proprietary corpus
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated cold snapshot contract, facade delegation/wiring regression,
|
||||||
|
capture source-boundary regression, renderer manifest/dry-run and repository gates
|
||||||
|
- Fixtures: normalization, non-finite rejection, missing/wrong controller,
|
||||||
|
exact snapshot delegation and existing runtime scene wiring
|
||||||
|
- Fidelity evidence: preserve existing fixed-time application and all DBC-derived
|
||||||
|
visual logic; seven checkpoint plans remain valid
|
||||||
|
- Performance budget: one immutable value allocation per environment update and
|
||||||
|
constant-time main-thread delegation
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs: snapshot, facade adapter and sky-controller application
|
||||||
|
- Module specification: update existing world-renderer specification
|
||||||
|
- Data-flow diagram: snapshot producer through facade into DBC sky controller
|
||||||
|
- Sequence/state/dependency diagrams: application/failure path and ownership notes
|
||||||
|
- Source map/status updates: world-renderer module, module registry if needed, `RENDER.md`
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `WorldEnvironmentSnapshot`, `apply_environment_snapshot`
|
||||||
|
- Simplest considered solution: one immutable time snapshot over the existing sky implementation
|
||||||
|
- Rejected complexity/abstractions: generic weather graph, duplicated lighting DTO,
|
||||||
|
event bus, service container or alternate Environment owner
|
||||||
|
- Unavoidable complexity and justification: facade resolves a separate scene-owned
|
||||||
|
sky controller because the streamer does not own outdoor environment rendering
|
||||||
|
- Measured optimization evidence: not applicable; bounded constant-time adapter
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready-for-review
|
||||||
|
- Done: immutable value, facade/controller adapter, checkpoint migration,
|
||||||
|
regressions and renderer module documentation
|
||||||
|
- Next: M03 integrator reviews and merges `2ee647a`
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-ENVIRONMENT-001:2ee647a -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-ENVIRONMENT-001:c69abd6 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `2ee647a`
|
||||||
|
- Results: `WorldEnvironmentSnapshot` normalizes finite time into one day and
|
||||||
|
exposes non-finite failure explicitly. `WorldRenderFacade` delegates the
|
||||||
|
immutable value to the existing sky controller; checkpoint capture no longer
|
||||||
|
locates or mutates that controller directly.
|
||||||
|
- Verification: snapshot `contract=5`; facade `delegation=6 ground_contract=2
|
||||||
|
environment_contract=7 runtime_scenes=2 tools=3`; internal access `43/7/9/1`;
|
||||||
|
terrain, coordinate (`files=116 consumers=5`), manifest (`7/7`), shutdown,
|
||||||
|
scheduler (`20000` iterations), planner (`250` iterations), focus, editor parse,
|
||||||
|
dry-run capture, coordination, documentation and diff gates passed.
|
||||||
|
- Fidelity: applying a snapshot retains the previous three fixed-clock assignments
|
||||||
|
and leaves DBC profile/area/skybox lookup, interpolation, fog, sun, shader,
|
||||||
|
cache and streaming rules unchanged. Dry-run retained all seven plans and
|
||||||
|
reported dusk as `time=19.00`. No build-12340 parity claim is added.
|
||||||
|
- External/local inputs: ignored native DLL and generated ADT resource scripts
|
||||||
|
were copied from master for parse/dry-run only. Missing proprietary ADT/DBC/
|
||||||
|
character data produced expected degraded diagnostics.
|
||||||
|
- Remaining risks: the snapshot currently covers exact world time only; weather,
|
||||||
|
indoor state and asset-backed paired zone-transition evidence remain open.
|
||||||
|
- Documentation updated: inline value/facade/controller API plus world-renderer
|
||||||
|
API/input-output/data-flow/sequence/ownership/failure/capability/source-map and
|
||||||
|
`RENDER.md` implementation notes.
|
||||||
|
- Integration: accepted in master merge `c69abd6`; post-merge snapshot, facade,
|
||||||
|
internal-access, manifest, shutdown, scheduler, planner and focus gates passed.
|
||||||
@@ -5,9 +5,9 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Status | Partial |
|
| Status | Partial |
|
||||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; `M03-RND-FACADE-001`; `M03-RND-STREAMING-PLANNER-001`; `M03-RND-SCHEDULER-001`; `M03-RND-INTERNAL-ACCESS-GATE-001`; `M03-RND-FACADE-GROUND-QUERY-001` |
|
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; `M03-RND-FACADE-001`; `M03-RND-STREAMING-PLANNER-001`; `M03-RND-SCHEDULER-001`; `M03-RND-INTERNAL-ACCESS-GATE-001`; `M03-RND-FACADE-GROUND-QUERY-001`; `M03-RND-FACADE-ENVIRONMENT-001` |
|
||||||
| Owners | Renderer workstream / milestone integrator |
|
| Owners | Renderer workstream / milestone integrator |
|
||||||
| Last verified | Worktree `work/sindo-main-codex/m03-render-budget-scheduler`, 2026-07-16 |
|
| Last verified | Worktree `work/sindo-main-codex/m03-facade-environment`, 2026-07-16 |
|
||||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
@@ -44,7 +44,10 @@ flowchart LR
|
|||||||
Parsed --> Loader
|
Parsed --> Loader
|
||||||
Loader --> Scene[SceneTree nodes]
|
Loader --> Scene[SceneTree nodes]
|
||||||
Loader --> RS[RenderingServer RIDs/MultiMesh]
|
Loader --> RS[RenderingServer RIDs/MultiMesh]
|
||||||
Sky[DBC sky/light data] --> SkyController[WowSkyController]
|
EnvironmentState[World time producer] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||||
|
EnvironmentSnapshot --> Facade
|
||||||
|
Facade --> SkyController[WowSkyController]
|
||||||
|
Sky[DBC sky/light data] --> SkyController
|
||||||
SkyController --> Scene
|
SkyController --> Scene
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,9 +68,10 @@ Forbidden dependencies:
|
|||||||
|
|
||||||
## Public API
|
## Public API
|
||||||
|
|
||||||
Runtime, capture and probe callers use `WorldRenderFacade` for focus, metrics and
|
Runtime, capture and probe callers use `WorldRenderFacade` for focus, immutable
|
||||||
rendered-ground diagnostics. `StreamingWorldLoader` remains the internal scene
|
environment input, metrics and rendered-ground diagnostics. `StreamingWorldLoader`
|
||||||
implementation while later M03 packages add environment and entity-visual contracts.
|
and `WowSkyController` remain internal scene implementations while a later M03
|
||||||
|
package adds entity-visual commands.
|
||||||
Gameplay modules and Godot `EditorPlugin` package sources are repository-gated
|
Gameplay modules and Godot `EditorPlugin` package sources are repository-gated
|
||||||
from externally reading/writing loader-private queue, task, cache and tile-state fields.
|
from externally reading/writing loader-private queue, task, cache and tile-state fields.
|
||||||
|
|
||||||
@@ -82,7 +86,10 @@ from externally reading/writing loader-private queue, task, cache and tile-state
|
|||||||
| `WorldRenderFacade.renderer_metrics_snapshot` | Public method | Returns a deep-detached queue/cache/activity snapshot | Main thread/caller-owned result | Missing/invalid implementation result returns empty snapshot with diagnostic |
|
| `WorldRenderFacade.renderer_metrics_snapshot` | Public method | Returns a deep-detached queue/cache/activity snapshot | Main thread/caller-owned result | Missing/invalid implementation result returns empty snapshot with diagnostic |
|
||||||
| `WorldRenderFacade.sample_ground_height` | Public read-only query | Samples already loaded render terrain at a typed Godot world position | Main thread; immutable caller-held result | Stable unavailable code for missing mesh/intersection/facade; not gameplay authority |
|
| `WorldRenderFacade.sample_ground_height` | Public read-only query | Samples already loaded render terrain at a typed Godot world position | Main thread; immutable caller-held result | Stable unavailable code for missing mesh/intersection/facade; not gameplay authority |
|
||||||
| `WorldRenderFacade.renderer_ground_query_snapshot` | Public diagnostic query | Returns ground height plus detached tile/readiness context without exposing mutable state | Main thread; caller-owned deep copy | Missing/invalid renderer returns empty snapshot with diagnostic |
|
| `WorldRenderFacade.renderer_ground_query_snapshot` | Public diagnostic query | Returns ground height plus detached tile/readiness context without exposing mutable state | Main thread; caller-owned deep copy | Missing/invalid renderer returns empty snapshot with diagnostic |
|
||||||
|
| `WorldEnvironmentSnapshot.at_time_of_day` | Immutable value factory | Normalizes a finite authoritative time into `[0, 24)` hours | Any thread; caller-held value | Non-finite input creates invalid value with `environment_time_not_finite` |
|
||||||
|
| `WorldRenderFacade.apply_environment_snapshot` | Public command | Delegates exact world time to the scene-owned DBC sky controller | Main thread; snapshot remains caller-owned | Returns `false` for invalid input or missing/incompatible controller |
|
||||||
| `WorldRenderFacade.streaming_world_loader_path` | Internal adapter property | Resolves the existing implementation without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
|
| `WorldRenderFacade.streaming_world_loader_path` | Internal adapter property | Resolves the existing implementation without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
|
||||||
|
| `WorldRenderFacade.world_environment_controller_path` | Internal adapter property | Resolves the existing `WowSkyController` without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
|
||||||
| `camera_path` | Exported property | Camera used only by optional automatic overview positioning | Main thread, scene lifetime | Missing camera skips automatic positioning |
|
| `camera_path` | Exported property | Camera used only by optional automatic overview positioning | Main thread, scene lifetime | Missing camera skips automatic positioning |
|
||||||
| `quality_preset` | Exported property | Applies renderer budgets/radii | Before/at runtime depending property | Invalid combination should be diagnosed |
|
| `quality_preset` | Exported property | Applies renderer budgets/radii | Before/at runtime depending property | Invalid combination should be diagnosed |
|
||||||
| `runtime_stats_enabled` | Exported property | Enables periodic performance snapshot | Runtime mutable | Logging overhead only |
|
| `runtime_stats_enabled` | Exported property | Enables periodic performance snapshot | Runtime mutable | Logging overhead only |
|
||||||
@@ -99,6 +106,7 @@ loader configuration remains transitional composition data, not a caller API.
|
|||||||
| Input | `StreamingFocus` | Player/spectator/editor/capture adapter | `WorldRenderFacade` | Immutable caller value | Main thread/session |
|
| Input | `StreamingFocus` | Player/spectator/editor/capture adapter | `WorldRenderFacade` | Immutable caller value | Main thread/session |
|
||||||
| Input | Focus-source `NodePath` | Runtime/test scene composition | `WorldRenderFacade` source adapter | Scene-owned node, sampled position | Main thread/scene lifetime |
|
| Input | Focus-source `NodePath` | Runtime/test scene composition | `WorldRenderFacade` source adapter | Scene-owned node, sampled position | Main thread/scene lifetime |
|
||||||
| Input | Ground-query `GodotWorldPosition` | Renderer probe/future adapter | `WorldRenderFacade` | Immutable caller-held value | One main-thread query |
|
| Input | Ground-query `GodotWorldPosition` | Renderer probe/future adapter | `WorldRenderFacade` | Immutable caller-held value | One main-thread query |
|
||||||
|
| Input | `WorldEnvironmentSnapshot` | World-state/capture adapter | `WorldRenderFacade` / `WowSkyController` | Immutable caller-held value; not retained | One main-thread update |
|
||||||
| Input | Map/configuration | Scene/app/editor | `StreamingWorldLoader` | Caller config, copied/read | Main thread/session |
|
| Input | Map/configuration | Scene/app/editor | `StreamingWorldLoader` | Caller config, copied/read | Main thread/session |
|
||||||
| Input | WDT/ADT/M2/WMO/BLP bytes | Extracted asset repository | Native loaders/builders | Loader result owns parsed data | Worker or controlled load |
|
| Input | WDT/ADT/M2/WMO/BLP bytes | Extracted asset repository | Native loaders/builders | Loader result owns parsed data | Worker or controlled load |
|
||||||
| Input | Baked resources | Cache build tools | Streamer/builders | Shared immutable resource/cache entry | Session/cache lifetime |
|
| Input | Baked resources | Cache build tools | Streamer/builders | Shared immutable resource/cache entry | Session/cache lifetime |
|
||||||
@@ -147,6 +155,11 @@ flowchart TD
|
|||||||
B --> Liquid[Liquid attach]
|
B --> Liquid[Liquid attach]
|
||||||
Loaded[Loaded terrain Mesh and Transform] --> Ground[Facade ground query]
|
Loaded[Loaded terrain Mesh and Transform] --> Ground[Facade ground query]
|
||||||
Ground --> GroundSample[RenderedGroundSample / detached diagnostics]
|
Ground --> GroundSample[RenderedGroundSample / detached diagnostics]
|
||||||
|
WorldTime[Authoritative/test time] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||||
|
EnvironmentSnapshot --> RF
|
||||||
|
RF --> SkyController[WowSkyController]
|
||||||
|
DBC[Light/Area/Skybox DBC] --> SkyController
|
||||||
|
SkyController --> World
|
||||||
Terrain --> World[Godot world]
|
Terrain --> World[Godot world]
|
||||||
M2 --> World
|
M2 --> World
|
||||||
WMO --> World
|
WMO --> World
|
||||||
@@ -202,6 +215,9 @@ sequenceDiagram
|
|||||||
Source->>Facade: sample_ground_height(typed position)
|
Source->>Facade: sample_ground_height(typed position)
|
||||||
Facade->>Stream: sample loaded render terrain
|
Facade->>Stream: sample loaded render terrain
|
||||||
Stream-->>Facade: RenderedGroundSample / diagnostic snapshot
|
Stream-->>Facade: RenderedGroundSample / diagnostic snapshot
|
||||||
|
Source->>Facade: apply_environment_snapshot(exact time)
|
||||||
|
Facade->>Render: delegate to WowSkyController
|
||||||
|
Render->>Render: freeze local clock; retain DBC sampling/apply rules
|
||||||
Stream->>Budget: shutdown: cancel permit issuance
|
Stream->>Budget: shutdown: cancel permit issuance
|
||||||
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
|
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
|
||||||
Stream->>Stream: shutdown: finish registered ResourceLoader requests
|
Stream->>Stream: shutdown: finish registered ResourceLoader requests
|
||||||
@@ -213,7 +229,13 @@ sequenceDiagram
|
|||||||
|
|
||||||
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
|
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
|
||||||
- `WorldRenderFacade` owns only scene-relative adapter paths. It does not own the
|
- `WorldRenderFacade` owns only scene-relative adapter paths. It does not own the
|
||||||
streamer, focus source, returned metrics, queues, caches, nodes or RIDs.
|
streamer, environment controller, focus source, returned metrics, queues,
|
||||||
|
caches, nodes or RIDs.
|
||||||
|
- `WorldEnvironmentSnapshot` is an immutable caller-owned scalar value. Neither
|
||||||
|
facade nor sky controller retains it; the controller copies normalized hours
|
||||||
|
into its existing fixed-clock fields on the main thread.
|
||||||
|
- `WowSkyController` continues to own DBC sampling state and mutations of its
|
||||||
|
duplicated Godot `Environment`, sun, skybox node and world shader globals.
|
||||||
- Rendered-ground query results and diagnostic snapshots are caller-owned values;
|
- Rendered-ground query results and diagnostic snapshots are caller-owned values;
|
||||||
the facade never returns Mesh, Node, tile-state or queue references.
|
the facade never returns Mesh, Node, tile-state or queue references.
|
||||||
- Loaded-mesh ground sampling is renderer diagnostics, not authoritative gameplay
|
- Loaded-mesh ground sampling is renderer diagnostics, not authoritative gameplay
|
||||||
@@ -240,6 +262,8 @@ sequenceDiagram
|
|||||||
| Missing focus source | Explicit NodePath resolution | Retain current streamed content and prior valid focus | One warning until source recovers | Fix composition path or call `set_streaming_focus` |
|
| Missing focus source | Explicit NodePath resolution | Retain current streamed content and prior valid focus | One warning until source recovers | Fix composition path or call `set_streaming_focus` |
|
||||||
| Missing renderer implementation | Facade path/method validation | Focus/metrics call fails without mutation | One facade diagnostic until recovery | Fix scene composition path/implementation |
|
| Missing renderer implementation | Facade path/method validation | Focus/metrics call fails without mutation | One facade diagnostic until recovery | Fix scene composition path/implementation |
|
||||||
| Invalid metrics result | Facade return-type validation | Return empty caller snapshot | Facade error | Restore `Dictionary` implementation contract |
|
| Invalid metrics result | Facade return-type validation | Return empty caller snapshot | Facade error | Restore `Dictionary` implementation contract |
|
||||||
|
| Invalid environment time | Snapshot finite-value validation | Reject update; retain prior sky clock | `environment_time_not_finite` on invalid value | Supply a finite time snapshot |
|
||||||
|
| Missing environment controller | Facade path/method validation | Reject environment update; other facade APIs remain available | One facade diagnostic until recovery | Fix scene composition path/implementation |
|
||||||
| No focus supplied | Refresh contract | No target recomputation; queues continue draining | `refresh_streaming_focus()` returns `false` | Supply player/editor/capture focus |
|
| No focus supplied | Refresh contract | No target recomputation; queues continue draining | `refresh_streaming_focus()` returns `false` | Supply player/editor/capture focus |
|
||||||
| Cache version mismatch | Required format constant | Reject stale cache/fallback | Version diagnostic | Rebuild matching cache |
|
| Cache version mismatch | Required format constant | Reject stale cache/fallback | Version diagnostic | Rebuild matching cache |
|
||||||
| Worker failure | Task result/empty data | Skip/degrade affected asset | Queue/task warning | Retry/rebuild source |
|
| Worker failure | Task result/empty data | Skip/degrade affected asset | Queue/task warning | Retry/rebuild source |
|
||||||
@@ -320,7 +344,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
|
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
|
||||||
| Camera-independent streaming focus | Implemented | Typed contract, two player-focused runtime scenes and three explicitly camera-focused diagnostic tools | Multi-focus/direction/velocity belong to planner extraction |
|
| Camera-independent streaming focus | Implemented | Typed contract, two player-focused runtime scenes and three explicitly camera-focused diagnostic tools | Multi-focus/direction/velocity belong to planner extraction |
|
||||||
| Golden server-spawn visualization | Implemented | Pinned AzerothCore fixture, validated renderer manifest and diagnostic origin-marker capture at ADT `(32,48)`/MCNK `(3,12)` | No TrinityCore row or original-client visual-parity claim |
|
| Golden server-spawn visualization | Implemented | Pinned AzerothCore fixture, validated renderer manifest and diagnostic origin-marker capture at ADT `(32,48)`/MCNK `(3,12)` | No TrinityCore row or original-client visual-parity claim |
|
||||||
| Stable renderer facade | Partial | Facade contracts cover focus, metrics and loaded-mesh ground query | Environment and entity visuals remain |
|
| Stable renderer facade | Partial | Facade contracts cover focus, environment time, metrics and loaded-mesh ground query | Entity visuals remain |
|
||||||
|
| Environment snapshot input | Implemented boundary / Partial fidelity | Immutable time contract, facade/controller delegation and checkpoint migration | Weather/indoor state and paired zone-transition fidelity remain |
|
||||||
| Pure streaming target planner | Implemented | `M03-RND-STREAMING-PLANNER-001` asset-free contract/performance regression | Queue scheduling and loaded-tile LOD application remain in streamer |
|
| Pure streaming target planner | Implemented | `M03-RND-STREAMING-PLANNER-001` asset-free contract/performance regression | Queue scheduling and loaded-tile LOD application remain in streamer |
|
||||||
| Bounded render budget scheduler | Implemented | `M03-RND-SCHEDULER-001` asset-free permit/cancellation/performance regression | Queue storage, worker concurrency and operation execution remain in streamer/services |
|
| Bounded render budget scheduler | Implemented | `M03-RND-SCHEDULER-001` asset-free permit/cancellation/performance regression | Queue storage, worker concurrency and operation execution remain in streamer/services |
|
||||||
| Gameplay/editor/tool internal-access boundary | Implemented | Access gate scans gameplay, EditorPlugin packages and terrain probe against actual private streamer declarations | Add future renderer diagnostic tools explicitly to the protected list |
|
| Gameplay/editor/tool internal-access boundary | Implemented | Access gate scans gameplay, EditorPlugin packages and terrain probe against actual private streamer declarations | Add future renderer diagnostic tools explicitly to the protected list |
|
||||||
@@ -353,7 +378,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
|
|
||||||
| Path | Responsibility |
|
| Path | Responsibility |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus, detached metrics and rendered-ground queries |
|
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus/environment, detached metrics and rendered-ground queries |
|
||||||
|
| `src/render/environment/world_environment_snapshot.gd` | Immutable normalized world time input with explicit invalid state |
|
||||||
| `src/render/terrain/rendered_ground_sample.gd` | Immutable renderer-owned available/unavailable ground query result |
|
| `src/render/terrain/rendered_ground_sample.gd` | Immutable renderer-owned available/unavailable ground query result |
|
||||||
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
|
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
|
||||||
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
|
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
|
||||||
@@ -362,7 +388,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| `src/scenes/streaming/streaming_world_loader.gd` | Current runtime orchestration, queues, caches and attachment |
|
| `src/scenes/streaming/streaming_world_loader.gd` | Current runtime orchestration, queues, caches and attachment |
|
||||||
| `src/domain/streaming/streaming_focus.gd` | Immutable camera-independent streaming position contract |
|
| `src/domain/streaming/streaming_focus.gd` | Immutable camera-independent streaming position contract |
|
||||||
| `src/tools/verify_streaming_focus.gd` | Headless contract, dependency and runtime/tool wiring regression |
|
| `src/tools/verify_streaming_focus.gd` | Headless contract, dependency and runtime/tool wiring regression |
|
||||||
| `src/tools/verify_world_render_facade.gd` | Focus/metrics/ground delegation, snapshot isolation and consumer wiring regression |
|
| `src/tools/verify_world_render_facade.gd` | Focus/environment/metrics/ground delegation, snapshot isolation and consumer wiring regression |
|
||||||
|
| `src/tools/verify_world_environment_snapshot.gd` | Cold-start environment value normalization and invalid-input regression |
|
||||||
| `src/tools/verify_rendered_ground_sample.gd` | Cold-start renderer ground result invariant regression |
|
| `src/tools/verify_rendered_ground_sample.gd` | Cold-start renderer ground result invariant regression |
|
||||||
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
|
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
|
||||||
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |
|
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
class_name WorldEnvironmentSnapshot
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
## Immutable renderer input describing the authoritative time within one world day.
|
||||||
|
## DBC light, area, fog and skybox selection remain owned by the sky controller.
|
||||||
|
|
||||||
|
const HOURS_PER_DAY := 24.0
|
||||||
|
const SCRIPT_PATH := "res://src/render/environment/world_environment_snapshot.gd"
|
||||||
|
|
||||||
|
var is_valid: bool:
|
||||||
|
get:
|
||||||
|
return _is_valid
|
||||||
|
|
||||||
|
var time_of_day_hours: float:
|
||||||
|
get:
|
||||||
|
return _time_of_day_hours
|
||||||
|
|
||||||
|
var failure_code: StringName:
|
||||||
|
get:
|
||||||
|
return _failure_code
|
||||||
|
|
||||||
|
var _is_valid: bool
|
||||||
|
var _time_of_day_hours: float
|
||||||
|
var _failure_code: StringName
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a snapshot and normalizes finite input into the half-open [0, 24) day.
|
||||||
|
## Non-finite input produces an explicit invalid value instead of renderer state.
|
||||||
|
static func at_time_of_day(time_of_day_hours_value: float) -> RefCounted:
|
||||||
|
return load(SCRIPT_PATH).new(time_of_day_hours_value)
|
||||||
|
|
||||||
|
|
||||||
|
func _init(time_of_day_hours_value: float) -> void:
|
||||||
|
_is_valid = is_finite(time_of_day_hours_value)
|
||||||
|
_time_of_day_hours = (
|
||||||
|
fposmod(time_of_day_hours_value, HOURS_PER_DAY)
|
||||||
|
if _is_valid
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
_failure_code = &"" if _is_valid else &"environment_time_not_finite"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dass4mi3ex2fu
|
||||||
@@ -8,17 +8,24 @@ extends Node
|
|||||||
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
||||||
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
||||||
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
|
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
|
||||||
|
const WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
||||||
|
|
||||||
## Internal renderer implementation resolved relative to this node.
|
## Internal renderer implementation resolved relative to this node.
|
||||||
## The referenced node remains owned by its scene parent.
|
## The referenced node remains owned by its scene parent.
|
||||||
@export var streaming_world_loader_path: NodePath = NodePath("..")
|
@export var streaming_world_loader_path: NodePath = NodePath("..")
|
||||||
|
|
||||||
|
## Scene-owned outdoor environment implementation resolved relative to this node.
|
||||||
|
## The facade delegates snapshots but owns neither the controller nor Environment.
|
||||||
|
@export var world_environment_controller_path: NodePath = NodePath("../WowSkyController")
|
||||||
|
|
||||||
## Optional explicit Node3D source sampled in Godot world coordinates.
|
## Optional explicit Node3D source sampled in Godot world coordinates.
|
||||||
## Runtime scenes use the player; capture and probe tools replace it with their camera.
|
## Runtime scenes use the player; capture and probe tools replace it with their camera.
|
||||||
@export var streaming_focus_source_path: NodePath
|
@export var streaming_focus_source_path: NodePath
|
||||||
|
|
||||||
var _streaming_world_loader: Node
|
var _streaming_world_loader: Node
|
||||||
|
var _world_environment_controller: Node
|
||||||
var _missing_loader_reported := false
|
var _missing_loader_reported := false
|
||||||
|
var _missing_environment_controller_reported := false
|
||||||
var _missing_focus_source_reported := false
|
var _missing_focus_source_reported := false
|
||||||
|
|
||||||
|
|
||||||
@@ -94,6 +101,21 @@ func renderer_ground_query_snapshot(godot_world_position: GodotWorldPosition) ->
|
|||||||
return (snapshot_variant as Dictionary).duplicate(true)
|
return (snapshot_variant as Dictionary).duplicate(true)
|
||||||
|
|
||||||
|
|
||||||
|
## Applies an immutable environment snapshot through the renderer-owned sky path.
|
||||||
|
## Returns false for an invalid value or unavailable/incompatible controller.
|
||||||
|
func apply_environment_snapshot(world_environment_snapshot: RefCounted) -> bool:
|
||||||
|
if (
|
||||||
|
world_environment_snapshot == null
|
||||||
|
or world_environment_snapshot.get_script() != WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT
|
||||||
|
or not bool(world_environment_snapshot.get("is_valid"))
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
var controller := _resolve_world_environment_controller()
|
||||||
|
if controller == null:
|
||||||
|
return false
|
||||||
|
return bool(controller.call("apply_environment_snapshot", world_environment_snapshot))
|
||||||
|
|
||||||
|
|
||||||
func _capture_streaming_focus_from_source() -> void:
|
func _capture_streaming_focus_from_source() -> void:
|
||||||
if streaming_focus_source_path == NodePath():
|
if streaming_focus_source_path == NodePath():
|
||||||
return
|
return
|
||||||
@@ -138,3 +160,20 @@ func _resolve_streaming_world_loader() -> Node:
|
|||||||
_streaming_world_loader = candidate
|
_streaming_world_loader = candidate
|
||||||
_missing_loader_reported = false
|
_missing_loader_reported = false
|
||||||
return _streaming_world_loader
|
return _streaming_world_loader
|
||||||
|
|
||||||
|
|
||||||
|
func _resolve_world_environment_controller() -> Node:
|
||||||
|
if is_instance_valid(_world_environment_controller):
|
||||||
|
return _world_environment_controller
|
||||||
|
var candidate := get_node_or_null(world_environment_controller_path)
|
||||||
|
if candidate == null or not candidate.has_method(&"apply_environment_snapshot"):
|
||||||
|
if not _missing_environment_controller_reported:
|
||||||
|
push_error(
|
||||||
|
"WorldRenderFacade cannot resolve a compatible environment controller at %s"
|
||||||
|
% world_environment_controller_path
|
||||||
|
)
|
||||||
|
_missing_environment_controller_reported = true
|
||||||
|
return null
|
||||||
|
_world_environment_controller = candidate
|
||||||
|
_missing_environment_controller_reported = false
|
||||||
|
return _world_environment_controller
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ extends Node
|
|||||||
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
||||||
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
||||||
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
||||||
|
const WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
||||||
|
|
||||||
const LIGHT_COORD_SCALE := 36.0
|
const LIGHT_COORD_SCALE := 36.0
|
||||||
const HALF_MINUTES_PER_DAY := 2880
|
const HALF_MINUTES_PER_DAY := 2880
|
||||||
@@ -133,6 +134,22 @@ func _process(delta: float) -> void:
|
|||||||
_update_skybox_transform()
|
_update_skybox_transform()
|
||||||
|
|
||||||
|
|
||||||
|
## Accepts an authoritative time snapshot without moving DBC visual selection out
|
||||||
|
## of this controller. Existing fixed-clock behavior is preserved and frozen until
|
||||||
|
## another snapshot or explicit local clock configuration is supplied.
|
||||||
|
func apply_environment_snapshot(world_environment_snapshot: RefCounted) -> bool:
|
||||||
|
if (
|
||||||
|
world_environment_snapshot == null
|
||||||
|
or world_environment_snapshot.get_script() != WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT
|
||||||
|
or not bool(world_environment_snapshot.get("is_valid"))
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
use_system_time = false
|
||||||
|
time_speed = 0.0
|
||||||
|
fixed_time_hours = float(world_environment_snapshot.get("time_of_day_hours"))
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func _resolve_nodes() -> void:
|
func _resolve_nodes() -> void:
|
||||||
if not world_environment_path.is_empty():
|
if not world_environment_path.is_empty():
|
||||||
_world_environment = get_node_or_null(world_environment_path) as WorldEnvironment
|
_world_environment = get_node_or_null(world_environment_path) as WorldEnvironment
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ hitch_profiler_enabled = true
|
|||||||
script = ExtResource("7_render_facade")
|
script = ExtResource("7_render_facade")
|
||||||
streaming_world_loader_path = NodePath("..")
|
streaming_world_loader_path = NodePath("..")
|
||||||
streaming_focus_source_path = NodePath("../ThirdPersonPlayer")
|
streaming_focus_source_path = NodePath("../ThirdPersonPlayer")
|
||||||
|
world_environment_controller_path = NodePath("../WowSkyController")
|
||||||
|
|
||||||
[node name="ThirdPersonPlayer" type="CharacterBody3D" parent="." unique_id=502573687]
|
[node name="ThirdPersonPlayer" type="CharacterBody3D" parent="." unique_id=502573687]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16800, 80, 26400)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16800, 80, 26400)
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ hitch_profiler_enabled = true
|
|||||||
script = ExtResource("7_render_facade")
|
script = ExtResource("7_render_facade")
|
||||||
streaming_world_loader_path = NodePath("..")
|
streaming_world_loader_path = NodePath("..")
|
||||||
streaming_focus_source_path = NodePath("../ThirdPersonPlayer")
|
streaming_focus_source_path = NodePath("../ThirdPersonPlayer")
|
||||||
|
world_environment_controller_path = NodePath("../WowSkyController")
|
||||||
|
|
||||||
[node name="ThirdPersonPlayer" type="CharacterBody3D" parent="." unique_id=502573687]
|
[node name="ThirdPersonPlayer" type="CharacterBody3D" parent="." unique_id=502573687]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16800, 80, 26400)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16800, 80, 26400)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ extends SceneTree
|
|||||||
|
|
||||||
const DEFAULT_MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json"
|
const DEFAULT_MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json"
|
||||||
const M2_NATIVE_ANIMATED_BUILDER := preload("res://addons/mpq_extractor/loaders/m2_native_animated_builder.gd")
|
const M2_NATIVE_ANIMATED_BUILDER := preload("res://addons/mpq_extractor/loaders/m2_native_animated_builder.gd")
|
||||||
|
const WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
||||||
const SHUTDOWN_DRAIN_FRAMES := 2
|
const SHUTDOWN_DRAIN_FRAMES := 2
|
||||||
|
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ func _capture_async() -> void:
|
|||||||
camera.make_current()
|
camera.make_current()
|
||||||
if player != null:
|
if player != null:
|
||||||
player.global_position = _vector3(checkpoint.get("player", checkpoint.get("target", [0.0, 0.0, 0.0])))
|
player.global_position = _vector3(checkpoint.get("player", checkpoint.get("target", [0.0, 0.0, 0.0])))
|
||||||
_set_sky_time(world, float(checkpoint.get("time_hours", 13.0)))
|
_apply_environment_time(render_facade, float(checkpoint.get("time_hours", 13.0)))
|
||||||
render_facade.call("refresh_streaming_focus", true)
|
render_facade.call("refresh_streaming_focus", true)
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
@@ -308,12 +309,10 @@ func _create_diagnostic_spawn_marker(checkpoint: Dictionary) -> Node3D:
|
|||||||
return marker_root
|
return marker_root
|
||||||
|
|
||||||
|
|
||||||
func _set_sky_time(world: Node, time_hours: float) -> void:
|
func _apply_environment_time(render_facade: Node, time_hours: float) -> void:
|
||||||
var sky := world.get_node_or_null("WowSkyController")
|
var environment_snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(time_hours)
|
||||||
if sky != null:
|
if not bool(render_facade.call("apply_environment_snapshot", environment_snapshot)):
|
||||||
sky.set("use_system_time", false)
|
push_error("Render checkpoint could not apply environment time %.3f" % time_hours)
|
||||||
sky.set("time_speed", 0.0)
|
|
||||||
sky.set("fixed_time_hours", time_hours)
|
|
||||||
|
|
||||||
|
|
||||||
func _environment_metadata() -> Dictionary:
|
func _environment_metadata() -> Dictionary:
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
## Cold-start contract for immutable renderer environment input.
|
||||||
|
|
||||||
|
const SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
var failures: Array[String] = []
|
||||||
|
var afternoon: RefCounted = SNAPSHOT_SCRIPT.at_time_of_day(13.5)
|
||||||
|
_expect_true(bool(afternoon.get("is_valid")), "finite snapshot", failures)
|
||||||
|
_expect_near(float(afternoon.get("time_of_day_hours")), 13.5, "finite time", failures)
|
||||||
|
|
||||||
|
var wrapped: RefCounted = SNAPSHOT_SCRIPT.at_time_of_day(25.25)
|
||||||
|
_expect_near(float(wrapped.get("time_of_day_hours")), 1.25, "positive day wrap", failures)
|
||||||
|
var negative: RefCounted = SNAPSHOT_SCRIPT.at_time_of_day(-1.0)
|
||||||
|
_expect_near(float(negative.get("time_of_day_hours")), 23.0, "negative day wrap", failures)
|
||||||
|
|
||||||
|
var invalid: RefCounted = SNAPSHOT_SCRIPT.at_time_of_day(INF)
|
||||||
|
_expect_true(not bool(invalid.get("is_valid")), "non-finite invalid", failures)
|
||||||
|
_expect_true(
|
||||||
|
StringName(invalid.get("failure_code")) == &"environment_time_not_finite",
|
||||||
|
"non-finite failure code",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
if not failures.is_empty():
|
||||||
|
for failure in failures:
|
||||||
|
push_error("WORLD_ENVIRONMENT_SNAPSHOT: %s" % failure)
|
||||||
|
quit(1)
|
||||||
|
return
|
||||||
|
print("WORLD_ENVIRONMENT_SNAPSHOT PASS contract=5")
|
||||||
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_near(actual_value: float, expected_value: float, label: String, failures: Array[String]) -> void:
|
||||||
|
if not is_equal_approx(actual_value, expected_value):
|
||||||
|
failures.append("%s expected %.3f, got %.3f" % [label, expected_value, actual_value])
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
||||||
|
if not actual_value:
|
||||||
|
failures.append("%s expected true" % label)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://e85kt58s0ca4
|
||||||
@@ -6,6 +6,8 @@ const WORLD_RENDER_FACADE_SCRIPT := preload("res://src/render/world_render_facad
|
|||||||
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
||||||
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
||||||
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
|
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
|
||||||
|
const WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT := preload("res://src/render/environment/world_environment_snapshot.gd")
|
||||||
|
const WOW_SKY_CONTROLLER_SCRIPT := preload("res://src/scenes/sky/wow_sky_controller.gd")
|
||||||
|
|
||||||
const RUNTIME_SCENE_PATHS: Array[String] = [
|
const RUNTIME_SCENE_PATHS: Array[String] = [
|
||||||
"res://src/scenes/streaming/eastern_kingdoms_streaming.tscn",
|
"res://src/scenes/streaming/eastern_kingdoms_streaming.tscn",
|
||||||
@@ -47,9 +49,18 @@ class StreamingWorldLoaderDouble extends Node:
|
|||||||
return ground_snapshot
|
return ground_snapshot
|
||||||
|
|
||||||
|
|
||||||
|
class WorldEnvironmentControllerDouble extends Node:
|
||||||
|
var applied_snapshot: RefCounted
|
||||||
|
|
||||||
|
func apply_environment_snapshot(world_environment_snapshot: RefCounted) -> bool:
|
||||||
|
applied_snapshot = world_environment_snapshot
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func _initialize() -> void:
|
func _initialize() -> void:
|
||||||
var failures: Array[String] = []
|
var failures: Array[String] = []
|
||||||
_verify_rendered_ground_sample_contract(failures)
|
_verify_rendered_ground_sample_contract(failures)
|
||||||
|
_verify_sky_controller_application(failures)
|
||||||
await _verify_delegation_and_snapshot_isolation(failures)
|
await _verify_delegation_and_snapshot_isolation(failures)
|
||||||
_verify_repository_wiring(failures)
|
_verify_repository_wiring(failures)
|
||||||
|
|
||||||
@@ -59,7 +70,7 @@ func _initialize() -> void:
|
|||||||
quit(1)
|
quit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
print("WORLD_RENDER_FACADE PASS delegation=5 ground_contract=2 runtime_scenes=2 tools=3")
|
print("WORLD_RENDER_FACADE PASS delegation=6 ground_contract=2 environment_contract=7 runtime_scenes=2 tools=3")
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -79,6 +90,23 @@ func _verify_rendered_ground_sample_contract(failures: Array[String]) -> void:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_sky_controller_application(failures: Array[String]) -> void:
|
||||||
|
var controller: Node = WOW_SKY_CONTROLLER_SCRIPT.new()
|
||||||
|
controller.set("use_system_time", true)
|
||||||
|
controller.set("time_speed", 4.0)
|
||||||
|
controller.set("fixed_time_hours", 3.0)
|
||||||
|
var snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(18.75)
|
||||||
|
_expect_true(
|
||||||
|
bool(controller.call("apply_environment_snapshot", snapshot)),
|
||||||
|
"sky controller accepts snapshot",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
_expect_true(not bool(controller.get("use_system_time")), "snapshot disables system clock", failures)
|
||||||
|
_expect_near(float(controller.get("time_speed")), 0.0, "snapshot freezes local clock", failures)
|
||||||
|
_expect_near(float(controller.get("fixed_time_hours")), 18.75, "snapshot applies exact time", failures)
|
||||||
|
controller.free()
|
||||||
|
|
||||||
|
|
||||||
func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
|
func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
|
||||||
var test_root := Node.new()
|
var test_root := Node.new()
|
||||||
var loader := StreamingWorldLoaderDouble.new()
|
var loader := StreamingWorldLoaderDouble.new()
|
||||||
@@ -86,12 +114,16 @@ func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
|
|||||||
var source := Node3D.new()
|
var source := Node3D.new()
|
||||||
source.name = "FocusSource"
|
source.name = "FocusSource"
|
||||||
source.position = Vector3(10.0, 20.0, 30.0)
|
source.position = Vector3(10.0, 20.0, 30.0)
|
||||||
|
var environment_controller := WorldEnvironmentControllerDouble.new()
|
||||||
|
environment_controller.name = "WorldEnvironmentControllerDouble"
|
||||||
var facade = WORLD_RENDER_FACADE_SCRIPT.new()
|
var facade = WORLD_RENDER_FACADE_SCRIPT.new()
|
||||||
facade.name = "WorldRenderFacade"
|
facade.name = "WorldRenderFacade"
|
||||||
facade.streaming_world_loader_path = NodePath("../StreamingWorldLoaderDouble")
|
facade.streaming_world_loader_path = NodePath("../StreamingWorldLoaderDouble")
|
||||||
facade.streaming_focus_source_path = NodePath("../FocusSource")
|
facade.streaming_focus_source_path = NodePath("../FocusSource")
|
||||||
|
facade.world_environment_controller_path = NodePath("../WorldEnvironmentControllerDouble")
|
||||||
test_root.add_child(loader)
|
test_root.add_child(loader)
|
||||||
test_root.add_child(source)
|
test_root.add_child(source)
|
||||||
|
test_root.add_child(environment_controller)
|
||||||
test_root.add_child(facade)
|
test_root.add_child(facade)
|
||||||
get_root().add_child(test_root)
|
get_root().add_child(test_root)
|
||||||
await process_frame
|
await process_frame
|
||||||
@@ -131,6 +163,24 @@ func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
|
|||||||
(ground_snapshot["diagnostic"] as Dictionary)["state_present"] = false
|
(ground_snapshot["diagnostic"] as Dictionary)["state_present"] = false
|
||||||
_expect_near(float(loader.ground_snapshot.terrain_height), 42.25, "ground snapshot top-level detached", failures)
|
_expect_near(float(loader.ground_snapshot.terrain_height), 42.25, "ground snapshot top-level detached", failures)
|
||||||
_expect_true(bool(loader.ground_snapshot.diagnostic.state_present), "ground snapshot nested detached", failures)
|
_expect_true(bool(loader.ground_snapshot.diagnostic.state_present), "ground snapshot nested detached", failures)
|
||||||
|
|
||||||
|
var environment_snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(18.75)
|
||||||
|
_expect_true(
|
||||||
|
facade.apply_environment_snapshot(environment_snapshot),
|
||||||
|
"environment snapshot delegates",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
_expect_true(
|
||||||
|
environment_controller.applied_snapshot == environment_snapshot,
|
||||||
|
"environment snapshot reference delegates",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var invalid_environment_snapshot: RefCounted = WORLD_ENVIRONMENT_SNAPSHOT_SCRIPT.at_time_of_day(NAN)
|
||||||
|
_expect_true(
|
||||||
|
not facade.apply_environment_snapshot(invalid_environment_snapshot),
|
||||||
|
"invalid environment snapshot rejected",
|
||||||
|
failures
|
||||||
|
)
|
||||||
test_root.queue_free()
|
test_root.queue_free()
|
||||||
|
|
||||||
|
|
||||||
@@ -140,6 +190,7 @@ func _verify_repository_wiring(failures: Array[String]) -> void:
|
|||||||
_expect_true(scene_source.contains('path="res://src/render/world_render_facade.gd"'), "%s loads facade" % scene_path, failures)
|
_expect_true(scene_source.contains('path="res://src/render/world_render_facade.gd"'), "%s loads facade" % scene_path, failures)
|
||||||
_expect_true(scene_source.contains('[node name="WorldRenderFacade" type="Node" parent="."]'), "%s owns facade node" % scene_path, failures)
|
_expect_true(scene_source.contains('[node name="WorldRenderFacade" type="Node" parent="."]'), "%s owns facade node" % scene_path, failures)
|
||||||
_expect_true(scene_source.contains('streaming_focus_source_path = NodePath("../ThirdPersonPlayer")'), "%s facade uses player focus" % scene_path, failures)
|
_expect_true(scene_source.contains('streaming_focus_source_path = NodePath("../ThirdPersonPlayer")'), "%s facade uses player focus" % scene_path, failures)
|
||||||
|
_expect_true(scene_source.contains('world_environment_controller_path = NodePath("../WowSkyController")'), "%s facade uses sky controller" % scene_path, failures)
|
||||||
|
|
||||||
for tool_path in TOOL_PATHS:
|
for tool_path in TOOL_PATHS:
|
||||||
var tool_source := _read_text(tool_path, failures)
|
var tool_source := _read_text(tool_path, failures)
|
||||||
@@ -171,6 +222,24 @@ func _verify_repository_wiring(failures: Array[String]) -> void:
|
|||||||
failures
|
failures
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var capture_source := _read_text("res://src/tools/capture_render_checkpoints.gd", failures)
|
||||||
|
_expect_true(
|
||||||
|
capture_source.contains('call("apply_environment_snapshot", environment_snapshot)'),
|
||||||
|
"capture applies environment through facade",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
for forbidden_capture_text in [
|
||||||
|
'get_node_or_null("WowSkyController")',
|
||||||
|
'sky.set("use_system_time"',
|
||||||
|
'sky.set("time_speed"',
|
||||||
|
'sky.set("fixed_time_hours"',
|
||||||
|
]:
|
||||||
|
_expect_true(
|
||||||
|
not capture_source.contains(forbidden_capture_text),
|
||||||
|
"capture omits direct sky mutation %s" % forbidden_capture_text,
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
var loader_source := _read_text(STREAMING_WORLD_LOADER_PATH, failures)
|
var loader_source := _read_text(STREAMING_WORLD_LOADER_PATH, failures)
|
||||||
for required_loader_text in [
|
for required_loader_text in [
|
||||||
"func sample_rendered_ground_height(godot_world_position: GodotWorldPosition)",
|
"func sample_rendered_ground_height(godot_world_position: GodotWorldPosition)",
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
- Date: 2026-07-16
|
- Date: 2026-07-16
|
||||||
- Revision/worktree: master merges `7ece2ab`, `7e35de7`, `80cb084`, `d6e5b53`;
|
- Revision/worktree: master merges `7ece2ab`, `7e35de7`, `80cb084`, `d6e5b53`, `c69abd6`;
|
||||||
packages `M03-RND-SCHEDULER-001`, `M03-RND-INTERNAL-ACCESS-GATE-001`,
|
packages `M03-RND-SCHEDULER-001`, `M03-RND-INTERNAL-ACCESS-GATE-001`,
|
||||||
`M03-RND-FACADE-GROUND-QUERY-001`
|
`M03-RND-FACADE-GROUND-QUERY-001`, `M03-RND-FACADE-ENVIRONMENT-001`
|
||||||
- Commands: dedicated scheduler/planner/facade/focus/coordinate/manifest/shutdown
|
- Commands: dedicated scheduler/planner/facade/focus/coordinate/manifest/shutdown
|
||||||
headless verifiers; Godot cold editor parse; coordination, documentation and diff gates.
|
headless verifiers; Godot cold editor parse; coordination, documentation and diff gates.
|
||||||
- Results: scheduler `cases=6 iterations=20000 elapsed_ms=10.216`; planner
|
- Results: scheduler `cases=6 iterations=20000 elapsed_ms=10.216`; planner
|
||||||
@@ -39,18 +39,25 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
derived `43` private symbols and found no forbidden access across `7` gameplay
|
derived `43` private symbols and found no forbidden access across `7` gameplay
|
||||||
and `9` EditorPlugin-package sources plus the terrain probe. Ground facade passed
|
and `9` EditorPlugin-package sources plus the terrain probe. Ground facade passed
|
||||||
`delegation=5 ground_contract=2`; cold `RenderedGroundSample contract=3`;
|
`delegation=5 ground_contract=2`; cold `RenderedGroundSample contract=3`;
|
||||||
M02 terrain-query regression remained green (13 pre-existing expired M00 claim warnings).
|
environment integration passed cold snapshot `contract=5` and facade
|
||||||
|
`delegation=6 environment_contract=7`; dry-run retained all `7/7` plans and
|
||||||
|
applied the dusk checkpoint at `time=19.00`. M02 terrain-query regression
|
||||||
|
remained green (13 pre-existing expired M00 claim warnings).
|
||||||
- Fidelity comparison: all 16 historical operation limits and drain sites were
|
- Fidelity comparison: all 16 historical operation limits and drain sites were
|
||||||
preserved; chunk removal/create retain their shared removal-first budget.
|
preserved; chunk removal/create retain their shared removal-first budget.
|
||||||
Defaults, quality presets, cache versions and visual rules are unchanged.
|
Defaults, quality presets, cache versions and visual rules are unchanged.
|
||||||
Rendered-ground diagnostics preserve the prior 5000-unit ray, 3x3 search and
|
Rendered-ground diagnostics preserve the prior 5000-unit ray, 3x3 search and
|
||||||
`2/5/10/20/40`-unit nearby fallback while hiding mutable renderer state.
|
`2/5/10/20/40`-unit nearby fallback while hiding mutable renderer state.
|
||||||
|
Environment snapshots preserve the prior fixed-clock assignments while DBC
|
||||||
|
profile/area/skybox selection, interpolation, fog, sun and shader rules remain unchanged.
|
||||||
No new build-12340 parity claim is made; asset-backed visual/p95/p99 comparison remains pending.
|
No new build-12340 parity claim is made; asset-backed visual/p95/p99 comparison remains pending.
|
||||||
- Changed files: scene-free scheduler and verifier; streamer budget composition/
|
- Changed files: scene-free scheduler and verifier; streamer budget composition/
|
||||||
permit delegation; scheduler module spec; world renderer/module registry/RENDER notes;
|
permit delegation; scheduler module spec; world renderer/module registry/RENDER notes;
|
||||||
renderer-owned ground sample, facade/loader ground query, migrated terrain probe,
|
renderer-owned ground sample, facade/loader ground query, migrated terrain probe,
|
||||||
expanded internal-access/coordinate verifiers; work-package claims and this evidence.
|
immutable environment snapshot, facade/sky adapter, migrated checkpoint time input,
|
||||||
|
expanded facade/internal-access/coordinate verifiers; work-package claims and this evidence.
|
||||||
- Remaining risks: queue storage length and worker concurrency/stale-result logic
|
- Remaining risks: queue storage length and worker concurrency/stale-result logic
|
||||||
remain streamer-owned; cancellation stops new permits but does not interrupt
|
remain streamer-owned; cancellation stops new permits but does not interrupt
|
||||||
in-flight work; M03 still needs environment/entity facade contracts, service extraction
|
in-flight work; the environment snapshot currently covers world time but not weather
|
||||||
|
or indoor state; M03 still needs entity facade commands, service extraction
|
||||||
and dependency/performance acceptance.
|
and dependency/performance acceptance.
|
||||||
|
|||||||
Reference in New Issue
Block a user