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> /// Finds and fixes call to base methods within remote calls /// <para>For example, changes `base.CmdDoSomething` to `base.UserCode_CmdDoSomething` within `this.UserCode_CmdDoSomething`</para> /// </summary> /// <param name="type"></param> /// <param name="method"></param> public void FixRemoteCallToBaseMethod(TypeDefinition type, MethodDefinition method) { string callName = method.Name; // all ServerRpcs/Rpc start with "UserCode_" // eg CallCmdDoSomething if (!callName.StartsWith(UserCodePrefix)) { return; } // eg CmdDoSomething string baseRemoteCallName = method.Name.Substring(UserCodePrefix.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(); MethodReference baseMethod = baseType.GetMethodInBaseType(callName); if (baseMethod == null) { logger.Error($"Could not find base method for {callName}", method); return; } if (!baseMethod.Resolve().IsVirtual) { logger.Error($"Could not find base method that was virtual {callName}", method); return; } instruction.Operand = method.Module.ImportReference(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); }
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; }