DaemonEngine / Daemon

The Dæmon game engine. With some bits of ioq3 and XreaL.
https://unvanquished.net
BSD 3-Clause "New" or "Revised" License
298 stars 61 forks source link

Entity baselines are of questionable utility #1321

Open slipher opened 3 days ago

slipher commented 3 days ago

When the server transmits an entity state to a client, it is always delta encoded against some base entity state. This base entity state is one of the following, in order of priority:

  1. The entity's state in the client's most recently acknowledged snapshot
  2. The entity's state in the "entity baseline"
  3. An all-zeroes entity state

I think (1) should be used the majority of the time under normal conditions. But when an entity first enters the client's PVS, (1) won't be available. Assuming entities stay in roughly the same position from frame to frame, and the network is not too flaky, (1) will start being available after a delay roughly equal to the client's ping. I assume (2) is intended to make things more efficient during that window.

This issue is about (2). So what is the entity baseline? I always imagined that entity baselines and delta compression work like keyframes in a video: periodically you get a new baseline/keyframe, and then for the next few seconds each frame is encoded via the diff from that base. (1) is kind of like that, but (2) is stupider: a baseline is taken only once, when the map is first loaded. It's never updated, not even on a map_restart (the cause of the inconsistent behavior in https://github.com/Unvanquished/Unvanquished/issues/2124). It's like if the entire video were encoded based on diffs from the first frame. This was probably good for Quake, since most entities that existed at the start could never be destroyed. But we have buildables that can be destroyed and moved, and beacons for those buildables. Entity numbers are recycled, so it's likely that the baseline entity with a given number isn't even the same type as the currently existing entity. It's likely that using the entity baseline results in more bandwidth consumption in such cases.

What should we do about this? The easiest thing would be to NUKE or disable baselines. Maybe they can be considered an unnecessary micro-optimization from today's standpoint. If not, we could try some of the following ideas:

slipher commented 1 day ago

@DolceTriade and I tested this on a server, having around 200 ping. On a torture test layout with around 600 buildables in a single room, baselines do have a significant advantage. With baselines turned off, the lag spike during the interval between the buildables entering the PVS and the client acknowledging the first snapshot is very noticeable. A bad thing is that the server has to keep sending the entities' full state every frame for several frames during this period, since there is not yet an acknowledged snapshot to delta against.

Note that the baseline advantage is only present when the entities already existed when the map started. If the layout was loaded with /layoutload, or if the buildables were manually constructed, it's the same as having no baseline. We would like to come up with a new design that works well for the latter scenario too. The tower defense mod is an example of a situation where you might construct hundreds of buildables in one room that aren't in the initial layout.

We discussed a couple possibilities based on transferring base states when entities enter PVS: (A) Get rid of the concept of baselines, and instead allow using entity deltas against arbitrarily old snapshots. This means there would be a lag spike the first time you enter a room full of entities, but once you have seen the entities for the first time, it would be smooth the next time you enter. (B) Lazily transmit entity baselines the first time an entity enters the PVS. The advantage over (A) is that the full entity states would only have to be transferred once, not several times while we await a snapshot. The drawback is that if the baseline packet were to be dropped or arrive late, some snapshots would not be fully readable. If we got a snapshot without having all up-to-date baselines, we would have to either discard it, or render it with some entities missing.

Another idea is to just continuously keep clients updated with the baseline for all entities, but we didn't like that this would disclose secret information about the enemy base location.

If option (B) is implemented, we need the sgame to have some control over baseline management. A simple idea is to set a baseline whenever a new entity spawns, if it is of a type that is likely to exist for a long time. Though for buildables it seems better to wait until construction finishes first. So we'd need some flags and/or trap calls to control baselines.