Networking Model
A comprehensive guide to Motion's multiplayer networking architecture, covering replication, prediction, and state synchronization for networked character movement.
Prerequisites
This guide assumes basic familiarity with Unreal Engine's gameplay framework. If you're new to GAS, read the GAS Primer first.
Introduction
Motion is built from the ground up with multiplayer support, leveraging Unreal Engine's robust networking system and the Gameplay Ability System (GAS) for state management. Every movement component in Motion is network-aware, providing smooth, responsive character control in both single-player and multiplayer environments.
Key Networking Features
- Server-Authoritative Movement: All movement decisions are validated by the server
- Client Prediction: Responsive controls with predictive movement
- GAS Integration: Leverages GAS's built-in replication for attributes and effects
- Minimal Network Overhead: Only essential data is replicated
- Universal Compatibility: Works with any networked ACharacter
Core Networking Concepts
Server-Client Architecture
Unreal Engine uses a server-client model where the server has ultimate authority over the game state:
Key Principles:
- Server validates all gameplay decisions
- Clients predict locally for responsiveness
- Server corrects client predictions when needed
- "Never trust the client" - validate everything server-side
Network Roles
Every actor in a networked game has a role that determines its behavior:
| Role | Description | Motion Example |
|---|---|---|
| Authority | Server version of the actor | Server's character instance |
| Autonomous Proxy | Locally controlled client actor | Your character on your client |
| Simulated Proxy | Remote client actor | Other players on your client |
| None | Not networked | UI elements, local effects |
Connection Flow
Understanding how players connect and initialize is crucial for Motion's networking:
Very Important
Returning false in a validation function like ServerRequestSprint_Validate will disconnect the client! In PIE this will look like your character is just resetting / respawning, but in a Release build this will kick them out!
Motion's Network Architecture
Component-Based Design
Motion uses a modular component architecture that integrates seamlessly with Unreal's networking:
PlayerState Ownership Pattern
Motion uses MotionPlayerState to own the AbilitySystemComponent, ensuring persistence across respawns:
// MotionPlayerState.cpp
AMotionPlayerState::AMotionPlayerState(const FObjectInitializer& ObjectInitializer)
{
// Enable replication
bReplicates = true;
// Create and configure ASC
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
AbilitySystemComponent->SetIsReplicated(true);
// Mixed mode: Server authority with client prediction
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
}Why PlayerState?
- Persists across character respawns
- Maintains attributes between deaths
- Simplifies ownership for networking
- Standard pattern for GAS games
GAS Replication Modes
Motion uses Mixed Replication Mode for optimal performance:
| Mode | GameplayEffects | GameplayCues | Use Case |
|---|---|---|---|
| Full | Replicate to all | Replicate to all | Single player |
| Mixed | Replicate to owner only | Replicate to all | Motion's choice ✓ |
| Minimal | Don't replicate | Replicate to all | AI/NPCs |
Replication in Motion
What Gets Replicated
Motion carefully manages what data is replicated to minimize bandwidth:
Attribute Replication
Motion's attributes replicate automatically through GAS:
// MotionAttributeSet.cpp
void UMotionAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Replicate movement attributes
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, WalkSpeed, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, SprintSpeed, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, JumpVelocity, COND_None, REPNOTIFY_Always);
// Replicate stamina attributes
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, Stamina, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, MaxStamina, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, StaminaRegenRate, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UMotionAttributeSet, StaminaDrainRate, COND_None, REPNOTIFY_Always);
}Tag-Based State Synchronization
Motion uses GameplayTags for state synchronization across the network:
Client-Server Communication
Input Flow Diagram
Motion implements client prediction for responsive movement:
Benefits:
- Zero perceived latency for local player
- Smooth movement even with high ping
- Server can still correct if needed
Movement State Networking
Crouch Network Handling
Crouch uses a dual-layer system combining client prediction with server authority:
void UMotionCrouchingComponent::HandleCrouchInputStateChange(bool bPressed)
{
if (!CachedCharacter->IsLocallyControlled())
return;
if (bPressed)
{
if (CachedCharacter->HasAuthority())
{
// Server: Direct authoritative state change
SetWantsToCrouch(true);
}
else
{
// Client: Apply loose tag for immediate feedback
UMotionAbilitySystemHelper::AddGameplayTagToActor(
CachedCharacter, MotionGameplayTags::Motion_State_WantsToCrouch);
// Send RPC to server
ServerRequestCrouch(true);
}
}
}
void UMotionCrouchingComponent::UpdateCrouchingState(float DeltaTime)
{
// Local client triggers built-in crouch system
if (CachedCharacter->IsLocallyControlled())
{
if (bWantsToCrouch && !bIsCurrentlyCrouching)
{
// Character->Crouch() triggers automatic server RPC
CachedCharacter->Crouch();
}
else if (!bWantsToCrouch && bIsCurrentlyCrouching)
{
// Character->UnCrouch() triggers automatic server RPC
CachedCharacter->UnCrouch();
}
}
}Key Features:
- Client prediction via loose tags for zero-latency feedback
- Server validation with rate limiting and collision checks
- Built-in CharacterMovementComponent handles capsule changes
- Camera height interpolation managed separately by MotionCameraComponent
Jump Prediction
Jump combines client prediction with custom enhancements for advanced features:
void UMotionJumpComponent::HandleJumpInputStateChange(bool bPressed)
{
if (!CachedCharacter->IsLocallyControlled())
return;
if (bPressed)
{
if (CachedCharacter->HasAuthority())
{
// Server: Direct authoritative state change
SetWantsToJump(true);
}
else
{
// Client: Apply loose tag for immediate feedback
UMotionAbilitySystemHelper::AddGameplayTagToActor(
CachedCharacter, MotionGameplayTags::Motion_State_WantsToJump);
// Send RPC to server
ServerRequestJump(true);
}
}
}
bool UMotionJumpComponent::PerformJump(bool bIsAdditionalJump)
{
// Custom jump counter avoids Unreal's synchronization bugs
if (IsInCoyoteTime() && !bUsedCoyoteJump)
{
bUsedCoyoteJump = true;
MotionJumpCount = 1; // First jump from coyote time
}
else if (!IsInCoyoteTime())
{
MotionJumpCount++; // Air jump increment
}
// Sync Unreal's counter before jumping
CachedCharacter->JumpCurrentCount = MotionJumpCount - 1;
// Apply custom velocity modifications
ApplyDirectJumpVelocity(bIsAdditionalJump);
if (bIsAdditionalJump && !IsInCoyoteTime())
{
// Directional air jumps with custom velocity
PerformDirectionalJump();
}
else
{
// Standard ground/coyote jump - uses built-in prediction
CachedCharacter->Jump();
}
return true;
}Advanced Features:
- Coyote Time: Grace period for jumping after leaving ground
- Jump Buffering: Queue jump input before landing
- Multi-Jump: Air jumps with velocity multipliers
- Custom Counter: Reliable jump tracking avoiding Unreal's bugs
- Directional Jumps: Air jumps respect movement input
Stamina Management
Stamina synchronization for sprint limitations:
Testing Multiplayer
Local Testing Setup
Motion supports easy local multiplayer testing:
- PIE Multiplayer Settings:
- Test with multiple players: `Play` -> `Number of Players`: 2
- Net Mode: Play As Client
- Use Single Process: Unchecked (for true networking)- Window Arrangement:
Advanced Settings -> Multiplayer Options
- Editor Multiplayer Mode: Play multiple clients
- Create Audio Device for Every Player: Disabled (Default)Network Simulation
Test with simulated network conditions:
# Console commands for network simulation
NetEmulation.PktLag=100 # 100ms latency
NetEmulation.PktLoss=2 # 2% packet loss
NetEmulation.PktDup=1 # 1% duplicate packetsAlternatively, enable Network Emulation in Editor Preferences:

