From 652d86a069e097d0dd93981b71b9ed2b59b42eeb Mon Sep 17 00:00:00 2001 From: Zack Darden Date: Thu, 9 Oct 2025 08:56:14 -0400 Subject: [PATCH 01/11] Allow syncobjects to be marked unreliable --- .../NetworkBehaviourSyncProcessor.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs index 3e9db160..d01f17be 100644 --- a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs @@ -1,4 +1,4 @@ -using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Extension; using FishNet.CodeGenerating.Helping; using FishNet.CodeGenerating.Helping.Extension; using FishNet.Configuring; @@ -324,7 +324,7 @@ internal SyncType GetSyncType_V4(FieldDefinition fieldDef, out CustomAttribute s * that the attributes are no longer used. If field is * not a syncvar then error that the user needs to convert to * SyncVar. - * + * * This is to transition users into the new SyncVar. After they change * their field tp SyncVar an error will appear that the * SyncVar/Object attributes are no longer used. */ @@ -648,7 +648,7 @@ private bool CreateSyncVar(uint syncCount, TypeDefinition typeDef, FieldDefiniti /// /// Creates or gets a SyncType class for originalFieldDef. /// - /// + /// private FieldDefinition CreateSyncVarFieldDefinition(TypeDefinition typeDef, FieldDefinition originalFieldDef, out CreatedSyncVar createdSyncVar) { createdSyncVar = base.GetClass().GetCreatedSyncVar(originalFieldDef, true); @@ -756,7 +756,7 @@ private FieldDefinition CreateSyncVarAccessor(FieldDefinition originalFd, FieldD ParameterDefinition calledByUserParameterDef = base.GetClass().CreateParameter(createdSetMethodDef, typeof(bool), "asServer"); processor = createdSetMethodDef.Body.GetILProcessor(); - /* Assign to new value. Do this first because SyncVar calls hook + /* Assign to new value. Do this first because SyncVar calls hook * and value needs to be updated before hook. Only update * value if calledByUser(asServer) or (!calledByUser && !base.IsServer). * This ensures clientHost will not overwrite server value. */ @@ -766,7 +766,7 @@ private FieldDefinition CreateSyncVarAccessor(FieldDefinition originalFd, FieldD //if (calledByUser || !base.IsServer) processor.Emit(OpCodes.Ldarg, calledByUserParameterDef); processor.Emit(OpCodes.Brtrue, beforeChangeFieldInst); - processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Ldarg_0); //this. processor.Emit(OpCodes.Call, base.GetClass().IsServerInitialized_MethodRef); processor.Emit(OpCodes.Brtrue, afterChangeFieldInst); @@ -852,7 +852,7 @@ internal bool InitializeCustom(uint syncCount, TypeDefinition typeDef, FieldDefi sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + channel = attribute.GetField("Channel", Channel.Reliable); } //Set needed methods from syncbase. @@ -915,7 +915,7 @@ internal bool InitializeSyncList_SyncHashSet(uint syncCount, TypeDefinition type sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + channel = attribute.GetField("Channel", Channel.Reliable); } //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. @@ -979,7 +979,7 @@ internal bool InitializeSyncDictionary(uint syncCount, TypeDefinition typeDef, F sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); + channel = attribute.GetField("Channel", Channel.Reliable); } //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. @@ -1415,7 +1415,7 @@ private MethodDefinition CreateSyncVarRead(TypeDefinition typeDef, uint syncInde typeDef.Methods.Add(readSyncVarMd); } - //Already created. + //Already created. else { processor = readSyncVarMd.Body.GetILProcessor(); From 113cdbda2550012a9d04c000c8930684fb1327c7 Mon Sep 17 00:00:00 2001 From: Zack Darden Date: Thu, 9 Oct 2025 08:58:11 -0400 Subject: [PATCH 02/11] Add SyncObject channel argument --- Assets/FishNet/Runtime/Object/Attributes.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/FishNet/Runtime/Object/Attributes.cs b/Assets/FishNet/Runtime/Object/Attributes.cs index 06e513a5..2cd27017 100644 --- a/Assets/FishNet/Runtime/Object/Attributes.cs +++ b/Assets/FishNet/Runtime/Object/Attributes.cs @@ -147,6 +147,10 @@ public class SyncObjectAttribute : PropertyAttribute /// Setting to false will allow inspector serialization of this object. When false you must still initialize this object on it's field declaration, but never anywhere else. /// public bool RequireReadOnly = true; + /// + /// Channel to use. Unreliable SyncObjectcs can be dangerous/inconsistent if used improperly. You should occassionally force a full sync of this object with DirtyAll and/or an RPC to update the object. + /// + public Channel Channel; } /// @@ -179,3 +183,4 @@ public class SyncVarAttribute : PropertyAttribute } } + From 1f1847a51857bc71fa02da6a27fec1dcff0e72ea Mon Sep 17 00:00:00 2001 From: Zack Darden Date: Thu, 9 Oct 2025 09:09:50 -0400 Subject: [PATCH 03/11] Try to make SyncDictionary.cs debuggable ? --- .../FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs index 632db15e..51618121 100644 --- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs @@ -288,8 +288,8 @@ public override void WriteFull(PooledWriter writer) /// /// Reads and sets the current values for server or client. /// - [APIExclude] - [MethodImpl(MethodImplOptions.AggressiveInlining)] + //[APIExclude] + //[MethodImpl(MethodImplOptions.AggressiveInlining)] public override void Read(PooledReader reader, bool asServer) { /* When !asServer don't make changes if server is running. @@ -634,3 +634,4 @@ public SyncDictionary(IEqualityComparer eq) : base(new Dictionary Date: Thu, 9 Oct 2025 10:19:48 -0400 Subject: [PATCH 04/11] Revert "Try to make SyncDictionary.cs debuggable ?" This reverts commit 1f1847a51857bc71fa02da6a27fec1dcff0e72ea. --- .../FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs index 51618121..632db15e 100644 --- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs @@ -288,8 +288,8 @@ public override void WriteFull(PooledWriter writer) /// /// Reads and sets the current values for server or client. /// - //[APIExclude] - //[MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void Read(PooledReader reader, bool asServer) { /* When !asServer don't make changes if server is running. @@ -634,4 +634,3 @@ public SyncDictionary(IEqualityComparer eq) : base(new Dictionary Date: Thu, 9 Oct 2025 14:03:20 -0400 Subject: [PATCH 05/11] Exposes synctypeRate getter --- Assets/FishNet/Runtime/Managing/Server/ServerManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs index 3b1288d4..4f7e4462 100644 --- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs @@ -115,7 +115,7 @@ public void SetRemoteClientTimeout(RemoteTimeoutType timeoutType, ushort duratio /// SyncTypeRate cannot yet be changed at runtime because this would require recalculating rates on SyncBase, which is not yet implemented. /// /// - internal float GetSynctypeRate() => _syncTypeRate; + public float GetSynctypeRate() => _syncTypeRate; [Tooltip("Default send rate for SyncTypes. A value of 0f will send changed values every tick.")] [Range(0f, 60f)] [SerializeField] From a34be66f1765e3db8d89008f5f49d79a4452fad7 Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Thu, 9 Oct 2025 17:51:43 -0400 Subject: [PATCH 06/11] Revert "Add SyncObject channel argument" This reverts commit 113cdbda2550012a9d04c000c8930684fb1327c7. --- Assets/FishNet/Runtime/Object/Attributes.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Assets/FishNet/Runtime/Object/Attributes.cs b/Assets/FishNet/Runtime/Object/Attributes.cs index 2cd27017..06e513a5 100644 --- a/Assets/FishNet/Runtime/Object/Attributes.cs +++ b/Assets/FishNet/Runtime/Object/Attributes.cs @@ -147,10 +147,6 @@ public class SyncObjectAttribute : PropertyAttribute /// Setting to false will allow inspector serialization of this object. When false you must still initialize this object on it's field declaration, but never anywhere else. /// public bool RequireReadOnly = true; - /// - /// Channel to use. Unreliable SyncObjectcs can be dangerous/inconsistent if used improperly. You should occassionally force a full sync of this object with DirtyAll and/or an RPC to update the object. - /// - public Channel Channel; } /// @@ -183,4 +179,3 @@ public class SyncVarAttribute : PropertyAttribute } } - From 3d8631fec2ef22148d62f066610fcf05abacb9d3 Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Thu, 9 Oct 2025 17:51:47 -0400 Subject: [PATCH 07/11] Revert "Allow syncobjects to be marked unreliable" This reverts commit 652d86a069e097d0dd93981b71b9ed2b59b42eeb. --- .../NetworkBehaviourSyncProcessor.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs index d01f17be..3e9db160 100644 --- a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourSyncProcessor.cs @@ -1,4 +1,4 @@ -using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Extension; using FishNet.CodeGenerating.Helping; using FishNet.CodeGenerating.Helping.Extension; using FishNet.Configuring; @@ -324,7 +324,7 @@ internal SyncType GetSyncType_V4(FieldDefinition fieldDef, out CustomAttribute s * that the attributes are no longer used. If field is * not a syncvar then error that the user needs to convert to * SyncVar. - * + * * This is to transition users into the new SyncVar. After they change * their field tp SyncVar an error will appear that the * SyncVar/Object attributes are no longer used. */ @@ -648,7 +648,7 @@ private bool CreateSyncVar(uint syncCount, TypeDefinition typeDef, FieldDefiniti /// /// Creates or gets a SyncType class for originalFieldDef. /// - /// + /// private FieldDefinition CreateSyncVarFieldDefinition(TypeDefinition typeDef, FieldDefinition originalFieldDef, out CreatedSyncVar createdSyncVar) { createdSyncVar = base.GetClass().GetCreatedSyncVar(originalFieldDef, true); @@ -756,7 +756,7 @@ private FieldDefinition CreateSyncVarAccessor(FieldDefinition originalFd, FieldD ParameterDefinition calledByUserParameterDef = base.GetClass().CreateParameter(createdSetMethodDef, typeof(bool), "asServer"); processor = createdSetMethodDef.Body.GetILProcessor(); - /* Assign to new value. Do this first because SyncVar calls hook + /* Assign to new value. Do this first because SyncVar calls hook * and value needs to be updated before hook. Only update * value if calledByUser(asServer) or (!calledByUser && !base.IsServer). * This ensures clientHost will not overwrite server value. */ @@ -766,7 +766,7 @@ private FieldDefinition CreateSyncVarAccessor(FieldDefinition originalFd, FieldD //if (calledByUser || !base.IsServer) processor.Emit(OpCodes.Ldarg, calledByUserParameterDef); processor.Emit(OpCodes.Brtrue, beforeChangeFieldInst); - processor.Emit(OpCodes.Ldarg_0); //this. + processor.Emit(OpCodes.Ldarg_0); //this. processor.Emit(OpCodes.Call, base.GetClass().IsServerInitialized_MethodRef); processor.Emit(OpCodes.Brtrue, afterChangeFieldInst); @@ -852,7 +852,7 @@ internal bool InitializeCustom(uint syncCount, TypeDefinition typeDef, FieldDefi sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = attribute.GetField("Channel", Channel.Reliable); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); } //Set needed methods from syncbase. @@ -915,7 +915,7 @@ internal bool InitializeSyncList_SyncHashSet(uint syncCount, TypeDefinition type sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = attribute.GetField("Channel", Channel.Reliable); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); } //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. @@ -979,7 +979,7 @@ internal bool InitializeSyncDictionary(uint syncCount, TypeDefinition typeDef, F sendRate = attribute.GetField(SENDRATE_NAME, -1f); writePermissions = attribute.GetField(WRITEPERMISSIONS_NAME, WritePermission.ServerOnly); readPermissions = attribute.GetField(READPERMISSIONS_NAME, ReadPermission.Observers); - channel = attribute.GetField("Channel", Channel.Reliable); + channel = Channel.Reliable; //attribute.GetField("Channel", Channel.Reliable); } //This import shouldn't be needed but cecil is stingy so rather be safe than sorry. @@ -1415,7 +1415,7 @@ private MethodDefinition CreateSyncVarRead(TypeDefinition typeDef, uint syncInde typeDef.Methods.Add(readSyncVarMd); } - //Already created. + //Already created. else { processor = readSyncVarMd.Body.GetILProcessor(); From 50bfe1c235b61ae515038c4bf45222c0a3a6d1b5 Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Thu, 9 Oct 2025 14:03:20 -0400 Subject: [PATCH 08/11] Exposes synctypeRate getter --- Assets/FishNet/Runtime/Managing/Server/ServerManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs index 3b1288d4..4f7e4462 100644 --- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs @@ -115,7 +115,7 @@ public void SetRemoteClientTimeout(RemoteTimeoutType timeoutType, ushort duratio /// SyncTypeRate cannot yet be changed at runtime because this would require recalculating rates on SyncBase, which is not yet implemented. /// /// - internal float GetSynctypeRate() => _syncTypeRate; + public float GetSynctypeRate() => _syncTypeRate; [Tooltip("Default send rate for SyncTypes. A value of 0f will send changed values every tick.")] [Range(0f, 60f)] [SerializeField] From d01446d30e5e8ac9051028f77d790c722db0aab5 Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Fri, 10 Oct 2025 09:25:14 -0400 Subject: [PATCH 09/11] Method names + correct size tracking for unreliable RPCs and other updates --- .../Client/Object/ClientObjects.RpcLinks.cs | 12 +++++++++++ .../Managing/Client/Object/ClientObjects.cs | 20 +++++++++++++++---- .../Object/NetworkBehaviour.Prediction.cs | 19 ++++++++++++++++++ .../Runtime/Object/NetworkBehaviour.RPCs.cs | 19 ++++++++++++++++++ .../Profiling/PacketInfoProvider.cs | 18 +++++++++++++++-- 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs index 30f3a667..59b11abe 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs @@ -46,13 +46,25 @@ internal void ParseRpcLink(PooledReader reader, ushort index, Channel channel) //Found NetworkObject for link. if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob)) { + // on unreliable packets, we get -2, which means.. read the rest of the buffer + var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; + NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex]; if (link.RpcType == RpcType.Target) + { + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.TargetRpc, packetReadLength)); nb.OnTargetRpc(link.RpcHash, reader, channel); + } else if (link.RpcType == RpcType.Observers) + { + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.ObserversRpc, packetReadLength)); nb.OnObserversRpc(link.RpcHash, reader, channel); + } else if (link.RpcType == RpcType.Reconcile) + { + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.Reconcile, packetReadLength)); nb.OnReconcileRpc(link.RpcHash, reader, channel); + } } //Could not find NetworkObject. else diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs index cdc8a6da..528b44ee 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -333,7 +333,10 @@ internal void ParseSyncType(PooledReader reader, bool isSyncObject, Channel chan if (nb != null) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)reader.PeekByte(), (PacketId)packetId, dataLength)); + // on unreliable packets, we get -2, which means.. read the rest of the buffer + var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; + + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)reader.PeekByte(), (PacketId)packetId, packetReadLength)); /* Length of data to be read for syncvars. * This is important because syncvars are never * a set length and data must be read through completion. @@ -376,7 +379,10 @@ internal void ParseReconcileRpc(PooledReader reader, Channel channel) if (nb != null) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.Reconcile, dataLength)); + // on unreliable packets, we get -2, which means.. read the rest of the buffer + var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; + + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.Reconcile, packetReadLength)); nb.OnReconcileRpc(null, reader, channel); } else @@ -395,7 +401,10 @@ internal void ParseObserversRpc(PooledReader reader, Channel channel) if (nb != null) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.ObserversRpc, dataLength)); + // on unreliable packets, we get -2, which means.. read the rest of the buffer + var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; + + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.ObserversRpc, packetReadLength)); nb.OnObserversRpc(null, reader, channel); } else @@ -413,7 +422,10 @@ internal void ParseTargetRpc(PooledReader reader, Channel channel) if (nb != null) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.TargetRpc, dataLength)); + // on unreliable packets, we get -2, which means.. read the rest of the buffer + var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; + + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.TargetRpc, packetReadLength)); nb.OnTargetRpc(null, reader, channel); } else diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs index 270e419b..a886ea62 100644 --- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.Prediction.cs @@ -79,6 +79,25 @@ private void SetLastReplicateTick(uint value, bool updateGlobals = true) /// Registered Reconcile methods. /// private readonly Dictionary _reconcileRpcDelegates = new Dictionary(); + + public bool TryGetMethodNameForRpcPrediction(uint hash, out string methodName) + { + if (_reconcileRpcDelegates.TryGetValueIL2CPP(hash, out ReconcileRpcDelegate del1)) + { + methodName = del1.Method.Name; + return true; + } + + if (_replicateRpcDelegates.TryGetValueIL2CPP(hash, out ReplicateRpcDelegate del2)) + { + methodName = del2.Method.Name; + return true; + } + + methodName = null; + return false; + } + /// /// True if initialized compnents for prediction. /// diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs index 27b363e1..5d527636 100644 --- a/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.RPCs.cs @@ -47,6 +47,25 @@ public BufferedRpc(PooledWriter writer, Channel channel, DataOrderType orderType /// Registered TargetRpc methods. /// private readonly Dictionary _targetRpcDelegates = new Dictionary(); + + public bool TryGetMethodNameForRpcRPC(uint hash, out string methodName) + { + if (_targetRpcDelegates.TryGetValueIL2CPP(hash, out ClientRpcDelegate del1)) + { + methodName = del1.Method.Name; + return true; + } + + if (_observersRpcDelegates.TryGetValueIL2CPP(hash, out ClientRpcDelegate del2)) + { + methodName = del2.Method.Name; + return true; + } + + methodName = null; + return false; + } + /// /// Number of total RPC methods for scripts in the same inheritance tree for this instance. /// diff --git a/Assets/FishNet/Runtime/Utility/Performance/Profiling/PacketInfoProvider.cs b/Assets/FishNet/Runtime/Utility/Performance/Profiling/PacketInfoProvider.cs index 95d4820a..95b45c4f 100644 --- a/Assets/FishNet/Runtime/Utility/Performance/Profiling/PacketInfoProvider.cs +++ b/Assets/FishNet/Runtime/Utility/Performance/Profiling/PacketInfoProvider.cs @@ -63,6 +63,17 @@ public static string GetPropertyName(NetworkBehaviour nb, int propertyHash, Pack } else { + // check for name of delegate + if (nb.TryGetMethodNameForRpcRPC((uint)propertyHash, out var methodName1)) + { + return methodName1; + } + if (nb.TryGetMethodNameForRpcPrediction((uint)propertyHash, out var methodName2)) + { + return methodName2; + } + + // otherwise.. not a delegate List rpcNamesList = new(); int rpcCount = 0; // Iterate the replicates first, that's what the codegen does @@ -77,11 +88,13 @@ public static string GetPropertyName(NetworkBehaviour nb, int propertyHash, Pack } } } + foreach (var methodInfo in nb.GetType().GetMethods()) { foreach (var customAttribute in methodInfo.CustomAttributes) { - if (customAttribute.AttributeType.FullName == typeof(ObserversRpcAttribute).FullName || + if (customAttribute.AttributeType.FullName == + typeof(ObserversRpcAttribute).FullName || customAttribute.AttributeType.FullName == typeof(TargetRpcAttribute).FullName || customAttribute.AttributeType.FullName == typeof(ReconcileAttribute).FullName) { @@ -90,13 +103,14 @@ public static string GetPropertyName(NetworkBehaviour nb, int propertyHash, Pack res = methodInfo.Name; break; } + rpcCount++; rpcNamesList.Add(methodInfo.Name); } } } - RpcNamesCache[nb.GetType()] = rpcNamesList; + } break; } From c7ac6d51bffe820f242ea7a83aeb32f25f70ae77 Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Fri, 10 Oct 2025 09:35:55 -0400 Subject: [PATCH 10/11] Correct RPC message sizes calculation --- .../Client/Object/ClientObjects.RpcLinks.cs | 15 ++++++----- .../Managing/Client/Object/ClientObjects.cs | 26 +++++++------------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs index 59b11abe..674bfaf2 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs @@ -46,24 +46,27 @@ internal void ParseRpcLink(PooledReader reader, ushort index, Channel channel) //Found NetworkObject for link. if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob)) { - // on unreliable packets, we get -2, which means.. read the rest of the buffer - var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; - NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex]; if (link.RpcType == RpcType.Target) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.TargetRpc, packetReadLength)); + var positionBefore = reader.Position; nb.OnTargetRpc(link.RpcHash, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.TargetRpc, reader.Position-positionBefore)); } else if (link.RpcType == RpcType.Observers) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.ObserversRpc, packetReadLength)); + var positionBefore = reader.Position; nb.OnObserversRpc(link.RpcHash, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.ObserversRpc, reader.Position-positionBefore)); } else if (link.RpcType == RpcType.Reconcile) { - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.Reconcile, packetReadLength)); + var positionBefore = reader.Position; nb.OnReconcileRpc(link.RpcHash, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)link.RpcHash, PacketId.Reconcile, reader.Position-positionBefore)); } } //Could not find NetworkObject. diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs index 528b44ee..fe549b35 100644 --- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -333,10 +333,7 @@ internal void ParseSyncType(PooledReader reader, bool isSyncObject, Channel chan if (nb != null) { - // on unreliable packets, we get -2, which means.. read the rest of the buffer - var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; - - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)reader.PeekByte(), (PacketId)packetId, packetReadLength)); + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)reader.PeekByte(), (PacketId)packetId, dataLength)); /* Length of data to be read for syncvars. * This is important because syncvars are never * a set length and data must be read through completion. @@ -379,11 +376,10 @@ internal void ParseReconcileRpc(PooledReader reader, Channel channel) if (nb != null) { - // on unreliable packets, we get -2, which means.. read the rest of the buffer - var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; - - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.Reconcile, packetReadLength)); + var positionBefore = reader.Position; nb.OnReconcileRpc(null, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.Reconcile, reader.Position-positionBefore)); } else SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); @@ -401,11 +397,10 @@ internal void ParseObserversRpc(PooledReader reader, Channel channel) if (nb != null) { - // on unreliable packets, we get -2, which means.. read the rest of the buffer - var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; - - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.ObserversRpc, packetReadLength)); + var positionBefore = reader.Position; nb.OnObserversRpc(null, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.ObserversRpc, reader.Position-positionBefore)); } else SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); @@ -422,11 +417,10 @@ internal void ParseTargetRpc(PooledReader reader, Channel channel) if (nb != null) { - // on unreliable packets, we get -2, which means.. read the rest of the buffer - var packetReadLength = dataLength == -2 ? reader.Remaining : dataLength; - - OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.TargetRpc, packetReadLength)); + var positionBefore = reader.Position; nb.OnTargetRpc(null, reader, channel); + // rpc read length is variable; so compare the before and after buffer position to know the real size + OnPacketRead?.Invoke(new PacketProcessingArgs(nb, (int)nb.PeekRpcHash(reader), PacketId.TargetRpc, reader.Position-positionBefore)); } else SkipDataLength((ushort)PacketId.TargetRpc, reader, dataLength); From 9681c19a2bd9f51883b52a48deb5a03378739a6d Mon Sep 17 00:00:00 2001 From: zack-pahdo Date: Wed, 15 Oct 2025 09:05:36 -0400 Subject: [PATCH 11/11] Adjust fishnet minimum server FPS logic to not be compile flag dependent --- .../Runtime/Managing/NetworkManager.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.cs index f4e95a8b..c570ca59 100644 --- a/Assets/FishNet/Runtime/Managing/NetworkManager.cs +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs @@ -339,16 +339,18 @@ internal void UpdateFramerate() else if (serverStarted) frameRate = ServerManager.FrameRate; - /* Make sure framerate isn't set to max on server. - * If it is then default to tick rate. If framerate is - * less than tickrate then also set to tickrate. */ -#if UNITY_SERVER - ushort minimumServerFramerate = (ushort)(TimeManager.TickRate + 1); - if (frameRate == MAXIMUM_FRAMERATE) - frameRate = minimumServerFramerate; - else if (frameRate < TimeManager.TickRate) - frameRate = minimumServerFramerate; -#endif + if (serverStarted) { + ushort minimumServerFramerate = (ushort)(TimeManager.TickRate + 1); + // Make sure framerate isn't set to max on server unless it is also a client. If it is then default to tick rate. + if (!clientStarted && frameRate == MAXIMUM_FRAMERATE) { + frameRate = minimumServerFramerate; + } + // If framerate is less than tickrate then set to tickrate + 1. + if (frameRate < TimeManager.TickRate) { + frameRate = minimumServerFramerate; + } + } + //If there is a framerate to set. if (frameRate > 0) {