Host project migrated from PROSERVE_UE_5_5 to PROSERVE_UE_5_7 in April 2026. CLAUDE.md, memory index, project notes, and build reference now point at the new path / target / engine version. The 5.5 host is flagged deprecated to avoid accidental builds against it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
139 lines
8.5 KiB
Markdown
139 lines
8.5 KiB
Markdown
# PS_ProserveEditor - Project Guidelines
|
||
|
||
## Project Overview
|
||
Unreal Engine 5.7 project with runtime editor plugin (PS_Editor). The plugin enables runtime scene editing in packaged builds: object placement, property editing, scenario definition, splines, lighting, and JSON scene save/load. The plugin was started on UE 5.5; the host project migrated to UE 5.7 in April 2026.
|
||
|
||
## Architecture
|
||
- **Plugin**: PS_Editor (runtime plugin, must work in packaged builds)
|
||
- **UI**: UMG driven by C++ (Slate)
|
||
- **Gizmos**: Custom actor with BasicShapes + unlit material (InteractiveToolsFramework is editor-only)
|
||
- **Serialization**: JSON via Json/JsonUtilities modules (FJsonObjectConverter)
|
||
- **Input**: Enhanced Input system (AZERTY: arrows + A/Q movement, E/R/T modes)
|
||
|
||
## Editor vs. Gameplay execution boundary — CRITICAL
|
||
|
||
The plugin runs in two distinct modes and it's essential never to leak editor-only code
|
||
into the gameplay runtime loaded via `UPS_Editor_SceneLoader::LoadScene` (standalone).
|
||
|
||
### Editor runtime mode
|
||
Activated when the `APS_Editor_PlayerController` + `APS_Editor_HUD` + `UPS_Editor_MainWidget*`
|
||
stack is created (typical during authoring — the user is in the runtime editor).
|
||
- `UPS_Editor_SceneLoader::bIsEditorModeActive == true`
|
||
- The HUD widget drives property editing, catalogue spawn, save/load, simulate, timeline
|
||
- Interface methods called **only** by the widget (never in gameplay):
|
||
- `GetCustomOptionsForProperty` / `GetCustomOptionsWithValuesForProperty` / `GetCurrentDisplayForProperty`
|
||
- `OnEditorModeChanged`, `OnEditorTick`
|
||
- `OnEditorNeedsReinit`, `OnEditorNeedsRespawn`
|
||
|
||
### Gameplay mode
|
||
The project (PROSERVE) loads a scenario via `UPS_Editor_SceneLoader::LoadScene` from a GameMode
|
||
BeginPlay, without ever instantiating the editor PlayerController / HUD / widget.
|
||
- `UPS_Editor_SceneLoader::bIsEditorModeActive == false`
|
||
- Actors spawn, their properties are applied from JSON, `OnPropertiesLoaded` fires once
|
||
- The timeline subsystem auto-plays if the scenario has tracks, driven **server-side only**
|
||
|
||
### Interface methods that fire in BOTH modes
|
||
- `OnPropertiesLoaded` — after every scene load, editor or gameplay (applied once post-spawn)
|
||
- `OnEditorPropertyChanged` — fires both in editor (on UI edit via `ApplyPropertyFromUI`) AND
|
||
in gameplay (on server when the timeline applies a new keyframe value via
|
||
`TimelineSubsystem::ApplyTrackAtTime`). Despite the name, the "Editor" refers to the
|
||
PS_Editor plugin — the event is the canonical hook for "a property changed at runtime".
|
||
In gameplay only the **server** invokes it; client-side reaction must go through
|
||
`ReplicatedUsing=OnRep_X` RepNotify on the replicated property.
|
||
|
||
### Rule when adding new features
|
||
- Anything tied to property panel UI (new interface method, new widget logic, dropdown
|
||
helpers, etc.) lives in the widget only — no extra work to scope it out of gameplay
|
||
- Anything added to the `SceneLoader::LoadScene` path runs in gameplay and must handle
|
||
`NM_Client`, replication, and server-authority correctly (the timeline subsystem is the
|
||
reference pattern)
|
||
- Expensive BP implementations of editor-UI interface methods (DataTable lookups, class
|
||
resolution, etc.) can stay expensive — they never run outside the authoring session
|
||
|
||
### How to guard code that could run in either mode
|
||
```cpp
|
||
if (UPS_Editor_SceneLoader::IsInEditorMode())
|
||
{
|
||
// editor-only behaviour
|
||
}
|
||
```
|
||
|
||
### Known PIE-only AI quirk — use Standalone for AI testing
|
||
|
||
When the editor's "Play" button calls `UGameplayStatics::OpenLevel(BaseLevel)` from a PIE
|
||
session, the BaseLevel's `RecastNavMesh` actor ends up pending-kill at registration time
|
||
(UE logs `RegistrationFailed_DataPendingKill` ×2 and silently spawns an empty
|
||
`AbstractNavData-Default` as fallback). All `FindPathAsync` / `ProjectPointToNavigation`
|
||
queries return empty after that — pathfinding looks broken with no error.
|
||
|
||
This is **PIE-specific**:
|
||
- Standalone Game (Window → Standalone) — works correctly
|
||
- Packaged builds — work correctly
|
||
- PIE direct on BaseLevel (no editor map / OpenLevel involved) — works correctly
|
||
- PIE on editor map → click Play (OpenLevel transition inside PIE) — broken
|
||
|
||
Multiple workaround attempts have all failed (cascading re-registrations through
|
||
`RegisterNavData` / `OnInitializeActors` / `AddNavigationSystemToWorld` / `Build` /
|
||
`SpawnMissingNavigationData`; transition map; duplicated sublevel suffixed `_default`).
|
||
The cause is internal to PIE's OpenLevel handling and cannot be fixed from the plugin
|
||
side without engine modifications.
|
||
|
||
**Workflow:** for AI iteration, run **Standalone Game** instead of PIE. It exercises the
|
||
exact same code path as packaged builds.
|
||
or gate on `bIsCurrentlyLoading` to skip BP init during JSON restore.
|
||
|
||
## UI Modes — IMPORTANT for widget changes
|
||
|
||
The runtime editor ships with **multiple widget implementations** that must all stay in sync:
|
||
- `PS_Editor_MainWidget.cpp/.h` — modern UMG-based widget (primary target long-term)
|
||
- `PS_Editor_MainWidget_Legacy.cpp/.h` — Slate-only legacy widget (**currently active** as of April 2026)
|
||
- `PS_Editor_MainWidget_UMG.cpp/.h` — UMG variant
|
||
|
||
The user can switch between them via a `UIMode` setting. At any given time only one is running, but the project keeps all three compiled.
|
||
|
||
**Rule:** when changing any behavior inside a widget (new interface calls, new property row logic, new UI controls, bug fixes in RebuildDynamicProperties / ApplyPropertyFromUI / RefreshDynamicPropertyValues, etc.), **apply the change to ALL widget implementations at the same time**. Otherwise the user's current UIMode gets the fix and the others don't — the "it's like you didn't change anything" bug.
|
||
|
||
Symptom that the wrong widget was edited: logs you added don't appear in the Output Log despite the build being clean and the DLL being correctly loaded. Always grep for the function name across all `PS_Editor_MainWidget*.cpp` files before assuming a single edit is sufficient.
|
||
|
||
## Naming Conventions
|
||
- All plugin files and classes use the `PS_Editor_` prefix (e.g., `PS_Editor_GameMode`, `PS_Editor_Pawn`)
|
||
- Code and comments in English
|
||
- Standard UE5 prefixes: `A` for Actors, `U` for UObjects, `F` for structs, `E` for enums, `I` for interfaces
|
||
|
||
## Code Quality
|
||
- Prefer correct, complete implementations over minimal ones.
|
||
- Use appropriate data structures and algorithms — don't brute-force what has a known better solution.
|
||
- When fixing a bug, fix the root cause, not the symptom.
|
||
- If something requires error handling or validation to work reliably, include it without asking.
|
||
|
||
## Workflow — CRITICAL
|
||
- **Do not make behavior / logic changes without first explaining the plan and waiting for the
|
||
user's go-ahead.** Explain what the change will be, why it fixes the issue, and what side
|
||
effects it could have. Only after the user approves (or asks to proceed), apply the edit.
|
||
- Diagnostic-only additions (temporary `UE_LOG`, read-only inspections) are fine without
|
||
approval because they can't regress the project.
|
||
- Refactors, algorithm changes, comportement changes, new defaults, removed features — all
|
||
require approval first, even when they seem obvious.
|
||
- When revealing new data (e.g. logs) suggests a fix, STATE the proposed fix in plain words
|
||
and wait. Don't pre-emptively write + apply the code change in the same turn.
|
||
|
||
## Build
|
||
- Engine: Unreal Engine 5.7 (host project migrated from 5.5 in April 2026)
|
||
- Plugin location: `Unreal/Plugins/PS_Editor/` (consumed via NTFS junction from the PROSERVE host project's `Plugins/` directory)
|
||
- Host project: `E:\ASTERION\SVN\DEV\PROSERVE_UE_5_7\PROSERVE_UE_5_7.uproject` — always build this, not the plugin in isolation
|
||
- Game module: `PROSERVE_UE_5_7` (in the host project's `Source/`)
|
||
- CLI build command (Editor, Development):
|
||
```
|
||
"C:/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles/Build.bat" PROSERVE_UE_5_7Editor Win64 Development -Project="E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/PROSERVE_UE_5_7.uproject" -WaitMutex -FromMsBuild
|
||
```
|
||
UE install path can be found via registry: `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.7` > `InstalledDirectory`
|
||
- The 5.5 host (`PROSERVE_UE_5_5`) is deprecated — do not build against it unless explicitly asked.
|
||
|
||
## Project Memory
|
||
Shared project memory is stored in `.claude/memory/` (versioned in the repository). When starting a new session on any machine, read these files to restore context:
|
||
|
||
- `MEMORY.md` — Index of all memory files with one-line summaries
|
||
- `project_ps_editor.md` — Project scope, technical decisions, current phase
|
||
- `feedback_naming.md` — Naming conventions and user preferences
|
||
- `reference_build.md` — Full build command and UE install path details
|