UE 5.4 / 5.5 / 5.6 / 5.7 Windows 64 Full C++ Source Blueprint Surface Pro · Commercial

Rollback Core Pro

Production-ready, GGPO-style rollback netcode for Unreal Engine 5 — a deterministic fixed-step simulation loop, automatic state capture via SaveGame property reflection, a peer-to-peer UDP transport with input redundancy and reliable ACKs, OnlineSubsystem matchmaking, and an in-editor frame-scrubber debugger. Ships with three demo maps and a complete automation-test suite.

60 Hz Default fixed tick
8 Max peers per match
~12 Typical rollback depth
3 Included demos

01 Overview

Rollback netcode is the prediction strategy invented for fighting games (GGPO, 2006) and now standard across the competitive genre — Street Fighter 6, Guilty Gear Strive, Skullgirls, Mortal Kombat 1. Instead of waiting for the remote player's input (delay-based netcode), each peer predicts that the opponent will repeat their last input and simulates immediately. When the real input arrives later, the simulation rolls back to the predicted frame, replays with the corrected input, and fast-forwards to the present. The local player feels zero latency.

The cost is CPU (resimulating up to a dozen frames per tick) and a hard requirement: the simulation must be deterministic. Same inputs in, same state out, every time.

Rollback Core Pro provides:

Why use this instead of rolling your own?

Rollback netcode is one of the highest-effort systems to build from scratch. The simulation loop, state capture, transport, prediction, resimulation, and debug tooling each take weeks to get right. This plugin is what was learned across multiple fighting-game prototypes, packaged so you can drop it in and ship.

How to read this manual

If you're new, follow sections 1–3 in order to install and run the demos, then read section 4 (Architecture) before writing any code. If you're integrating into an existing project, jump straight to Installation and API Reference. If you're debugging desyncs, head to Determinism and Visual Debugger.

Run the demo
Rollback.SpawnDemo
Host a UDP peer
Rollback.NetHost 7777
Open the debugger
Rollback.SpawnDebugger
Dump performance
Rollback.Perf

02 Quick Start

Before writing any code, run the included demo environments. Each demonstrates a different facet of the plugin and serves as a working reference you can read alongside this manual.

Demo 1 — Basic prediction & correction

Open any empty level and type into the Unreal console (default ~):

Rollback.SpawnDemo

The demo spawns two pawns. You control the Blue pawn with WASD. The Red pawn moves automatically — but its inputs reach your simulation delayed by 15 frames. When Red changes direction, your prediction fails, and you see Red snap to its true state while a cyan ghost trail shows the corrected timeline.

N N+1 N+2 N+3 N+4 N+5 LOCAL SIM predicting opponent... remote input from N arrives at N+3 cyan ghost trail = rolled-back & resimulated

The local pawn keeps running; when remote input lands, frames are silently resimulated and the ghost trail visualizes the correction.

Demo 2 — Network packet-loss simulator

To exercise the UDP transport, packet-loss simulation, input redundancy, and rollback correction path in a single PIE session:

Rollback.SpawnNetworkDemo

The Blue pawn is local WASD. The Red pawn is driven through the rollback network buffer with simulated packet loss and latency. The on-screen HUD reports:

Demo 3 — 2D Viking duel

The sprite-based fighting demo shows Rollback Core Pro in a genre-relevant setup with extracted transparent frames, metadata-driven animation, hitboxes, hurtboxes, blocking, hitstun, KO states, and rollback correction:

Rollback.SpawnVikingDemo
PlayerMoveAttackBlock
Player 1AD or arrow keysJ or left mouseK or right mouse
Player 2Driven by delayed deterministic AI input — triggers rollback corrections continuously

Use Rollback.SpawnDebugger alongside any demo to open the runtime frame-scrubber panel.

03 Installation

Requirements

RequirementSupportedNotes
Unreal Engine5.4 / 5.5 / 5.6 / 5.7Pre-built binary packages for each version on Fab
PlatformWindows 10/11 x64Other platforms compile but are not officially verified
CompilerVS2022 (17.8+) or VS2026Source build only — binary plugins ship pre-compiled
Project typeC++ or Blueprint-onlyBlueprint-only projects auto-build the plugin's runtime module on first launch if installed via Fab
OnlineSubsystem (optional)NULL / EOS / Steam / customRequired only for matchmaking; transport itself is provider-agnostic

Install from Fab marketplace

  1. Open Epic Games Launcher → Library → Fab → search for Rollback Core Pro.
  2. Click Install to Engine and select your engine version (5.4–5.7).
  3. In your project, open Edit → Plugins, search for Rollback Core Pro, and check Enabled.
  4. Restart the editor when prompted. Done.

Install into project Plugins folder (source build)

  1. Copy or clone the RollbackCorePro/ folder into YourProject/Plugins/.
  2. Right-click your .uproject file and choose Generate Visual Studio project files.
  3. Open the resulting .sln in Visual Studio and build the editor target.
  4. Launch the editor — the plugin auto-enables on first load.
Tip — Blueprint-only projects

Even if your project has no Source/ folder, installing from Fab works fine. The marketplace package ships pre-compiled binaries for each supported engine version. You only need a source build if you want to modify the plugin itself.

Verify the installation

After enabling the plugin and restarting the editor, open the console (~) and run:

Rollback.SpawnDemo

If a two-pawn demo appears with rollback correction visualization, you're ready. If the command isn't recognized, the plugin failed to load — check Edit → Plugins → Networking → Rollback Core Pro and verify there are no compile errors in the Output Log.

Project layout reference

