Architecture
Table of Contents
Module Structure
PulseQuest ships two UE modules:
| Module | Type | Purpose |
|---|---|---|
PulseQuest | Runtime | All quest runtime logic: manager, objectives, director, save/load, MVVM |
PulseQuestEditor | Editor | StateTree editor extensions (schema compilation, Director editor extension) |
Runtime dependencies (public)
Core, StateTreeModule, GameplayStateTreeModule, GameplayTags, NetCore, GameplayTasks, ModelViewViewModel, EnhancedInput
Editor dependencies (public)
Core, StateTreeModule, StateTreeEditorModule, UnrealEd, PulseQuest, PropertyBindingUtils
Object Model
IPulseQuestManagerInterface
├── UPulseQuestManager (Component, owner-only replication)
└── APulseSharedQuestHost (Actor, shared multi-player)
UPulseQuest (Data Asset – static definition)
└── FStateTreeReference → UStateTree (schema: UPulseQuestStateTreeSchema)
UPulseQuestSet (Data Asset – quest pool)
├── TSet<UPulseQuest*>
└── TSet<UPulseQuestSet*> // nestable
UPulseQuestInstance (server-only runtime)
├── FStateTreeInstanceData // StateTree execution
└── UPulseQuestData // replicated runtime data
└── TArray<UPulseQuestObjective*>
UPulseQuestObjective (server + optional client)
└── UMVVMPulseObjective // MVVM ViewModel
UPulseQuestManagerData (replicated manager-level data)
└── UMVVMPulseQuestManagerData
UPulseQuestDirector (server-only, optional)
└── UPulseQuestStateTreeDirector
Lifecycle
Manager initialization
BeginPlay()callsInitializeQuestManager().- On Authority:
- Creates
UPulseQuestManagerData(class fromManagerParams). - Creates
UPulseQuestDirectorif configured; callsInitializeDirector.
- Creates
- On all machines:
- Creates
UMVVMPulseQuestManagerViewModel if class is set.
- Creates
Event Queue
All mutations (add/remove quest, add/remove objective, client replication updates) are processed through a first-in-first-out event queue that is flushed every tick.
Key event types:
| Event | Direction | Purpose |
|---|---|---|
FPulseQuestManagerEvent_AddQuest | Server | Start a new quest |
FPulseQuestManagerEvent_RemoveQuest | Server+Client | Remove quest from active map |
FPulseQuestManagerEvent_AddObjective | Server | Start a new objective |
FPulseQuestManagerEvent_RemoveObjective | Server+Client | Remove objective |
FPulseQuestManagerEvent_UpdateReplicatedQuests | Client | Sync replicated quests |
FPulseQuestManagerEvent_UpdateReplicatedObjectives | Client | Sync replicated objectives |
FPulseQuestManagerEvent_BreakQueue | Any | Halt queue processing this tick |
Breaking the queue
BreakEventQueue(Event, bKeepEvents) pauses processing until the next tick. Use this around resets or large state changes to prevent stale references.
Replication Strategy
UPulseQuestManager (Component)
| Property | Condition |
|---|---|
QuestManagerDataContainer | COND_OwnerOnly (push-model) |
ActiveQuestsReplication | COND_OwnerOnly (push-model) |
ActiveObjectivesReplication | COND_OwnerOnly (push-model) |
Subobjects (UPulseQuestData, UPulseQuestObjective, UPulseQuestManagerData) are registered to the replicated subobject list with COND_OwnerOnly.
APulseSharedQuestHost (Actor)
All quests/objectives are replicated to all clients that are net-relevant to the actor. Stop events are multicast (UFUNCTION(NetMulticast, Reliable)). Players are tracked via FPulseSharedQuestPlayersContainer (FastArray, replicated).
FastArray containers
Both FPulseActiveQuestReplicationContainer and FPulseActiveObjectiveReplicationContainer use FFastArraySerializer for delta-compressed replication.
Diagnostics
Stat QUEST
Stats groups registered in the runtime module:
STAT_PulseQuestManager_TickSTAT_PulseQuestManager_EventQueueSTAT_PulseQuestObjective_Tick