Debugging Network Issues
Debug Commands
Essential console commands for network debugging:
# Show network debug info
showdebug net
showdebug abilitysystem
stat net
stat game
# Network details
p.NetShowCorrections 1 # Show prediction corrections
net.EnableNetStats 1 # Show connection status
# GAS debugging
showdebug abilitysystem # Show ability system state
log LogMotionCore Verbose # Motion-specific logging
log LogAbilitySystem Verbose # GAS logging
# Performance monitoring
stat fps # Frame rate
stat unit # Frame time breakdown
stat rhi # Rendering statsCommon Network Issues
| Issue | Symptoms | Solution |
|---|---|---|
| Rubber-banding | Character snaps back | Check server validation logic, increase tolerance |
| Stamina Desync | Different stamina values | Ensure GAS effects replicate properly |
| Missing Movement | Others don't see movement | Check actor replication settings |
| Tag Mismatch | States out of sync | Verify tag replication, check authority |
Network Log Analysis
Key log patterns to watch for:
// Motion network logging
UE_LOG(LogMotionCore, Verbose, TEXT("Sprint RPC: Client->Server"));
UE_LOG(LogMotionCore, Warning, TEXT("Sprint validation failed"));
// GAS replication logging
UE_LOG(LogAbilitySystem, Verbose, TEXT("Effect applied: %s"), *EffectName);
UE_LOG(LogAbilitySystem, Warning, TEXT("Prediction miss: %s"), *AttributeName);
// Movement logging
UE_LOG(LogCharacterMovement, Verbose, TEXT("Correction delta: %f"), Delta);Best Practices
Do's ✅
- Always validate on server:
if (HasAuthority())
{
// Make authoritative decisions here
}- Use GAS for state management:
// Let GAS handle replication
ApplyGameplayEffectToSelf(SprintEffect);- Implement client prediction:
// Predict locally, validate on server
PredictMovement();
ServerValidateMovement();- Use Motion's helpers:
UMotionAbilitySystemHelper::GetAbilitySystemComponentFromActor(Actor);Don'ts ❌
- Don't trust client input:
// Bad: Direct application
void OnSprintInput()
{
StartSprinting(); // No validation!
}
// Good: Server validation
void OnSprintInput()
{
ServerRequestSprint(true);
}- Don't replicate everything:
// Bad: Replicate visual-only properties
UPROPERTY(Replicated)
float CameraShakeIntensity;
// Good: Keep visual properties local
UPROPERTY()
float CameraShakeIntensity;- Don't ignore network roles:
// Bad: Assume authority
ApplyDamage(Target, Amount);
// Good: Check authority
if (HasAuthority())
{
ApplyDamage(Target, Amount);
}- Don't use reliable RPCs on Tick:
// Bad: Flooding network
void Tick(float DeltaTime)
{
ServerUpdatePosition(GetActorLocation()); // Reliable RPC
}
// Good: Use replication
UPROPERTY(ReplicatedUsing=OnRep_Position)
FVector ReplicatedPosition;Advanced Topics
Custom Replication
Implementing custom replication for Motion components:
void UMotionCustomComponent::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Conditional replication
DOREPLIFETIME_CONDITION(UMotionCustomComponent, CustomProperty,
COND_OwnerOnly);
// Custom replication with callback
DOREPLIFETIME_CONDITION_NOTIFY(UMotionCustomComponent, ImportantValue,
COND_None, REPNOTIFY_Always);
}
void UMotionCustomComponent::OnRep_ImportantValue()
{
// Handle replication callback
OnValueReplicated.Broadcast(ImportantValue);
}Summary
Motion's networking model provides:
- Robust multiplayer support through server-authoritative architecture
- Smooth gameplay via client prediction and interpolation
- Efficient bandwidth usage with selective replication
- Easy debugging with comprehensive logging and debug commands
- Scalability from small co-op to larger multiplayer games
By understanding these networking concepts and Motion's implementation, you can create responsive, fair, and scalable multiplayer experiences.
Next Steps
- Review the GAS Primer for attribute and effect replication details
- Test your implementation with the Quick Start Guide
- Explore individual components for specific networking features
- Join the Discord community for multiplayer testing partners
Additional Resources
Unreal Documentation
Community Resources
Pro Tip
Start with local multiplayer testing using PIE before moving to dedicated servers. This allows rapid iteration and easier debugging of networking issues.