United Offensive network tuning¶
Executive recommendations¶
There is no single best value for every connection. For a stable broadband client, this is a coherent balanced profile:
seta com_maxfps 125
seta cl_maxpackets 62
seta cl_packetdup 1
seta rate 50000
seta snaps 30
set cl_timeNudge 0
set cl_nodelta 0
set cg_nopredict 0
At an ideal 8 ms client-frame cadence, cl_maxpackets 62 has a 16 ms packet
threshold and therefore sends on every second frame: approximately 62.5 client
packets per second. rate 50000 is high enough that even the largest packet
size charged by the stock snapshot scheduler does not rate-delay a 30-snapshot
request. snaps 30 requests the server-side maximum, but a stock
sv_fps 20 server can still produce only about 20 snapshots per second.
Use a different profile when its tradeoff is intentional:
Purpose |
|
|
|
|
Tradeoff |
|---|---|---|---|---|---|
Stock baseline |
|
|
|
|
Conservative bandwidth use; already aligned with the stock 20 Hz server. |
Balanced broadband |
|
|
|
|
About 62.5 outgoing packets/s with enough receive allowance for the worst scheduler-accounted packet at 30 snapshots/s. |
Minimum command batching |
|
|
|
|
One packet opportunity per 10 ms client frame; more packet overhead and server packet-processing work. |
Constrained connection |
|
|
|
|
Fewer datagrams and a lower server-to-client allowance; large snapshots can be rate-delayed. |
Keep cl_packetdup 1 for these profiles. Try 0 only on a demonstrably
clean path when minimizing uplink bytes matters. Try 2 when actual packet
loss is visible. Increasing it does not reduce ping or snapshot jitter.
For a server, choose one of the two deliberate tick/snapshot models:
Purpose |
|
Client |
|
Tradeoff |
|---|---|---|---|---|
Stock-aligned |
|
|
|
50 ms game frames and at most about 20 snapshots/s. |
Higher update rate |
|
|
At least |
33 ms integer game frames and about 30.3 snapshot opportunities/s; greater CPU and uplink demand. |
sv_maxRate 0 means that the server imposes no additional cap. Stock
clients are still clamped to a maximum requested rate of 90000. A hosted
server should use a nonzero cap when the aggregate uplink budget requires one.
Stock defaults and enforced domains¶
Cvar |
Stock default |
Effective domain |
What it controls |
|---|---|---|---|
|
|
Positive values use |
The client main-loop frame gate. One user command and at most one normal packet opportunity are produced per rendered client frame. |
|
|
Clamped to |
Minimum elapsed time between normal client packets to a non-LAN server. |
|
|
Clamped to |
How far back in sent-packet history the next packet reaches when selecting user commands to include. |
|
|
Server clamps to |
The client’s requested server-to-client scheduling allowance. It does not cap client FPS or client packet rate. |
|
|
Server clamps to |
The client’s requested snapshot interval, stored as |
|
|
Runtime clamp |
Moves client presentation time relative to the synchronized server timeline. |
|
|
Boolean use |
Requests full rather than delta-compressed server snapshots. |
|
|
Boolean use |
Disables local movement prediction and uses interpolation. |
|
|
Boolean use |
Enables local item-pickup prediction; it is also sent in userinfo. |
|
|
Values below |
Integer server-frame quantum and game-module update cadence. |
|
|
|
Caps each remote client’s effective server-to-client rate. |
None of these stock tuning cvars carries CVAR_CHEAT. The archive flag is
separate from cheat protection: rate, snaps, cl_maxpackets,
cl_packetdup, and com_maxfps are archived, whereas such diagnostic or
runtime controls as cl_timeNudge are not.
The stock multiplayer menu applies only coarse connection presets. When its
ui_setRate action sees rate >= 5000, it selects
cl_maxpackets 30 and cl_packetdup 1. From 4000 through 4999 it
selects 15 and 2; below 4000 it selects 15 and 1. These
are bandwidth-era presets, not evidence that 30 packets/s is an engine limit.
The four independent clocks¶
Tuning is easier when the four rates are kept separate:
Client render/user-command rate.
CL_CreateNewCommandscreates one user command each active client frame.Client packet rate.
CL_ReadyToSendPacketcan send only when a client frame calls it and thecl_maxpacketselapsed-time threshold has passed.Server game rate.
SV_Frameruns the game VM in quanta of1000 / sv_fpsinteger milliseconds.Server snapshot rate.
SV_SendClientMessagesconsiders a client only after a server frame and only when that client’s snapshot/rate timer is due.
Therefore:
com_maxfpsdoes not set packet rate directly;cl_maxpacketsdoes not set snapshot rate;snapsdoes not set the authoritative game tick;sv_fpsdoes not directly cap incoming client packets; andmatching all four numeric values is neither required nor generally possible.
Client FPS and cl_maxpackets¶
The stock client performs both limits with integer millisecond division:
client frame threshold = floor(1000 / com_maxfps) ms
packet threshold = floor(1000 / cl_maxpackets) ms
The packet test runs once per client frame. If a stable client frame period is
F milliseconds and the packet threshold is P, the idealized packet
cadence is:
frames per packet = ceil(P / F)
packet interval = frames per packet * F
packets per second ~= 1000 / packet interval
Actual rendering load, timer granularity, and frame-time jitter can only alter
the observed result. The formula explains why raising cl_maxpackets can
produce no change: a packet still waits for the next rendered frame.
|
Frame threshold |
|
Packet threshold |
Frames/packet |
Approx. packets/s |
|---|---|---|---|---|---|
|
11 ms |
|
33 ms |
3 |
30.3 |
|
11 ms |
|
10 ms |
1 |
90.9 |
|
10 ms |
|
10 ms |
1 |
100 |
|
8 ms |
|
33 ms |
5 |
25 |
|
8 ms |
|
16 ms |
2 |
62.5 |
|
8 ms |
|
10 ms |
2 |
62.5 |
|
5 ms |
|
10 ms |
2 |
100 |
|
4 ms |
|
12 ms |
3 |
83.3 |
|
4 ms |
|
10 ms |
3 |
83.3 |
Two unintuitive consequences are important:
At a stable 125 FPS, changing
cl_maxpacketsfrom 62 to 100 does not create 100 packet opportunities per second; both thresholds are first met on the second 8 ms frame.At a stable 250 FPS, the stock maximum
cl_maxpackets 100is first met on the third 4 ms frame, yielding about 83.3 packets/s rather than 100.
Choose a client FPS cap the machine can sustain, then choose the packet cadence.
Do not select a higher cl_maxpackets merely because its number looks closer
to the FPS cap. A stable aligned cadence is more predictable than a nominally
higher value crossed irregularly by frame boundaries.
The dedicated-server tick is not an additional gate inside
CL_ReadyToSendPacket. The server processes a valid packet when its network
event is dispatched and passes each newer bundled user command to
GAME_CLIENT_THINK. Sending faster than sv_fps can therefore reduce the
time commands wait on the client, even though authoritative game frames and
outgoing snapshots still occur at the server cadence. Conversely, matching
cl_maxpackets 20 to sv_fps 20 is not required: packets can bundle more
than one user command.
LAN exception¶
For a loopback or LAN address, the stock client bypasses the
cl_maxpackets threshold and permits a packet on every client frame. The
server also bypasses its remote rate/snapshot timer for LAN addresses and makes
the client due again immediately. Delivery is still considered only when the
server reaches SV_SendClientMessages, so the server frame remains the
snapshot ceiling.
cl_packetdup and command redundancy¶
cl_packetdup does not transmit identical copies of a UDP datagram. Each
new client packet chooses its user-command starting point from an older
outgoing-packet history entry:
history sequence = outgoing sequence - cl_packetdup - 1
With cl_packetdup 0, the packet includes commands created since the
immediately preceding packet. With 1, it reaches one packet farther back,
so recently sent commands are carried again. Values through 5 increase
that overlap. Reliable text commands use their own acknowledgement/retry
window and are not the reason to raise cl_packetdup.
The movement command section has a hard limit of 32 commands per packet. A useful approximation is:
commands selected per packet ~=
(1 + cl_packetdup) * rendered FPS / actual client packets per second
Keep that result comfortably below 32. High FPS combined with a low packet
rate and a large duplication value reaches the cap; the client then prints
MAX_PACKET_USERCMDS and retains only the newest 32-command window. More
duplication is therefore not unconditionally safer.
Recommended use:
1is the balanced stock default and should remain the starting point;0reduces redundant movement data on a clean, bandwidth-constrained uplink;2can cover a wider command history when packet loss is observed; andvalues
3..5should be reserved for diagnosed loss after checking the 32-command budget.
rate and snapshot bandwidth¶
rate is a byte-per-second request sent in userinfo. For an Internet
client, the server clamps it to 1000..90000 and then applies
sv_maxRate when that cvar is nonzero and lower. Define:
effective rate = min(client rate, nonzero sv_maxRate)
The server’s snapshot scheduler charges 48 bytes of packet overhead and caps the message-size term at 1500 bytes:
charged size = min(compressed packet size, 1500) + 48
rate delay = floor(charged size * 1000 / effective rate) ms
next delay = max(rate delay, client snapshot interval)
The due time is subsequently observed only on a server frame. rate is
therefore an allowance, not a guaranteed usage level and not a packet count.
Raising it does not lower propagation latency or repair packet loss. It only
prevents the server’s byte scheduler from spacing packets more widely and also
permits faster stock in-band downloads.
For the largest 1500-byte size charged by the scheduler:
Effective |
Computed delay |
Result on 20 Hz server |
Result on 30 Hz server |
|---|---|---|---|
|
61 ms |
Quantized to 100 ms, about 10 snapshots/s |
Quantized to 66 ms, about 15.2 snapshots/s |
|
51 ms |
Quantized to 100 ms, about 10 snapshots/s |
Quantized to 66 ms, about 15.2 snapshots/s |
|
49 ms |
50 ms, about 20 snapshots/s |
Quantized to 66 ms, about 15.2 snapshots/s |
|
30 ms |
50 ms, about 20 snapshots/s |
33 ms, about 30.3 snapshots/s |
|
17 ms |
50 ms, about 20 snapshots/s |
33 ms, about 30.3 snapshots/s |
This table is a worst case for scheduling because ordinary delta snapshots can
be smaller. At rate 25000, a packet up to roughly 1201 charged message
bytes avoids slowing a 50 ms request, while only roughly 776 message bytes fit
without slowing a 33 ms request. Thus the stock 25000 default can be
adequate for ordinary 20 Hz play but cannot guarantee that a large snapshot
will avoid rate delay.
Use rate 50000 when the path can sustain it and the goal is unthrottled
30-snapshot gameplay. Use 90000 when maximizing the permitted in-band
transfer/fragment pace is also useful. Retain or lower 25000 when the
connection or server budget requires it; requesting bytes the path cannot
carry merely moves loss and queueing outside the engine.
Server operators should budget sv_maxRate per active client. As a
conservative allowance calculation, 20 clients at 50000 bytes/s represent
1,000,000 bytes/s (about 8 Mbit/s) before additional transport and
connectionless traffic. Setting sv_maxRate 0 allows each stock client to
request as much as 90000 bytes/s; a nonzero cap should reflect the server’s
real sustained uplink rather than a desired ping value.
snaps and sv_fps¶
The server converts the userinfo snaps value to an integer interval after
clamping it to 1..30:
snapshot interval = floor(1000 / snaps) ms
It also converts sv_fps to a server-frame quantum:
server frame = floor(1000 / sv_fps) ms
Snapshot due times are checked only after a server frame. Ignoring rate delay, the idealized remote-client result is therefore:
actual snapshot interval =
ceil(snapshot interval / server frame) * server frame
When rate scheduling is active, replace snapshot interval in that formula
with the larger of the snapshot interval and the computed rate delay.
|
Server frame |
|
Requested interval |
Actual interval |
Approx. snapshots/s |
|---|---|---|---|---|---|
|
50 ms |
|
50 ms |
50 ms |
20 |
|
50 ms |
|
33 ms |
50 ms |
20 |
|
40 ms |
|
50 ms |
80 ms |
12.5 |
|
40 ms |
|
40 ms |
40 ms |
25 |
|
33 ms |
|
50 ms |
66 ms |
15.2 |
|
33 ms |
|
33 ms |
33 ms |
30.3 |
|
25 ms |
|
33 ms |
50 ms |
20 |
|
16 ms |
|
33 ms |
48 ms |
20.8 |
This is why sv_fps 30 and snaps 30 form a useful high-rate pair, while
raising sv_fps to 40 or 60 does not produce 40 or 60 snapshots per second:
the client request is capped at 30 and the due time is rounded up to a whole
server frame. It is also why a client using snaps 20 can receive fewer
than 20 snapshots/s on a 25 or 30 Hz server. The two integer intervals do not
accumulate fractional remainder; the next due time starts from the frame on
which the last packet was sent.
sv_fps also controls calls to the game module’s frame update, not just
network output. Raising it increases authoritative simulation work, snapshot
construction opportunities, and bandwidth demand. Leave it at the stock 20
unless the server is intentionally provisioned and tested for a different
cadence. If choosing 30, tell clients to use snaps 30. Values above 30
can change game-update cadence but cannot overcome the stock 30-snapshot
request ceiling.
Prediction and presentation latency¶
Prediction switches¶
Keep cg_nopredict 0 and server g_synchronousClients 0 for ordinary
Internet play. With either no-prediction/synchronous mode active, the cgame
interpolates the player instead of replaying buffered user commands locally.
The server’s synchronous mode also defers real client movement processing to
the game frame. These are behavior/debug choices, not remedies for a poor
connection.
Keep cl_nodelta 0. Setting it causes move packets to tell the server not
to use an acknowledged snapshot as the delta base, increasing snapshot data.
It is useful for protocol diagnosis, not routine tuning. Keep
cg_predictItems 1 unless testing item-prediction behavior. It selects
predicted versus ordinary pickup-event handling; it is not a packet- or
snapshot-scheduling control.
pmove_fixed and pmove_msec affect movement time quantization on both
the server and predicting client. Their stock defaults are 0 and 8;
pmove_msec is forced into 8..33 when movement runs. They are not
packet- or snapshot-rate controls and should not be changed as generic network
optimizations.
Diagnosing before changing values¶
The stock client and server expose enough diagnostics to identify the limiting stage:
cg_lagometer 1Displays snapshot ping and loss history. A received snapshot is green when it is not marked rate-delayed, yellow when the snapshot flag carries the server’s rate-delayed bit, and a missing snapshot sample is red. Repeated yellow samples point to
rate/sv_maxRateor packet size; red samples indicate missing snapshot sequence entries and should not be treated as a request simply to raiserate.cl_showSend 1Prints the number of user commands selected for a packet and the transmitted packet size; a dot marks a client frame on which the packet-rate gate did not allow a send. It is the direct way to verify FPS/
cl_maxpacketsquantization and whether duplication is approaching the 32-command cap.showpackets 1andshowdrop 1Print netchan send/receive sequencing, fragmentation, dropped sequences, and fragment errors. Use them temporarily because the console volume is high.
net_profile/net_showprofile/net_dumpprofileEnable and display or dump the stock per-channel byte, packet-size, and fragmentation statistics. The active profile side is selected from the current client/server runtime state and the cvar value.
sv_showAverageBPS 1Produces a server-side rolling report of compressed bytes per client, aggregate bytes, peak packet size, and compression ratio. The report is emitted through the developer-print path, so console developer visibility must also permit it.
Return diagnostics to 0 after measuring. They report behavior; none
improves the connection.
Decision guide¶
Observation |
Change to consider |
Do not assume |
|---|---|---|
Yellow rate-delayed lagometer samples |
Raise client |
Raising |
Red dropped-snapshot samples |
Investigate loss, path congestion, and fragmentation; sufficient spare
downstream capacity may justify lowering |
A higher |
Too many commands in each outgoing packet |
Raise actual packet cadence, lower rendered FPS, or reduce
|
The configured |
High client-to-server input queueing on a clean link |
Select an aligned FPS/packet pair such as 125/62 or 100/100. |
|
Smooth snapshots but delayed visual response |
Confirm prediction is enabled and return positive |
|
Jitter/extrapolation with no packet loss |
Test a small positive |
A negative nudge is safer; it spends interpolation buffer. |
Server uplink saturation |
Set a sustainable |
Raising |
Settings that do not optimize gameplay traffic¶
Leave the following at their defaults unless administering their specific policy:
sv_minPingandsv_maxPingonly reject non-LAN clients during connection based on challenge ping. They do not improve an accepted client’s latency.cl_timeoutandsv_timeoutdecide when silent peers are considered timed out. They do not change retransmission or packet cadence.sv_zombietimecontrols how long a disconnected client remains in zombie state.sv_padPacketsappends literal padding bytes to snapshots. Keep it at0outside deliberate transport tests.sv_packet_inforeports connectionless commands; it is not a sequenced gameplay performance control.
Source path map¶
The principal stock behaviors described here are implemented in:
src/client/engine/client/client_lifecycle.c— stock client cvar defaults;src/client/engine/com_frame.c— client frame limiter;src/client/engine/client/cgame_frame.c— user-command generation, packet gate, packet duplication window, time nudge, and prediction clock;src/client/ui/state/ui_update.c— stock connection-rate presets;src/server/engine/server_client_message.c— userinfo rate/snaps clamps and bundled user-command processing;src/server/engine/server_frame.c— integer server-frame scheduling;src/server/engine/server_snapshot_send.c— snapshot due checks, rate equation, rate-delay flag, and fragment pacing;src/client/cgame/hud/lagometer.c— lagometer interpretation;src/client/cgame/prediction/cg_predictplayerstate_internal.candsrc/server/game/client_think.c— prediction and movement-time behavior; andsrc/qcommon/netchan.candsrc/qcommon/net_profile.c— sequencing, fragmentation, drop reporting, and profile statistics.