TargetRpcProcessor.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using Mono.CecilX;
  2. using Mono.CecilX.Cil;
  3. namespace Mirror.Weaver
  4. {
  5. /// <summary>
  6. /// Processes [TargetRpc] methods in NetworkBehaviour
  7. /// </summary>
  8. public static class TargetRpcProcessor
  9. {
  10. // helper functions to check if the method has a NetworkConnection parameter
  11. public static bool HasNetworkConnectionParameter(MethodDefinition md)
  12. {
  13. return md.Parameters.Count > 0 &&
  14. md.Parameters[0].ParameterType.Is<NetworkConnection>();
  15. }
  16. public static MethodDefinition ProcessTargetRpcInvoke(TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc)
  17. {
  18. MethodDefinition rpc = new MethodDefinition(Weaver.InvokeRpcPrefix + md.Name, MethodAttributes.Family |
  19. MethodAttributes.Static |
  20. MethodAttributes.HideBySig,
  21. WeaverTypes.Import(typeof(void)));
  22. ILProcessor worker = rpc.Body.GetILProcessor();
  23. Instruction label = worker.Create(OpCodes.Nop);
  24. NetworkBehaviourProcessor.WriteClientActiveCheck(worker, md.Name, label, "TargetRPC");
  25. // setup for reader
  26. worker.Emit(OpCodes.Ldarg_0);
  27. worker.Emit(OpCodes.Castclass, td);
  28. // NetworkConnection parameter is optional
  29. if (HasNetworkConnectionParameter(md))
  30. {
  31. // on server, the NetworkConnection parameter is a connection to client.
  32. // when the rpc is invoked on the client, it still has the same
  33. // function signature. we pass in the connection to server,
  34. // which is cleaner than just passing null)
  35. //NetworkClient.readyconnection
  36. //
  37. // TODO
  38. // a) .connectionToServer = best solution. no doubt.
  39. // b) NetworkClient.connection for now. add TODO to not use static later.
  40. worker.Emit(OpCodes.Call, WeaverTypes.ReadyConnectionReference);
  41. }
  42. // process reader parameters and skip first one if first one is NetworkConnection
  43. if (!NetworkBehaviourProcessor.ReadArguments(md, worker, RemoteCallType.TargetRpc))
  44. return null;
  45. // invoke actual command function
  46. worker.Emit(OpCodes.Callvirt, rpcCallFunc);
  47. worker.Emit(OpCodes.Ret);
  48. NetworkBehaviourProcessor.AddInvokeParameters(rpc.Parameters);
  49. td.Methods.Add(rpc);
  50. return rpc;
  51. }
  52. /* generates code like:
  53. public void TargetTest (NetworkConnection conn, int param)
  54. {
  55. NetworkWriter writer = new NetworkWriter ();
  56. writer.WritePackedUInt32 ((uint)param);
  57. base.SendTargetRPCInternal (conn, typeof(class), "TargetTest", val);
  58. }
  59. public void CallTargetTest (NetworkConnection conn, int param)
  60. {
  61. // whatever the user did before
  62. }
  63. or if optional:
  64. public void TargetTest (int param)
  65. {
  66. NetworkWriter writer = new NetworkWriter ();
  67. writer.WritePackedUInt32 ((uint)param);
  68. base.SendTargetRPCInternal (null, typeof(class), "TargetTest", val);
  69. }
  70. public void CallTargetTest (int param)
  71. {
  72. // whatever the user did before
  73. }
  74. Originally HLAPI put the send message code inside the Call function
  75. and then proceeded to replace every call to TargetTest with CallTargetTest
  76. This method moves all the user's code into the "CallTargetRpc" method
  77. and replaces the body of the original method with the send message code.
  78. This way we do not need to modify the code anywhere else, and this works
  79. correctly in dependent assemblies
  80. */
  81. public static MethodDefinition ProcessTargetRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute targetRpcAttr)
  82. {
  83. MethodDefinition rpc = MethodProcessor.SubstituteMethod(td, md);
  84. ILProcessor worker = md.Body.GetILProcessor();
  85. NetworkBehaviourProcessor.WriteSetupLocals(worker);
  86. NetworkBehaviourProcessor.WriteCreateWriter(worker);
  87. // write all the arguments that the user passed to the TargetRpc call
  88. // (skip first one if first one is NetworkConnection)
  89. if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.TargetRpc))
  90. return null;
  91. string rpcName = md.Name;
  92. // invoke SendInternal and return
  93. // this
  94. worker.Emit(OpCodes.Ldarg_0);
  95. if (HasNetworkConnectionParameter(md))
  96. {
  97. // connection
  98. worker.Emit(OpCodes.Ldarg_1);
  99. }
  100. else
  101. {
  102. // null
  103. worker.Emit(OpCodes.Ldnull);
  104. }
  105. worker.Emit(OpCodes.Ldtoken, td);
  106. // invokerClass
  107. worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference);
  108. worker.Emit(OpCodes.Ldstr, rpcName);
  109. // writer
  110. worker.Emit(OpCodes.Ldloc_0);
  111. worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0));
  112. worker.Emit(OpCodes.Callvirt, WeaverTypes.sendTargetRpcInternal);
  113. NetworkBehaviourProcessor.WriteRecycleWriter(worker);
  114. worker.Emit(OpCodes.Ret);
  115. return rpc;
  116. }
  117. }
  118. }