Multiplayer

Table of Contents

  1. Overview
  2. UPulseQuestManager – Owner-Only Replication
    1. Replicated properties
    2. FastArray containers
    3. Subobject replication
    4. Stop RPCs
  3. APulseSharedQuestHost – Shared Quest Replication
    1. Stop RPCs
    2. Player management (Authority only)
  4. Objective Net Modes
  5. Authority Helpers
  6. Replication Events on Clients
  7. 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 UPulseQuestData instances
  • All active UPulseQuestObjective instances (when NetMode != 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

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 ServerAndOwner for personal progress bars shown only to that player.
  • Use ServerAndAll for shared indicators visible to everyone in the group.

Back to top

Copyright © 2026 LuQ Studios. Distributed under the MIT License.

This site uses Just the Docs, a documentation theme for Jekyll.