Multiplayer Support

Documentation Unreal Engine AI Multiplayer Replication

Server-authoritative AI with replicated pawn state for client UI, animation, and feedback.


AAIController and its SEC components exist only on the server. Clients read replicated pawn fields for telegraphs, combat roles, equipped weapons, and GAS montages.

When to Use This

  • Listen server or dedicated server co-op.
  • Client HUD for enemy health, current action, or combat role.
  • Server-side hit resolution with client-facing attack animation through GAS.
Single-player needs no extra replication wiring.

How It Works

Unreal runs AI on the server. The plugin splits work three ways:
  1. Server brain: score actions, move, assign roles, trace melee, run awareness, evaluate reactions.
  2. Bridge: on possession, SECCombatControllerComponent binds eval delegates to pawn notify methods and pushes role tags through SetCombatRole.
  3. Client mirror: bind UI and VFX to replicated pawn delegates and GAS montage playback.

Server Side

AI controller

These components live on AEnemyControllerBase (or your controller). AI controllers exist only on the server, so none of this replicates to clients:
ComponentRole
SECCombatControllerComponentAIConfig cache, role sync, delegate bridge to pawn
ActionEvaluationComponentAction selection and execution
ReactionEvaluationComponentReaction selection and execution
MovementEvaluatorComponentTactical movement input
USECAwarenessComponentPerception memory (header: server-only)
ThreatDetectionComponentPlayer stare detection
USECBrainComponentStateTree or native loop
World subsystems (server authority, not pawn components):
SubsystemRole
UAICombatRoleSubsystemRole assignment, combat targets
USECWorldTagSubsystemWorld tag writes

Pawn (authority only)

These stay on the possessed pawn but skip client simulation:
PieceNotes
USECMeleeTraceComponentStartTracing and tick gate on ShouldDetectHits, a virtual defaulting to HasAuthority. Override it in a subclass to sweep on another machine. See Melee Trace.
Melee damageISECDamageable::HandleIncomingDamage or legacy ApplyPointDamage on authority.
Clients see swings through GAS montage replication. OnMeleeHitResponse fires on authority.
A landed hit runs its damage config's Hit Effects on whichever machine ran the trace, and each effect decides its own reach from there. SEC Play Gameplay Cues goes through the ability system and travels to every client; SEC Apply Physics Impulse stays on the machine that ran it. Calling Run Hit Effects yourself from both the server and a predicting client fires the cues twice, so gate the call when you drive damage by hand.

Replicated on the Pawn

AEnemyCharacterBase also creates USECMeleeTraceComponent by default (authority-only; see above). These five replicate combat state to clients:
ComponentClients receive
SECActionSetComponentCurrent action id, executing flag, active action set, equipped weapon ref, cooldown events
SECReactionSetComponentCurrent reaction id, executing flag, active reaction set, cooldown events
SECCombatRoleComponentCombatRole gameplay tag
UAbilitySystemComponentGameplay cues, replicated tags and montages. Its replication mode is Minimal, which keeps effect bookkeeping on the server while the cues an effect fires still reach every client.
USECVitalsComponentPer-vital anchors (value, server timestamp, regen rate); every machine extrapolates the same smooth GetVitalValue from them. See Vitals.
Panels below show replicated runtime fields on each pawn component (what clients receive during play, not Blueprint defaults you set once in the editor):
Details
SEC Action Set Component (SECActionSetComponent)
SEC|Action State
Current Action Id
None
Action Executing
Active Action Set
DA_EnemyActions
SEC|ActionSet
Provided Action Set
DA_GreatswordActions
Replicated runtime state on the pawn. The server writes via NotifyActionStarted and role sync; clients read fields or bind OnActionExecutionStarted.
Details
SEC Combat Role Component (SECCombatRoleComponent)
AI|Combat Role
Combat Role
SEC.Role.Attacker
Replicated runtime state on the pawn. SECCombatControllerComponent calls SetCombatRole on the server; clients bind OnCombatRoleChanged.
SECCombatControllerComponent wires controller delegates into pawn notify methods: NotifyActionStarted, NotifyActionCompleted, NotifyCooldownStarted, NotifyCooldownExpired, and USECCombatRoleComponent::SetCombatRole. Reaction evaluation uses the same bridge with NotifyReactionStarted, NotifyReactionCompleted, and matching cooldown notifies on SECReactionSetComponent.
Clients bind OnActionExecutionStarted, OnCombatRoleChanged, OnReactionExecutionStarted, and related cooldown delegates on the pawn.
ASECWeaponBase sets bReplicates = true. Attachment replicates with the character mesh.
UGameplayAbilityBase (used by GA_SEC_Attack) defaults to server-initiated execution with replicated montages. Exact enum values sit in the GameplayAbilityBase defaults collapsible.

