Replication that manages bandwidth by itself, and stops your vehicles, projectiles and physics props from teleporting. You do not change a line of gameplay code.
Every number on this page is measured. How, and on what, is written next to each one in Measured results.
In plain words, before any of the technical detail.
When your game replicates an actor, Unreal sends its position and properties to every client that can see it. Two things about how it does that cost you money and look bad:
AdrenalineNet replaces that layer. It sends the difference from the last state your client actually confirmed, packed to the precision you choose, and it spends your bandwidth on the actors the player is looking at. On the client it plays those updates back through a small buffer, so a lost packet becomes a brief smooth slowdown instead of a jump.
UPROPERTY(Replicated) markup your project already has. No component to
add, no property to re-declare, no macro to sprinkle. Actor spawning, ownership and the transport
stay with Unreal, which is why it keeps working with Steam Sockets, EOS and any custom net driver.
Anyone shipping multiplayer with things that move and are not Characters: racing and driving games, shooters with projectiles, survival games with physics props, co-op with doors, platforms and vehicles, and anything where you have ever been told "it is laggy" and the ping was fine.
Two minutes. Works on a C++ project, and on a Blueprint project that is allowed to compile plugins.
Unzip the download. Copy the AdrenalineNet folder into your project's
Plugins folder, so you end up with:
MyGame/
MyGame.uproject
Plugins/
AdrenalineNet/
AdrenalineNet.uplugin
Source/
Config/
If your project has no Plugins folder, make one with exactly that name.
Start the editor. It will say the plugin's modules are missing and offer to rebuild them. Click Yes and wait. This happens once.
Edit → Plugins, search for AdrenalineNet. The checkbox should already be
ticked, because the .uplugin ships enabled. If it is not, tick it and restart.
There is no step four. The plugin is on by default and starts working on its own. The next section is how to see that it did.
Do this once, on your own project, before you trust any number on this page.
Edit → Project Settings → Plugins → AdrenalineNet, tick Show HUD under Diagnostics.
In the editor toolbar next to Play: set Number of Players to 2 and
Net Mode to Play As Listen Server. Press Play.
On the server window you should see something like this, and the numbers should move:
AdrenalineNet [active] upstream 12.4 kB/s budget 1626 B/tick actors 83 (sent 41) rtt 24 ms jitter 4 ms loss 0.3% interp 96 ms 41.2% BP_Crate.[Location] 18240 B, 1821 sends, 0 dropped
If upstream is above zero and the bottom lines name your own actors, it is working.
Open the console (~) and type ANet.Report. It writes an HTML file
to Saved/AdrenalineNet/ with every property ranked by cost, plus exactly what the
plugin did and did not take over in your project.
Read this even if you read nothing else.
CharacterMovementComponent already predicts movement on the owning client and smooths
it on everyone else, and that code is good. Competing with it would make your game worse, not
better. The gap we fill is everything Unreal does not predict or smooth: vehicles,
projectiles, physics props, doors, platforms, AI pawns without a movement component. That is also
where most of the jerky motion in Unreal multiplayer comes from.
So: your characters behave exactly as before. Everything else gets smooth and cheaper.
What each line of ANet.Stats and the HTML report actually means.
| Line | What it means | What is wrong if… |
|---|---|---|
| upstream | Bytes per second this server is sending through the plugin, measured over a window. | It is 0 while players are connected → nothing is being replicated by us. See Troubleshooting. |
| budget | How many bytes the plugin allows itself this tick. It is derived from the link, not from a setting. | It is stuck at 66 → the floor. The link looks 100 % lossy, which almost always means acks are not arriving. |
| rtt / jitter / loss | Measured round trip, its variation, and packet loss over the recent window. | It says "not measured yet" after ten seconds of play → same cause as above. |
| interp | How far behind the client plays what it receives. Follows measured jitter and loss, so a clean LAN gets a short delay and a bad connection gets the buffer it needs. | It sits at the 300 ms maximum → your connection has heavy jitter, or snapshot rate is set very low. |
| packets sent / acknowledged | In the HTML report. These two should be nearly equal. | Acknowledged is 0 while sent climbs → the report says so in orange. Go straight to Troubleshooting. |
| Received (client report) | Snapshots, data, object updates applied, and updates discarded. | Discarded is a large share of applied → the client's history is too short for this link. Raise snapshot rate or lower it; see FAQ. |
This is the part worth your attention. Every replicated property is listed with its share of the traffic, heaviest first:
share bytes sends dropped property
41.2% 18240 1821 0 BP_Crate.[Location]
22.8% 10096 1821 0 BP_Crate.[Rotation]
9.1% 4032 504 0 BP_Door.bIsOpen
Names in square brackets, like [Location], are the actor's transform. Everything else
is your own property, named as you named it. If something is 40 % of your traffic and you did
not expect it to be, that is your answer - and it is usually one line of code away from being
cheaper.
dropped counts how often a field was skipped because the budget ran out that tick. A few is normal and healthy - that is the plugin spending on what matters. Thousands on a property you care about means your bandwidth ceiling is too low for your scene.
Project Settings → Plugins → AdrenalineNet. Every default is chosen so that you never have to open this page.
| Setting | Default | What it does |
|---|---|---|
| Enabled | on | Off = Unreal's replication, completely untouched. Useful for an A/B test of your own. |
| Measure Engine Instead | off | Sends nothing, only measures what Unreal does. This is how you compare both on the same scene instead of taking our word for it. |
| Position Quantum | 1.0 cm | Precision of replicated positions. 1.0 means positions land on centimetres. Raise it to 5 for big outdoor worlds and save more; set 0 to disable quantization. |
| Rotation Bits | 12 | Bits per rotation component. 12 measures 0.07° of error. 9 is visibly steppy, 16 is wasteful. |
| Velocity Quantum | 1.0 cm/s | Same idea for anything named like a velocity. |
| Max Upstream Per Client | 64 kB/s | A ceiling, not a target. The real budget is measured from the link and is usually far lower. |
| Initial Upstream Per Client | 8 kB/s | What it allows itself before the link has been measured. Deliberately quiet: better to start slow than to flood a weak connection in the first second. |
| Default Cull Distance | 15000 cm | Past this, an actor is irrelevant - unless it just affected the player, which is never ignored. |
| Snapshot Rate | 30 Hz | How often the server produces snapshots. Note Unreal's own NetServerMaxTickRate is 30 too; going above it buys nothing. |
| Interpolate | on | Off = actors snap, exactly like Unreal. Turn it off once to see the difference, then turn it back on. |
| Max Extrapolation | 250 ms | How long the client may guess ahead before holding position. Holding reads as lag; overshooting and snapping back reads as a broken game. |
| Collect Stats | on | Per-property costs for the HUD and the report. Cheap; leave it on. |
| Show HUD | off | The live overlay. |
DefaultEngine.ini, so normally
this is automatic.
| Command | What it does |
|---|---|
ANet.Stats | Live bandwidth, link quality, and the per-property breakdown, into the log. |
ANet.Coverage | Per class: what the plugin took over, and what it left to the engine and why. |
ANet.Report | Writes the full HTML report to Saved/AdrenalineNet/. Takes an optional path. |
These are real failures, with the symptom you will actually see first.
Check the log for this line, which appears once at startup:
LogANetModule: AdrenalineNet: snapshot channel registered on 2 net driver class(es)
If it is missing, the module did not load - check that the plugin is enabled and compiled.
If it is there but upstream stays 0 while clients are connected, run ANet.Coverage.
If it lists nothing, none of your actors qualified: they need bReplicates = true and at
least one UPROPERTY(Replicated) or replicated movement.
This is the one failure that matters most, because the plugin still works - just far more expensively. Without acknowledgements it cannot confirm which state the client holds, so every update degenerates into a full state, and the bandwidth estimator reads the silence as 100 % packet loss and drops the budget to its floor.
Causes, in order of likelihood:
UChannel::ReceivedAck. Rare, but it
happens with hand-rolled transports.ChannelDefinitions at runtime, check the ordering.AdrenalineNet uses static channel index 3. Unreal reserves 0 (Control), 1 (Voice) and 2 (DataStream). If another plugin claims 3, one of you loses.
Change ours in Plugins/AdrenalineNet/Config/DefaultEngine.ini - the
StaticChannelIndex value in the ChannelDefinitions line - and set the
same number on both server and client builds.
The server and this client disagree on a setting that changes the wire format: Position Quantum, Velocity Quantum or Rotation Bits. The plugin refuses the data on purpose - decoding it would place actors at wrong positions with no error anywhere, which is far worse than a clear failure.
Make both builds ship the same values and the message goes away. Note the engine's own replication keeps working the whole time, so the game does not break.
Correct, and deliberate. See The one thing worth knowing. Characters keep
CharacterMovementComponent's prediction and smoothing. Their other replicated
properties are still handled by AdrenalineNet - only the movement is left alone.
Run ANet.Coverage. It prints, per class, every property the plugin declined to take
over and why - object references, arrays, maps, and structs other than vectors and rotations stay
with the engine. Those keep replicating exactly as before, so if one of them is broken, the cause
is not here.
One real exception worth knowing: a RepNotify function that takes the previous
value as a parameter is not called by AdrenalineNet. Plain OnRep_Something() is
called normally, and only when the value really changed.
That is the interpolation buffer, and it is the trade you are making: a small, constant delay instead of occasional jumps. The delay follows your measured jitter, so it is as short as your connection allows.
If you want it shorter, raise Snapshot Rate (more samples, shorter buffer) or accept more snapping by turning Interpolate off. For anything competitive and fast, raise the rate rather than disabling smoothing.
The client keeps the last 64 snapshots to decode deltas against. If updates arrive referring to something older than that, they are discarded rather than applied to the wrong state - which would decode cleanly and silently place the actor wrong.
In practice this needs a round trip longer than two seconds at 30 Hz. If you see it on a normal connection, something is stalling the client's game thread for seconds at a time, and that is worth finding for its own sake.
What was measured, how, and what it does not prove.
A 34-second run, 60 moving replicated actors, identical script in every case, on loopback. The
figure is the engine's own OutTotalBytes counter on the server, so it covers
everything that left the machine - not just what the plugin measures about itself.
| Configuration | Total sent |
|---|---|
| Unreal, default settings | 1 091 kB |
| Unreal, update rate matched to the plugin's 30 Hz | 1 073 kB |
| AdrenalineNet | 403 kB |
Matching the update rate changed almost nothing, because Unreal is already limited by
NetServerMaxTickRate (30 by default). The saving is in how the bytes are encoded and
prioritised, not in sending less often.
| Server | Client | |
|---|---|---|
| Packets | 656 sent, 655 acknowledged, 0.00 % loss | — |
| Link | 14.5 ms round trip, 3.2 ms jitter | — |
| Bandwidth | 18.94 kB/s peak, budget self-raised to 2133 B/tick | 374.5 kB received |
| Updates | 32 424 position updates at 3.1 B each | 30 593 applied, 0 discarded |
| Interpolation | — | 117 ms, 57 actors |
The replication core is plain C++ with no engine dependency and ships with 69 tests, every one of which plants a fault on purpose and fails if the core does not catch it.
Iris is Epic's own new replication system. We have not benchmarked against it and make
no claim about being faster. What is verifiable: in 5.8 it ships under
Plugins/Experimental, and adopting it means moving your gameplay onto
NetObject and ReplicationFragment. AdrenalineNet's advantage over it is
cost of adoption, not bytes.
Where the edges of this version are, so nothing surprises you later.
bReplicateMovement off, because having both systems write the same actor every tick
would defeat the point. If your game reads or toggles that flag at runtime, know that we touch it.
AActor*,
UObject*), arrays, maps, sets, and structs other than vectors, rotators and quats.
ANet.Coverage lists them for your project.Source is included and the plugin compiles itself into your project. A Blueprint-only project works, as long as the machine has Visual Studio with the C++ game development workload so Unreal can build the plugin once. There is nothing to call from Blueprint - it works underneath your game.
Yes. The plugin is a runtime module and runs on dedicated servers, listen servers and clients. It is the dedicated server case where the bandwidth saving turns directly into money.
No, and that is a deliberate design decision. We did not write our own UDP transport precisely so that your existing networking stack keeps working. The plugin registers one additional channel on the driver you already use.
Today it is per project, not per actor: any replicated actor with supported
properties is taken over, except Characters. Per-actor opt-out is on the roadmap. If you need it
now, the source is yours - the decision is made in one function in
ANetSubsystem.cpp.
Your game goes back to Unreal's replication, unchanged. Nothing in your gameplay code depends on the plugin, because you never had to change it to adopt the plugin. That is the point of the design.
The client keeps up to 64 small snapshots per replicated actor and interpolates between two of them per frame. That is cheap - interpolating a transform is arithmetic, and the memory is bounded. The cost that matters in this version is on the server, and it is relevance computation; see Limits.
Something not working, or a number on this page that does not match what you see in your project? Tell us - a report that contradicts our measurements is the most useful mail we can get.
Please include: your engine version, whether it is a listen or dedicated server, and
the output of ANet.Report. That file usually contains the answer.