Terraria ModLoader  0.11.1
A framework for Terraria mods
ModLoader.cs
Go to the documentation of this file.
1 using Microsoft.Xna.Framework.Audio;
2 using Microsoft.Xna.Framework.Graphics;
3 using ReLogic.OS;
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 using System.Linq.Expressions;
9 using System.Reflection;
10 using System.Security.Cryptography;
11 using System.Threading;
12 using System.Windows.Forms;
13 using Terraria.Localization;
15 using Terraria.ModLoader.Core;
16 using Terraria.ModLoader.Default;
17 using Terraria.ModLoader.Engine;
18 
19 namespace Terraria.ModLoader
20 {
24  public static class ModLoader
25  {
26  public static readonly Version version = new Version(0, 11, 1);
27 
28  public static readonly string branchName = "";
29  // beta > 0 cannot publish to mod browser
30  public static readonly int beta = 0;
31 
32  public static readonly string versionedName = $"tModLoader v{version}" +
33  (branchName.Length == 0 ? "" : $" {branchName}") +
34  (beta == 0 ? "" : $" Beta {beta}");
35  public static readonly string versionTag = $"v{version}" +
36  (branchName.Length == 0 ? "" : $"-{branchName.ToLower()}") +
37  (beta == 0 ? "" : $"-beta{beta}");
38 
39  [Obsolete("Use Platform.IsWindows")]
40  public static readonly bool windows = Platform.IsWindows;
41  [Obsolete("Use Platform.IsLinux")]
42  public static readonly bool linux = Platform.IsLinux;
43  [Obsolete("Use Platform.IsOSX")]
44  public static readonly bool mac = Platform.IsOSX;
45 
46  [Obsolete("Use CompressedPlatformRepresentation instead")]
47  public static readonly string compressedPlatformRepresentation = Platform.IsWindows ? "w" : (Platform.IsLinux ? "l" : "m");
48 
49  public static string CompressedPlatformRepresentation => (Platform.IsWindows ? "w" : (Platform.IsLinux ? "l" : "m")) + (GoGVerifier.IsGoG ? "g" : "s") + (FrameworkVersion.Framework == Framework.NetFramework ? "n" : (FrameworkVersion.Framework == Framework.Mono ? "o" : "u"));
50 
51  public static string ModPath => ModOrganizer.modPath;
52 
53  private static readonly IDictionary<string, Mod> modsByName = new Dictionary<string, Mod>(StringComparer.OrdinalIgnoreCase);
54  private static WeakReference[] weakModReferences = new WeakReference[0];
55 
56  internal static readonly string modBrowserPublicKey = "<RSAKeyValue><Modulus>oCZObovrqLjlgTXY/BKy72dRZhoaA6nWRSGuA+aAIzlvtcxkBK5uKev3DZzIj0X51dE/qgRS3OHkcrukqvrdKdsuluu0JmQXCv+m7sDYjPQ0E6rN4nYQhgfRn2kfSvKYWGefp+kqmMF9xoAq666YNGVoERPm3j99vA+6EIwKaeqLB24MrNMO/TIf9ysb0SSxoV8pC/5P/N6ViIOk3adSnrgGbXnFkNQwD0qsgOWDks8jbYyrxUFMc4rFmZ8lZKhikVR+AisQtPGUs3ruVh4EWbiZGM2NOkhOCOM4k1hsdBOyX2gUliD0yjK5tiU3LBqkxoi2t342hWAkNNb4ZxLotw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
57  internal static string modBrowserPassphrase = "";
58 
59  private static string steamID64 = "";
60  internal static string SteamID64 {
61  get => GoGVerifier.IsGoG ? steamID64 : Steamworks.SteamUser.GetSteamID().ToString();
62  set => steamID64 = value;
63  }
64 
65  internal static bool autoReloadAndEnableModsLeavingModBrowser = true;
66  internal static bool dontRemindModBrowserUpdateReload;
67  internal static bool dontRemindModBrowserDownloadEnable;
68  internal static bool removeForcedMinimumZoom;
69  internal static bool showMemoryEstimates;
70 
71  internal static bool skipLoad;
72 
73  internal static Action OnSuccessfulLoad;
74 
75  public static Mod[] Mods { get; private set; } = new Mod[0];
76 
80  public static Mod GetMod(string name) {
81  modsByName.TryGetValue(name, out Mod m);
82  return m;
83  }
84 
85  public static Mod GetMod(int index) => index >= 0 && index < Mods.Length ? Mods[index] : null;
86 
87  [Obsolete("Use ModLoader.Mods", true)]
88  public static Mod[] LoadedMods => Mods;
89 
90  [Obsolete("Use ModLoader.Mods.Length", true)]
91  public static int ModCount => Mods.Length;
92 
93  [Obsolete("Use ModLoader.Mods.Select(m => m.Name)", true)]
94  public static string[] GetLoadedMods() => Mods.Reverse().Select(m => m.Name).ToArray();
95 
96  internal static void EngineInit() {
97  DotNet45Check();
98  FileAssociationSupport.UpdateFileAssociation();
99  GLCallLocker.Init();
100  HiDefGraphicsIssues.Init();
101  MonoModHooks.Initialize();
102  }
103 
104  internal static void BeginLoad() => ThreadPool.QueueUserWorkItem(_ => Load());
105 
106  internal static void Load() {
107  try {
108  var modInstances = ModOrganizer.LoadMods();
109 
110  weakModReferences = modInstances.Select(x => new WeakReference(x)).ToArray();
111  modInstances.Insert(0, new ModLoaderMod());
112  Mods = modInstances.ToArray();
113  foreach (var mod in Mods)
114  modsByName[mod.Name] = mod;
115 
116  ModContent.Load();
117 
118  if (OnSuccessfulLoad != null) {
119  OnSuccessfulLoad();
120  OnSuccessfulLoad = null;
121  }
122  else {
123  Main.menuMode = 0;
124  }
125  }
126  catch (Exception e) {
127  var responsibleMods = new List<string>();
128  if (e.Data.Contains("mod"))
129  responsibleMods.Add((string)e.Data["mod"]);
130  if (e.Data.Contains("mods"))
131  responsibleMods.AddRange((IEnumerable<string>)e.Data["mods"]);
132  responsibleMods.Remove("ModLoader");
133 
134  if (responsibleMods.Count == 0 && AssemblyManager.FirstModInStackTrace(new StackTrace(e), out var stackMod))
135  responsibleMods.Add(stackMod);
136 
137  var msg = Language.GetTextValue("tModLoader.LoadError", string.Join(", ", responsibleMods));
138  if (responsibleMods.Count == 1) {
139  var mod = ModOrganizer.FindMods().SingleOrDefault(m => m.Name == responsibleMods[0]);
140  if (mod != null && mod.tModLoaderVersion != version)
141  msg += "\n" + Language.GetTextValue("tModLoader.LoadErrorVersionMessage", mod.tModLoaderVersion, versionedName);
142  }
143  if (responsibleMods.Count > 0)
144  msg += "\n" + Language.GetTextValue("tModLoader.LoadErrorDisabled");
145  else
146  msg += "\n" + Language.GetTextValue("tModLoader.LoadErrorCulpritUnknown");
147 
148  Logging.tML.Error(msg, e);
149 
150  foreach (var mod in responsibleMods)
151  DisableMod(mod);
152 
153  DisplayLoadError(msg, e, e.Data.Contains("fatal"), responsibleMods.Count == 0);
154  }
155  }
156 
157  private static void DotNet45Check() {
158  if (FrameworkVersion.Framework != Framework.NetFramework || FrameworkVersion.Version >= new Version(4, 5))
159  return;
160 
161  var msg = Language.GetTextValue("tModLoader.LoadErrorDotNet45Required");
162 #if CLIENT
163  MessageBox.Show(msg);
164  Process.Start("https://www.microsoft.com/net/download/thank-you/net472");
165 #else
166  Console.ForegroundColor = ConsoleColor.Red;
167  Console.WriteLine(msg);
168  Console.ResetColor();
169  Console.WriteLine("Press any key to exit...");
170  Console.ReadKey();
171 #endif
172  Environment.Exit(-1);
173  }
174 
175  internal static void Reload() {
176  try {
177  Unload();
178 
179  if (Main.dedServ)
180  Load();
181  else
182  Main.menuMode = Interface.loadModsID;
183  }
184  catch (Exception e) {
185  var msg = Language.GetTextValue("tModLoader.UnloadError");
186 
187  if (e.Data.Contains("mod"))
188  msg += "\n" + Language.GetTextValue("tModLoader.DefensiveUnload", e.Data["mod"]);
189 
190  Logging.tML.Fatal(msg, e);
191  DisplayLoadError(msg, e, true);
192  }
193  }
194 
195  internal static List<string> badUnloaders = new List<string>();
196  private static void Unload() {
197  Logging.tML.Info("Unloading mods");
198  if (Main.dedServ)
199  Console.WriteLine("Unloading mods...");
200 
201  ModContent.UnloadModContent();
202  Mods = new Mod[0];
203  modsByName.Clear();
204  ModContent.Unload();
205 
206  MemoryTracking.Clear();
207  Thread.MemoryBarrier();
208  GC.Collect();
209  badUnloaders.Clear();
210  foreach (var mod in weakModReferences.Where(r => r.IsAlive).Select(r => (Mod)r.Target)) {
211  Logging.tML.WarnFormat("{0} not fully unloaded during unload.", mod.Name);
212  badUnloaders.Add(mod.Name);
213  }
214  }
215 
216  private static void DisplayLoadError(string msg, Exception e, bool fatal, bool continueIsRetry = false) {
217  msg += "\n\n" + (e.Data.Contains("hideStackTrace") ? e.Message : e.ToString());
218 
219  if (Main.dedServ) {
220  Console.ForegroundColor = ConsoleColor.Red;
221  Console.WriteLine(msg);
222  Console.ResetColor();
223 
224  if (fatal) {
225  Console.WriteLine("Press any key to exit...");
226  Console.ReadKey();
227  Environment.Exit(-1);
228  }
229  else {
230  Reload();
231  }
232  }
233  else {
234  Interface.errorMessage.Show(msg,
235  gotoMenu: fatal ? -1 : Interface.reloadModsID,
236  webHelpURL: e.HelpLink,
237  showRetry: continueIsRetry,
238  showSkip: !fatal);
239  }
240  }
241 
242  // TODO: This doesn't work on mono for some reason. Investigate.
243  public static bool IsSignedBy(TmodFile mod, string xmlPublicKey) {
244  var f = new RSAPKCS1SignatureDeformatter();
245  var v = AsymmetricAlgorithm.Create("RSA");
246  f.SetHashAlgorithm("SHA1");
247  v.FromXmlString(xmlPublicKey);
248  f.SetKey(v);
249  return f.VerifySignature(mod.hash, mod.signature);
250  }
251 
253  private static HashSet<string> _enabledMods;
254  internal static HashSet<string> EnabledMods => _enabledMods ?? (_enabledMods = ModOrganizer.LoadEnabledMods());
255 
256  internal static bool IsEnabled(string modName) => EnabledMods.Contains(modName);
257  internal static void EnableMod(string modName) => SetModEnabled(modName, true);
258  internal static void DisableMod(string modName) => SetModEnabled(modName, false);
259  internal static void SetModEnabled(string modName, bool active) {
260  if (active) {
261  EnabledMods.Add(modName);
262  Logging.tML.InfoFormat("Enabling Mod: {0}", modName);
263  }
264  else {
265  EnabledMods.Remove(modName);
266  Logging.tML.InfoFormat("Disabling Mod: {0}", modName);
267  }
268 
269  ModOrganizer.SaveEnabledMods();
270  }
271 
272  internal static void SaveConfiguration() {
273  Main.Configuration.Put("ModBrowserPassphrase", modBrowserPassphrase);
274  Main.Configuration.Put("SteamID64", steamID64);
275  Main.Configuration.Put("DownloadModsFromServers", ModNet.downloadModsFromServers);
276  Main.Configuration.Put("OnlyDownloadSignedModsFromServers", ModNet.onlyDownloadSignedMods);
277  Main.Configuration.Put("AutomaticallyReloadAndEnableModsLeavingModBrowser", autoReloadAndEnableModsLeavingModBrowser);
278  Main.Configuration.Put("DontRemindModBrowserUpdateReload", dontRemindModBrowserUpdateReload);
279  Main.Configuration.Put("DontRemindModBrowserDownloadEnable", dontRemindModBrowserDownloadEnable);
280  Main.Configuration.Put("RemoveForcedMinimumZoom", removeForcedMinimumZoom);
281  Main.Configuration.Put("ShowMemoryEstimates", showMemoryEstimates);
282  }
283 
284  internal static void LoadConfiguration() {
285  Main.Configuration.Get("ModBrowserPassphrase", ref modBrowserPassphrase);
286  Main.Configuration.Get("SteamID64", ref steamID64);
287  Main.Configuration.Get("DownloadModsFromServers", ref ModNet.downloadModsFromServers);
288  Main.Configuration.Get("OnlyDownloadSignedModsFromServers", ref ModNet.onlyDownloadSignedMods);
289  Main.Configuration.Get("AutomaticallyReloadAndEnableModsLeavingModBrowser", ref autoReloadAndEnableModsLeavingModBrowser);
290  Main.Configuration.Get("DontRemindModBrowserUpdateReload", ref dontRemindModBrowserUpdateReload);
291  Main.Configuration.Get("DontRemindModBrowserDownloadEnable", ref dontRemindModBrowserDownloadEnable);
292  Main.Configuration.Get("RemoveForcedMinimumZoom", ref removeForcedMinimumZoom);
293  Main.Configuration.Get("ShowMemoryEstimates", ref showMemoryEstimates);
294  }
295 
299  internal static void BuildGlobalHook<T, F>(ref F[] list, IList<T> providers, Expression<Func<T, F>> expr) {
300  list = BuildGlobalHook(providers, expr).Select(expr.Compile()).ToArray();
301  }
302 
303  internal static T[] BuildGlobalHook<T, F>(IList<T> providers, Expression<Func<T, F>> expr) {
304  return BuildGlobalHook(providers, Method(expr));
305  }
306 
307  internal static T[] BuildGlobalHook<T>(IList<T> providers, MethodInfo method) {
308  if (!method.IsVirtual) throw new ArgumentException("Cannot build hook for non-virtual method " + method);
309  var argTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
310  return providers.Where(p => p.GetType().GetMethod(method.Name, argTypes).DeclaringType != typeof(T)).ToArray();
311  }
312 
313  internal static MethodInfo Method<T, F>(Expression<Func<T, F>> expr) {
314  MethodInfo method;
315  try {
316  var convert = expr.Body as UnaryExpression;
317  var makeDelegate = convert.Operand as MethodCallExpression;
318  var methodArg = makeDelegate.Object as ConstantExpression;
319  method = methodArg.Value as MethodInfo;
320  if (method == null) throw new NullReferenceException();
321  }
322  catch (Exception e) {
323  throw new ArgumentException("Invalid hook expression " + expr, e);
324  }
325  return method;
326  }
327  /*
328  * Forwarder, deprecated, methods
329  * These are methods used likely by many modders, which may need some time to adjust to changes
330  */
331  [Obsolete("ModLoader.GetFileBytes is deprecated since v0.11, use ModContent.GetFileBytes instead.", true)]
332  public static byte[] GetFileBytes(string name) => ModContent.GetFileBytes(name);
333 
334  [Obsolete("ModLoader.FileExists is deprecated since v0.11, use ModContent.FileExists instead.", true)]
335  public static bool FileExists(string name) => ModContent.FileExists(name);
336 
337  [Obsolete("ModLoader.GetTexture is deprecated since v0.11, use ModContent.GetTexture instead.", true)]
338  public static Texture2D GetTexture(string name) => ModContent.GetTexture(name);
339 
340  [Obsolete("ModLoader.TextureExists is deprecated since v0.11, use ModContent.TextureExists instead.", true)]
341  public static bool TextureExists(string name) => ModContent.TextureExists(name);
342 
343  [Obsolete("ModLoader.GetSound is deprecated since v0.11, use ModContent.GetSound instead.", true)]
344  public static SoundEffect GetSound(string name) => ModContent.GetSound(name);
345 
346  [Obsolete("ModLoader.SoundExists is deprecated since v0.1, use ModContent.SoundExists instead.", true)]
347  public static bool SoundExists(string name) => ModContent.SoundExists(name);
348 
349  [Obsolete("ModLoader.GetMusic is deprecated since v0.11, use ModContent.GetMusic instead.", true)]
350  public static Music GetMusic(string name) => ModContent.GetMusic(name);
351 
352  [Obsolete("ModLoader.MusicExists is deprecated since v0.11, use ModContent.MusicExists instead.", true)]
353  public static bool MusicExists(string name) => ModContent.MusicExists(name);
354  }
355 }
static byte [] GetFileBytes(string name)
Gets the byte representation of the file with the specified name. The name is in the format of "ModFo...
Definition: ModContent.cs:41
static bool IsSignedBy(TmodFile mod, string xmlPublicKey)
Definition: ModLoader.cs:243
static Music GetMusic(string name)
Gets the music with the specified name. The name is in the same format as for texture names...
Definition: ModContent.cs:171
static Texture2D GetTexture(string name)
Gets the texture with the specified name. The name is in the format of "ModFolder/OtherFolders/FileNa...
Definition: ModContent.cs:70
static HashSet< string > _enabledMods
A cached list of enabled mods (not necessarily currently loaded or even installed), mirroring the enabled.json file.
Definition: ModLoader.cs:253
This serves as the central class which loads mods. It contains many static fields and methods related...
Definition: ModLoader.cs:24
Command can be used in server console during MP.
Manages content added by mods. Liasons between mod content and Terraria&#39;s arrays and oversees the Loa...
Definition: ModContent.cs:22
static bool TextureExists(string name)
Returns whether or not a texture with the specified name exists.
Definition: ModContent.cs:89
static void DisplayLoadError(string msg, Exception e, bool fatal, bool continueIsRetry=false)
Definition: ModLoader.cs:216
static bool MusicExists(string name)
Returns whether or not a sound with the specified name exists.
Definition: ModContent.cs:183
Sandstorm, Hell, Above surface during Eclipse, Space
static void DotNet45Check()
Definition: ModLoader.cs:157
static SoundEffect GetSound(string name)
Gets the sound with the specified name. The name is in the same format as for texture names...
Definition: ModContent.cs:139
static readonly Framework Framework
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:23
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
Definition: ModLoader.cs:80
static bool SoundExists(string name)
Returns whether or not a sound with the specified name exists.
Definition: ModContent.cs:156
static bool FileExists(string name)
Returns whether or not a file with the specified name exists.
Definition: ModContent.cs:55