Setup

1. Register player targets

Call RegisterCombatTarget for each player pawn from GameMode or player spawn:
UAICombatRoleSubsystem* Roles = GetWorld()->GetSubsystem<UAICombatRoleSubsystem>();
Roles->RegisterCombatTarget(PlayerPawn, /*bAutoAssignUnassigned=*/ true);
Co-op needs one entry per player. See Targeting System.

2. Bind client UI to pawn components

USECActionSetComponent* Actions = Enemy->FindComponentByClass<USECActionSetComponent>();
Actions->OnActionExecutionStarted.AddDynamic(this, &UMyHUD::OnEnemyAction);
 
USECCombatRoleComponent* Role = Enemy->FindComponentByClass<USECCombatRoleComponent>();
Role->OnCombatRoleChanged.AddDynamic(this, &UMyHUD::OnRoleChanged);
Mirror the same pattern on SECReactionSetComponent with OnReactionExecutionStarted (and completion/cooldown delegates as needed).

3. Vitals for client health UI

AEnemyCharacterBase ships with USECVitalsComponent (Vitals), authored with one Health row. Bind OnVitalChanged for a client health bar, or poll GetVitalValue / GetVitalFraction directly: every machine extrapolates the same value from the last replicated anchor, so a client reads it with no extra RPC. See Vitals for the full mutator, reader, and event surface.
SECCombatControllerComponent binds HandleVitalDepleted to the pawn's OnVitalDepleted and calls HandleDeath when the depleted vital is the configured health tag, while bAutoHandleDeathOnHealthDepleted is true (default). Disable that flag if you drive death from GAS or a custom system.
ISECDamageable on EnemyCharacterBase is the preferred damage path for SEC actors.

4. Dedicated server animation

AEnemyCharacterBase::BeginPlay sets AlwaysTickPoseAndRefreshBones on dedicated servers. Without it, DS socket positions stay at bind pose and melee traces miss.

Integration

SystemMultiplayer note
Action SystemExecution on server; state on SECActionSetComponent.
Reaction SystemSame bridge via SECReactionSetComponent.
Combat RolesSubsystem on server; tag on SECCombatRoleComponent.
Melee TraceTraces and damage on authority only.
Awareness SystemServer-only perception.
Movement SystemMovementEvaluatorComponent on server; CharacterMovementComponent replicates pose.

AdvancedGameplayAbilityBase defaults
UGameplayAbilityBase constructor sets:
NetExecutionPolicy = ServerInitiated;
ReplicationPolicy = ReplicateYes;
InstancingPolicy = InstancedPerActor;
Server activates abilities; GAS replicates montage playback to clients.
AdvancedWorld tags on clients
USECWorldTagSubsystem mutators are server-only. For client reads, add USECWorldTagComponent to GameState. It mirrors subsystem tags through OnRep_Tags. USECWorldTagLibrary read helpers route by net mode.
AdvancedClient HUD example
void UEnemyHUD::BindToPawn(APawn* Enemy)
{
    if (auto* Vitals = Enemy->FindComponentByClass<USECVitalsComponent>())
    {
        Vitals->OnVitalChanged.AddDynamic(this, &UEnemyHUD::OnVitalChanged);
    }
    if (auto* Actions = Enemy->FindComponentByClass<USECActionSetComponent>())
    {
        Actions->OnActionExecutionStarted.AddDynamic(this, &UEnemyHUD::ShowTelegraph);
    }
    if (auto* Role = Enemy->FindComponentByClass<USECCombatRoleComponent>())
    {
        Role->OnCombatRoleChanged.AddDynamic(this, &UEnemyHUD::SetRoleIcon);
    }
}
 
// OnVitalChanged fires for every vital on the pawn; filter by tag for the health bar.
void UEnemyHUD::OnVitalChanged(FGameplayTag VitalTag, float NewValue, float OldValue, float MaxValue)
{
    if (VitalTag != USoulslikeEnemyCombatSettings::GetHealthVitalTag())
    {
        return;
    }
    RefreshBar(NewValue / MaxValue);
    if (NewValue < OldValue)
    {
        FlashDamage();
    }
}