ue-gameplay-framework
Use this skill when working with Unreal Engine's gameplay framework classes: GameMode, GameState, PlayerController, PlayerState, Pawn, Character, or GameInstance. Also use when the user mentions 'gameplay framework', 'game rules', 'player management', 'match flow', or 'player spawning'. See references/framework-class-map.md for the full authority/presence matrix. For networking/replication, see ue-networking-replication. For input setup, see ue-input-system.
What this skill does
# UE Gameplay Framework
You are an expert in Unreal Engine's gameplay framework architecture.
## Context Check
Read `.agents/ue-project-context.md` before proceeding. The game type (single player, co-op, competitive multiplayer, dedicated vs listen server) determines which classes to subclass and which replication patterns apply. Resolve: single-player or multiplayer? Dedicated or listen server? What are you implementing?
---
## Class Responsibility Map
Each class exists on specific machines for specific reasons. Getting this wrong is the primary source of multiplayer bugs.
### AGameModeBase / AGameMode — Server Only
**Exists on:** Server and standalone only. Never instantiated on clients.
**Why server-only:** GameMode is the authoritative referee. It decides who joins, when the match starts, where players spawn, and what the win conditions are. Client execution would allow cheating via local state manipulation.
**AGameMode adds** the full match-state machine (`EnteringMap` → `WaitingToStart` → `InProgress` → `WaitingPostMatch` → `LeavingMap`; `Aborted` on failure) with `ReadyToStartMatch` and `ReadyToEndMatch` hooks. Use `AGameModeBase` for lobby/simple games, `AGameMode` for match flow.
**Key API from source (GameModeBase.h):**
```cpp
// Class assignments — set in constructor
TSubclassOf<APawn> DefaultPawnClass;
TSubclassOf<AGameStateBase> GameStateClass;
TSubclassOf<APlayerController> PlayerControllerClass;
TSubclassOf<APlayerState> PlayerStateClass;
TSubclassOf<AHUD> HUDClass;
uint32 bUseSeamlessTravel : 1;
// Server startup and player join lifecycle (server only)
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage);
virtual void PreLogin(const FString& Options, const FString& Address,
const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage);
virtual APlayerController* Login(UPlayer* NewPlayer, ENetRole InRemoteRole,
const FString& Portal, const FString& Options,
const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage);
virtual void PostLogin(APlayerController* NewPlayer); // first safe point for RPCs (DispatchPostLogin deprecated 5.6 — override PostLogin directly)
virtual void Logout(AController* Exiting);
virtual void HandleStartingNewPlayer(APlayerController* NewPlayer);
// Spawn pipeline
virtual AActor* FindPlayerStart(AController* Player, const FString& IncomingName = TEXT(""));
virtual void RestartPlayer(AController* NewPlayer);
virtual APawn* SpawnDefaultPawnFor(AController* NewPlayer, AActor* StartSpot);
// Travel
virtual void ProcessServerTravel(const FString& URL, bool bAbsolute = false);
virtual void GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList);
```
---
### AGameStateBase / AGameState — Server + All Clients
**Exists on:** Everywhere. Fully replicated.
**Why everywhere:** Clients cannot read GameMode (it does not exist on them). Any global data clients need — scores, match timer, phase — belongs in GameState. `PlayerArray` exposes all connected `APlayerState` instances to every machine.
**Key API from source (GameStateBase.h):**
```cpp
// All PlayerStates, always replicated
UPROPERTY(Transient, BlueprintReadOnly)
TArray<TObjectPtr<APlayerState>> PlayerArray;
// The GameMode class (not instance) replicated to clients
UPROPERTY(Transient, BlueprintReadOnly, ReplicatedUsing=OnRep_GameModeClass)
TSubclassOf<AGameModeBase> GameModeClass;
// Server-authoritative clock, automatically synced
virtual double GetServerWorldTimeSeconds() const;
virtual bool HasBegunPlay() const;
virtual bool HasMatchStarted() const;
virtual bool HasMatchEnded() const;
```
**Custom replicated match data:**
```cpp
UCLASS()
class AMyGameState : public AGameStateBase
{
GENERATED_BODY()
public:
UPROPERTY(Replicated, BlueprintReadOnly) int32 TeamAScore;
UPROPERTY(Replicated, BlueprintReadOnly) int32 TeamBScore;
UPROPERTY(ReplicatedUsing=OnRep_MatchTimer) float MatchTimeRemaining;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};
void AMyGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyGameState, TeamAScore);
DOREPLIFETIME(AMyGameState, TeamBScore);
DOREPLIFETIME(AMyGameState, MatchTimeRemaining);
}
```
---
### APlayerController — Server (all) + Owning Client (own only)
**Exists on:** Server holds one per connected player. Each client holds only its own. Remote clients do not see other players' PlayerControllers.
**Why this split:** The PlayerController bridges one human to the server. Both ends run it for client-side prediction and server validation. A client has no reason to know another player's input state.
**Key API from source (PlayerController.h):**
```cpp
TObjectPtr<APlayerCameraManager> PlayerCameraManager; // camera, local only
TObjectPtr<APawn> AcknowledgedPawn; // server-confirmed possession
TObjectPtr<AHUD> MyHUD; // local only
uint32 bShowMouseCursor : 1;
uint32 bEnableStreamingSource : 1; // drives World Partition loading for this viewport
void SetInputMode(const FInputModeDataBase& InData); // FInputModeGameOnly, UIOnly, GameAndUI
virtual void PlayerTick(float DeltaTime); // only ticked locally
virtual void SetupInputComponent() override;
```
**`SetupInputComponent` on PlayerController** is for non-pawn input: spectator actions, UI shortcuts, or global keybinds that persist across possession changes. For pawn-specific input, override `APawn::SetupPlayerInputComponent()` instead — see `ue-input-system`.
**Enhanced Input setup:**
```cpp
void AMyPlayerController::BeginPlay()
{
Super::BeginPlay();
if (IsLocalController())
{
if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
Sub->AddMappingContext(DefaultMappingContext, 0);
SetInputMode(FInputModeGameOnly());
}
}
```
**RPC patterns:**
```cpp
UFUNCTION(Server, Reliable, WithValidation) void ServerRequestRespawn(); // client → server
UFUNCTION(Client, Reliable) void ClientNotifyMatchStart(); // server → client
```
**Possess/UnPossess (server-authority required):**
```cpp
// Take control of a new pawn — must run on server
PlayerController->Possess(NewPawn);
// Release the currently possessed pawn
PlayerController->UnPossess();
```
**Listen-server dual-role:** On a listen server, the host's PlayerController is both `ROLE_Authority` and locally controlled. Guard dual-role logic with `IsLocalController()` checks. This is a common source of bugs where code assumes authority implies non-local (i.e., code written for dedicated servers runs incorrectly on a listen server host).
**ClientTravel — connect this client to a different server:**
```cpp
PlayerController->ClientTravel(TEXT("127.0.0.1:7777"), TRAVEL_Absolute);
```
**ServerTravel — move all players to a new map (called from GameMode, server only):**
```cpp
GetWorld()->ServerTravel(TEXT("/Game/Maps/NewMap?listen"));
```
---
### AController — Shared Base
`AController` is the base class for both `APlayerController` and `AAIController`. It owns the pawn possession interface (`Possess`, `UnPossess`, `GetPawn`) and the rotation used to drive pawn orientation (`ControlRotation`). Subclass `APlayerController` for human players and `AAIController` for AI.
---
### APlayerState — Server + All Clients (Always Relevant)
**Exists on:** Server and all clients. Marked always-relevant so it replicates to everyone regardless of distance.
**Why always relevant:** Scoreboards, team displays, and player lists need to show data for every player, not just nearby ones. PlayerState survives pawn death — when a pawn is destroyed and respawned, the PlayerController keeps its PlayerState, preserving accumulated stats.
```cpp
UCRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.