Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: NetworkTickManager - Calculating tick predictions and controlling physics ticks to synchronize the server with the client. #3943

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

NiftyliuS
Copy link

This is a phase 1 of creating a fully Tick oriented setup.

What is the goal:

The goal is to allow a convenient solution to synchronize the Prediction tick and Server Replay ticks to allow for smooth
and CONSISTENT player experience when running server authoritative architecture while minimizing the latency based on network conditions such as packet loss.

The Network Tick Manager and Network Physics Controller are designed to work in tandem to ensure that the client-predicted tick aligns closely with the server's execution of that tick.
Additionally, it will adjust the server replay tick on the client to ensure that the server replay consistently has the necessary information from the server for accurate playback.

note: The actual timing doesn't matter and can be un-equal for sending and receiving.
All we care about is that my current client inputs arrive at the correct time on the server
and that the server send me the information before i replay server characters actions on the client.

In addition the NetworkTick will be available for easy access to the current status and tick counters.
See its description bellow.

What are the effects

There are 2 main effects of this:

  1. Visible - Client prediction tick adjustments happen by pausing ( skipping ) or fast forwarding ( executing multiple simulations) of the Physics engine.
  2. Hidden - server replay tick on the clients will be adjusted without changing or touching the physics engine but right before simulation occurs.
  3. Reconcile - When a reconcile tick is registered the NTM will roll back the tick counters, set a flag on NetworkTick and run thse ticks again BEFORE proceeding with the next simulation.

How to add:

Make sure that the physics is set to script instead of fixedUpdate.
Attach NetworkTickManager and PhysicsController to an Entity.

Physics Controller

To request tick to reconcile from:

RequestReconcileFromTick.RequestReconcileFromTick( int reconcileStartTick )

Physics controller is also built in a way that allows for adjusting for non unity built physics. For example:

  public class KinematicPhysicsController: NetworkPhysicsController{
    public override void PhysicsTick(float deltaTime) {
      KinematicCharacterSystem.PreSimulationInterpolationUpdate(deltaTime);
      KinematicCharacterSystem.Simulate(deltaTime, KinematicCharacterSystem.CharacterMotors, KinematicCharacterSystem.PhysicsMovers);
      KinematicCharacterSystem.PostSimulationInterpolationUpdate(deltaTime);
      Physics.Simulate(deltaTime);
    }
  }

NetworkTick

The class NetworkTick is there with static fields to allow for access across the project and consists of several static getters:

// When packet loss is detected and compensated for  you can access their counters with
NetworkTick.ClientToServerPacketLossCompensation;
NetworkTick.ServerToClientPacketLossCompensation;
// This is crucial to send current inputs + past inputs when packet loss is present in addition to instruction the server
// to do the same using "reliable" flag.
// NOTE: These are only availabe on CLIENT and not the server. The server does not keep this information.


NetworkTick.IsServer;               // indicates if the instance has a server running
NetworkTick.IsSynchronizing;  // In the middle of synchronization with the server
NetworkTick.IsSynchronized;   // Is the client synchronized with the server
NetworkTick.IsReconciling;     // Is the currently reconciling - used to apply stored inputs vs current ones on client for example

// Note: These ticks are 0-2047 and are looping to reduce network overhead.
NetworkTick.CurrentTick;  // Will return Client or Server tick depending on IsServer
NetworkTick.ClientTick;    // Will return the current Client tick ( predicted tick )
NetworkTick.ServerTick;   // Will return the Server tick ( Replay on client and executing tick on server )

// These are absolute ticks from beginning of the session
NetworkTick.CurrentAbsoluteTick;  // Will return Client or Server tick depending on IsServer
NetworkTick.ClientAbsoluteTick;     // Will return the current Client tick ( predicted tick )
NetworkTick.ServerAbsoluteTick;     // Will return the Server tick ( Replay on client and executing tick on server )

// If you want to work with 0-2047 ticks you have two functions for correct calculations:
NetworkTick.SubtractTicks(tickOne, tickTwo);  // this will calculate the looping ticks correctly
NetworkTick.IncrementTick(tick, increment);   // Will correctly increment the tick with the looping in-mind

Implementing NetworkedItems and NetworkPhysicsEntity

To make your entities networked the INetworkedItem interface was added:

// Called before the main network update, allowing the item to perform any necessary preparation or pre-update logic.
void BeforeNetworkUpdate(int deltaTicks, float deltaTime);


// Called during the main network update, allowing the item to handle core updates 
// related to network state, physics, or entity positioning.
void OnNetworkUpdate(int deltaTicks, float deltaTime);


// Called after the main network update, allowing the item to perform any necessary cleanup or post-update logic.
void AfterNetworkUpdate(int deltaTicks, float deltaTime);

Usage example:

public class NetworkPlayerController : NetworkBehaviour, INetworkedItem{
    private void OnEnable() {
      NetworkPhysicsEntity.AddNetworkEntity(this, 1);
    }

    private void OnDisable() {
      NetworkPhysicsEntity.RemoveNetworkEntity(this);
    }
    
    public void BeforeNetworkUpdate(int deltaTicks, float deltaTime) {
        // for example: Position the character and clean up last ticks data if needed
        // if NetworkTick.IsReconciling use server character position instead
    }
    
    public void OnNetworkUpdate(int deltaTicks, float deltaTime) {
        // for example: Apply the most recent player inputs
        // if NetworkTick.IsReconciling -> set inputs from history
    }
    
    public void AfterNetworkUpdate(int deltaTicks, float deltaTime) {
        // for example: Compare ticks to server history and send inputs to the server
        // if NetworkTick.IsReconciling -> dont send data to server
    }
    
    // When to send the data to sever is up to the developer - im not sure whats best myself yet.
    // Regardless it is safe to send ticks both in before and after the simulation
   ...

How the numbers are calculated:

Client will send his tick to the server and the server will simply return how far the client tick is from the server.
image

The client will then adjust based on the available data and running minimums.

Same goes for the server but instead of sending to the server we just compare current reply tick with what we receive from the server
image


Notes:

While testing the most likely tick offset between client predicted and server replay is going to be 2-3.
This is due to Unity fixedUpdate being tied to the framerate and Mirrors optimizations ( batching ).

image

While 1 tick is possible it is very rare on high tick systems ( 50+ ticks )
image

@MrGadget1024 MrGadget1024 added the enhancement New feature or request label Nov 12, 2024
@miwarnec
Copy link
Collaborator

that's gonna take us some time to review, but thanks for sure!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants