CommandProcessor.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. using Mono.CecilX;
  2. using Mono.CecilX.Cil;
  3. namespace Mirror.Weaver
  4. {
  5. /// <summary>
  6. /// Processes [Command] methods in NetworkBehaviour
  7. /// </summary>
  8. public static class CommandProcessor
  9. {
  10. /*
  11. // generates code like:
  12. public void CmdThrust(float thrusting, int spin)
  13. {
  14. NetworkWriter networkWriter = new NetworkWriter();
  15. networkWriter.Write(thrusting);
  16. networkWriter.WritePackedUInt32((uint)spin);
  17. base.SendCommandInternal(cmdName, networkWriter, cmdName);
  18. }
  19. public void CallCmdThrust(float thrusting, int spin)
  20. {
  21. // whatever the user was doing before
  22. }
  23. Originally HLAPI put the send message code inside the Call function
  24. and then proceeded to replace every call to CmdTrust with CallCmdTrust
  25. This method moves all the user's code into the "CallCmd" method
  26. and replaces the body of the original method with the send message code.
  27. This way we do not need to modify the code anywhere else, and this works
  28. correctly in dependent assemblies
  29. */
  30. public static MethodDefinition ProcessCommandCall(TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr)
  31. {
  32. MethodDefinition cmd = MethodProcessor.SubstituteMethod(td, md);
  33. ILProcessor worker = md.Body.GetILProcessor();
  34. NetworkBehaviourProcessor.WriteSetupLocals(worker);
  35. // NetworkWriter writer = new NetworkWriter();
  36. NetworkBehaviourProcessor.WriteCreateWriter(worker);
  37. // write all the arguments that the user passed to the Cmd call
  38. if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.Command))
  39. return null;
  40. string cmdName = md.Name;
  41. int channel = commandAttr.GetField("channel", 0);
  42. bool requiresAuthority = commandAttr.GetField("requiresAuthority", true);
  43. // invoke internal send and return
  44. // load 'base.' to call the SendCommand function with
  45. worker.Emit(OpCodes.Ldarg_0);
  46. worker.Emit(OpCodes.Ldtoken, td);
  47. // invokerClass
  48. worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference);
  49. worker.Emit(OpCodes.Ldstr, cmdName);
  50. // writer
  51. worker.Emit(OpCodes.Ldloc_0);
  52. worker.Emit(OpCodes.Ldc_I4, channel);
  53. // requiresAuthority ? 1 : 0
  54. worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  55. worker.Emit(OpCodes.Call, WeaverTypes.sendCommandInternal);
  56. NetworkBehaviourProcessor.WriteRecycleWriter(worker);
  57. worker.Emit(OpCodes.Ret);
  58. return cmd;
  59. }
  60. /*
  61. // generates code like:
  62. protected static void InvokeCmdCmdThrust(NetworkBehaviour obj, NetworkReader reader, NetworkConnection senderConnection)
  63. {
  64. if (!NetworkServer.active)
  65. {
  66. return;
  67. }
  68. ((ShipControl)obj).CmdThrust(reader.ReadSingle(), (int)reader.ReadPackedUInt32());
  69. }
  70. */
  71. public static MethodDefinition ProcessCommandInvoke(TypeDefinition td, MethodDefinition method, MethodDefinition cmdCallFunc)
  72. {
  73. MethodDefinition cmd = new MethodDefinition(Weaver.InvokeRpcPrefix + method.Name,
  74. MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig,
  75. WeaverTypes.Import(typeof(void)));
  76. ILProcessor worker = cmd.Body.GetILProcessor();
  77. Instruction label = worker.Create(OpCodes.Nop);
  78. NetworkBehaviourProcessor.WriteServerActiveCheck(worker, method.Name, label, "Command");
  79. // setup for reader
  80. worker.Emit(OpCodes.Ldarg_0);
  81. worker.Emit(OpCodes.Castclass, td);
  82. if (!NetworkBehaviourProcessor.ReadArguments(method, worker, RemoteCallType.Command))
  83. return null;
  84. AddSenderConnection(method, worker);
  85. // invoke actual command function
  86. worker.Emit(OpCodes.Callvirt, cmdCallFunc);
  87. worker.Emit(OpCodes.Ret);
  88. NetworkBehaviourProcessor.AddInvokeParameters(cmd.Parameters);
  89. td.Methods.Add(cmd);
  90. return cmd;
  91. }
  92. static void AddSenderConnection(MethodDefinition method, ILProcessor worker)
  93. {
  94. foreach (ParameterDefinition param in method.Parameters)
  95. {
  96. if (NetworkBehaviourProcessor.IsSenderConnection(param, RemoteCallType.Command))
  97. {
  98. // NetworkConnection is 3nd arg (arg0 is "obj" not "this" because method is static)
  99. // example: static void InvokeCmdCmdSendCommand(NetworkBehaviour obj, NetworkReader reader, NetworkConnection connection)
  100. worker.Emit(OpCodes.Ldarg_2);
  101. }
  102. }
  103. }
  104. }
  105. }