YourProject/
├── Plugins/
│   └── RollbackCorePro/
│       ├── RollbackCorePro.uplugin     # Plugin descriptor
│       ├── Config/
│       │   └── FilterPlugin.ini        # Files staged in packaged builds
│       ├── Content/
│       │   ├── Demo/                   # Generated viking sprites + JSON
│       │   └── Maps/                   # RC_BasicDemo, RC_NetworkDemo, RC_VikingDemo
│       ├── Docs/
│       │   ├── index.html              # This manual
│       │   ├── editor-ui.md
│       │   ├── viking-demo.md
│       │   └── network-prediction-mover.md
│       ├── Resources/                  # Plugin icon
│       └── Source/
│           ├── RollbackCorePro/        # Runtime module
│           │   ├── Public/             # Headers
│           │   └── Private/            # Implementations + Tests
│           └── RollbackCoreProEditor/  # Editor-only module (debugger, wizard)

04 Architecture

Rollback Core Pro is a runtime plugin built around three world subsystems, one actor component, and one Blueprint function library. Each piece has a single, well-bounded responsibility.

YOUR GAMEPLAY CODE APawn / AActor IRollbackInputProvider UPROPERTY(SaveGame) Velocity, Health, etc. Mesh / Anim / VFX visual shell, not rolled back ROLLBACK CORE PRO · RUNTIME MODULE URollbackStateComponent per-actor SaveGame reflection ring-buffer history URollbackManager world subsystem AdvanceFrame() RollbackToFrame() URollbackNetSubsystem world subsystem UDP transport multi-peer · matchmaking URollbackNetworkBlueprintLibrary static helpers — wraps everything for Blueprint URollbackCoreProSettings UDeveloperSettings — Project Settings entries RollbackCoreProEditor module · ARollbackDebuggerPanel · Setup wizard · Editor styling Unreal Sockets · OnlineSubsystem (NULL · EOS · Steam) · OS UDP stack

High-level architecture. The runtime module is the entire C++ surface; the editor module adds the debugger panel and setup wizard.

The simulation lifecycle

Standard Unreal games tick on render-frame cadence. Rollback netcode must tick at a fixed rate, identical across peers — otherwise the same input sequence won't produce identical results. URollbackManager takes over the gameplay loop using an accumulator:

// Conceptual per-engine-tick loop inside URollbackManager
Accumulator += DeltaTime;
while (Accumulator >= FixedTimeStep) {       // FixedTimeStep = 1/60 by default
    AdvanceFrame();                             // — one deterministic step
    Accumulator -= FixedTimeStep;
}

Each AdvanceFrame() does the following, in order:

  1. Increment CurrentFrame.
  2. For each registered entity (anything implementing IRollbackEntity): call RollbackTick(dt, frame). This is where gameplay logic runs.
  3. For each registered entity: call SaveRollbackState(frame). The state component writes the actor's SaveGame properties + transform + velocity into the ring buffer.

The rollback path

When a late authoritative input arrives over UDP and changes a frame from the past:

  1. The network subsystem buffers the input under (PlayerId, Frame).
  2. Your gameplay code (or ApplyBufferedInputsAndRollback from the Blueprint library) calls URollbackManager::RollbackToFrame(EarliestMismatchFrame).
  3. For each entity, LoadRollbackState(Frame) restores the snapshot.
  4. The manager replays frames Frame+1..Current with the corrected input history.
  5. Latest state is now authoritative; the visual shell (mesh, animation, camera) follows on the next render frame.
Note

The player never sees a hitch because rendering is decoupled from simulation. Only the final resimulated state is visible on the next draw call.

05 Auto-State Saving

The most error-prone part of any rollback system is state capture — getting every byte of gameplay state into a snapshot, and back out, without missing a field or accidentally including non-deterministic data. Rollback Core Pro solves this by repurposing Unreal's existing SaveGame property flag.

The SaveGame flag

UE's reflection system already supports marking properties as part of a save file:

UPROPERTY(SaveGame, BlueprintReadWrite) int32   Health = 100;
UPROPERTY(SaveGame, BlueprintReadWrite) FVector SimulatedVelocity = FVector::ZeroVector;
UPROPERTY(SaveGame, BlueprintReadWrite) uint8   HitstunFramesRemaining = 0;
UPROPERTY(SaveGame, BlueprintReadWrite) uint8   FacingDirection = 0;

In Blueprints, you mark a variable as SaveGame by selecting it and toggling the SaveGame checkbox in the Advanced section of the Details panel.

Rollback Core Pro repurposes this same flag for per-frame snapshot/restore. One annotation, two uses — your rollback state list and your save-file state list are identical, which is almost always what you want anyway.

AYourFighter + UPROPERTY(SaveGame) Health: int32 + UPROPERTY(SaveGame) Velocity: FVector + UPROPERTY(SaveGame) FacingDir: uint8 UPROPERTY() CosmeticTimer (ignored) + URollbackStateComponent BeginPlay scan cache FProperty pointers StateBuffer (ring, 60 frames) frame 117 → [ActorData: 24B] T=(...) V=(...) frame 118 → [ActorData: 24B] T=(...) V=(...) frame 119 → [ActorData: 24B] T=(...) V=(...) frame 120 → [ActorData: 24B] T=(...) V=(...) ... RollbackToFrame(119) → restore the highlighted slot

The component scans the owning actor once at BeginPlay, caches the SaveGame-flagged FProperty pointers, and writes them into the ring buffer each tick.

How the component works internally

On BeginPlay, URollbackStateComponent walks the owning actor's class properties via UE's reflection system. Every FProperty whose PropertyFlags contains CPF_SaveGame is cached into TrackedProperties. Iteration happens once; per-frame serialization just walks the cached array.

Each tick the component writes the tracked properties into an FArchive wrapping a TArray<uint8>, plus the actor's Transform and Velocity directly (cheap path, no Serialize overhead). The byte array is stored in StateBuffer[Frame]. On restore, the inverse happens.

What gets captured

Captured automaticallyCaptured because flaggedNot captured
Actor world transform (location + rotation)
Velocity (FVector)
Any UPROPERTY(SaveGame) on the owning actor — integers, floats, FVectors, enums, structs, arrays of any of those, etc. Any UPROPERTY() without the SaveGame flag
Plain C++ members not exposed to reflection
UObject pointers (must be re-resolved deterministically)
Sub-actors and components (each needs its own state component)

