/// <summary> /// Replaces the user code with a stub. /// Moves the original code to a new method /// </summary> /// <param name="td">The class containing the method </param> /// <param name="md">The method to be stubbed </param> /// <param name="ServerRpcAttr">The attribute that made this an RPC</param> /// <returns>The method containing the original code</returns> /// <remarks> /// Generates code like this: (Observers case) /// <code> /// public void Test (int param) /// { /// NetworkWriter writer = new NetworkWriter(); /// writer.WritePackedUInt32((uint) param); /// base.SendRpcInternal(typeof(class),"RpcTest", writer, 0); /// } /// public void UserCode_Test(int param) /// { /// // whatever the user did before /// } /// </code> /// /// Generates code like this: (Owner/Connection case) /// <code> /// public void TargetTest(NetworkConnection conn, int param) /// { /// NetworkWriter writer = new NetworkWriter(); /// writer.WritePackedUInt32((uint)param); /// base.SendTargetRpcInternal(conn, typeof(class), "TargetTest", val); /// } /// /// public void UserCode_TargetTest(NetworkConnection conn, int param) /// { /// // whatever the user did before /// } /// </code> /// or if no connection is specified /// /// <code> /// public void TargetTest (int param) /// { /// NetworkWriter writer = new NetworkWriter(); /// writer.WritePackedUInt32((uint) param); /// base.SendTargetRpcInternal(null, typeof(class), "TargetTest", val); /// } /// /// public void UserCode_TargetTest(int param) /// { /// // whatever the user did before /// } /// </code> /// </remarks> private MethodDefinition GenerateStub(MethodDefinition md, CustomAttribute clientRpcAttr, int rpcIndex, ValueSerializer[] paramSerializers) { var rpc = SubstituteMethod(md); var worker = md.Body.GetILProcessor(); // if (IsClient) // { // call the body // } CallBody(worker, rpc); // NetworkWriter writer = NetworkWriterPool.GetWriter() var writer = md.AddLocal <PooledNetworkWriter>(); worker.Append(worker.Create(OpCodes.Call, () => NetworkWriterPool.GetWriter())); worker.Append(worker.Create(OpCodes.Stloc, writer)); // write all the arguments that the user passed to the Rpc call WriteArguments(worker, md, writer, paramSerializers, RemoteCallType.ClientRpc); var rpcName = md.FullName; var target = clientRpcAttr.GetField(nameof(ClientRpcAttribute.target), RpcTarget.Observers); var channel = clientRpcAttr.GetField(nameof(ClientRpcAttribute.channel), 0); var excludeOwner = clientRpcAttr.GetField(nameof(ClientRpcAttribute.excludeOwner), false); var sendMethod = GetSendMethod(md, target); // ClientRpcSender.Send(this, 12345, writer, channel, requireAuthority) worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I4, rpcIndex)); worker.Append(worker.Create(OpCodes.Ldloc, writer)); worker.Append(worker.Create(OpCodes.Ldc_I4, channel)); // last arg of send is either bool, or NetworkPlayer // see ClientRpcSender.Send methods if (target == RpcTarget.Observers) { worker.Append(worker.Create(excludeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); } else if (target == RpcTarget.Player && HasNetworkPlayerParameter(md)) { worker.Append(worker.Create(OpCodes.Ldarg_1)); } else // owner, or Player with no arg { worker.Append(worker.Create(OpCodes.Ldnull)); } worker.Append(worker.Create(OpCodes.Call, sendMethod)); NetworkWriterHelper.CallRelease(module, worker, writer); worker.Append(worker.Create(OpCodes.Ret)); return(rpc); }
/// <summary> /// Replaces the user code with a stub. /// Moves the original code to a new method /// </summary> /// <param name="td">The class containing the method </param> /// <param name="md">The method to be stubbed </param> /// <param name="ServerRpcAttr">The attribute that made this an RPC</param> /// <returns>The method containing the original code</returns> /// <remarks> /// Generates code like this: /// <code> /// public void MyServerRpc(float thrusting, int spin) /// { /// NetworkWriter networkWriter = new NetworkWriter(); /// networkWriter.Write(thrusting); /// networkWriter.WritePackedUInt32((uint) spin); /// base.SendServerRpcInternal(cmdName, networkWriter, cmdName); /// } /// /// public void UserCode_MyServerRpc(float thrusting, int spin) /// { /// // whatever the user was doing before /// /// } /// </code> /// </remarks> private MethodDefinition GenerateStub(MethodDefinition md, CustomAttribute serverRpcAttr, int rpcIndex, ValueSerializer[] paramSerializers) { var cmd = SubstituteMethod(md); var worker = md.Body.GetILProcessor(); // if (IsServer) // { // call the body // return; // } CallBody(worker, cmd); // NetworkWriter writer = NetworkWriterPool.GetWriter() var writer = md.AddLocal <PooledNetworkWriter>(); worker.Append(worker.Create(OpCodes.Call, md.Module.ImportReference(() => NetworkWriterPool.GetWriter()))); worker.Append(worker.Create(OpCodes.Stloc, writer)); // write all the arguments that the user passed to the Cmd call WriteArguments(worker, md, writer, paramSerializers, RemoteCallType.ServerRpc); var cmdName = md.FullName; var channel = serverRpcAttr.GetField(nameof(ServerRpcAttribute.channel), 0); var requireAuthority = serverRpcAttr.GetField(nameof(ServerRpcAttribute.requireAuthority), true); var sendMethod = GetSendMethod(md, worker); // ServerRpcSender.Send(this, 12345, writer, channel, requireAuthority) worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I4, rpcIndex)); worker.Append(worker.Create(OpCodes.Ldloc, writer)); worker.Append(worker.Create(OpCodes.Ldc_I4, channel)); worker.Append(worker.Create(requireAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Call, sendMethod)); NetworkWriterHelper.CallRelease(module, worker, writer); worker.Append(worker.Create(OpCodes.Ret)); return(cmd); }