NETCODE PROTOTYPE
Sep 24, 2024 - Solo School Project - Unity C#
Sep 24, 2024 - Solo School Project - Unity C#
For the longest time it has been my dream to make an online game properly. Here is what I've been working on thus far. 📘
The message that a client has opened fire on another client takes time to reach the server
Therefore, the server needs to wind back time to when the shot was taken ( recreating the client's conditions )
On the server, I have implemented this by saving discreet snapshots of all the players and things that can stop bullets
Combines the best of server authority ( cheat-proof ) and client authority ( responsiveness ), but is more complicated than both combined, too
Things get complicated when the client disagrees with the server
In that case, the server is always right and the client needs to reconcile his present based on the server's account of the past
<-- The blue capsule represents what the client receives from the server.
☆ Here we see how the reconsile feature is implemented.
The interpolator is a component that sits on the camera and smooths out the fixed tickrate of the movement system.
However, in order to have the player get up to speed with the server as fast as possible, we can "interject" where the lerp is headed on that component ( change the end position ).
☆ This is what the server does every tick. In short, it takes client movement input and processes it.
If there is no InputMessage, it will repeat the last one it got instead. The reason I didn't use a queue here is because there is some measure of ordering and reshuffling going on when a message reaches the server. This is because I use the unreliable messaging mode, because this is faster.
One of the main goals that is being reached here is that the player experiences smooth and seamless movement. This is done by having lots of checks and fallbacks tailored to different network conditions.
☆ This is my server-side method for when a player opens fire. The ShootMessage struct contains the data to perfectly recreate the aiming conditions of the client. In RewindTime(), entity is the shooting player's own lag compensation tracking component. This has to be passed so it gets excluded. Shoot() basically needs to happen BETWEEN physics frames. Therefore, we need to use Physics.SyncTransforms() to make it so the raycast can actually hit the "rollbacked" colliders.
private void Move(InputMessage input, bool applyToInterpolation)
{
// RECREATE THE MOVEMENT OF THE PLAYER IN THIS MOMENT.
mouseLook.SetRotationTemporarily(input.xRotation, input.yRotation);
movement.Move(input.GetVerticalInput(), input.GetHorizontalInput(), standardInterval);
if (isLocalPlayer)
{
movementAbilities.ForEach(x => x.CheckAgainstTickClient(input.tick, movement));
EventManager<int>.RaiseEvent(Occasion.DoUnlocalMovementAbilities, input.tick);
// APPLY CHANGES.
Physics.Simulate(standardInterval);
LimitSpeed();
}
// THE SERVER MOVEMENT ABILITIES ARE BATCHED FOR ALL PLAYERS.
// BECAUSE OTHERWISE YOU GET OUT-OF-ORDER BUGS.
if (applyToInterpolation)
cameraInterpolation.Apply(eyes.position, rb.linearVelocity);
}
☆ This is the Move() method that is called the same each tick on both the server and client. Notice the implementation of movement abilities.
<--
For as long as I've wanted to make games, I've wanted to make multiplayer games. By following tutorials, I got something that worked. The more work the server does, the more advanced the multiplayer game. Moving things server-side is a process that highlights the weak spots of a game's netcode; once one thing goes server-side, so does the rest. This is good because client-authoritative games are easy to cheat in.
This was on of my early multiplayer games -->
I began expanding the server's influence. This eventually led me to the frontier of preventing no-regs ( more on that later ). I began working on a server authoritative and client predicted multiplayer movement system. Once I had implemented this, I worked on further perfecting it using my own reasoning. This is the stage where the project is still at, as I had to pivot attention to school assignments.
Here you see a later version of that game -->
This project was not so much about making a multiplayer game as making everything around the game ( the netcode ) work right. That meant going as far as I could on research alone, and then testing my ideas on how to improve what I already have.
A no-reg is when a client registers a hit but the server disagrees
Under normal network conditions, this means the client has mispredicted his position
This can happen when a client has his position externally changed by an explosion for example
Player A cannot predict when player B will affect player A's position over the network, and this causes the "bug"
My goal is to develop a way to have unpredictable knockback while still upholding a zero no-reg policy
Fast-Paced Multiplayer, an article series
This devlog explains what makes netcode complicated
This GDC talk about Overwatch's netcode and architecture