Multiplayer
Table of Contents
- Overview
UPulseQuestManager– Owner-Only ReplicationAPulseSharedQuestHost– Shared Quest Replication- Objective Net Modes
- Authority Helpers
- Replication Events on Clients
- Recommended Pattern for Multiplayer Quests
Overview
PulseQuest is built for networked games. Quest logic runs authority-only on the server; runtime data is replicated to clients.
Two replication modes exist:
| Mode | Class | Audience |
|---|---|---|
| Owner-only | UPulseQuestManager | Only the owning player’s client |
| Shared / broadcast | APulseSharedQuestHost | All relevant clients in net range |
UPulseQuestManager – Owner-Only Replication
Replicated properties
| Property | Condition |
|---|---|
QuestManagerDataContainer | COND_OwnerOnly + Push-Model |
ActiveQuestsReplication | COND_OwnerOnly + Push-Model |
ActiveObjectivesReplication | COND_OwnerOnly + Push-Model |
FastArray containers
FPulseActiveQuestReplicationContainer
└── TArray<FPulseActiveQuestReplicationEntry>
├── QuestTag : FGameplayTag
└── QuestData : UPulseQuestData* (replicated subobject)
FPulseActiveObjectiveReplicationContainer
└── TArray<FPulseActiveObjectiveReplicationEntry>
├── Handle : FPulseObjectiveHandle
└── Objective : UPulseQuestObjective* (replicated subobject)
FFastArraySerializer provides delta-compressed, order-safe replication of these arrays.
Subobject replication
The following subobjects are registered with COND_OwnerOnly:
UPulseQuestManagerData- All active
UPulseQuestDatainstances - All active
UPulseQuestObjectiveinstances (whenNetMode != ServerOnly)
Stop RPCs
Stopping is notified to the client via a reliable RPC because subobjects can be destroyed before the client processes the removal:
UFUNCTION(Client, Reliable)
void ClientQuestStopped(FGameplayTag QuestTag, EPulseQuestFinishResult Result);
UFUNCTION(Client, Reliable)
void ClientObjectiveStopped(FPulseObjectiveHandle Handle, EPulseObjectiveCompletionStatus Status);
APulseSharedQuestHost – Shared Quest Replication
Shared quests are owned by a world-placed or dynamically spawned APulseSharedQuestHost. Replication is to all clients that are net-relevant to the actor.
Stop RPCs
Instead of Client, Reliable, the shared host uses multicast:
UFUNCTION(NetMulticast, Reliable)
void MulticastQuestStopped(FGameplayTag QuestTag, EPulseQuestFinishResult Result);
UFUNCTION(NetMulticast, Reliable)
void MulticastObjectiveStopped(FPulseObjectiveHandle Handle, EPulseObjectiveCompletionStatus Status);
Player management (Authority only)
bool AddPlayer(TScriptInterface<IPulseSharedQuestPlayerInterface> NewPlayer);
bool RemovePlayer(TScriptInterface<IPulseSharedQuestPlayerInterface> Player);
bool HasPlayer(TScriptInterface<IPulseSharedQuestPlayerInterface> Player) const;
bool HasPlayerState(const APlayerState* PlayerState) const;
void GetAllPlayers(TArray<FPulseSharedQuestPlayer>& Out) const;
void GetAllPlayerStates(TArray<APlayerState*>& Out) const;
Players are tracked via:
FPulseSharedQuestPlayersContainer (FFastArraySerializer)
└── TArray<FPulseSharedQuestPlayer>
├── PlayerState : APlayerState*
└── ExtraData : any custom payload
IPulseSharedQuestPlayerInterface – implement this on APlayerController or APlayerState to participate in shared quests.
Objective Net Modes
EPulseObjectiveNetMode | Where objective exists | Replication condition |
|---|---|---|
ServerOnly | Server only | Not replicated |
ServerAndOwner | Server + owner client | COND_OwnerOnly |
ServerAndAll | Server + all clients | No condition |
Set via FPulseQuestObjectiveAddParams::NetMode (passed through FPulseStateTreeTask_StartObjective).
Authority Helpers
// Namespace Pulse::Quest
bool IsQuestManagerAuthority(const TScriptInterface<IPulseQuestManagerInterface>& Manager);
bool IsQuestManagerLocallyControlled(const TScriptInterface<IPulseQuestManagerInterface>& Manager);
Blueprint wrappers via UPulseQuestUtils:
IsAuthorityQuestManager(Manager) → bool
IsLocalQuestManager(Manager) → bool
Use these guards before calling write operations:
if IsAuthorityQuestManager → StartQuest / StopQuest
if IsLocalQuestManager → UI bind, input handling
Replication Events on Clients
Clients receive changes through FastArray callbacks. These automatically enqueue the correct FPulseQuestManagerEvent_UpdateReplicated* events, which in turn trigger Blueprint delegates and MVVM updates:
OnRep_ActiveQuestsReplication
→ FPulseQuestManagerEvent_UpdateReplicatedQuests::Process()
→ QuestData->OnClientDataUpdated()
→ OnQuestStarted / OnQuestStopped broadcast
OnRep_ActiveObjectivesReplication
→ FPulseQuestManagerEvent_UpdateReplicatedObjectives::Process()
→ OnObjectiveStarted / OnObjectiveStopped broadcast
Recommended Pattern for Multiplayer Quests
Server (PlayerController / PlayerState)
└── UPulseQuestManager ← regular player-specific quests
Dedicated Actor (e.g. ARaidZoneActor)
└── APulseSharedQuestHost ← shared quest (boss fight, co-op objective)
├── Player A (added via AddPlayer)
├── Player B
└── UStateTree quest running on server
Objective NetMode:
- Use
ServerAndOwnerfor personal progress bars shown only to that player. - Use
ServerAndAllfor shared indicators visible to everyone in the group.