Multi-actor and projectiles

Every actor that should be rolled back needs its own URollbackStateComponent. Projectiles, particle-driven hitboxes, status-effect carriers — give each a state component and they'll automatically participate in the rollback. The component registers itself with the world's URollbackManager on BeginPlay; you don't need to call anything manually.

Inspecting captured state

The component exposes diagnostics that are useful for inline UI or debug overlays:

PropertyMeaning
LastSavedFrameFrame index of the most recent snapshot
LastRestoredFrameMost recently restored frame
LastSavedByteCountSnapshot size — useful for budgeting
LastSavedChecksumFNV-style checksum of the snapshot bytes; compare across peers to detect desyncs
GetTrackedPropertyCount()How many SaveGame fields the component found at BeginPlay

06 Deterministic Movement

Unreal's built-in CharacterMovementComponent and Chaos Physics are fundamentally non-deterministic across peers. Sub-tick interpolation, root-motion blending, physics sub-stepping, and floating-point error accumulation all cause two clients to diverge given identical inputs over enough frames.

Rollback Core Pro ships URollbackMovementComponent, a minimal deterministic alternative. It's intentionally tiny — it's a starting point you customize, not a full character controller.

What it does

URollbackMovementComponent::DeterministicMove(FVector InputVector, float DeltaTime) integrates input into actor location using:

Typical usage

void AMyFighter::HandleRollbackTick(float Dt, int32 Frame, FRollbackInput Input)
{
    const FVector InputVec(Input.Axes.X, Input.Axes.Y, 0.f);
    MoveComp->DeterministicMove(InputVec, Dt);

    // Then deterministic combat logic here, reading from Input.Buttons
    if (Input.Buttons & ButtonAttack) {
        StartAttackFrame(Frame);
    }
}
OnRollbackTick event:
  → URollbackMovementComponent → DeterministicMove(InputVector, DeltaTime)
  → Branch on Input.Buttons bits → custom attack/block logic
Important

Do not use CharacterMovementComponent on the same actor you want to roll back. Pick one: either use this deterministic movement (or your own deterministic kinematic code), or follow the "separated systems" pattern in section 12 where Mover/NetPred handles locomotion and Rollback Core handles combat state only.

07 Network Transport

URollbackNetSubsystem is a thin, purpose-built UDP transport for rollback inputs. It is symmetric — any peer can be a host. It supports up to 8 peers per match. It is intentionally not wrapped through Unreal's replication system, because rollback transport has different priorities (latency > ordering > reliability) than gameplay replication.

Wire packet layout

Every packet is a single UDP datagram, capped by MaxPacketBytes (default 1200, safe under typical MTU):

PACKET LAYOUT (network byte order) MAGICu32 VERu8 TYPEu8 SEQu32 ACKu32 PLAYERi32 Nu8 frames INPUT FRAMES[N]{ frame, buttons, axes } × N 8 byte fixed header · 8 byte transport meta · 5 byte input meta · variable-length input list PACKET TYPES 1 · Hello 2 · Input 3 · Ack 4 · Heartbeat REDUNDANCY Each input packet repeats the last N frames of input. If packet for frame F is lost, the next packet (F+1) carries F again. N = InputRedundancyFrames, default 8.

Compact, hand-rolled wire format. Total overhead per packet: 21 bytes of header + 5 bytes per repeated input frame.

Reliability model

Input packets are sent unreliably by default — the redundancy window almost always covers transient packet loss without round-trips. For data that must arrive (initial Hello, configuration changes), the transport supports a reliable mode:

Peer lifecycle

  1. Connection starts when either side calls ConnectToPeer(PlayerId, Host, Port) or accepts an incoming Hello.
  2. Heartbeats are sent at HeartbeatIntervalSeconds (default 2 s) if no other packet has been sent recently.
  3. If no packet is received for PeerTimeoutSeconds (default 10 s), the peer is dropped and OnPeerDisconnected fires.

Usage from C++

URollbackNetSubsystem* Net = GetWorld()->GetSubsystem<URollbackNetSubsystem>();

// 1. Open the UDP socket.
FRollbackTransportConfig Config;
Config.LocalPort = 7777;
Config.InputRedundancyFrames = 8;
FString Error;
if (!Net->StartUdpPeer(Config, Error)) {
    UE_LOG(LogTemp, Error, TEXT("Transport failed: %s"), *Error);
    return;
}

// 2. Connect to a remote peer.
Net->ConnectToPeer(/*PlayerId*/ 2, TEXT("203.0.113.42"), 7777, Error);

// 3. Each deterministic frame, send local input.
Net->SendInputFrame(LocalPlayerId, CurrentFrame, MyInput, /*bReliable*/ false);

// 4. Apply received remote input and trigger rollback if predictions diverged.
bool bChanged = false;
int32 EarliestChangedFrame = -1;
Net->ApplyBufferedInputsToState(RemoteStateComp, RemotePlayerId,
                                CurrentFrame - 15, CurrentFrame,
                                bChanged, EarliestChangedFrame);
if (bChanged) {
    Manager->RollbackToFrame(EarliestChangedFrame);
}

Usage from Blueprint

URollbackNetworkBlueprintLibrary wraps the common cases:

MakeLoopbackTransportConfig(LocalPort, PacketLossPercent, MinLatencyMs, MaxLatencyMs)
StartLoopbackPacketLossTransport(WorldContext, LocalPort, ...)
SendRollbackInputForCurrentFrame(WorldContext, PlayerId, Input, bReliable)
SendRollbackInputToAllPeers(WorldContext, PlayerId, Input, bReliable)
ApplyBufferedInputsAndRollback(WorldContext, RemoteStateComp, RemotePlayerId,
                               FromFrame, ToFrame, bOutRolledBack, OutRollbackFrame)
