public static bool WriteArguments(ILProcessor worker, MethodDefinition md, bool skipFirst) { // write each argument short argNum = 1; foreach (ParameterDefinition pd in md.Parameters) { if (argNum == 1 && skipFirst) { argNum += 1; continue; } MethodReference writeFunc = Writers.GetWriteFunc(pd.ParameterType); if (writeFunc == null) { Weaver.Error($"{md} has invalid parameter {pd}"); return(false); } // use built-in writer func on writer object worker.Append(worker.Create(OpCodes.Ldloc_0)); // writer object worker.Append(worker.Create(OpCodes.Ldarg, argNum)); // argument worker.Append(worker.Create(OpCodes.Call, writeFunc)); // call writer func on writer object argNum += 1; } return(true); }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == "Serialize") { return; } } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error($"{td} has field ${field} that references itself"); return; } } MethodDefinition serializeFunc = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type"); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); }
private static void LoadMessageReadWriter(ModuleDefinition module, TypeDefinition klass) { if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface <NetworkMessage>()) { Readers.GetReadFunc(module.ImportReference(klass)); Writers.GetWriteFunc(module.ImportReference(klass)); } foreach (TypeDefinition td in klass.NestedTypes) { LoadMessageReadWriter(module, td); } }
public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) { // write each argument // example result /* * writer.WritePackedInt32(someNumber); * writer.WriteNetworkIdentity(someTarget); */ bool skipFirst = callType == RemoteCallType.TargetRpc && TargetRpcProcessor.HasNetworkConnectionParameter(method); // arg of calling function, arg 0 is "this" so start counting at 1 int argNum = 1; foreach (ParameterDefinition param in method.Parameters) { // NetworkConnection is not sent via the NetworkWriter so skip it here // skip first for NetworkConnection in TargetRpc if (argNum == 1 && skipFirst) { argNum += 1; continue; } // skip SenderConnection in Command if (IsSenderConnection(param, callType)) { argNum += 1; continue; } MethodReference writeFunc = writers.GetWriteFunc(param.ParameterType, ref WeavingFailed); if (writeFunc == null) { Log.Error($"{method.Name} has invalid parameter {param}", method); WeavingFailed = true; return(false); } // use built-in writer func on writer object // NetworkWriter object worker.Emit(OpCodes.Ldloc_0); // add argument to call worker.Emit(OpCodes.Ldarg, argNum); // call writer extension method worker.Emit(OpCodes.Call, writeFunc); argNum += 1; } return(true); }
public bool WriteArguments(ILProcessor worker, MethodDefinition method, VariableDefinition writer, RemoteCallType callType) { // write each argument // example result /* * writer.WritePackedInt32(someNumber); * writer.WriteNetworkIdentity(someTarget); */ bool skipFirst = callType == RemoteCallType.ClientRpc && HasNetworkConnectionParameter(method); // arg of calling function, arg 0 is "this" so start counting at 1 int argNum = 1; foreach (ParameterDefinition param in method.Parameters) { // NetworkConnection is not sent via the NetworkWriter so skip it here // skip first for NetworkConnection in TargetRpc if (argNum == 1 && skipFirst) { argNum += 1; continue; } // skip SenderConnection in ServerRpc if (IsNetworkConnection(param.ParameterType)) { argNum += 1; continue; } MethodReference writeFunc = writers.GetWriteFunc(param.ParameterType, method.DebugInformation.SequencePoints.FirstOrDefault()); if (writeFunc == null) { logger.Error($"{method.Name} has invalid parameter {param}", method, method.DebugInformation.SequencePoints.FirstOrDefault()); return(false); } // use built-in writer func on writer object // NetworkWriter object worker.Append(worker.Create(OpCodes.Ldloc, writer)); // add argument to call worker.Append(worker.Create(OpCodes.Ldarg, argNum)); // call writer extension method worker.Append(worker.Create(OpCodes.Call, writeFunc)); argNum += 1; } return(true); }
private static MethodDefinition GenerateEnumWriteFunc(TypeReference variable) { MethodDefinition writerFunc = GenerateWriterFunc(variable); ILProcessor worker = writerFunc.Body.GetILProcessor(); MethodReference underlyingWriter = Writers.GetWriteFunc(variable.Resolve().GetEnumUnderlyingType()); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Call, underlyingWriter)); worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
// serialization of individual element private static MethodReference GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateSerialization"); foreach (var m in td.Methods) { if (m.Name == methodName) { return(m); } } var serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); var serWorker = serializeFunc.Body.GetILProcessor(); if (itemType.IsGenericInstance) { Weaver.Error($"{td} cannot have generic elements {itemType}"); return(null); } var writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{td} cannot have item of type {itemType}. Use a type supported by mirror instead"); return(null); } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(serializeFunc); }
private static void CallWriter(ILProcessor serWorker, FieldDefinition field) { MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field.Name} has unsupported type", field); } }
// serialization of individual element static bool GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType, TypeReference mirrorBaseType) { Weaver.DLog(td, " GenerateSerialization"); bool existing = td.HasMethodInBaseType(methodName, mirrorBaseType); if (existing) { return(true); } // this check needs to happen inside GenerateSerialization because // we need to check if user has made custom function above if (itemType.IsGenericInstance) { Weaver.Error($"Can not create Serialize or Deserialize for generic element in {td.Name}. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList", td); return(false); } var serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, WeaverTypes.Import(typeof(void))); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, WeaverTypes.Import <NetworkWriter>())); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); ILProcessor worker = serializeFunc.Body.GetILProcessor(); MethodReference writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldarg_2)); worker.Append(worker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{td.Name} has sync object generic type {itemType.Name}. Use a type supported by mirror instead", td); return(false); } worker.Append(worker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(true); }
static bool LoadMessageReadWriter(ModuleDefinition module, TypeDefinition klass) { bool modified = false; if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface <NetworkMessage>()) { Readers.GetReadFunc(module.ImportReference(klass)); Writers.GetWriteFunc(module.ImportReference(klass)); modified = true; } foreach (TypeDefinition td in klass.NestedTypes) { modified |= LoadMessageReadWriter(module, td); } return(modified); }
// serialization of individual element static MethodReference GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == methodName) { return(m); } } MethodDefinition serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType)); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); if (itemType.IsGenericInstance) { Weaver.Error("GenerateSerialization for " + Helpers.PrettyPrintType(itemType) + " failed. Can't have generic parameters"); return(null); } MethodReference writeFunc = Writers.GetWriteFunc(itemType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + itemType + "/" + itemType.FullName + "]. Member variables must be basic types."); return(null); } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); return(serializeFunc); }
// Generates serialization methods for synclists static void GenerateReadersAndWriters(Writers writers, Readers readers, TypeReference tr, ref bool WeavingFailed) { if (tr is GenericInstanceType genericInstance) { foreach (TypeReference argument in genericInstance.GenericArguments) { if (!argument.IsGenericParameter) { readers.GetReadFunc(argument, ref WeavingFailed); writers.GetWriteFunc(argument, ref WeavingFailed); } } } if (tr != null) { GenerateReadersAndWriters(writers, readers, tr.Resolve().BaseType, ref WeavingFailed); } }
/// <summary> /// Generates serialization methods for synclists /// </summary> /// <param name="td">The synclist class</param> /// <param name="mirrorBaseType">the base SyncObject td inherits from</param> static void GenerateReadersAndWriters(TypeReference tr) { if (tr is GenericInstanceType genericInstance) { foreach (TypeReference argument in genericInstance.GenericArguments) { if (!argument.IsGenericParameter) { Readers.GetReadFunc(argument); Writers.GetWriteFunc(argument); } } } if (tr != null) { GenerateReadersAndWriters(tr.Resolve().BaseType); } }
public static bool WriteArguments(ILProcessor worker, MethodDefinition method, bool skipFirst) { // write each argument // example result /* * writer.WritePackedInt32(someNumber); * writer.WriteNetworkIdentity(someTarget); */ // arg of calling function, arg 0 is "this" so start counting at 1 int argNum = 1; foreach (ParameterDefinition param in method.Parameters) { // NetworkConnection is not sent via the NetworkWriter so skip it here // skip first for NetworkConnection in TargetRpc if (argNum == 1 && skipFirst) { argNum += 1; continue; } MethodReference writeFunc = Writers.GetWriteFunc(param.ParameterType); if (writeFunc == null) { Weaver.Error($"{method.Name} has invalid parameter {param}", method); return(false); } // use built-in writer func on writer object // NetworkWriter object worker.Append(worker.Create(OpCodes.Ldloc_0)); // add argument to call worker.Append(worker.Create(OpCodes.Ldarg, argNum)); // call writer extension method worker.Append(worker.Create(OpCodes.Call, writeFunc)); argNum += 1; } return(true); }
private void GenerateReadersWriters(TypeReference parameterType, SequencePoint sequencePoint) { if (!parameterType.IsGenericParameter && parameterType.CanBeResolved()) { TypeDefinition typeDefinition = parameterType.Resolve(); if (typeDefinition.IsClass && !typeDefinition.IsValueType) { MethodDefinition constructor = typeDefinition.GetMethod(".ctor"); bool hasAccess = constructor.IsPublic || constructor.IsAssembly && typeDefinition.Module == module; if (!hasAccess) { return; } } writers.GetWriteFunc(parameterType, sequencePoint); readers.GetReadFunc(parameterType, sequencePoint); } }
private static void GenerateReadersWriters(ModuleDefinition module, TypeReference parameterType) { if (!parameterType.IsGenericParameter && parameterType.CanBeResolved()) { TypeDefinition typeDefinition = parameterType.Resolve(); if (typeDefinition.IsClass && !typeDefinition.IsValueType) { MethodDefinition constructor = typeDefinition.GetMethod(".ctor"); bool hasAccess = constructor.IsPublic || constructor.IsAssembly && typeDefinition.Module == module; if (!hasAccess) { return; } } parameterType = module.ImportReference(parameterType); Writers.GetWriteFunc(parameterType); Readers.GetReadFunc(parameterType); } }
void GenerateSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateSerialization"); foreach (MethodDefinition m in netBehaviourSubclass.Methods) { if (m.Name == "OnSerialize") { return; } } if (syncVars.Count == 0) { // no synvars, no need for custom OnSerialize return; } MethodDefinition serialize = new MethodDefinition("OnSerialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.boolType); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty VariableDefinition dirtyLocal = new VariableDefinition(Weaver.boolType); serialize.Body.Variables.Add(dirtyLocal); MethodReference baseSerialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnSerialize"); if (baseSerialize != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); // forceAll serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize)); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // set dirtyLocal to result of base.OnSerialize() } // Generates: if (forceAll); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); // forceAll serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { // Generates a writer call for each sync variable serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } } // always return true if forceAll // Generates: return true serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (forceAll); serWorker.Append(initialStateLabel); // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); serWorker.Append(serWorker.Create(OpCodes.Call, Writers.GetWriteFunc(Weaver.uint64Type))); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); // 8 bytes = long serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); // Generates a call to the writer for that field serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } // something was dirty serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // set dirtyLocal to true serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Serialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } // generate: return dirtyLocal serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
void GenerateSerialization(ref bool WeavingFailed) { const string SerializeMethodName = "SerializeSyncVars"; if (netBehaviourSubclass.GetMethod(SerializeMethodName) != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnSerialize return; } MethodDefinition serialize = new MethodDefinition(SerializeMethodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, weaverTypes.Import <bool>()); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import <NetworkWriter>())); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import <bool>())); ILProcessor worker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import <bool>()); serialize.Body.Variables.Add(dirtyLocal); MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); if (baseSerialize != null) { // base worker.Emit(OpCodes.Ldarg_0); // writer worker.Emit(OpCodes.Ldarg_1); // forceAll worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Call, baseSerialize); // set dirtyLocal to result of base.OnSerialize() worker.Emit(OpCodes.Stloc_0); } // Generates: if (forceAll); Instruction initialStateLabel = worker.Create(OpCodes.Nop); // forceAll worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Brfalse, initialStateLabel); foreach (FieldDefinition syncVarDef in syncVars) { FieldReference syncVar = syncVarDef; if (netBehaviourSubclass.HasGenericParameters) { syncVar = syncVarDef.MakeHostInstanceGeneric(); } // Generates a writer call for each sync variable // writer worker.Emit(OpCodes.Ldarg_1); // this worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); WeavingFailed = true; return; } } // always return true if forceAll // Generates: return true worker.Emit(OpCodes.Ldc_I4_1); worker.Emit(OpCodes.Ret); // Generates: end if (forceAll); worker.Append(initialStateLabel); // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // writer worker.Emit(OpCodes.Ldarg_1); // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import <ulong>(), ref WeavingFailed); worker.Emit(OpCodes.Call, writeUint64Func); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVarDef in syncVars) { FieldReference syncVar = syncVarDef; if (netBehaviourSubclass.HasGenericParameters) { syncVar = syncVarDef.MakeHostInstanceGeneric(); } Instruction varLabel = worker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); // 8 bytes = long worker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); worker.Emit(OpCodes.And); worker.Emit(OpCodes.Brfalse, varLabel); // Generates a call to the writer for that field // writer worker.Emit(OpCodes.Ldarg_1); // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); WeavingFailed = true; return; } // something was dirty worker.Emit(OpCodes.Ldc_I4_1); // set dirtyLocal to true worker.Emit(OpCodes.Stloc_0); worker.Append(varLabel); dirtyBit += 1; } // add a log message if needed for debugging //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); // generate: return dirtyLocal worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ret); netBehaviourSubclass.Methods.Add(serialize); }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == "Serialize") { return; } } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FullName + "]. [MessageBase] member cannot be self referencing."); return; } } MethodDefinition serializeFunc = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } if (field.FieldType.Resolve().HasGenericParameters) { Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member cannot have generic parameters."); return; } if (field.FieldType.Resolve().IsInterface) { Weaver.Error("GenerateSerialization for " + td.Name + " [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member cannot be an interface."); return; } MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + field.FieldType + "/" + field.FieldType.FullName + "]. [MessageBase] member variables must be basic types."); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == "Serialize") { return; } } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error($"{td} has field ${field} that references itself"); return; } } MethodDefinition serializeFunc = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } if (field.FieldType.Resolve().HasGenericParameters&& !field.FieldType.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal)) { Weaver.Error($"{field} cannot have generic type {field.FieldType}. Consider creating a class that derives the generic type"); return; } if (field.FieldType.Resolve().IsInterface) { Weaver.Error($"{field} has unsupported type. Use a concrete class instead of interface {field.FieldType}"); return; } MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type"); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); MethodDefinition existingMethod = td.Methods.FirstOrDefault(md => md.Name == "Serialize"); if (existingMethod != null && !existingMethod.Body.IsEmptyDefault()) { return; } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error($"{td} has field ${field} that references itself"); return; } } MethodDefinition serializeFunc = existingMethod ?? new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); if (existingMethod == null) //only add to new method { serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); } ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); if (existingMethod != null) { serWorker.Body.Instructions.Clear(); //remove default nop&ret from existing empty interface method } if (!td.IsValueType) //if not struct(IMessageBase), likely same as using else {} here in all cases { // call base MethodReference baseSerialize = Resolvers.ResolveMethodInParents(td.BaseType, Weaver.CurrentAssembly, "Serialize"); if (baseSerialize != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // writer serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize)); } } foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, field)); serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type"); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); if (existingMethod == null) //only add if not just replaced body { td.Methods.Add(serializeFunc); } }