From 09bddc144eb3ee2447be1a014073955ae3962797 Mon Sep 17 00:00:00 2001 From: aurorax-neo <15047150695@163.com> Date: Tue, 24 Feb 2026 15:44:55 +0800 Subject: [PATCH 1/4] refactor(NetworkBehaviour): improve SyncVar and SyncCollection handling during serialization and deserialization --- Assets/Mirror/Core/NetworkBehaviour.cs | 10 ++- .../Processors/NetworkBehaviourProcessor.cs | 73 ++++++++----------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/Assets/Mirror/Core/NetworkBehaviour.cs b/Assets/Mirror/Core/NetworkBehaviour.cs index 0749dcd1bf2..24b782a8974 100644 --- a/Assets/Mirror/Core/NetworkBehaviour.cs +++ b/Assets/Mirror/Core/NetworkBehaviour.cs @@ -168,7 +168,7 @@ public bool authority // Hooks are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncVarHooks = new List(); - // Queue for deferred SyncCollection Actions during initial spawn. + // Queue for deferred SyncCollection Actions during initial spawn. // Only used on pure client (not host mode) when isSpawnFinished = false. // Actions are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncCollectionActions = new List(); @@ -287,7 +287,7 @@ protected void InitSyncObject(SyncObject syncObject) // Store back-reference to this NetworkBehaviour syncObject.networkBehaviour = this; - + // add it, remember the index in list (if Count=0, index=0 etc.) int index = syncObjects.Count; syncObjects.Add(syncObject); @@ -1260,6 +1260,9 @@ void DeserializeSyncObjects(NetworkReader reader, bool initialState) // USED BY WEAVER protected virtual void SerializeSyncVars(NetworkWriter writer, bool initialState) { + if (!initialState) + writer.WriteVarULong(syncVarDirtyBits); + // SyncVar are written here in subclass // if initialState @@ -1272,6 +1275,9 @@ protected virtual void SerializeSyncVars(NetworkWriter writer, bool initialState // USED BY WEAVER protected virtual void DeserializeSyncVars(NetworkReader reader, bool initialState) { + if (!initialState) + syncVarDirtyBits = reader.ReadVarULong(); + // SyncVars are read here in subclass // if initialState diff --git a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs index 47f1b940487..c5a3809ebbf 100644 --- a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -501,12 +501,12 @@ void GenerateSerialization(ref bool WeavingFailed) // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // writer - worker.Emit(OpCodes.Ldarg_1); + // worker.Emit(OpCodes.Ldarg_1); // base - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, weaverTypes.NetworkBehaviourDirtyBitsReference); - MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); - worker.Emit(OpCodes.Call, writeUint64Func); + // worker.Emit(OpCodes.Ldarg_0); + // worker.Emit(OpCodes.Ldfld, weaverTypes.NetworkBehaviourDirtyBitsReference); + // MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); + // worker.Emit(OpCodes.Call, writeUint64Func); // generate a writer call for any dirty variable in this class @@ -673,7 +673,7 @@ void DeserializeField(FieldDefinition syncVar, ILProcessor worker, ref bool Weav } } - void GenerateDeSerialization(ref bool WeavingFailed) + void GenerateDeSerialization(ref bool WeavingFailed) { const string DeserializeMethodName = "DeserializeSyncVars"; if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null) @@ -681,55 +681,50 @@ void GenerateDeSerialization(ref bool WeavingFailed) if (syncVars.Count == 0) { - // no synvars, no need for custom OnDeserialize + // 没有 SyncVars,不需要生成自定义反序列化 return; } - MethodDefinition serialize = new MethodDefinition(DeserializeMethodName, - MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, - weaverTypes.Import(typeof(void))); + MethodDefinition deserialize = new MethodDefinition(DeserializeMethodName, + MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); - serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); - serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import())); - ILProcessor serWorker = serialize.Body.GetILProcessor(); + deserialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); + deserialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import())); + ILProcessor serWorker = deserialize.Body.GetILProcessor(); // setup local for dirty bits - serialize.Body.InitLocals = true; - VariableDefinition dirtyBitsLocal = new VariableDefinition(weaverTypes.Import()); - serialize.Body.Variables.Add(dirtyBitsLocal); + deserialize.Body.InitLocals = true; + + MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, DeserializeMethodName); if (baseDeserialize != null) { // base - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - // reader - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); + serWorker.Emit(OpCodes.Ldarg_0); + // reader + serWorker.Emit(OpCodes.Ldarg_1); // initialState - serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); - serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); + serWorker.Emit(OpCodes.Ldarg_2); + serWorker.Emit(OpCodes.Call, baseDeserialize); } // Generates: if (initialState); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); - serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); - serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); + serWorker.Emit(OpCodes.Ldarg_2); + serWorker.Emit(OpCodes.Brfalse, initialStateLabel); + // A. initialState 为 true: 直接反序列化所有字段 foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, ref WeavingFailed); } - - serWorker.Append(serWorker.Create(OpCodes.Ret)); + serWorker.Emit(OpCodes.Ret); // Generates: end if (initialState); serWorker.Append(initialStateLabel); - // get dirty bits - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - serWorker.Append(serWorker.Create(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed))); - serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); - // conditionally read each syncvar // start at number of syncvars in parent int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); @@ -738,23 +733,19 @@ void GenerateDeSerialization(ref bool WeavingFailed) Instruction varLabel = serWorker.Create(OpCodes.Nop); // check if dirty bit is set - serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); - serWorker.Append(serWorker.Create(OpCodes.And)); - serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); + serWorker.Emit(OpCodes.Ldarg_0); + serWorker.Emit(OpCodes.Ldfld, weaverTypes.NetworkBehaviourDirtyBitsReference); + serWorker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); + serWorker.Emit(OpCodes.And); + serWorker.Emit(OpCodes.Brfalse, varLabel); DeserializeField(syncVar, serWorker, ref WeavingFailed); serWorker.Append(varLabel); dirtyBit += 1; } - - // add a log message if needed for debugging - //serWorker.Append(serWorker.Create(OpCodes.Ldstr, $"Injected Deserialize {netBehaviourSubclass.Name}")); - //serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); - - serWorker.Append(serWorker.Create(OpCodes.Ret)); - netBehaviourSubclass.Methods.Add(serialize); + serWorker.Emit(OpCodes.Ret); + netBehaviourSubclass.Methods.Add(deserialize); } public static bool ReadArguments(MethodDefinition method, Readers readers, Logger Log, ILProcessor worker, RemoteCallType callType, ref bool WeavingFailed) From 66602e3c1f7172e190022250fa57507ca9a61e75 Mon Sep 17 00:00:00 2001 From: aurorax-neo <15047150695@163.com> Date: Tue, 24 Feb 2026 15:46:08 +0800 Subject: [PATCH 2/4] refactor(NetworkBehaviour): remove commented-out code for SyncVar and SyncCollection handling --- Assets/Mirror/Core/NetworkBehaviour.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Assets/Mirror/Core/NetworkBehaviour.cs b/Assets/Mirror/Core/NetworkBehaviour.cs index 24b782a8974..ac56266f19d 100644 --- a/Assets/Mirror/Core/NetworkBehaviour.cs +++ b/Assets/Mirror/Core/NetworkBehaviour.cs @@ -1268,7 +1268,6 @@ protected virtual void SerializeSyncVars(NetworkWriter writer, bool initialState // if initialState // write all SyncVars // else - // write syncVarDirtyBits // write dirty SyncVars } @@ -1283,7 +1282,6 @@ protected virtual void DeserializeSyncVars(NetworkReader reader, bool initialSta // if initialState // read all SyncVars // else - // read syncVarDirtyBits // read dirty SyncVars } From 16a15dd0cb2dad8d69447557ab878293d677d9c4 Mon Sep 17 00:00:00 2001 From: aurorax-neo <15047150695@163.com> Date: Wed, 4 Mar 2026 14:24:45 +0800 Subject: [PATCH 3/4] refactor(NetworkBehaviour): update comments for clarity and consistency --- Assets/Mirror/Core/NetworkBehaviour.cs | 3 +-- .../Editor/Weaver/Processors/NetworkBehaviourProcessor.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Mirror/Core/NetworkBehaviour.cs b/Assets/Mirror/Core/NetworkBehaviour.cs index ac56266f19d..5481c93d77c 100644 --- a/Assets/Mirror/Core/NetworkBehaviour.cs +++ b/Assets/Mirror/Core/NetworkBehaviour.cs @@ -168,7 +168,7 @@ public bool authority // Hooks are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncVarHooks = new List(); - // Queue for deferred SyncCollection Actions during initial spawn. + // Queue for deferred SyncCollection Actions during initial spawn. // Only used on pure client (not host mode) when isSpawnFinished = false. // Actions are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncCollectionActions = new List(); @@ -287,7 +287,6 @@ protected void InitSyncObject(SyncObject syncObject) // Store back-reference to this NetworkBehaviour syncObject.networkBehaviour = this; - // add it, remember the index in list (if Count=0, index=0 etc.) int index = syncObjects.Count; syncObjects.Add(syncObject); diff --git a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs index c5a3809ebbf..48cfaf4edd3 100644 --- a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -681,7 +681,7 @@ void GenerateDeSerialization(ref bool WeavingFailed) if (syncVars.Count == 0) { - // 没有 SyncVars,不需要生成自定义反序列化 + // no synvars, no need for custom OnDeserialize return; } From 18ec3394e349b52c169bcc93b157a35b8b1bf82d Mon Sep 17 00:00:00 2001 From: aurorax-neo <15047150695@163.com> Date: Wed, 4 Mar 2026 14:28:55 +0800 Subject: [PATCH 4/4] refactor(NetworkBehaviour): correct comment for deferred SyncCollection Actions and clean up whitespace --- Assets/Mirror/Core/NetworkBehaviour.cs | 2 +- .../Editor/Weaver/Processors/NetworkBehaviourProcessor.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Mirror/Core/NetworkBehaviour.cs b/Assets/Mirror/Core/NetworkBehaviour.cs index 5481c93d77c..ee251c654b7 100644 --- a/Assets/Mirror/Core/NetworkBehaviour.cs +++ b/Assets/Mirror/Core/NetworkBehaviour.cs @@ -168,7 +168,7 @@ public bool authority // Hooks are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncVarHooks = new List(); - // Queue for deferred SyncCollection Actions during initial spawn. + // Queue for deferred SyncCollection Actions during initial spawn. // Only used on pure client (not host mode) when isSpawnFinished = false. // Actions are queued during deserialization and invoked in OnObjectSpawnFinished. internal readonly List deferredSyncCollectionActions = new List(); diff --git a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs index 48cfaf4edd3..6ccd7c162d6 100644 --- a/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -714,8 +714,7 @@ void GenerateDeSerialization(ref bool WeavingFailed) serWorker.Emit(OpCodes.Ldarg_2); serWorker.Emit(OpCodes.Brfalse, initialStateLabel); - - // A. initialState 为 true: 直接反序列化所有字段 + foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, ref WeavingFailed);