ConnectToRemotePeer(WorldContext, PlayerId, RemoteHost, RemotePort)
GetConnectedPeerIds(WorldContext) / GetAllPeerInfo(WorldContext)

Local two-instance smoke test

Run two editor instances or two machines on the same LAN. On the host:

Rollback.NetHost 7777 RollbackCoreProSmoke 0

On the client, replace the address with the host's IP:

Rollback.NetClient 192.168.0.10 7777 7778 0

The final numeric argument is simulated packet loss percent. Use 10 for a stress test:

Rollback.NetClient 192.168.0.10 7777 7778 10

Configuration reference

FieldDefaultPurpose
LocalPort7777UDP bind port (0 = auto)
RemoteHost / RemotePort"" / 7778Optional auto-connect on start
SocketSubsystemNameNAME_NoneOverride OS sockets, e.g. for platform P2P
MaxPacketBytes1200Safe UDP MTU floor
InputRedundancyFrames8How many past frames each packet repeats
ResendAfterSeconds0.08Reliable resend timeout
MaxReliableRetryCount20Resends before declaring peer failed
HeartbeatIntervalSeconds2.0Keepalive interval
PeerTimeoutSeconds10.0Drop peer after this much silence
MaxPeers4Hard cap on connected peers (≤ 8)
bAcceptFirstRemotePeertrueIf true, an inbound Hello from any address creates a peer
bEnablePacketLossSimulationfalseMaster switch for the simulated network
SimulatedOutgoingPacketLossPercent0Drop outgoing packets by chance
SimulatedIncomingPacketLossPercent0Drop incoming packets by chance
SimulatedMinLatencyMs / SimulatedMaxLatencyMs0 / 0Range of artificial delay applied to delivered packets

08 Platform Matchmaking

For LAN testing or shipping platform integration, URollbackNetSubsystem can advertise and discover matches through Unreal's configured OnlineSubsystem. The session itself carries the rollback transport's UDP endpoint as session settings, so a client that joins a session immediately knows where to point its UDP socket.

Provider matrix

ProviderUse forSetup
NULLLAN tests, in-editor PIEEnabled by default in this sample project — no credentials required
EOSCross-platform shippingConfigure EOS plugin + Developer Portal credentials in host project
SteamSteam-only shippingSteamworks SDK + AppID configured in DefaultEngine.ini
CustomFirst-party platforms (PSN, Xbox Live, etc.)Implement a custom IOnlineSubsystem; matchmaking abstraction is provider-agnostic

Host flow

FRollbackTransportConfig Config;
Config.LocalPort = 7777;
FString Error;
RollbackNetSubsystem->StartUdpPeer(Config, Error);

// Advertise the rollback endpoint via OnlineSubsystem.
RollbackNetSubsystem->CreatePlatformSession(
    /*SessionName*/ FName("RollbackCorePro"),
    /*MatchId    */ "Ranked-1v1",
    /*PublicConn */ 2,
    /*bLan       */ false,
    Error);

Client flow

// 1. Search.
RollbackNetSubsystem->FindPlatformSessions("Ranked-1v1", /*MaxResults*/ 20, /*bLan*/ false, Error);

// 2. Pick a result (delivered via OnMatchmakingResult delegate).
RollbackNetSubsystem->OnMatchmakingResult.AddDynamic(this, &UMyMenu::HandleResult);

// 3. Join.
RollbackNetSubsystem->JoinPlatformSessionByIndex(ResultIndex, Error);

On successful join, the transport auto-connects its UDP socket to the host's advertised endpoint and OnMatchmakingComplete fires with bWasSuccessful = true.

Delegates

DelegateFires when
OnMatchmakingResultEach session that matches a FindPlatformSessions query (provider, ID, owner, ping, host:port)
OnMatchmakingCompleteAsync create / find / join operation finishes; carries success bool + error string
OnTransportConnectedThe UDP transport has a remote peer talking back
OnPeerDisconnectedA peer timed out or was explicitly disconnected
OnPeerMaxRetriesExceededA reliable packet exhausted its resend budget

Provider validation

Before shipping a new provider, validate the OSS is loaded:

Rollback.NetProvider default 1
Rollback.NetProvider NULL    1
Rollback.NetProvider EOS     1
Rollback.NetProvider STEAM   1
Rollback.NetFindNull RollbackCoreProSmoke 20

The first three forms check that the named provider is loaded and exposes a session interface. The last one performs an actual LAN session search using OnlineSubsystemNull, which is the fastest in-editor sanity check.

NAT and connectivity

The matchmaking layer publishes the host's advertised address and port as session settings. Public IPs and LAN setups work out of the box. For NAT-restricted setups, configure a platform-specific socket subsystem (EOS P2P, Steam Sockets) and the transport will route through it rather than raw UDP.

09 Visual Debugger

Rollback bugs are invisible by default — the player just sees a snap or a desync. The debugger surfaces what was actually happening on each historical frame.

Inline ghost trails

When bEnableVisualDebugging is true on URollbackManager, the manager continuously renders a cyan ghost (drawn via DrawDebugBox) at the actor's true position from DebugLiveFrameLag frames ago (default 5). If your local prediction is drifting, the ghost separates from the live actor.

Frame-scrubber panel

For frame-by-frame inspection, spawn the runtime debugger panel:

Rollback.SpawnDebugger

The panel reads from URollbackManager's saved frames and surfaces:

Scrubber console interface

CommandEffect
Rollback.Debugger.Follow 1Resume following the live frame
Rollback.Debugger.Follow 0Pause at current scrub frame
Rollback.Debugger.Frame 120Jump to absolute frame 120
Rollback.Debugger.Step -1Step back one frame
Rollback.Debugger.Step 1Step forward one frame

Programmatic access

C++ and Blueprint projects can use the same data without the panel:

