public (List <FieldDefinition> syncVars, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds) ProcessSyncVars(TypeDefinition td, ref bool WeavingFailed) { List <FieldDefinition> syncVars = new List <FieldDefinition>(); Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds = new Dictionary <FieldDefinition, FieldDefinition>(); // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. // start assigning syncvars at the place the base class stopped, if any int dirtyBitCounter = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName); // find syncvars foreach (FieldDefinition fd in td.Fields) { if (fd.HasCustomAttribute <SyncVarAttribute>()) { if ((fd.Attributes & FieldAttributes.Static) != 0) { Log.Error($"{fd.Name} cannot be static", fd); WeavingFailed = true; continue; } if (fd.FieldType.IsArray) { Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd); WeavingFailed = true; continue; } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Log.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd); } else { syncVars.Add(fd); ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter, ref WeavingFailed); dirtyBitCounter += 1; if (dirtyBitCounter > SyncVarLimit) { Log.Error($"{td.Name} has > {SyncVarLimit} SyncVars. Consider refactoring your class into multiple components", td); WeavingFailed = true; continue; } } } } // add all the new SyncVar __netId fields foreach (FieldDefinition fd in syncVarNetIds.Values) { td.Fields.Add(fd); } syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count); return(syncVars, syncVarNetIds); }
static void ProcessSyncVars(TypeDefinition td) { // find syncvars foreach (FieldDefinition fd in td.Fields) { if (fd.HasCustomAttribute(WeaverTypes.SyncVarType)) { Weaver.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Weaver.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); } } }
static void ProcessSyncVars(Logger Log, TypeDefinition td, ref bool WeavingFailed) { // find syncvars foreach (FieldDefinition fd in td.Fields) { if (fd.HasCustomAttribute <SyncVarAttribute>()) { Log.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); WeavingFailed = true; } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Log.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); WeavingFailed = true; } } }
static void ProcessSyncVars(TypeDefinition td) { // find syncvars foreach (FieldDefinition fd in td.Fields) { foreach (CustomAttribute ca in fd.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName) { Weaver.Error($"[SyncVar] {fd} must be inside a NetworkBehaviour. {td} is not a NetworkBehaviour"); } } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Weaver.Error($"{fd} is a SyncObject and must be inside a NetworkBehaviour. {td} is not a NetworkBehaviour"); } } }
static void ProcessSyncVars(TypeDefinition td) { // find syncvars foreach (FieldDefinition fd in td.Fields) { foreach (CustomAttribute ca in fd.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName) { Weaver.Error("Script " + td.FullName + " uses [SyncVar] " + fd.Name + " but is not a NetworkBehaviour."); } } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Weaver.Error(string.Format("Script {0} defines field {1} with type {2}, but it's not a NetworkBehaviour", td.FullName, fd.Name, Helpers.PrettyPrintType(fd.FieldType))); } } }
// we need to inject several initializations into NetworkBehaviour ctor void InjectIntoInstanceConstructor(ref bool WeavingFailed) { if (syncObjects.Count == 0) { return; } // find instance constructor MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); if (ctor == null) { Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); WeavingFailed = true; return; } // remove the return opcode from end of function. will add our own later. if (!RemoveFinalRetInstruction(ctor)) { Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); WeavingFailed = true; return; } ILProcessor ctorWorker = ctor.Body.GetILProcessor(); // initialize all sync objects in ctor foreach (FieldDefinition fd in syncObjects) { SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, weaverTypes, fd); } // add final 'Ret' instruction to ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); }
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; }
public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds) { int numSyncVars = 0; // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. // start assigning syncvars at the place the base class stopped, if any int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName); syncVarNetIds.Clear(); // find syncvars foreach (FieldDefinition fd in td.Fields) { if (fd.HasCustomAttribute(Weaver.SyncVarType)) { if ((fd.Attributes & FieldAttributes.Static) != 0) { Weaver.Error($"{fd} cannot be static"); return; } if (fd.FieldType.IsArray) { Weaver.Error($"{fd} has invalid type. Use SyncLists instead of arrays"); return; } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Log.Warning($"{fd} has [SyncVar] attribute. SyncLists should not be marked with SyncVar"); } else { syncVars.Add(fd); ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter); dirtyBitCounter += 1; numSyncVars += 1; if (dirtyBitCounter == SyncVarLimit) { Weaver.Error($"{td} has too many SyncVars. Consider refactoring your class into multiple components"); return; } } } if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType)) { if (fd.IsStatic) { Weaver.Error($"{fd} cannot be static"); return; } syncObjects.Add(fd); } } // add all the new SyncVar __netId fields foreach (FieldDefinition fd in syncVarNetIds.Values) { td.Fields.Add(fd); } Weaver.SetNumSyncVars(td.FullName, numSyncVars); }
public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds) { int numSyncVars = 0; // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. // start assigning syncvars at the place the base class stopped, if any int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName); syncVarNetIds.Clear(); // find syncvars foreach (FieldDefinition fd in td.Fields) { foreach (CustomAttribute ca in fd.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName) { TypeDefinition resolvedField = fd.FieldType.Resolve(); if (resolvedField.IsDerivedFrom(Weaver.NetworkBehaviourType)) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot be derived from NetworkBehaviour."); return; } if (resolvedField.IsDerivedFrom(Weaver.ScriptableObjectType)) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot be derived from ScriptableObject."); return; } if ((fd.Attributes & FieldAttributes.Static) != 0) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot be static."); return; } if (resolvedField.HasGenericParameters) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot have generic parameters."); return; } if (resolvedField.IsInterface) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot be an interface."); return; } string fieldModuleName = resolvedField.Module.Name; if (fieldModuleName != Weaver.CurrentAssembly.MainModule.Name && fieldModuleName != Weaver.UnityAssembly.MainModule.Name && fieldModuleName != Weaver.NetAssembly.MainModule.Name && fieldModuleName != Weaver.CorLibModule.Name && fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro fieldModuleName != "netstandard.dll" // handle built-in types when weaving new C#7 compiler assemblies ) { Weaver.Error("SyncVar [" + fd.FullName + "] from " + resolvedField.Module.ToString() + " cannot be a different module."); return; } if (fd.FieldType.IsArray) { Weaver.Error("SyncVar [" + fd.FullName + "] cannot be an array. Use a SyncList instead."); return; } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Log.Warning(string.Format("Script class [{0}] has [SyncVar] attribute on SyncList field {1}, SyncLists should not be marked with SyncVar.", td.FullName, fd.Name)); break; } syncVars.Add(fd); ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter); dirtyBitCounter += 1; numSyncVars += 1; if (dirtyBitCounter == SyncVarLimit) { Weaver.Error("Script class [" + td.FullName + "] has too many SyncVars (" + SyncVarLimit + "). (This could include base classes)"); return; } break; } } if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType)) { if (fd.IsStatic) { Weaver.Error("SyncList [" + td.FullName + ":" + fd.FullName + "] cannot be a static"); return; } syncObjects.Add(fd); } } // add all the new SyncVar __netId fields foreach (FieldDefinition fd in syncVarNetIds.Values) { td.Fields.Add(fd); } Weaver.SetNumSyncVars(td.FullName, numSyncVars); }
public static void ProcessSyncVars(TypeDefinition td, List <FieldDefinition> syncVars, List <FieldDefinition> syncObjects, Dictionary <FieldDefinition, FieldDefinition> syncVarNetIds) { int numSyncVars = 0; // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. // start assigning syncvars at the place the base class stopped, if any int dirtyBitCounter = Weaver.GetSyncVarStart(td.BaseType.FullName); syncVarNetIds.Clear(); // find syncvars foreach (FieldDefinition fd in td.Fields) { foreach (CustomAttribute ca in fd.CustomAttributes) { if (ca.AttributeType.FullName == Weaver.SyncVarType.FullName) { TypeDefinition resolvedField = fd.FieldType.Resolve(); if (resolvedField.IsDerivedFrom(Weaver.NetworkBehaviourType)) { Weaver.Error($"{fd} has invalid type. SyncVars cannot be NetworkBehaviours"); return; } if (resolvedField.IsDerivedFrom(Weaver.ScriptableObjectType)) { Weaver.Error($"{fd} has invalid type. SyncVars cannot be scriptable objects"); return; } if ((fd.Attributes & FieldAttributes.Static) != 0) { Weaver.Error($"{fd} cannot be static"); return; } if (resolvedField.HasGenericParameters) { Weaver.Error($"{fd} has invalid type. SyncVars cannot have generic parameters"); return; } if (resolvedField.IsInterface) { Weaver.Error($"{fd} has invalid type. Use a concrete type instead of interface {fd.FieldType}"); return; } string fieldModuleName = resolvedField.Module.Name; if (fieldModuleName != Weaver.CurrentAssembly.MainModule.Name && fieldModuleName != Weaver.UnityAssembly.MainModule.Name && fieldModuleName != Weaver.NetAssembly.MainModule.Name && fieldModuleName != Weaver.CorLibModule.Name && fieldModuleName != "System.Runtime.dll" && // this is only for Metro, built-in types are not in corlib on metro fieldModuleName != "netstandard.dll" // handle built-in types when weaving new C#7 compiler assemblies ) { Weaver.Error($"{fd} has invalid type. Use a type defined in the same module {fd.Module}"); return; } if (fd.FieldType.IsArray) { Weaver.Error($"{fd} has invalid type. Use SyncLists instead of arrays"); return; } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { Log.Warning($"{fd} has [SyncVar] attribute. SyncLists should not be marked with SyncVar"); break; } syncVars.Add(fd); ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter); dirtyBitCounter += 1; numSyncVars += 1; if (dirtyBitCounter == SyncVarLimit) { Weaver.Error($"{td} has too many SyncVars. Consider refactoring your class into multiple components"); return; } break; } } if (fd.FieldType.Resolve().ImplementsInterface(Weaver.SyncObjectType)) { if (fd.IsStatic) { Weaver.Error($"{fd} cannot be static"); return; } syncObjects.Add(fd); } } // add all the new SyncVar __netId fields foreach (FieldDefinition fd in syncVarNetIds.Values) { td.Fields.Add(fd); } Weaver.SetNumSyncVars(td.FullName, numSyncVars); }