ServerClientAttributeProcessor.cs 6.7 KB

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