public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds, long dirtyBit) { string originalName = fd.Name; Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType); // GameObject/NetworkIdentity SyncVars have a new field for netId FieldDefinition netIdField = null; // NetworkBehaviour has different field type than other NetworkIdentityFields if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { netIdField = new FieldDefinition("___" + fd.Name + "NetId", FieldAttributes.Private, WeaverTypes.Import <NetworkBehaviour.NetworkBehaviourSyncVar>()); syncVarNetIds[fd] = netIdField; } else if (fd.FieldType.IsNetworkIdentityField()) { netIdField = new FieldDefinition("___" + fd.Name + "NetId", FieldAttributes.Private, WeaverTypes.Import <uint>()); syncVarNetIds[fd] = netIdField; } MethodDefinition get = GenerateSyncVarGetter(fd, originalName, netIdField); MethodDefinition set = GenerateSyncVarSetter(td, fd, originalName, dirtyBit, netIdField); //NOTE: is property even needed? Could just use a setter function? //create the property PropertyDefinition propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType) { GetMethod = get, SetMethod = set }; //add the methods and property to the type. td.Methods.Add(get); td.Methods.Add(set); td.Properties.Add(propertyDefinition); Weaver.WeaveLists.replacementSetterProperties[fd] = set; // replace getter field if GameObject/NetworkIdentity so it uses // netId instead // -> only for GameObjects, otherwise an int syncvar's getter would // end up in recursion. if (fd.FieldType.IsNetworkIdentityField()) { Weaver.WeaveLists.replacementGetterProperties[fd] = get; } }
public static void Process(TypeDefinition td) { Weaver.DLog(td, "MessageClassProcessor Start"); GenerateSerialization(td); if (Weaver.WeavingFailed) { return; } GenerateDeSerialization(td); Weaver.DLog(td, "MessageClassProcessor Done"); }
void GenerateDeSerialization() { Weaver.DLog(m_td, " GenerateDeserialization"); foreach (var m in m_td.Methods) { if (m.Name == "Deserialize") { return; } } if (m_td.Fields.Count == 0) { return; } MethodDefinition serializeFunc = new MethodDefinition("Deserialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkReaderType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (var field in m_td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } MethodReference readerFunc = Weaver.GetReadFunc(field.FieldType); if (readerFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc)); serWorker.Append(serWorker.Create(OpCodes.Stfld, field)); } else { Weaver.fail = true; Log.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + field.FieldType + "]. [SyncVar] member variables must be basic types."); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); m_td.Methods.Add(serializeFunc); }
public void Process() { Weaver.DLog(m_td, "MessageClassProcessor Start"); Weaver.ResetRecursionCount(); GenerateSerialization(); if (Weaver.fail) { return; } GenerateDeSerialization(); Weaver.DLog(m_td, "MessageClassProcessor Done"); }
public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds, long dirtyBit) { var originalName = fd.Name; Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType); // GameObject/NetworkIdentity SyncVars have a new field for netId FieldDefinition netIdField = null; if (fd.FieldType.FullName == Weaver.gameObjectType.FullName || fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { netIdField = new FieldDefinition("___" + fd.Name + "NetId", FieldAttributes.Private, Weaver.uint32Type); syncVarNetIds[fd] = netIdField; } var get = ProcessSyncVarGet(fd, originalName, netIdField); var set = ProcessSyncVarSet(td, fd, originalName, dirtyBit, netIdField); //NOTE: is property even needed? Could just use a setter function? //create the property var propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType) { GetMethod = get, SetMethod = set }; //add the methods and property to the type. td.Methods.Add(get); td.Methods.Add(set); td.Properties.Add(propertyDefinition); Weaver.WeaveLists.replacementSetterProperties[fd] = set; // replace getter field if GameObject/NetworkIdentity so it uses // netId instead // -> only for GameObjects, otherwise an int syncvar's getter would // end up in recursion. if (fd.FieldType.FullName == Weaver.gameObjectType.FullName || fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { Weaver.WeaveLists.replacementGetterProperties[fd] = get; } }
static void GenerateDeSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateDeserialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == "Deserialize") { return; } } if (td.Fields.Count == 0) { return; } MethodDefinition serializeFunc = new MethodDefinition("Deserialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serializeFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } MethodReference readerFunc = Readers.GetReadFunc(field.FieldType); if (readerFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc)); serWorker.Append(serWorker.Create(OpCodes.Stfld, field)); } else { Weaver.Error($"{field} has unsupported type"); return; } } serWorker.Append(serWorker.Create(OpCodes.Ret)); td.Methods.Add(serializeFunc); }
// 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); }
public static void ProcessEvents(TypeDefinition td, List <EventDefinition> events, List <MethodDefinition> eventInvocationFuncs) { // find events foreach (var ed in td.Events) { foreach (var ca in ed.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncEventType.FullName) { if (!ed.Name.StartsWith("Event")) { Weaver.Error($"{ed} must start with Event. Consider renaming it to Event{ed.Name}"); return; } if (ed.EventType.Resolve().HasGenericParameters) { Weaver.Error( $"{ed} must not have generic parameters. Consider creating a new class that inherits from {ed.EventType} instead"); return; } events.Add(ed); var eventFunc = ProcessEventInvoke(td, ed); if (eventFunc == null) { return; } td.Methods.Add(eventFunc); eventInvocationFuncs.Add(eventFunc); Weaver.DLog(td, "ProcessEvent " + ed); var eventCallFunc = ProcessEventCall(td, ed, ca); td.Methods.Add(eventCallFunc); Weaver.WeaveLists.replaceEvents[ed.Name] = eventCallFunc; // original weaver compares .Name, not EventDefinition. Weaver.DLog(td, " Event: " + ed.Name); break; } } } }
public static void ProcessEvents(TypeDefinition td, List <EventDefinition> events, List <MethodDefinition> eventInvocationFuncs) { // find events foreach (EventDefinition ed in td.Events) { foreach (var ca in ed.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncEventType.FullName) { if (ed.Name.Length > 4 && ed.Name.Substring(0, 5) != "Event") { Log.Error("Event [" + td.FullName + ":" + ed.FullName + "] doesnt have 'Event' prefix"); Weaver.fail = true; return; } if (ed.EventType.Resolve().HasGenericParameters) { Log.Error("Event [" + td.FullName + ":" + ed.FullName + "] cannot have generic parameters"); Weaver.fail = true; return; } events.Add(ed); MethodDefinition eventFunc = ProcessEventInvoke(td, ed); if (eventFunc == null) { return; } td.Methods.Add(eventFunc); eventInvocationFuncs.Add(eventFunc); Weaver.DLog(td, "ProcessEvent " + ed); MethodDefinition eventCallFunc = ProcessEventCall(td, ed, ca); td.Methods.Add(eventCallFunc); Weaver.lists.replacedEvents.Add(ed); Weaver.lists.replacementEvents.Add(eventCallFunc); Weaver.DLog(td, " Event: " + ed.Name); break; } } } }
public static void ProcessSyncVar(FieldDefinition fd, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds, long dirtyBit) { string originalName = fd.Name; Weaver.DLog(fd.DeclaringType, "Sync Var " + fd.Name + " " + fd.FieldType); // GameObject/NetworkIdentity SyncVars have a new field for netId FieldDefinition netIdField = null; if (fd.FieldType.Is <UnityEngine.GameObject>() || fd.FieldType.Is <NetworkIdentity>()) { netIdField = new FieldDefinition("___" + fd.Name + "NetId", FieldAttributes.Private, WeaverTypes.Import <uint>()); syncVarNetIds[fd] = netIdField; } MethodDefinition get = GenerateSyncVarGetter(fd, originalName, netIdField); MethodDefinition set = GenerateSyncVarSetter(fd, originalName, dirtyBit, netIdField); //NOTE: is property even needed? Could just use a setter function? //create the property var propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType) { GetMethod = get, SetMethod = set }; propertyDefinition.DeclaringType = fd.DeclaringType; //add the methods and property to the type. fd.DeclaringType.Methods.Add(get); fd.DeclaringType.Methods.Add(set); fd.DeclaringType.Properties.Add(propertyDefinition); Weaver.WeaveLists.replacementSetterProperties[fd] = set; // replace getter field if GameObject/NetworkIdentity so it uses // netId instead // -> only for GameObjects, otherwise an int syncvar's getter would // end up in recursion. if (fd.FieldType.Is <UnityEngine.GameObject>() || fd.FieldType.Is <NetworkIdentity>()) { Weaver.WeaveLists.replacementGetterProperties[fd] = get; } }
// return true if modified public bool Process() { // only process once if (WasProcessed(netBehaviourSubclass)) { return(false); } Weaver.DLog(netBehaviourSubclass, "Found NetworkBehaviour " + netBehaviourSubclass.FullName); if (netBehaviourSubclass.HasGenericParameters) { Weaver.Error($"{netBehaviourSubclass.Name} cannot have generic parameters", netBehaviourSubclass); // originally Process returned true in every case, except if already processed. // maybe return false here in the future. return(true); } Weaver.DLog(netBehaviourSubclass, "Process Start"); MarkAsProcessed(netBehaviourSubclass); // deconstruct tuple and set fields (syncVars, syncVarNetIds) = SyncVarProcessor.ProcessSyncVars(netBehaviourSubclass); syncObjects = SyncObjectProcessor.FindSyncObjectsFields(netBehaviourSubclass); ProcessMethods(); SyncEventProcessor.ProcessEvents(netBehaviourSubclass, eventRpcs, eventRpcInvocationFuncs); if (Weaver.WeavingFailed) { // originally Process returned true in every case, except if already processed. // maybe return false here in the future. return(true); } GenerateConstants(); GenerateSerialization(); if (Weaver.WeavingFailed) { // originally Process returned true in every case, except if already processed. // maybe return false here in the future. return(true); } GenerateDeSerialization(); Weaver.DLog(netBehaviourSubclass, "Process Done"); return(true); }
// 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); }
// 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); }
static bool GenerateDeserialization(string methodName, TypeDefinition td, TypeReference itemType, TypeReference mirrorBaseType) { Weaver.DLog(td, " GenerateDeserialization"); bool existing = td.HasMethodInBaseType(methodName, mirrorBaseType); if (existing) { return(true); } // this check needs to happen inside GenerateDeserialization because // we need to check if user has made custom function above if (itemType.IsGenericInstance) { Weaver.Error($"{td} Can not create Serialize or Deserialize for generic element. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList"); return(false); } MethodDefinition deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, itemType); deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); ILProcessor serWorker = deserializeFunction.Body.GetILProcessor(); MethodReference readerFunc = Readers.GetReadFunc(itemType); if (readerFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc)); serWorker.Append(serWorker.Create(OpCodes.Ret)); } else { Weaver.Error($"{td} cannot have item of type {itemType}. Use a type supported by mirror instead"); return(false); } td.Methods.Add(deserializeFunction); return(true); }
void RegisterRpcs() { Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (cctor.Body.Instructions.Count != 0) { Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; if (retInstr.OpCode == OpCodes.Ret) { cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); } else { logger.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); return; } } } else { // make one! cctor = netBehaviourSubclass.AddMethod(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static); } ILProcessor cctorWorker = cctor.Body.GetILProcessor(); serverRpcProcessor.RegisterServerRpcs(cctorWorker); clientRpcProcessor.RegisterClientRpcs(cctorWorker); cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
/// <summary> /// Generates the serialization and deserialization methods for a specified generic argument /// </summary> /// <param name="td">The type of the class that needs serialization methods</param> /// <param name="itemType">generic argument to serialize</param> /// <param name="mirrorBaseType">the base SyncObject td inherits from</param> /// <param name="serializeMethod">The name of the serialize method</param> /// <param name="deserializeMethod">The name of the deserialize method</param> public static void GenerateSerialization(TypeDefinition td, TypeReference itemType, TypeReference mirrorBaseType, string serializeMethod, string deserializeMethod) { Weaver.DLog(td, "SyncObjectProcessor Start item:" + itemType.FullName); bool success = GenerateSerialization(serializeMethod, td, itemType, mirrorBaseType); if (Weaver.WeavingFailed) { return; } success |= GenerateDeserialization(deserializeMethod, td, itemType, mirrorBaseType); if (success) { Weaver.DLog(td, "SyncObjectProcessor Done"); } }
public static void ProcessEvents(TypeDefinition td, List <EventDefinition> events, List <MethodDefinition> eventInvocationFuncs) { // find events foreach (EventDefinition ed in td.Events) { foreach (CustomAttribute ca in ed.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncEventType.FullName) { if (!ed.Name.StartsWith("Event")) { Weaver.Error("Event [" + td.FullName + ":" + ed.FullName + "] doesnt have 'Event' prefix"); return; } if (ed.EventType.Resolve().HasGenericParameters) { Weaver.Error("Event [" + td.FullName + ":" + ed.FullName + "] cannot have generic parameters"); return; } events.Add(ed); MethodDefinition eventFunc = ProcessEventInvoke(td, ed); if (eventFunc == null) { return; } td.Methods.Add(eventFunc); eventInvocationFuncs.Add(eventFunc); Weaver.DLog(td, "ProcessEvent " + ed); MethodDefinition eventCallFunc = ProcessEventCall(td, ed, ca); td.Methods.Add(eventCallFunc); Weaver.WeaveLists.replaceEvents[ed.Name] = eventCallFunc; // original weaver compares .Name, not EventDefinition. Weaver.DLog(td, " Event: " + ed.Name); break; } } } }
/// <summary> /// Finds and fixes call to base methods within remote calls /// <para>For example, changes `base.CmdDoSomething` to `base.CallCmdDoSomething` within `this.CallCmdDoSomething`</para> /// </summary> /// <param name="type"></param> /// <param name="method"></param> public static void FixRemoteCallToBaseMethod(TypeDefinition type, MethodDefinition method) { string callName = method.Name; // Cmd/rpc start with Weaver.RpcPrefix // eg CallCmdDoSomething if (!callName.StartsWith(RpcPrefix)) { return; } // eg CmdDoSomething string baseRemoteCallName = method.Name.Substring(RpcPrefix.Length); foreach (Instruction instruction in method.Body.Instructions) { // if call to base.CmdDoSomething within this.CallCmdDoSomething if (IsCallToMethod(instruction, out MethodDefinition calledMethod) && calledMethod.Name == baseRemoteCallName) { TypeDefinition baseType = type.BaseType.Resolve(); MethodDefinition baseMethod = baseType.GetMethodInBaseType(callName); if (baseMethod == null) { Weaver.Error($"Could not find base method for {callName}", method); return; } if (!baseMethod.IsVirtual) { Weaver.Error($"Could not find base method that was virutal {callName}", method); return; } instruction.Operand = baseMethod; Weaver.DLog(type, "Replacing call to '{0}' with '{1}' inside '{2}'", calledMethod.FullName, baseMethod.FullName, method.FullName); } } }
// return true if modified public bool Process() { // only process once if (WasProcessed(netBehaviourSubclass)) { return(false); } Weaver.DLog(netBehaviourSubclass, "Found NetworkBehaviour " + netBehaviourSubclass.FullName); Weaver.DLog(netBehaviourSubclass, "Process Start"); MarkAsProcessed(netBehaviourSubclass); syncVarProcessor.ProcessSyncVars(netBehaviourSubclass); syncObjectProcessor.ProcessSyncObjects(netBehaviourSubclass); ProcessRpcs(); Weaver.DLog(netBehaviourSubclass, "Process Done"); return(true); }
private static MethodReference GenerateDeserialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateDeserialization"); foreach (var m in td.Methods) { if (m.Name == methodName) { return(m); } } var deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, itemType); deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); var serWorker = deserializeFunction.Body.GetILProcessor(); var readerFunc = Readers.GetReadFunc(itemType); if (readerFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc)); serWorker.Append(serWorker.Create(OpCodes.Ret)); } else { Weaver.Error($"{td} cannot have item of type {itemType}. Use a type supported by mirror instead"); return(null); } td.Methods.Add(deserializeFunction); return(deserializeFunction); }
static MethodReference GenerateDeserialization(string methodName, TypeDefinition td, TypeReference itemType) { Weaver.DLog(td, " GenerateDeserialization"); foreach (MethodDefinition m in td.Methods) { if (m.Name == methodName) { return(m); } } MethodDefinition deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.HideBySig, itemType); deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); ILProcessor serWorker = deserializeFunction.Body.GetILProcessor(); MethodReference readerFunc = Readers.GetReadFunc(itemType); if (readerFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc)); serWorker.Append(serWorker.Create(OpCodes.Ret)); } else { Weaver.Error("GenerateDeserialization for " + td.Name + " unknown type [" + itemType + "]. Member variables must be basic types."); return(null); } td.Methods.Add(deserializeFunction); return(deserializeFunction); }
void RegisterSyncObjects(TypeDefinition netBehaviourSubclass) { Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find instance constructor MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); if (ctor == null) { logger.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); return; } Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); } else { logger.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor, ctor.DebugInformation.SequencePoints.FirstOrDefault()); return; } ILProcessor ctorWorker = ctor.Body.GetILProcessor(); foreach (FieldDefinition fd in syncObjects) { GenerateSyncObjectRegistration(ctorWorker, fd); } // finish ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
public static void ProcessEvents(TypeDefinition td, List <EventDefinition> events, List <MethodDefinition> eventInvocationFuncs) { // find events foreach (EventDefinition ed in td.Events) { CustomAttribute syncEventAttr = ed.GetCustomAttribute(Weaver.SyncEventType.FullName); if (syncEventAttr != null) { if (ed.EventType.Resolve().HasGenericParameters) { Weaver.Error($"{ed.Name} must not have generic parameters. Consider creating a new class that inherits from {ed.EventType} instead", ed); return; } events.Add(ed); MethodDefinition eventFunc = ProcessEventInvoke(td, ed); if (eventFunc == null) { return; } td.Methods.Add(eventFunc); eventInvocationFuncs.Add(eventFunc); Weaver.DLog(td, "ProcessEvent " + ed); MethodDefinition eventCallFunc = ProcessEventCall(td, ed, syncEventAttr); td.Methods.Add(eventCallFunc); // original weaver compares .Name, not EventDefinition. Weaver.WeaveLists.replaceEvents[ed.FullName] = eventCallFunc; Weaver.DLog(td, " Event: " + ed.Name); } } }
public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List <FieldDefinition> syncVarNetIds, long dirtyBit) { string originalName = fd.Name; Weaver.lists.replacedFields.Add(fd); Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType); // GameObject SyncVars have a new field for netId FieldDefinition netFieldId = null; if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { netFieldId = new FieldDefinition("___" + fd.Name + "NetId", FieldAttributes.Private, Weaver.uint32Type); syncVarNetIds.Add(netFieldId); Weaver.lists.netIdFields.Add(netFieldId); } var get = ProcessSyncVarGet(fd, originalName); var set = ProcessSyncVarSet(td, fd, originalName, dirtyBit, netFieldId); //NOTE: is property even needed? Could just use a setter function? //create the property PropertyDefinition propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType) { GetMethod = get, SetMethod = set }; //add the methods and property to the type. td.Methods.Add(get); td.Methods.Add(set); td.Properties.Add(propertyDefinition); Weaver.lists.replacementProperties.Add(set); }
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(); // 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)); td.Methods.Add(serializeFunc); }
void GenerateDeSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateDeSerialization"); foreach (MethodDefinition m in netBehaviourSubclass.Methods) { if (m.Name == "OnDeserialize") { return; } } if (syncVars.Count == 0) { // no synvars, no need for custom OnDeserialize return; } MethodDefinition serialize = new MethodDefinition("OnDeserialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); // setup local for dirty bits serialize.Body.InitLocals = true; VariableDefinition dirtyBitsLocal = new VariableDefinition(Weaver.int64Type); serialize.Body.Variables.Add(dirtyBitsLocal); MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnDeserialize"); if (baseDeserialize != null) { serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); // initialState serWorker.Append(serWorker.Create(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)); foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, serialize); } serWorker.Append(serWorker.Create(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(Weaver.uint64Type))); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // conditionally read each syncvar int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); // start at number of syncvars in parent foreach (FieldDefinition syncVar in syncVars) { 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)); DeserializeField(syncVar, serWorker, serialize); serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Deserialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
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); }
public NetworkBehaviourProcessor(TypeDefinition td) { Weaver.DLog(td, "NetworkBehaviourProcessor"); netBehaviourSubclass = td; }
void GenerateConstants() { if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && eventRpcs.Count == 0 && syncObjects.Count == 0) { return; } Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find static constructor MethodDefinition cctor = null; bool cctorFound = false; foreach (MethodDefinition md in netBehaviourSubclass.Methods) { if (md.Name == ".cctor") { cctor = md; cctorFound = true; } } if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (cctor.Body.Instructions.Count != 0) { Instruction ret = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid class constructor"); return; } } } else { // make one! cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, Weaver.voidType); } // find instance constructor MethodDefinition ctor = null; foreach (MethodDefinition md in netBehaviourSubclass.Methods) { if (md.Name == ".ctor") { ctor = md; Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } break; } } if (ctor == null) { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } ILProcessor ctorWorker = ctor.Body.GetILProcessor(); ILProcessor cctorWorker = cctor.Body.GetILProcessor(); for (int i = 0; i < commands.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerCommandDelegateReference, commandInvocationFuncs[i], commands[i].Name); } for (int i = 0; i < clientRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcs[i].Name); } for (int i = 0; i < targetRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name); } for (int i = 0; i < eventRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerEventDelegateReference, eventRpcInvocationFuncs[i], eventRpcs[i].Name); } foreach (FieldDefinition fd in syncObjects) { SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd); } cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } // finish ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
void GenerateConstants() { if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && syncObjects.Count == 0) { return; } Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); bool cctorFound = cctor != null; if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (cctor.Body.Instructions.Count != 0) { Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; if (retInstr.OpCode == OpCodes.Ret) { cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); return; } } } else { // make one! cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, WeaverTypes.Import(typeof(void))); } // find instance constructor MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); if (ctor == null) { Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); return; } Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); return; } // TODO: find out if the order below matters. If it doesn't split code below into 2 functions ILProcessor ctorWorker = ctor.Body.GetILProcessor(); ILProcessor cctorWorker = cctor.Body.GetILProcessor(); for (int i = 0; i < commands.Count; ++i) { CmdResult cmdResult = commands[i]; GenerateRegisterCommandDelegate(cctorWorker, WeaverTypes.registerCommandDelegateReference, commandInvocationFuncs[i], cmdResult); } for (int i = 0; i < clientRpcs.Count; ++i) { ClientRpcResult clientRpcResult = clientRpcs[i]; GenerateRegisterRemoteDelegate(cctorWorker, WeaverTypes.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcResult.method.Name); } for (int i = 0; i < targetRpcs.Count; ++i) { GenerateRegisterRemoteDelegate(cctorWorker, WeaverTypes.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name); } foreach (FieldDefinition fd in syncObjects) { SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd); } cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } // finish ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }