Terraria ModLoader  0.11.7.8
A mod to make and play Terraria mods
MonoModHooks.cs
Go to the documentation of this file.
1 using MonoMod.RuntimeDetour;
2 using MonoMod.RuntimeDetour.HookGen;
3 using MonoMod.Utils;
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 using System.Reflection;
9 using System.Reflection.Emit;
10 using Terraria.ModLoader.Core;
11 using Terraria.ModLoader.Default;
12 
13 namespace Terraria.ModLoader
14 {
15  public static class MonoModHooks
16  {
17  private static Dictionary<Type, string> defaultAliases = new Dictionary<Type, string> {
18  { typeof(object), "object" },
19  { typeof(bool), "bool" },
20  { typeof(float), "float" },
21  { typeof(double), "double" },
22  { typeof(decimal), "decimal" },
23  { typeof(byte), "byte" },
24  { typeof(sbyte), "sbyte" },
25  { typeof(short), "short" },
26  { typeof(ushort), "ushort" },
27  { typeof(int), "int" },
28  { typeof(uint), "uint" },
29  { typeof(long), "long" },
30  { typeof(ulong), "ulong" },
31  { typeof(char), "char" },
32  { typeof(string), "string" }
33  };
34 
35  private static DetourModManager manager = new DetourModManager();
36  private static HashSet<Assembly> NativeDetouringGranted = new HashSet<Assembly>();
37 
38  private static bool isInitialized;
39  internal static void Initialize() {
40  if (isInitialized)
41  return;
42 
43  HookEndpointManager.OnAdd += (m, d) => {
44  Logging.tML.Debug($"Hook On.{StringRep(m)} added by {GetOwnerName(d)}");
45  return true;
46  };
47  HookEndpointManager.OnRemove += (m, d) => {
48  Logging.tML.Debug($"Hook On.{StringRep(m)} removed by {GetOwnerName(d)}");
49  return true;
50  };
51  HookEndpointManager.OnModify += (m, d) => {
52  Logging.tML.Debug($"Hook IL.{StringRep(m)} modified by {GetOwnerName(d)}");
53  return true;
54  };
55  HookEndpointManager.OnUnmodify += (m, d) => {
56  Logging.tML.Debug($"Hook IL.{StringRep(m)} unmodified by {GetOwnerName(d)}");
57  return true;
58  };
59 
60  manager.OnHook += (asm, from, to, target) => {
61  NativeAccessCheck(asm);
62  Logging.tML.Debug($"Hook {StringRep(from)} -> {StringRep(to)} by {asm.GetName().Name}");
63  };
64 
65  manager.OnILHook += (asm, from, manipulator) => {
66  NativeAccessCheck(asm);
67  Logging.tML.Debug($"ILHook {StringRep(from)} by {asm.GetName().Name}");
68  };
69 
70  manager.OnDetour += (asm, from, to) => {
71  NativeAccessCheck(asm);
72  Logging.tML.Debug($"Detour {StringRep(from)} -> {StringRep(to)} by {asm.GetName().Name}");
73  };
74 
75  manager.OnNativeDetour += (asm, method, from, to) => {
76  NativeAccessCheck(asm);
77  Logging.tML.Debug($"NativeDetour {StringRep(method)} [{from} -> {to}] by {asm.GetName().Name}");
78  };
79 
80  isInitialized = true;
81  }
82 
83  private static void NativeAccessCheck(Assembly asm) {
84  if (NativeDetouringGranted.Contains(asm))
85  return;
86 
87  throw new UnauthorizedAccessException(
88  $"Native detouring permissions not granted to {asm.GetName().Name}. \n" +
89  $"Mods should use HookEndpointManager for compatibility. \n" +
90  $"If Detour or NativeDetour are required, call MonoModHooks.RequestNativeAccess()");
91  }
92 
93  public static void RequestNativeAccess() {
94  var stack = new StackTrace();
95  var asm = stack.GetFrame(1).GetMethod().DeclaringType.Assembly;
96  NativeDetouringGranted.Add(asm);
97  Logging.tML.Warn($"Granted native detouring access to {asm.GetName().Name}");
98  }
99 
100  private static string GetOwnerName(Delegate d) {
101  return d.Method.DeclaringType.Assembly.GetName().Name;
102  }
103 
104  private static string StringRep(MethodBase m) {
105  var paramString = string.Join(", ", m.GetParameters().Select(p => {
106  var t = p.ParameterType;
107  var s = "";
108 
109  if (t.IsByRef) {
110  s = p.IsOut ? "out " : "ref ";
111  t = t.GetElementType();
112  }
113 
114  return s + (defaultAliases.TryGetValue(t, out string n) ? n : t.Name);
115  }));
116 
117  var owner = m.DeclaringType?.FullName ??
118  (m is DynamicMethod ? "dynamic" : "unknown");
119  return $"{owner}::{m.Name}({paramString})";
120  }
121 
122  internal static void RemoveAll(Mod mod) {
123  if (mod is ModLoaderMod)
124  return;
125 
126  int hooks = 0, detours = 0, ndetours = 0;
127  bool OnHookUndo(object obj) {
128  hooks++;
129  return true;
130  }
131  bool OnDetourUndo(object obj) {
132  detours++;
133  return true;
134  }
135  bool OnNativeDetourUndo(object obj) {
136  ndetours++;
137  return true;
138  }
139 
140  Hook.OnUndo += OnHookUndo;
141  Detour.OnUndo += OnDetourUndo;
142  NativeDetour.OnUndo += OnNativeDetourUndo;
143 
144  foreach (var asm in AssemblyManager.GetModAssemblies(mod.Name))
145  manager.Unload(asm);
146 
147  Hook.OnUndo -= OnHookUndo;
148  Detour.OnUndo -= OnDetourUndo;
149  NativeDetour.OnUndo -= OnNativeDetourUndo;
150 
151  if (hooks > 0 || detours > 0 || ndetours > 0)
152  Logging.tML.Debug($"Unloaded {hooks} hooks, {detours} detours and {ndetours} native detours from {mod.Name}");
153  }
154  }
155 }
static string GetOwnerName(Delegate d)
static void NativeAccessCheck(Assembly asm)
Definition: MonoModHooks.cs:83
virtual string Name
Stores the name of the mod. This name serves as the mod&#39;s identification, and also helps with saving ...
Definition: Mod.cs:42
Mod is an abstract class that you will override. It serves as a central place from which the mod&#39;s co...
Definition: Mod.cs:24
static string StringRep(MethodBase m)