When entities are culled due to Potentially Visible Sets (PVS), a player moving fast enough near a vis brush may appear to pop into existence out of thin air. This occurs because of latency. A simple fix would be for the QVM code to extrapolate the player’s position into future frames to determine whether the entity should be culled.
The original Q3 netcode lacked lag compensation for hitscan weapons. As a result, players often had to "lead" their crosshair to register proper hits. This issue was later addressed by the Unlagged mod, which introduced lag compensation.
Input commands had a back buffer to handle lost input frames, but instead of applying them with a delay, the inputs were applied to entities instantly. Consequently, other players' entities sometimes appeared "choppy" when the respective player had a poor connection.
That being said, they got almost everything right:
* Snapshots with entities: Efficiently delivering game state updates to clients.
* Lerping entities: Smoothing movement on the client side using a snapshot buffer.
* Delta encoding: Transmitting only changes in entity states within snapshots.
* Huffman encoding: Compressing snapshots for reduced bandwidth usage.
I can't speak for CMPA or QL, but I can speak a little bit on Unlagged:
- w/ Unlagged, the Server now stores a history buffer (g_activeSnapshots[]) of player states. When processing a action shot, it uses the timestamp sent by the client (cmd.serverTime) to interpolate or rewind states for collision checks (G_EvaluateTrajectory()). This allows the server rewinds player positions to the moment a client action occurred (eg: firing a shot)
- Unlagged reduces choppy movements by making clients use a snapshot buffer (cg.snap, cg.nextSnap) to interpolate positions with CG_InterpolateEntityPosition(). Extrapolation uses velocity vectors from the last known snapshot (currentState.pos.trDelta).
- Unlagged tries to compensate for high latency by rewinding entities to their positions at the time of the shot (ClientThink_real() and G_RewindEntities()). Uses a circular buffer (via lagometerSnapshot_t) to store past states.
- Position corrections (cg.predictedPlayerState) are interpolated over several frames using VectorLerp() to fix prediction mismatches between client and server.
- Commands are stored in a circular buffer (ucmds[]) and replayed when missing packets are detected (ClientThink_cmd()). This attempts to mitigate packet loss by buffering input commands for later execution
Also; The server calculated projectile trajectories independently of client-side predictions.It was buggy on certain? versions. We (OSP) noticed this on earlier Quake 3 releases and it may have been fixed.
This results in visible "misfires" where projectiles didn't behave as expected
When entities are culled due to Potentially Visible Sets (PVS), a player moving fast enough near a vis brush may appear to pop into existence out of thin air. This occurs because of latency. A simple fix would be for the QVM code to extrapolate the player’s position into future frames to determine whether the entity should be culled.
The original Q3 netcode lacked lag compensation for hitscan weapons. As a result, players often had to "lead" their crosshair to register proper hits. This issue was later addressed by the Unlagged mod, which introduced lag compensation.
Input commands had a back buffer to handle lost input frames, but instead of applying them with a delay, the inputs were applied to entities instantly. Consequently, other players' entities sometimes appeared "choppy" when the respective player had a poor connection.
That being said, they got almost everything right:
* Snapshots with entities: Efficiently delivering game state updates to clients.
* Lerping entities: Smoothing movement on the client side using a snapshot buffer.
* Delta encoding: Transmitting only changes in entity states within snapshots.
* Huffman encoding: Compressing snapshots for reduced bandwidth usage.