I'm using Unity with Fishnet networking and trying to make client side prediction work.
I have been asking this question on the discord help channel for 3 days now and I haven't received any response. So I'm posting here in hopes that someone can help me track down what's going on.
I have a [Reconcile]
and [Replicate]
function setup inside a player controller script. I'm using the Unity starter assets and have been referencing this: https://github.com/RidefortGames/FishNet-ThirdPersonPrediction/blob/main/Assets/Script/PredictionMotor.cs
to try and setup a multiplayer client side prediction controller. I have setup a NetworkTransform
, NetworkObserver
, and PredictedObject
on my player controller.
I have registered to the OnTick Event like so:
private void Awake()
{
InstanceFinder.TimeManager.OnTick += TimeManager_OnTick; // Called right before physics sim, liked FixedUpdate
InstanceFinder.TimeManager.OnUpdate += TimeManager_OnUpdate;
_controller = GetComponent<CharacterController>();
}
My OnTick Looks like the following:
private void TimeManager_OnTick()
{
if (base.IsOwner)
{
Reconciliation(default, false);
CheckInput(out MoveData md);
Move(md, false);
}
if (base.IsServer)
{
Move(default, true);
ReconcileData rd = new ReconcileData()
{
Position = transform.position,
Rotation = transform.rotation,
VerticalVelocity = _verticalVelocity,
FallTimeout = _fallTimeoutDelta,
JumpTimeout = _jumpTimeoutDelta,
Grounded = Grounded
};
Reconciliation(rd, true);
}
}
I have my Move(...
setup like:
[Replicate]
private void Move(MoveData md, bool asServer, Channel channel = Channel.Unreliable, bool replaying = false)
{
if (!base.IsOwner) Debug.Log("REPLICATE");
if (asServer || replaying)
{
float delta = (float)base.TimeManager.TickDelta;
JumpAndGravity(md, delta);
GroundedCheck();
MoveWithData(md, delta);
}
else if (!asServer)
_clientMoveData = md;
}
I decided to move my character inside the TimeManager_Update
like the following because it produced smoother movement:
private void TimeManager_OnUpdate()
{
if (base.IsOwner)
{
// NOTE: The "_clientMoveData" is set inside the Tick, but played here.
JumpAndGravity(_clientMoveData, Time.deltaTime);
GroundedCheck();
MoveWithData(_clientMoveData, Time.deltaTime);
}
}
This all works great when I move the owner client and all the animations and movement play on the server. However, if I move a Owner client on the server and look at it on the other client (that isn't also the server client) it will not play any animations.
So I added the following into my [Replicate]
function:
if (!base.IsOwner) Debug.Log("REPLICATE");
and it is never called.
My thought was that replicate and reconcile work like the following:
[Replicate]
[Reconcile]
From what I'm seeing it looks like [Replicate]
and [Reconcile]
are only ever called on the Owner and Server, but never NoneOwners.
Is this right? Am I doing something wrong? Any help on this one would be greatly appreciated as I'm super confused.
Here are some helpful videos demonstrating my issue.
Okay I did get some answer and it doesn't work like I thought it did. Basically this is how it works.
[Replicate]
This will ONLY send data from the owning client to the server. The owning client will run the functions first with the data it sends then the server will receive that data and run the function. Unfortunately the server WILL NOT send that data on to other clients.
[Reconcile]
This will ONLY send from the server back to the owning client. It will not send this data to none-owned clients.
So basically there are a couple of options for me to pursue. You can add a NetworkTransform
component to sync position/rotation/scale, and a NetworkAnimator
to sync the animations. The other alternative is to run your own RPC methods, excluding the owner, to get the clients to perform the actions.
Since the NetworkTransform
and NetworkAnimator
are fairly optimized I will be sticking with this method. I just hope it will work if I plan on running 100 players on a server.