const TArray<int32> AvailableFrames = Manager->GetAvailableDebugFrames();
const TArray<FRollbackDebugFrameRecord> Records = Manager->GetDebugFrameRecords(Frame);
Manager->SetDebugScrubFrame(Frame);
Manager->StepDebugScrubFrame(+1);
Manager->SetDebugScrubFollowLive(true);

Desync detection

The state component stores an FNV-style checksum of every snapshot. The manager compares pre-rollback vs post-rollback checksums for the same frame; any mismatch indicates a non-deterministic actor and fires the OnDesyncDetected delegate with:

Tip — chasing a desync

If OnDesyncDetected fires repeatedly on the same actor, set Rollback.Debugger.Frame <F> to the reported frame and inspect FRollbackDebugFrameRecord::Input — comparing inputs at the moment of divergence almost always reveals the source.

10 Performance & Tuning

Rollback's CPU cost is proportional to (rollback depth) × (per-frame sim cost) × (number of entities). The plugin instruments all three so you can tune confidently.

Performance stats

URollbackNetSubsystem::GetPerformanceStats() returns an FRollbackPerformanceStats struct sampled over a rolling 60-frame window plus 1-second bandwidth window:

FieldWhat it measures
AvgSimulationTimeMs / MaxSimulationTimeMsHow long one normal AdvanceFrame() takes
AvgRollbackTimeMs / MaxRollbackTimeMsHow long a RollbackToFrame() call takes (restore + replay)
MaxRollbackDepthFramesLargest rollback distance seen recently
TotalRollbackCount / RollbacksInLastSecondFrequency tracking
AvgStateSnapshotBytesHow much memory each frame's snapshot consumes
AvgStateSerializeTimeMs / MaxStateSerializeTimeMsSnapshot serialization cost
BytesSentPerSecond / BytesReceivedPerSecondTransport bandwidth
ConnectedPeerCountCurrent peer count
RegisteredEntityCountHow many entities the manager simulates
DesyncCountCumulative desync detections
FramesSimulated / FramesWithRollbackLifetime counters

Quick dump

Rollback.Perf

Prints a multi-line summary to the on-screen log and the output log.

Tuning levers

If you're seeing...Try...
High AvgRollbackTimeMsReduce FixedTickRateHz (e.g. 60→30) or trim per-frame sim cost. Reduce MaxRollbackDepthFrames to cap worst-case.
High AvgStateSnapshotBytesRemove unused SaveGame flags. Pack booleans into bitmasks. Use smaller integer types.
High BytesSentPerSecondLower InputRedundancyFrames. Send less often (every 2 ticks instead of every tick).
High DesyncCountYou have non-deterministic gameplay. See section 11.
Large RollbacksInLastSecondEither your network is genuinely lossy, or your prediction is wrong too often. Inspect via the visual debugger.

Memory footprint estimate

Default MaxBufferSize on the state component is 60 frames. With 4 entities averaging 64 bytes of SaveGame state each, that's 60 × 4 × 64 = 15 KB of rolling snapshot memory. Comfortable for any platform.

11 Determinism Rules

If "same inputs in, same state out" breaks for one entity on one frame, every peer's prediction silently diverges. This section is the canonical reference for what you may and may not do inside rollback-authoritative code.

The do / don't matrix

Safe inside rollback authorityWill cause desyncs
UPROPERTY(SaveGame) POD state (int, float, FVector, enum, FName, FString) CharacterMovementComponent on the rolled-back actor
Integer-tick countdowns and hitstun/freeze timers Mover plugin on the rolled-back actor
Deterministic LUTs and frame-data tables loaded once at startup Chaos physics simulation on the rolled-back actor
Seeded FRandomStream where the seed is derived from the frame index Latent Blueprint actions, Delay nodes, FTimerManager callbacks
Fixed-step integration via URollbackMovementComponent::DeterministicMove FMath::Rand* without a frame-derived seed
Anything entirely driven by the current frame's FRollbackInput Wall-clock time (FDateTime::Now, FPlatformTime::Seconds) as gameplay input
Bitmask reads on Input.Buttons and component reads on Input.Axes Async asset loads gating gameplay behavior
Reading data tables, curves, and content via cached pointers Floating-point math that depends on CPU/SSE rounding mode

The two-layer pattern

The pattern every shipping rollback game uses is what GGPO calls the deterministic core plus visual shell:

DETERMINISTIC CORE

What is rolled back

  • Logical position (FVector)
  • Health, meter, status effects
  • Hitbox/hurtbox active flags
  • Hitstun and freeze counters
  • Facing direction, blocking state
  • Combat state machines
VISUAL SHELL

What follows the corrected state

  • Skeletal mesh animation playback
  • Particle effects and VFX
  • Sound effects and music
  • Camera shake and zoom
  • UI flashes and screen-space FX
  • Decals and screen-space effects

The visual shell is allowed to be non-deterministic — it can use timelines, latent actions, async loads, anything you want. It just reads from the core's state and reacts. If a rollback happens, the visual shell will see the corrected state and naturally update; it never participates in the rollback itself.

Verifying determinism

Two tools detect non-determinism early:

  1. Checksum monitoring. Bind OnDesyncDetected and log when the post-rollback state for any entity differs from the original snapshot. If this fires ever during normal gameplay, you have a non-deterministic actor.
  2. The packet-loss demo. Run Rollback.SpawnNetworkDemo with high simulated loss (30%+). If gameplay diverges visibly between local and remote pawns, something in the rolled-back code is non-deterministic.
Common desync sources

In order of how often they trip projects: (1) using CharacterMovementComponent on a rolled-back actor, (2) reading FApp::GetDeltaTime() inside rollback code instead of the explicit step delta, (3) iterating TMap/TSet (unordered) instead of TArray inside rollback code, (4) random number generation without a frame-derived seed.

12 Network Prediction & Mover Integration

Rollback Core Pro is intentionally a standalone deterministic layer. It does not wrap Unreal's Network Prediction plugin or Mover because the two systems solve different problems with different assumptions.

Rollback Core ProNetwork Prediction / Mover
TopologySymmetric peer-to-peerServer-authoritative
Tick modelFixed-step, both peers run identical simVariable, server reconciles client predictions
Correction styleResimulate N frames from snapshotReceive server state, blend/snap to it
State captureSaveGame reflection, automaticMove record structs, manually authored
Best forCompact deterministic gameplay (fighting, combat, projectiles)Predicted locomotion that interops with Epic's movement stack

When to use which

Recommended integration patterns

  1. Deterministic core + visual shell. Rollback Core Pro simulates compact gameplay state. Meshes, animation, VFX, audio, and cameras follow the corrected state after rollback.
  2. Separated systems. Mover or Network Prediction owns locomotion. Rollback Core Pro owns deterministic combat, projectile, hit-validation, or frame-data logic. The two systems exchange information only through clean read interfaces — no shared mutable state.
  3. Prototype migration. Use Rollback Core Pro to validate rollback game feel during prototyping. Migrate production movement to Network Prediction or Mover when the project needs Epic's server-authoritative pipeline, but keep the combat layer on Rollback Core Pro.
Test mixed architectures aggressively

Any mixed architecture must be tested under forced packet loss, latency, and repeated rollback. A system is safe to include in the rollback authority only if resimulating the same frame range from the same input history produces the same gameplay state, every time.

13 API Reference

URollbackManager — world subsystem

MemberTypeDescription
AdvanceFrame()methodTick all registered entities one fixed step; snapshot state
RollbackToFrame(Frame, EarliestMismatch = -1)methodRestore state at Frame, replay forward to current
RegisterEntity(IRollbackEntity)methodAdd an entity to the simulated set (auto-called by URollbackStateComponent)
UnregisterEntity(IRollbackEntity)methodRemove from the simulated set
DrawDebugState(Frame)methodRender cached state for a frame
GetDebugFrameRecords(Frame)method → TArray<FRollbackDebugFrameRecord>Per-entity inspectable record for a frame
GetAvailableDebugFrames()method → TArray<int32>All frame numbers currently in the ring buffer
SetDebugScrubFrame(Frame) / StepDebugScrubFrame(Δ)methodProgrammatic scrubber control
CurrentFrameint32The live simulation frame index
RollbackCount / LastRollbackFrame / LastRollbackFramesReplayedint32Lifetime rollback diagnostics
DesyncCount / LastDesyncFrameint32Desync diagnostics
OnDesyncDetecteddelegate (Frame, EntityName, ChecksumMismatch)Fires when post-rollback checksum diverges from snapshot
bEnableVisualDebuggingboolToggle inline ghost rendering
DebugLiveFrameLagint32How many frames behind to draw the ghost

URollbackStateComponent — per-actor component

MemberTypeDescription
CurrentLocalInputFRollbackInputWrite here before AdvanceFrame; used as the input for the next tick
InjectInputForFrame(Frame, Input)methodAuthoritatively override the input that was used at a past frame
GetInputForFrame(Frame)method → FRollbackInputRead the input recorded at a past frame
OnRollbackTickBlueprint event (Δt, Frame, Input)Fires during each deterministic tick — main gameplay hook for Blueprint actors
OnRollbackTickDelegatedelegate (Δt, Frame, Input)C++ counterpart of the above
MaxBufferSizeint32 (default 60)Ring-buffer length for state and input history
LastSavedFrame / LastRestoredFrameint32Most recent snapshot/restore frame
LastSavedByteCountint32Size of the most recent snapshot in bytes
LastSavedChecksum / LastRestoredChecksumint32FNV-style checksum of the snapshot bytes
GetTrackedPropertyCount()method → int32How many SaveGame properties the component is capturing

URollbackNetSubsystem — world subsystem

MemberDescription
StartUdpPeer(Config, Error)Open the UDP socket, start ticking the transport
StopTransport()Close the socket and tear down all peers
ConnectToPeer(PlayerId, Host, Port, Error)Add a remote peer (sends a Hello and waits for acceptance)
DisconnectPeer(PlayerId)Drop a peer
SendInputFrame(PlayerId, Frame, Input, bReliable)Send one input frame; unreliable by default
ConsumeRemoteInput(PlayerId, Frame, OutInput)Pop one received input
BufferRemoteInputForRollback(PlayerId, Frame, Input)Queue a received input for the rollback path
ApplyBufferedInputsToState(StateComp, PlayerId, FromFrame, ToFrame, bOutChanged, OutEarliestChanged)Apply buffered inputs and report what changed
IsTransportRunning() / IsConnectedToPeer()State queries
GetTransportStats() / GetPerformanceStats()Diagnostics
GetRemoteEndpointString() / GetLocalEndpointString()Human-readable endpoints
GetConnectedPeerIds() / GetAllPeerInfo()Multi-peer queries
FlushTransport()Force send/receive immediately (DevelopmentOnly)
CreatePlatformSession(Name, MatchId, Connections, bLan, Error)Advertise the rollback endpoint via OnlineSubsystem
FindPlatformSessions(MatchId, MaxResults, bLan, Error)Search for advertised sessions
JoinPlatformSessionByIndex(ResultIndex, Error)Join a result and auto-connect the UDP transport
DestroyPlatformSession(Name)Remove the platform session
ValidateOnlineProvider(SubsystemName, bRequireSessions, OutStatus)Provider diagnostic check
GetCachedMatchmakingResults()Last search results
delegatesOnTransportConnected, OnTransportError, OnRemoteInputReceived, OnMatchmakingResult, OnMatchmakingComplete, OnPeerDisconnected, OnPeerMaxRetriesExceeded, OnDesyncDetected

URollbackNetworkBlueprintLibrary — Blueprint helpers

Static helpers that wrap the most common URollbackNetSubsystem calls in WorldContext-aware Blueprint nodes. See section 7 for the full list.

Core data types

