Prediction and lag compensation
Players move the moment they press a key; the client predicts its own character and quietly reconciles with the server. Hits are judged against what the shooter saw.
Prediction
The client runs its own character through the same fixed-step character step as the server, with the same settings (walk speed, run and crouch multipliers, jump speed, gravity, air control) taken from the level's Character Controller. Inputs go to the server with a sequence number; when the server's authoritative state comes back, the client replays the inputs the server has not seen yet on top of it. Small differences correct themselves without the player noticing.
Clients never send positions or results: they send input and intent, and the server decides.
Remote players
Other players and replicated objects are shown slightly in the past, interpolated between the server's snapshots, so they move smoothly even when packets arrive unevenly.
Lag compensation
For hitscan weapons, a client sends its view time (the server time it shows remote players at) with every shot. The server tests the shot against the players' animated bone hitboxes rewound to that time, within a rewind limit, and against the level as it is now. Damage and effects are applied to the present authoritative state and replicated.
Custom messages
For gameplay actions that are not movement, send named messages. Choose the delivery by what the data is: Unreliable for frequent, disposable updates, Reliable for important events in any order, ReliableOrdered for transactions that must arrive in order.
The server answers in its own script (Dedicated server), checking what the client claims against its own state before changing anything.
CraftingClient.cs
// Client side: ask the server, show its answer. [ClientOnly] public sealed class CraftingClient : EntityScript { protected override void OnUpdate(float deltaTime) { if (!Network.IsClient || !Network.IsConnected || !Input.IsKeyPressed(KeyCode.F)) return; NetworkWriter writer = new(); writer.Write("IronSword"); writer.Write(Transform.WorldPosition); Network.SendToServer("Craft.Start", writer, NetworkDelivery.ReliableOrdered); } protected override void OnNetworkMessage(NetworkMessage message) { if (message.Name != "Craft.Accepted") return; NetworkReader reader = message.CreateReader(); Debug.Log($"crafting {reader.ReadString()} for {reader.ReadSingle():0.0} s"); } }
Watch it
KroysGameClient draws the local predicted player and, when an authoritative state arrives, a ghost of the server's position with a line between them. Small gaps are normal; large ones mean prediction and server rules diverge. Test both under bad conditions with network simulation.
KroysEngine 0.1.0 documentation, built from the engine repository by Tools/BuildDocsSite.py.