ServerClientAttributeProcessor.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Injects server/client active checks for [Server/Client] attributes
  2. using Mono.CecilX;
  3. using Mono.CecilX.Cil;
  4. namespace Mirror.Weaver
  5. {
  6. static class ServerClientAttributeProcessor
  7. {
  8. public static bool Process(TypeDefinition td)
  9. {
  10. bool modified = false;
  11. foreach (MethodDefinition md in td.Methods)
  12. {
  13. modified |= ProcessSiteMethod(md);
  14. }
  15. foreach (TypeDefinition nested in td.NestedTypes)
  16. {
  17. modified |= Process(nested);
  18. }
  19. return modified;
  20. }
  21. static bool ProcessSiteMethod(MethodDefinition md)
  22. {
  23. if (md.Name == ".cctor" ||
  24. md.Name == NetworkBehaviourProcessor.ProcessedFunctionName ||
  25. md.Name.StartsWith(Weaver.InvokeRpcPrefix))
  26. return false;
  27. if (md.IsAbstract)
  28. {
  29. if (HasServerClientAttribute(md))
  30. {
  31. Weaver.Error("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", md);
  32. }
  33. return false;
  34. }
  35. if (md.Body != null && md.Body.Instructions != null)
  36. {
  37. return ProcessMethodAttributes(md);
  38. }
  39. return false;
  40. }
  41. public static bool HasServerClientAttribute(MethodDefinition md)
  42. {
  43. foreach (CustomAttribute attr in md.CustomAttributes)
  44. {
  45. switch (attr.Constructor.DeclaringType.ToString())
  46. {
  47. case "Mirror.ServerAttribute":
  48. case "Mirror.ServerCallbackAttribute":
  49. case "Mirror.ClientAttribute":
  50. case "Mirror.ClientCallbackAttribute":
  51. return true;
  52. default:
  53. break;
  54. }
  55. }
  56. return false;
  57. }
  58. public static bool ProcessMethodAttributes(MethodDefinition md)
  59. {
  60. if (md.HasCustomAttribute<ServerAttribute>())
  61. InjectServerGuard(md, true);
  62. else if (md.HasCustomAttribute<ServerCallbackAttribute>())
  63. InjectServerGuard(md, false);
  64. else if (md.HasCustomAttribute<ClientAttribute>())
  65. InjectClientGuard(md, true);
  66. else if (md.HasCustomAttribute<ClientCallbackAttribute>())
  67. InjectClientGuard(md, false);
  68. else
  69. return false;
  70. return true;
  71. }
  72. static void InjectServerGuard(MethodDefinition md, bool logWarning)
  73. {
  74. ILProcessor worker = md.Body.GetILProcessor();
  75. Instruction top = md.Body.Instructions[0];
  76. worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.NetworkServerGetActive));
  77. worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top));
  78. if (logWarning)
  79. {
  80. worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Server] function '{md.FullName}' called when server was not active"));
  81. worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.logWarningReference));
  82. }
  83. InjectGuardParameters(md, worker, top);
  84. InjectGuardReturnValue(md, worker, top);
  85. worker.InsertBefore(top, worker.Create(OpCodes.Ret));
  86. }
  87. static void InjectClientGuard(MethodDefinition md, bool logWarning)
  88. {
  89. ILProcessor worker = md.Body.GetILProcessor();
  90. Instruction top = md.Body.Instructions[0];
  91. worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.NetworkClientGetActive));
  92. worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top));
  93. if (logWarning)
  94. {
  95. worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Client] function '{md.FullName}' called when client was not active"));
  96. worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.logWarningReference));
  97. }
  98. InjectGuardParameters(md, worker, top);
  99. InjectGuardReturnValue(md, worker, top);
  100. worker.InsertBefore(top, worker.Create(OpCodes.Ret));
  101. }
  102. // this is required to early-out from a function with "ref" or "out" parameters
  103. static void InjectGuardParameters(MethodDefinition md, ILProcessor worker, Instruction top)
  104. {
  105. int offset = md.Resolve().IsStatic ? 0 : 1;
  106. for (int index = 0; index < md.Parameters.Count; index++)
  107. {
  108. ParameterDefinition param = md.Parameters[index];
  109. if (param.IsOut)
  110. {
  111. TypeReference elementType = param.ParameterType.GetElementType();
  112. md.Body.Variables.Add(new VariableDefinition(elementType));
  113. md.Body.InitLocals = true;
  114. worker.InsertBefore(top, worker.Create(OpCodes.Ldarg, index + offset));
  115. worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1)));
  116. worker.InsertBefore(top, worker.Create(OpCodes.Initobj, elementType));
  117. worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1));
  118. worker.InsertBefore(top, worker.Create(OpCodes.Stobj, elementType));
  119. }
  120. }
  121. }
  122. // this is required to early-out from a function with a return value.
  123. static void InjectGuardReturnValue(MethodDefinition md, ILProcessor worker, Instruction top)
  124. {
  125. if (!md.ReturnType.Is(typeof(void)))
  126. {
  127. md.Body.Variables.Add(new VariableDefinition(md.ReturnType));
  128. md.Body.InitLocals = true;
  129. worker.InsertBefore(top, worker.Create(OpCodes.Ldloca_S, (byte)(md.Body.Variables.Count - 1)));
  130. worker.InsertBefore(top, worker.Create(OpCodes.Initobj, md.ReturnType));
  131. worker.InsertBefore(top, worker.Create(OpCodes.Ldloc, md.Body.Variables.Count - 1));
  132. }
  133. }
  134. }
  135. }