TypeFields
FRollbackInputint32 Buttons (bitmask), FVector Axes
FRollbackFrameStateTArray<uint8> ActorData, FVector Location, FQuat Rotation, FVector Velocity
FRollbackTransportConfigSee section 7 reference table
FRollbackTransportStatsPacket/byte counters, RTT, peer count
FRollbackPerformanceStatsSim/rollback/serialize timing, bandwidth, rollback frequency
FRollbackPeerInfoPer-peer PlayerId, endpoint, connection state, RTT, packet counts
FRollbackMatchmakingResultSessionId, provider, owner, host:port, ping, open slots
FRollbackDebugFrameRecordFrame, entity name, state availability, location/rotation/velocity, byte count, checksum, input

Interfaces

InterfacePurpose
IRollbackEntityImplemented by URollbackStateComponent. Override RollbackTick, SaveRollbackState, LoadRollbackState if you implement your own entity type.
IRollbackInputProviderImplement on your pawn. GetRollbackInput(OutInput) is called once per deterministic tick.

14 Console Commands

All commands are registered globally and operate on the active UWorld's subsystems. Open the console with ~.

CommandArgumentsPurpose
Rollback.SpawnDemoSpawn the basic two-pawn demo
Rollback.SpawnNetworkDemoSpawn the network packet-loss demo
Rollback.SpawnVikingDemoSpawn the 2D viking duel demo
Rollback.SpawnDebuggerSpawn the frame-scrubber debugger panel
Rollback.NetHost[LocalPort=7777] [MatchId] [LossPercent=0] [MaxPeers=4]Start UDP transport and advertise via OnlineSubsystemNull
Rollback.NetClient<RemoteHost> <RemotePort> [LocalPort=7778] [PlayerId=-1] [LossPercent=0]Start UDP transport and connect to a host
Rollback.NetConnect<RemoteHost> <RemotePort> <PlayerId>Add an additional peer to a running transport
Rollback.NetDisconnect<PlayerId>Drop a peer by ID
Rollback.NetPeersList connected peers with RTT and packet counts
Rollback.NetProvider[SubsystemName=default] [RequireSessions=1]Validate that a named OnlineSubsystem is loaded
Rollback.NetFindNull[MatchId] [MaxResults=20]Find OnlineSubsystemNull LAN sessions
Rollback.PerfDump full performance statistics
Rollback.Debugger.Follow<0|1>Pause / resume live-frame following in the debugger
Rollback.Debugger.Frame<Frame>Jump to an absolute frame
Rollback.Debugger.Step<Δ>Step the scrub frame by a delta
Tip — bind for iteration

For fast iteration in PIE, add bindings to your project's DefaultInput.ini:

+ConsoleKeys=Tilde
+ActionMappings=(ActionName="ToggleDebugger",Key=F2,bShift=False,bCtrl=False,bAlt=False,bCmd=False)

15 Project Settings

Surfaced under Edit → Project Settings → Plugins → Rollback Core Pro. Backed by URollbackCoreProSettings (UDeveloperSettings) and persisted to DefaultEngine.ini.

GroupSettingDefaultRangePurpose
SimulationFixedTickRateHz6015–240Deterministic simulation rate. Must match across all peers.
SimulationMaxRollbackDepthFrames120–240Soft target for the deepest rollback the sim will perform.
NetworkingDefaultLocalPort77771024–65535Used by Blueprint helpers and demos.
NetworkingDefaultInputRedundancyFrames80–32Past input frames each packet repeats.
NetworkingDefaultMaxPeers41–8Peer cap for default transport configs.
NetworkingDefaultResendAfterSeconds0.080.01–2.0Reliable resend timeout.
Networking → ReliabilityDefaultHeartbeatIntervalSeconds2.00.1–30.0Keepalive interval.
Networking → ReliabilityDefaultPeerTimeoutSeconds10.01.0–120.0Peer timeout.
DebugbEnableVisualDebuggingByDefaulttrueboolInline ghost rendering on by default.
DebugDebugLiveFrameLag50–60How many frames behind to draw the ghost.
DebugbAutoOpenStatsPanelInPIEfalseboolAuto-open the stats panel on first PIE each session.
OnboardingbShowSetupWizardOnStartuptrueboolShow the setup wizard once when the project loads.

Settings are read by URollbackManager::Initialize and by any code that constructs an FRollbackTransportConfig from defaults. Changing them while the editor is running takes effect on the next PIE session or world load.

16 Automation Tests

The plugin ships with automation coverage under WITH_DEV_AUTOMATION_TESTS in Source/RollbackCorePro/Private/Tests/RollbackCoreProAutomationTests.cpp. Run them from the Session Frontend (Tools → Test Automation → Automation) or via the command line:

"<UE_path>\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" YourProject.uproject ^
  -ExecCmds="Automation RunTests RollbackCorePro; Quit" ^
  -unattended -nop4 -nosplash -nullrhi

What's covered

TestValidates
State.SaveRestoreSaveGame property round-trip through the state component
Rollback.LateInputCorrectionThe canonical rollback case: late input changes a past frame
Network.BufferApplyReceived input applied to a state component, change detected
Debug.HistoryScrubFrame-by-frame scrubber data integrity
VikingDemo.MetadataCombatMetadata-driven hit detection in the viking demo
Network.UdpLoopbackSmokeTwo bound peers exchange a reliable rollback input through real sockets
Network.MultiPeerConnectThree peers connect and exchange input simultaneously
Network.PeerDisconnectHeartbeat timeout detects a vanished peer
Matchmaking.NullProviderValidationOnlineSubsystemNull diagnostic check
Desync.DetectionChecksum-based desync surfacing
Performance.StatsPerformance counters populate after sim and rollback

17 Troubleshooting & FAQ

The plugin won't enable / the console commands are not recognized.

