FirstGearGames / FishNet

FishNet: Unity Networking Evolved.
Other
1.37k stars 147 forks source link

SyncVars with custom writer/reader fail when set during prediction #60

Closed adamscoble closed 2 years ago

adamscoble commented 2 years ago

The custom struct I've included to reproduce this works perfectly fine as a SyncVar outside of prediction code. However when set from within a [Replicate] function, I get a repeating error on the client only:

Client received an unhandled PacketId of 0. Remaining data has been purged.

The last 3 packets to arrive are: 
Unset
Broadcast
SyncVar

The last parsed NetworkObject is Id 12612 on gameObject CharacterControllerPrediction(Clone), and NetworkBehaviour Test.

To reproduce, add this as Test.cs to the same directory as CharacterControllerPrediction.cs, and add the Test component to the CharacterControllerPrediction prefab:

using FishNet.Object;
using FishNet.Object.Synchronizing;
using FishNet.Serializing;
using FishNet.Transporting;
using UnityEngine;

public class Test : NetworkBehaviour {
    [SyncVar(ReadPermissions = ReadPermission.ExcludeOwner, Channel = Channel.Unreliable, OnChange = nameof(OnCustomSyncVarTypeChanged))]
    CustomSyncVarType _customSyncVarType;

    float _nextChange;

    public void OnTick() {
        if (!IsServer || Time.time < _nextChange) { return; }

        _nextChange = Time.time + 0.5f;

        _customSyncVarType = new CustomSyncVarType((byte)Random.Range(0, 255), TimeManager.Tick);
    }

    void OnCustomSyncVarTypeChanged(CustomSyncVarType prev, CustomSyncVarType next, bool asServer) {
        Debug.Log($"{prev} > {next}");
    }
}

public readonly struct CustomSyncVarType {
    public readonly byte MyByte;
    public readonly uint MyUint;

    public CustomSyncVarType(byte myByte, uint myUint) {
        MyByte = myByte;
        MyUint = myUint;
    }

    public void Serialize(Writer writer) {
        writer.WriteByte(MyByte);
        writer.WriteUInt32(MyUint, AutoPackType.Unpacked);
    }

    public CustomSyncVarType(Reader reader) {
        MyByte = reader.ReadByte();
        MyUint = reader.ReadUInt32(AutoPackType.Unpacked);
    }

    public override string ToString() {
        return $"{MyByte} {MyUint}";
    }
}

public static class CustomSerializer {
    public static void WriteCustomSyncVarType(this Writer writer, CustomSyncVarType value) => value.Serialize(writer);
    public static CustomSyncVarType ReadCustomSyncVarType(this Reader reader) => new(reader);
}

On line 105 of CharacterControllerPrediction.cs after the call to _characterController.Move(), call:

GetComponent<Test>().OnTick();

Make a build, with the build running as host, then connect the Editor as a client. You should see the error intermittently in the console.

tkuebler commented 2 years ago

Also seeing this 1.5.3 pro - network animator, predition, no user syncvars

FirstGearGames commented 2 years ago

Also seeing this 1.5.3 pro - network animator, predition, no user syncvars

Not sure what you mean you're seeing it in network animator. That output is there to help you track down the problem, it doesn't necessarily mean that the latest packets are the problem.

FirstGearGames commented 2 years ago

Make a build, with the build running as host, then connect the Editor as a client. You should see the error intermittently in the console.

I tried reproducing on a smaller sample and was not able to. It looks like the problem might be a broadcast packet though? Does it reliably show the previous packetId as broadcast?

I'll try with your code in the next few days. Here's how I tested on mine, let me know if you see any obvious differences. I dropped it on a player prefab. The IL code looked correct as well.

using FishNet.Serializing;

public class MTT
{
    public int Value;
}

public static class CustomRWThingy
{
    public static void WriteMTT(this Writer w, MTT v)
    {
        w.WriteInt32(v.Value);
    }

    public static MTT ReadMTT(this Reader r)
    {
        return new MTT()
        { Value = r.ReadInt32(), };
    }
}
using FishNet.Object.Prediction;
using FishNet.Object.Synchronizing;
using UnityEngine;

public class PredictionIL : NetworkBehaviour
{
    private struct MD
    {
        public int Frame;    
    }
    [SyncVar]
    private MTT _sv;

    private void Update()
    {
        //being lazy with code.
        if (base.NetworkManager == null)
            return;
        //Dont ever use this for prediction.
        if (!base.TimeManager.FrameTicked)
            return;

        if (base.IsOwner)
        {
            Reconcile(default, false);
            Replicate(new MD() { Frame = Time.frameCount }, false); 
        }
        if (base.IsServer)
        {
            Replicate(default, true);
            Reconcile(new MD() { Frame = Time.frameCount }, true);
        }
    }

    [Replicate]
    private void Replicate(MD data, bool asServer, bool replaying = false)
    {
        _sv = new MTT() { Value = Time.frameCount };
    }
    [Reconcile]
    private void Reconcile(MD md, bool asServer)
    {
        _sv = new MTT() { Value = Time.frameCount };
    }
}
adamscoble commented 2 years ago

Does it reliably show the previous packetId as broadcast?

Sorry just to clarify, what does this mean? I'm sure I've had it as something like

Unset
Unset
SyncVar

before, though I'm failing to achieve it now.

FirstGearGames commented 2 years ago

Could be something else entirely but that unfortunately isn't enough to get me in the right direction. Please update to 156 and see if problem persist.

adamscoble commented 2 years ago

Could be something else entirely but that unfortunately isn't enough to get me in the right direction. Please update to 156 and see if problem persist.

Did you try out the reproduction script I included in your CharacterControllerPrediction scene? It reproduces the error every time.

tkuebler commented 2 years ago

Disappeared for me on upgrade to 1.5.6. don't keep it open on my account.

FirstGearGames commented 2 years ago

Thanks for letting me know.