Sniper's Paradise!


[Players Guide to Unreal Netcode]

6. Advanced Netcode

Contents 

This chapter goes into more detail how Unreal netcode actually works. To understand all details, knowing the basic concept of object oriented programming is helpfull, but hopefully not really neccessary.

So how does Unreal work online? To understand this, you first have to understand the basic problem that all games played over the internet share. This problem is latency - sending data over the net takes time, so it is absolutely impossible to have all players being totally synchronized. That means players will never be able to see exactly the same thing, there always is some kind of delay before player A can see what player B did.

Like most games today, Unreal uses a netcode model centering around an authoritative server. Authoritative means that the server decides what "really" happens; he has the authority over gameplay. If the server "sees" player A hitting player B, player B will take damage - regardless if player B saw A hitting him or even A seeing that he hit B. It also means the server decides where players actually are.

Clients will try to simulate the "real world" of the server as exactly as possible. The accuracy of this simulation obviously depends on ping. To make this simulation as precise as possible, there are basically two ways used in todays online games. One of them is to let the client try to predict the state of the "real game" from the information he has (this is what Unreal does), the other is to let the server "backtrack" the actions of players to the time when they actually took place (HL does this, zeroping, an Unreal mod, tries to do something similar for Unreal). The second model is mentioned here only for information purposes and the zeroping community; it is not relevant for Unreal itself.

Both of these models have pros and cons. With the first model, clients have an outdated version of the real world - other players will have moved further on the server already, and in addition the server gets the clients movement with some delay (both together are the reason why you have to lead your aim with ping). While some other games clients try to extrapolate other players movement by ping (they take the current movement of players and act as if this movement goes on for ping milliseconds; QW is one of these games), Unreal does not. However, since the framerate on clients is usually higher than the number of updates the client gets from the server, Unreal actually extrapolates movement in the same way as mentioned above until it gets a new update on the clients movement. This extrapolation is the reason for seeing players "sliding" when having lag spikes - Unreal keeps extrapolating their "old" movement until it gets updates on their "new" movement.

With the second model, players who see they hit someone on their client will always do so in the "real world" of the server, but since the "real time" on the servers is ahead a bit, it means players will notice they are hit (or even died) some time AFTER they were at the place where they were hit. Any shots they fired after that will not actually have been fired, and in some circumstances they can be already out of sight in their own simulation before they are notified they have been hit.

So now that we know how the basic concept works, let's have a look at how it's implemented. The obvious way to simulate the server world on the client would be to send everything that happens from server to the client. Unfortunately, this would use far too much bandwidth. To reduce bandwidth usage, several things are done; some of them (those who can have an effect on gameplay) will be explained here.

To understand how these methods work, we first have to look at how things are actually represented in Unreal. For everything that exists in the game, there is a template called "class". Each actual object (for everything that is relevant in gameplay, these objects are called "actors" in Unreal) is called an "instance" of such a class. E.g. there is a class for "rockets", and every time a rocket is created (i.e. fired) it will have the properties defined in the class.

Unreal uses various properties for actors to define how they are replicated (sent) to the client (or in the case of player movement, from client to server for the actual player's client and server->client for the other clients).

Back to the bandwidth saving methods used. The first is pretty obvious: If the client is not able to see or hear actors (be it other players, rockets, flakshards or whatever), there is no reason at all to tell the client what they do. Unreal actually checks this (it's called relevancy check) and does not replicate actors that are "out of sight" for the client. The variable "relevanttimeout" in ipdrv.tcpnetdriver on the server defines how long an actor has to be out of sight before it becomes irrelevant to the client. The effects of this relevancy check can be seen when playing (clientside) demos with ?3rdperson (allowing you to spectate the match in freeflight) - players that are not in sight of the player who actually recorded the demo are not visible, you can actually see them disappear when they are not in sight any longer. Of course, as soon as an actor becomes visible (again or for the first time), the server replicates it to the client.

The next important method used is simulating an actors behaviour on the client where possible. This applies to most projectiles (like rockets). When a projectile is fired, the server tells the client where exactly it is spawned (created) and in which direction it is flying. From that point on, the client simulates the projectiles flight without any additional information from the server - the client knows the speed, and since no direction changes happen, there is no reason why the server should have to send the client position/movement updates. This simulation, however, is what causes the "phantom rocket" situations where a client sees his rockets hitting another player without the player dying when he seemingly should. The reason is simple - since the rockets are simulated on the client, the client also decides when they "explode" (visually - damage is of course decided on the server alone). So if the client sees the rockets hit a player, they blow up - but our client world is not exact, especially for other players who may have moved away a bit in the "real world" on the server already. To sum it up - while the rockets miss the "real" other player on the server, they hit the slightly wrong positioned player on the client. You can be sure that you did damage if you see the other player bleed or "being bumped around" because both the blood effect and the player momentum change are done server-side.

Another property used for bandwidth usage is the "netupdatefrequency" which is defined for each class. While some actors (like players movement) obviously need updates as often as possible, some others dont. Gameplay does not take any negative effect when stuff like player scores (which obviously have to be replicated, too, since without them the client would not be able to show them to the player) is not replicated 50 times per second. Netupdatefrequency defines how often actors are replicated to clients (that's a maximum value). Netupdatefrequency is responsible for the delay players notice in their armor status when taking hits - armor is "only" updated 10 times per second, so there may be up to 100 ms delay before a player is informed that his armor is going down. With the highest netupdatefrequency value used being 100, it also becomes obvious why tickrates > 100 are totally useless - nothing will be updated for clients more than 100 times per second anyway.

There is, of course, much more stuff involved in replication, but some of that would be too complex for the purposes of this guide, and others just dont directly influence gameplay.

With all this information, the "channels" and "bunches" values from stat net can be explained easily - channels is the number of actors that currently is relevant to you, while bunches is the number of actor updates (that's not the precise definition, but basically equals that number) that has been recieved in the last second. With these numbers, it is very easy to understand how important the "no variable change - no update" rule is - channels often exceeds bunches by a LOT.

Another thing worth knowing is that Unreal servers send a packet to clients at the end of a tick whenever there is any actor that has an update to be sent. That means that at most times, Unreal does send one packet per tick even though not hardcoded to do so - there always is an actor that has something to replicate. The only reason why number of packets/sec could be lower than actual tickrate during a real Unreal game (not netservermaxtickrate - see the explanation about the problems with linux servers in chapter 5) is that a client would exceed its netspeed limitation on bandwidth with that packet.

<< 5. Server configuration                 7. Credits + Contact >>

Spam Killer

Back To Top
2005 Sniper's Paradise
All logos and trademarks are properties of their respective owners.
Unreal™ is a registered trademark of Epic Games Inc.
Privacy Policy
Website by Softly
Powered by RUSH