Multiplayer
Multiplayer is a first-class requirement. The runtime is built for a dedicated server + multiple clients from the ground up.
Replication
| State | Mechanism |
|---|---|
Per-node NodeState / CurrentLevel | FastArray (FCrimsonNodeStateArray) - delta-only, MarkItemDirty at every mutation |
Per-tree bIsActive | ReplicatedUsing = OnRep_IsActive -> broadcasts OnSkillTreeActivationChanged |
| Resource pool allocations | FastArray (FCrimsonResourceAllocationArray) - server-authoritative budgets |
| Tree instances on the manager | AddReplicatedSubObject per instance |
Authority
The player-driven node actions (ActivateNode, DeactivateNode, IncrementLevel, DecrementLevel) early-return unless GetOwnerRole() == ROLE_Authority, and clients reach them only through validated Server_ RPCs. The lifecycle methods the manager calls on the authoritative path (ShutdownNode, RestoreNodeToState, SuspendNodeEffects / ResumeNodeEffects) run server-side as part of that flow - RestoreNodeToState applies its GAS events only under GetOwner()->HasAuthority().
HasAuthority(), not IsOwnerActorAuthoritative()
IsOwnerActorAuthoritative() is a GAS-only helper on UGameplayAbility. A generic UActorComponent/UObject uses GetOwner()->HasAuthority() or GetOwnerRole() == ROLE_Authority.RPC entry points
| RPC | Purpose |
|---|---|
Server_RequestSkillNodeAction | Single-node action; _Validate rejects malformed Guids |
Server_CommitNodeChanges | Atomic batch - all-or-nothing |
Server_RespecAndCommit | Atomic respec - clear then apply a new allocation |
Client_DisplayNodeFailureMessage | Unreliable client RPC: shows a failure message when a server-side action is rejected |