// creates a method substitute // For example, if we have this: // public void CmdThrust(float thrusting, int spin) // { // xxxxx // } // // it will substitute the method and move the code to a new method with a provided name // for example: // // public void CmdTrust(float thrusting, int spin) // { // } // // public void <newName>(float thrusting, int spin) // { // xxxxx // } // // Note that all the calls to the method remain untouched // // the original method definition loses all code // this returns the newly created method with all the user provided code public MethodDefinition SubstituteMethod(MethodDefinition md) { string newName = UserCodePrefix + md.Name; MethodDefinition cmd = md.DeclaringType.AddMethod(newName, md.Attributes, md.ReturnType); // add parameters foreach (ParameterDefinition pd in md.Parameters) { _ = cmd.AddParam(pd.ParameterType, pd.Name); } // swap bodies (cmd.Body, md.Body) = (md.Body, cmd.Body); // Move over all the debugging information foreach (SequencePoint sequencePoint in md.DebugInformation.SequencePoints) { cmd.DebugInformation.SequencePoints.Add(sequencePoint); } md.DebugInformation.SequencePoints.Clear(); foreach (CustomDebugInformation customInfo in md.CustomDebugInformations) { cmd.CustomDebugInformations.Add(customInfo); } md.CustomDebugInformations.Clear(); (md.DebugInformation.Scope, cmd.DebugInformation.Scope) = (cmd.DebugInformation.Scope, md.DebugInformation.Scope); FixRemoteCallToBaseMethod(md.DeclaringType, cmd); return(cmd); }
MethodDefinition GenerateSyncVarSetter(FoundSyncVar syncVar) { FieldDefinition fd = syncVar.FieldDefinition; TypeReference originalType = syncVar.OriginalType; string originalName = syncVar.OriginalName; //Create the set method MethodDefinition set = fd.DeclaringType.AddMethod("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig); ParameterDefinition valueParam = set.AddParam(originalType, "value"); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; ILProcessor worker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = worker.Create(OpCodes.Nop); // this worker.Append(worker.Create(OpCodes.Ldarg_0)); // new value to set worker.Append(worker.Create(OpCodes.Ldarg, valueParam)); // reference to field to set // make generic version of SetSyncVar with field type WriteLoadField(worker, syncVar); MethodReference syncVarEqual = module.ImportReference <NetworkBehaviour>(nb => nb.SyncVarEqual <object>(default, default));
/// <summary> /// Generates a skeleton for an RPC /// </summary> /// <param name="td"></param> /// <param name="method"></param> /// <param name="cmdCallFunc"></param> /// <returns>The newly created skeleton method</returns> /// <remarks> /// Generates code like this: /// <code> /// protected static void Skeleton_Test(NetworkBehaviour obj, NetworkReader reader, NetworkConnection senderConnection) /// { /// if (!obj.netIdentity.server.active) /// { /// return; /// } /// ((ShipControl) obj).UserCode_Test(reader.ReadSingle(), (int) reader.ReadPackedUInt32()); /// } /// </code> /// </remarks> MethodDefinition GenerateSkeleton(MethodDefinition md, MethodDefinition userCodeFunc, CustomAttribute clientRpcAttr) { MethodDefinition rpc = md.DeclaringType.AddMethod( SkeletonPrefix + md.Name, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig); #if FIX _ = rpc.AddParam <NetworkBehaviour>("obj"); #endif _ = rpc.AddParam <NetworkReader>("reader"); _ = rpc.AddParam <INetworkConnection>("senderConnection"); _ = rpc.AddParam <int>("replyId"); ILProcessor worker = rpc.Body.GetILProcessor(); // setup for reader worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Castclass, md.DeclaringType)); // NetworkConnection parameter is only required for Client.Connection Client target = clientRpcAttr.GetField("target", Client.Observers); bool hasNetworkConnection = target == Client.Connection && HasNetworkConnectionParameter(md); if (hasNetworkConnection) { //client.connection worker.Append(worker.Create(OpCodes.Ldarg_0)); #if FIX worker.Append(worker.Create(OpCodes.Call, (NetworkBehaviour nb) => nb.ConnectionToServer)); #endif } if (!ReadArguments(md, worker, hasNetworkConnection)) { return(rpc); } // invoke actual ServerRpc function worker.Append(worker.Create(OpCodes.Callvirt, userCodeFunc)); worker.Append(worker.Create(OpCodes.Ret)); return(rpc); }
/// <summary> /// Generates a skeleton for an RPC /// </summary> /// <param name="td"></param> /// <param name="method"></param> /// <param name="cmdCallFunc"></param> /// <returns>The newly created skeleton method</returns> /// <remarks> /// Generates code like this: /// <code> /// protected static void Skeleton_Test(NetworkBehaviour obj, NetworkReader reader, NetworkConnection senderConnection) /// { /// if (!obj.netIdentity.server.active) /// { /// return; /// } /// ((ShipControl) obj).UserCode_Test(reader.ReadSingle(), (int) reader.ReadPackedUInt32()); /// } /// </code> /// </remarks> MethodDefinition GenerateSkeleton(MethodDefinition md, MethodDefinition userCodeFunc, CustomAttribute clientRpcAttr) { MethodDefinition rpc = md.DeclaringType.AddMethod( SkeletonPrefix + md.Name, MethodAttributes.Family | MethodAttributes.HideBySig); _ = rpc.AddParam <NetworkReader>("reader"); _ = rpc.AddParam <INetworkPlayer>("senderConnection"); _ = rpc.AddParam <int>("replyId"); ILProcessor worker = rpc.Body.GetILProcessor(); worker.Append(worker.Create(OpCodes.Ldarg_0)); // NetworkConnection parameter is only required for Client.Connection Client target = clientRpcAttr.GetField("target", Client.Observers); bool hasNetworkConnection = target == Client.Connection && HasNetworkConnectionParameter(md); if (hasNetworkConnection) { // this is called in the skeleton (the client) // the client should just get the connection to the server and pass that in worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Call, (NetworkBehaviour nb) => nb.Client)); worker.Append(worker.Create(OpCodes.Call, (NetworkClient nb) => nb.Player)); } if (!ReadArguments(md, worker, hasNetworkConnection)) { return(rpc); } // invoke actual ServerRpc function worker.Append(worker.Create(OpCodes.Callvirt, userCodeFunc)); worker.Append(worker.Create(OpCodes.Ret)); return(rpc); }
MethodDefinition GenerateSyncVarSetterInitialOnly(FoundSyncVar syncVar) { // todo reduce duplicate code with this and GenerateSyncVarSetter FieldDefinition fd = syncVar.FieldDefinition; TypeReference originalType = syncVar.OriginalType; string originalName = syncVar.OriginalName; //Create the set method MethodDefinition set = fd.DeclaringType.AddMethod("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig); ParameterDefinition valueParam = set.AddParam(originalType, "value"); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; ILProcessor worker = set.Body.GetILProcessor(); WriteStoreField(worker, valueParam, syncVar); worker.Append(worker.Create(OpCodes.Ret)); return(set); }