The plugin failed to compile or load. Open Edit → Plugins and look for a yellow warning icon next to Rollback Core Pro — clicking shows the load error. Common causes:

  • The plugin folder was placed inside YourProject/Content/ instead of YourProject/Plugins/.
  • For source builds, you didn't regenerate the Visual Studio project files after copying the plugin in.
  • OnlineSubsystem dependencies failed to load. Open Saved/Logs/YourProject.log and search for LogPluginManager.
Predictions look perfect locally but the remote pawn rubber-bands.

Either the remote inputs are arriving outside the InputRedundancyFrames window (raise the value), or your rollback path isn't actually rolling back when it should. Check Rollback.Perf — if TotalRollbackCount isn't growing, your code isn't calling RollbackToFrame. The Blueprint helper ApplyBufferedInputsAndRollback handles this for you; if you're going manual, verify your call site.

The pawn slides indefinitely after I press a movement key.

You're applying input persistently. FRollbackInput should represent the input for that frame only. If you set Axes to a non-zero value and never clear it, every frame will use it. Either reset CurrentLocalInput at the start of input gathering, or only set it when input is actually held.

OnDesyncDetected fires on the same actor every match.

An actor with a state component is doing something non-deterministic during RollbackTick. The most common culprit is iterating a TMap or TSet inside the tick — iteration order is not stable. Use TArray for any container you walk inside rolled-back code.

How do I support Steam matchmaking?

Enable the OnlineSubsystem Steam plugin in your project, add your Steam AppID to DefaultEngine.ini under [OnlineSubsystem] / [OnlineSubsystemSteam], and verify with Rollback.NetProvider STEAM 1. The matchmaking API calls are unchanged — the provider abstraction handles the rest.

The visual debugger panel doesn't render anything.

The panel renders state from the world's URollbackManager. If no rollback entities are registered, there's nothing to show. Confirm by running Rollback.SpawnDemo first — if the panel shows demo entities, your gameplay code isn't registering with the manager (which usually means no URollbackStateComponent on your actors).

Can I use this with dedicated servers?

The transport is symmetric — any peer can host. There's no client/server asymmetry in the protocol. For a server-authoritative topology, run a headless peer and treat its state as ground truth. The plugin won't object.

What's the maximum match size?

Hard cap is 8 peers (protocol limit). Practical limit depends on per-frame simulation cost — every rollback resimulates every registered entity. Two-player fighting games are well within budget at 60 Hz with depth-12 rollbacks on a 2019 CPU. Heavier simulations should reduce MaxRollbackDepthFrames or drop the tick rate.

How do I integrate this with GAS (Gameplay Ability System)?

GAS ability execution is generally deterministic if you avoid latent tasks, WaitDelay, and async loads inside abilities. Replication mode Mixed or Minimal is friendlier. The recommended pattern is to keep abilities purely as input → instant state change, then let Rollback Core Pro snapshot the resulting attributes via SaveGame-flagged FGameplayAttributeData.

18 Glossary

TermMeaning
Fixed-step tickSimulation runs at a constant rate (60 Hz here) regardless of render framerate, ensuring identical state evolution across peers.
RollbackRestoring the simulation to a past frame and replaying with corrected inputs.
Rollback depthHow many frames the simulation goes back during a single RollbackToFrame call.
PredictionFilling in unknown remote input by repeating the most recent known input.
Snapshot / state captureWriting the simulation state into a buffer that can be restored later.
DesyncTwo peers diverging in simulation state despite identical inputs. Caused by non-determinism.
Input redundancyRepeating recent input frames in every packet so transient loss doesn't stall the simulation.
Reliable ACKAcknowledgment mechanism for packets that must be delivered; absence triggers resend.
HitstunCombat state where a character is unable to act after being hit. Always integer-frame counted.
Visual shellThe non-deterministic rendering layer (mesh, anim, VFX, audio) that follows the deterministic core.
OnlineSubsystemUnreal's pluggable abstraction over platform online services (LAN NULL, EOS, Steam, custom).
Peer-to-peer (P2P)Topology where each player is equally authoritative; no central server. This plugin is P2P.

19 Rollback Core (OSS) vs Rollback Core Pro

A stripped-down open-source distribution — Rollback Core — is available on GitHub at gregorik/Rollback-Core under the MIT license. It contains the same deterministic simulation, transport, and basic demo. Rollback Core Pro (this product) adds matchmaking, the visual debugger, multi-engine-version packages, additional demos, and direct support.

Feature Rollback Core (OSS) Rollback Core Pro
PriceFree, MITPaid, Fab EULA
Source availableYesYes
Engine versions5.7 verified5.4 / 5.5 / 5.6 / 5.7 packages
SupportCommunity (GitHub Issues)Direct author support
Core simulation
Deterministic fixed-step tick
SaveGame-reflection state capture
RollbackToFrame API
Per-frame ring-buffer history
Network transport
UDP transport
Input redundancy + reliable ACK
Multi-peer (up to 8)
Heartbeats + peer-timeout detection
Simulated packet loss + latency
Matchmaking
OnlineSubsystem LAN session discovery
OnlineSubsystem EOS / Steam ready
CreatePlatformSession / Find / Join
Editor tooling
Visual frame-scrubber debugger
Per-frame state ghost rendering
Desync inspector with checksum diff
Setup wizard on first project load
Demos & content
Basic top-down demo with correction markers
Network-packet-loss simulator demo
2D Viking fighter demo (sprite, hitboxes, combat)

When to use which

FREE / MIT

Rollback Core (OSS)

  • Prototyping rollback into an existing project
  • You already have matchmaking (Steamworks, EOS, custom backend)
  • Comfortable building your own debug UI
  • Open project or commercial title that can't accept a marketplace EULA
  • Only targeting one engine version
COMMERCIAL · FAB

Rollback Core Pro

  • OnlineSubsystem-backed matchmaking out of the box
  • Visual frame-scrubber debugger for desync hunting
  • 5.4–5.7 binary packages without maintaining backports
  • Worked 2D fighter demo as a starting point
  • Time-to-first-rollback-match matters more than license cost