contents
This
chapter goes into more detail how UT 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 UT 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, UT 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 UT 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 UT mod, tries to do something similar for UT).
The second model is mentioned here only for information purposes
and the zeroping community; it is not relevant for UT 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), UT does not. However, since the framerate on clients is
usually higher than the number of updates the client gets from
the server, UT 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 - UT 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 UT. 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 UT) 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.
UT
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. UT 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 UT 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, UT 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 UT 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 >>