├── icon.png
├── ILRepack.exe
├── _input
├── FNA.dll
├── Terraria.FNA.exe
├── Terraria.XNA.exe
└── Newtonsoft.Json.dll
├── .gitmodules
├── Stripper
├── App.config
├── Properties
│ └── AssemblyInfo.cs
├── Program.cs
└── Stripper.csproj
├── TerrariaHookGen
├── App.config
├── Properties
│ └── AssemblyInfo.cs
├── TerrariaHookGen.csproj
└── Program.cs
├── TerrariaHooks
├── TerrariaHooksMod.cs
├── Properties
│ └── AssemblyInfo.cs
├── TerrariaHooks.csproj
└── TerrariaHooksManager.cs
├── description.txt
├── LICENSE
├── .gitattributes
├── README.md
├── TerrariaHooks.sln
└── .gitignore
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/icon.png
--------------------------------------------------------------------------------
/ILRepack.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/ILRepack.exe
--------------------------------------------------------------------------------
/_input/FNA.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/_input/FNA.dll
--------------------------------------------------------------------------------
/_input/Terraria.FNA.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/_input/Terraria.FNA.exe
--------------------------------------------------------------------------------
/_input/Terraria.XNA.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/_input/Terraria.XNA.exe
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "MonoMod"]
2 | path = MonoMod
3 | url = https://github.com/0x0ade/MonoMod.git
4 |
--------------------------------------------------------------------------------
/_input/Newtonsoft.Json.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0x0ade/TerrariaHooks/HEAD/_input/Newtonsoft.Json.dll
--------------------------------------------------------------------------------
/Stripper/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/TerrariaHookGen/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/TerrariaHooks/TerrariaHooksMod.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 | using Mono.Cecil.Cil;
3 | using MonoMod.RuntimeDetour;
4 | using MonoMod.RuntimeDetour.HookGen;
5 | using MonoMod.Utils;
6 | using Newtonsoft.Json;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Reflection;
12 | using Terraria;
13 | using Terraria.ModLoader;
14 |
15 | namespace TerrariaHooks {
16 | class TerrariaHooksMod : Mod {
17 |
18 | public TerrariaHooksMod() {
19 | TerrariaHooksManager.Init();
20 |
21 | Properties = new ModProperties() {
22 | };
23 | }
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/description.txt:
--------------------------------------------------------------------------------
1 | [i:109] TerrariaHooks
2 | The power of HookGen in the palm of your hands.
3 |
4 | [i:109] Note
5 | This mod only "works" together with other mods that need it.
6 | Please visit the homepage to the up-to-date feature list and more information.
7 |
8 | [i:109] Features
9 |
10 | [i:75] "Hook" any arbitrary method you want easily:
11 | On.Terraria.Player.CheckMana += (...) => {...}
12 |
13 | [i:75] Use "hooks" to detect when a method runs, change how it runs or even replace it.
14 | "Hooks" are automatically undone whenever your mod unloads. This is handled by TerrariaHooks for you.
15 |
16 | [i:75] Manipulate any method at runtime via IL... += (...) => {...}
17 | Special thanks to Chicken-Bones for the great discussions!
18 |
19 | [i:75] Use RuntimeDetour to quickly port your existing "method swapping" code.
20 | Note that this requires you to undo your detours on unload.
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Everest Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Stripper/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Stripper")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Stripper")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("fb9ed388-b9e7-4786-a2b5-04790a34dd37")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/TerrariaHooks/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("TerrariaHooks")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("TerrariaHooks")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("0ddc1049-13ab-463d-91a9-ef4e56bff5af")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/TerrariaHookGen/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("TerrariaHookGen")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("TerrariaHookGen")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("b2dba9ae-9a85-41d0-a868-29d60cfc6249")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.0.6.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Stripper/Program.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 | using Mono.Cecil.Cil;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace TerrariaHookGen {
11 | public static class Stripper {
12 |
13 | static void Main(string[] args) {
14 | string inputDir;
15 | if (args.Length != 1 || !Directory.Exists(inputDir = args[0])) {
16 | Console.Error.WriteLine("Usage: inputdir");
17 | return;
18 | }
19 |
20 | foreach (string path in Directory.GetFiles(inputDir)) {
21 | Console.WriteLine($"Stripping: {path}");
22 | Strip(path);
23 | }
24 | }
25 |
26 | public static void Strip(string path, ReaderParameters readerParams = null) {
27 | if (readerParams == null) {
28 | DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver();
29 | asmResolver.AddSearchDirectory(Path.GetDirectoryName(path));
30 | readerParams = new ReaderParameters() {
31 | AssemblyResolver = asmResolver
32 | };
33 | }
34 |
35 | ModuleDefinition module = ModuleDefinition.ReadModule(path, readerParams);
36 | int changes = 0;
37 |
38 | if ((module.Resources?.Count ?? 0) != 0) {
39 | changes += module.Resources.Count;
40 | module.Resources.Clear();
41 | }
42 |
43 | foreach (TypeDefinition type in module.Types)
44 | Strip(ref changes, type);
45 |
46 | if (changes != 0) {
47 | module.Write(path);
48 | }
49 | }
50 |
51 | static void Strip(ref int changes, TypeDefinition type) {
52 | foreach (MethodDefinition method in type.Methods)
53 | if (method.HasBody && method.Body.Instructions.Count != 0) {
54 | changes++;
55 | method.Body = new MethodBody(method);
56 | }
57 |
58 | foreach (TypeDefinition nested in type.NestedTypes)
59 | Strip(ref changes, nested);
60 | }
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Stripper/Stripper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FB9ED388-B9E7-4786-A2B5-04790A34DD37}
8 | Exe
9 | TerrariaHookGen
10 | Stripper
11 | v4.5.2
12 | 512
13 | true
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 0.9.6
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/TerrariaHooks/TerrariaHooks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}
7 | Library
8 | TerrariaHooks
9 | TerrariaHooks
10 | net452
11 | 7.3
12 | false
13 | false
14 | false
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | ..\_input\Newtonsoft.Json.dll
36 |
37 |
38 |
39 | ..\_input\Terraria.XNA.exe
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 0.9.6
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OBSOLETE
2 | ### tModLoader 0.11+ includes its own version of TerrariaHooks / MonoMod.
3 | Please tell any mod developers which still use this old version to update their mods.
4 |
5 | # TerrariaHooks
6 |
7 | ### License: MIT
8 |
9 | ----
10 |
11 | HookGen "recipe" for Terraria, resulting in a TML mod that other mods can depend on.
12 |
13 | Built with [MonoMod](https://github.com/0x0ade/MonoMod).
14 |
15 | ## Features
16 |
17 | ### For Players
18 |
19 | This mod is a helper for other mods. Just install it if a mod needs it, it won't change Terraria on its own.
20 |
21 | ### For Modders
22 |
23 | - "Hook" any arbitrary method you want easily:
24 | ```cs
25 | On.Terraria.Player.CheckMana += (orig, player, amount, pay, blockQuickMana) => {
26 | // We can either make this act as a method replacement
27 | // or just call our code around the original code.
28 |
29 | // Let's double the mana cost of everything.
30 | // We can pass on custom values.
31 | bool spendable = orig(player, amount * 2, pay, blockQuickMana);
32 |
33 | // ... but give back half of it if it was spent.
34 | if (spendable && pay) {
35 | player.statMana += amount / 2;
36 | }
37 |
38 | // We can return custom values.
39 | return spendable;
40 | }
41 | ```
42 |
43 | For an extended example, take a look at [this gist](https://gist.github.com/0x0ade/1d1013d6ae1ff450aa76f252b0f3b62c).
44 |
45 | - Use "hooks" to detect when a method runs, change how it runs or even replace it.
46 | "Hooks" are automatically undone whenever your mod unloads. This is handled by TerrariaHooks for you.
47 |
48 | - Manipulate any method at runtime via IL... += (...) => {...} using Cecil and many helpers.
49 | Special thanks to Chicken-Bones for the great ideas and feedback along the way!
50 |
51 | - Use RuntimeDetour to quickly port your existing "method swapping" code.
52 | Note that this requires you to undo your detours on unload.
53 |
54 | If you need more info, read the [MonoMod RuntimeDetour README](https://github.com/0x0ade/MonoMod/blob/master/README-RuntimeDetour.md).
55 |
56 | ## Instructions
57 |
58 | If you want to use TerrariaHooks in your mod:
59 |
60 | - Download `TerrariaHooks.dll` from the [latest release](https://github.com/0x0ade/TerrariaHooks/releases).
61 | - Put it into your mod `lib` folder.
62 | - Add it as a reference in Visual Studio.
63 | - In your `build.txt`, add `modReferences = TerrariaHooks`
64 | - Add your hooks in your mod `Load` method.
65 | - TerrariaHooks will automatically undo your hook when your mod unloads.
66 |
67 | ## Building TerrariaHooks yourself
68 |
69 | - `git clone --recursive https://github.com/0x0ade/TerrariaHookGen.git`
70 | - Alternatively, pull in the MonoMod submodule manually
71 | - Update the files in `_input`
72 | - The files redistributed in this repo are stripped.
73 | - The files in that directory will be stripped automatically on each run.
74 | - Build the solution.
75 | - Run `./TerrariaHookGen/bin/Debug/TerrariaHookGen.exe _input .`
76 | - In the Terraria directory, run `./tModLoaderServer.exe -build 'path/to/TerrariaHooks' -eac`
77 |
78 | This repository overcomplicates the entire procedure. It boils down to:
79 | ```bash
80 | ./MonoMod.RuntimeDetour.HookGen.exe --private Terraria.exe TerrariaHooksPre.dll
81 | ./ILRepack.exe /out:TerrariaHooks.dll TerrariaHooksPre.dll MonoMod.*.dll MonoMod.exe
82 | ```
83 |
84 | When running the above two lines in the Terraria directory (with all dependencies present), it generates `TerrariaHooks.dll` for your `Terraria.exe`.
85 |
86 | The sole purpose of this repository is to automate the process entirely, and to allow publishing `TerrariaHooks.dll` as a TML mod.
87 |
--------------------------------------------------------------------------------
/TerrariaHookGen/TerrariaHookGen.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B2DBA9AE-9A85-41D0-A868-29D60CFC6249}
8 | Exe
9 | TerrariaHookGen
10 | TerrariaHookGen
11 | v4.5.2
12 | 512
13 | true
14 | true
15 |
16 |
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | ..\ILRepack.exe
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {032b42a5-c409-4b69-b8e6-ec468eedfec1}
53 | MonoMod.RuntimeDetour.HookGen
54 |
55 |
56 | {d0c584c0-81d7-486e-b70e-d7f9256e0909}
57 | MonoMod.RuntimeDetour
58 |
59 |
60 | {1839cfe2-3db0-45a8-b03d-9aa797479a3a}
61 | MonoMod.Utils
62 |
63 |
64 | {8a17c958-5c33-4035-af76-f94a3aa2dc4f}
65 | MonoMod
66 |
67 |
68 | {fb9ed388-b9e7-4786-a2b5-04790a34dd37}
69 | Stripper
70 |
71 |
72 | {0ddc1049-13ab-463d-91a9-ef4e56bff5af}
73 | TerrariaHooks
74 |
75 |
76 |
77 |
78 | 0.9.6
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/TerrariaHooks.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerrariaHookGen", "TerrariaHookGen\TerrariaHookGen.csproj", "{B2DBA9AE-9A85-41D0-A868-29D60CFC6249}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stripper", "Stripper\Stripper.csproj", "{FB9ED388-B9E7-4786-A2B5-04790A34DD37}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Utils", "MonoMod\MonoMod.Utils\MonoMod.Utils.csproj", "{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour", "MonoMod\MonoMod.RuntimeDetour\MonoMod.RuntimeDetour.csproj", "{D0C584C0-81D7-486E-B70E-D7F9256E0909}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour.HookGen", "MonoMod\MonoMod.RuntimeDetour.HookGen\MonoMod.RuntimeDetour.HookGen.csproj", "{032B42A5-C409-4B69-B8E6-EC468EEDFEC1}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod", "MonoMod\MonoMod\MonoMod.csproj", "{8A17C958-5C33-4035-AF76-F94A3AA2DC4F}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerrariaHooks", "TerrariaHooks\TerrariaHooks.csproj", "{0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {B2DBA9AE-9A85-41D0-A868-29D60CFC6249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {B2DBA9AE-9A85-41D0-A868-29D60CFC6249}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {B2DBA9AE-9A85-41D0-A868-29D60CFC6249}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {B2DBA9AE-9A85-41D0-A868-29D60CFC6249}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {FB9ED388-B9E7-4786-A2B5-04790A34DD37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {FB9ED388-B9E7-4786-A2B5-04790A34DD37}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {FB9ED388-B9E7-4786-A2B5-04790A34DD37}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {FB9ED388-B9E7-4786-A2B5-04790A34DD37}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.ActiveCfg = DebugLegacy|Any CPU
35 | {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.Build.0 = DebugLegacy|Any CPU
36 | {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.ActiveCfg = ReleaseLegacy|Any CPU
37 | {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.Build.0 = ReleaseLegacy|Any CPU
38 | {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.ActiveCfg = DebugLegacy|Any CPU
39 | {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.Build.0 = DebugLegacy|Any CPU
40 | {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.ActiveCfg = ReleaseLegacy|Any CPU
41 | {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.Build.0 = ReleaseLegacy|Any CPU
42 | {032B42A5-C409-4B69-B8E6-EC468EEDFEC1}.Debug|Any CPU.ActiveCfg = DebugLegacy|Any CPU
43 | {032B42A5-C409-4B69-B8E6-EC468EEDFEC1}.Debug|Any CPU.Build.0 = DebugLegacy|Any CPU
44 | {032B42A5-C409-4B69-B8E6-EC468EEDFEC1}.Release|Any CPU.ActiveCfg = ReleaseLegacy|Any CPU
45 | {032B42A5-C409-4B69-B8E6-EC468EEDFEC1}.Release|Any CPU.Build.0 = ReleaseLegacy|Any CPU
46 | {8A17C958-5C33-4035-AF76-F94A3AA2DC4F}.Debug|Any CPU.ActiveCfg = DebugLegacy|Any CPU
47 | {8A17C958-5C33-4035-AF76-F94A3AA2DC4F}.Debug|Any CPU.Build.0 = DebugLegacy|Any CPU
48 | {8A17C958-5C33-4035-AF76-F94A3AA2DC4F}.Release|Any CPU.ActiveCfg = ReleaseLegacy|Any CPU
49 | {8A17C958-5C33-4035-AF76-F94A3AA2DC4F}.Release|Any CPU.Build.0 = ReleaseLegacy|Any CPU
50 | {0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {0DDC1049-13AB-463D-91A9-EF4E56BFF5AF}.Release|Any CPU.Build.0 = Release|Any CPU
54 | EndGlobalSection
55 | GlobalSection(SolutionProperties) = preSolution
56 | HideSolutionNode = FALSE
57 | EndGlobalSection
58 | GlobalSection(ExtensibilityGlobals) = postSolution
59 | SolutionGuid = {FDBC892E-FD69-4D1C-93CD-3B994A329B7D}
60 | EndGlobalSection
61 | EndGlobal
62 |
--------------------------------------------------------------------------------
/TerrariaHookGen/Program.cs:
--------------------------------------------------------------------------------
1 | using ILRepacking;
2 | using Mono.Cecil;
3 | using Mono.Cecil.Cil;
4 | using MonoMod;
5 | using MonoMod.RuntimeDetour.HookGen;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Reflection;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 |
14 | namespace TerrariaHookGen {
15 | class Program {
16 |
17 | static void Main(string[] args) {
18 | // Required for the relative extra paths to work properly.
19 | if (!File.Exists("MonoMod.RuntimeDetour.dll"))
20 | Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
21 |
22 | string inputDir, outputDir;
23 | if (args.Length != 2 ||
24 | !Directory.Exists(inputDir = args[0]) ||
25 | !Directory.Exists(outputDir = args[1])) {
26 | Console.Error.WriteLine("Usage: inputdir outputdir");
27 | return;
28 | }
29 |
30 | // Check that the files exist.
31 | if (!VerifyFile(out string inputXNA, inputDir, "Terraria.XNA.exe"))
32 | return;
33 | if (!VerifyFile(out string inputFNA, inputDir, "Terraria.FNA.exe"))
34 | return;
35 |
36 | // Strip or copy.
37 | foreach (string path in Directory.GetFiles(inputDir)) {
38 | if (!path.EndsWith(".exe") && !path.EndsWith(".dll")) {
39 | Console.WriteLine($"Copying: {path}");
40 | File.Copy(path, Path.Combine(outputDir, Path.GetFileName(path)));
41 | continue;
42 | }
43 |
44 | Console.WriteLine($"Stripping: {path}");
45 | Stripper.Strip(path);
46 | }
47 |
48 | // Generate hooks.
49 | string hooksXNA = Path.Combine(outputDir, "Windows.Pre.dll");
50 | string hooksFNA = Path.Combine(outputDir, "Mono.Pre.dll");
51 | GenHooks(inputXNA, hooksXNA);
52 | GenHooks(inputFNA, hooksFNA);
53 |
54 | // Merge generated .dlls and MonoMod into one .dll per environment.
55 | string[] extrasMod = {
56 | "TerrariaHooks.dll",
57 | "MonoMod.exe",
58 | "MonoMod.RuntimeDetour.dll",
59 | "MonoMod.Utils.dll"
60 | };
61 | Repack(hooksXNA, extrasMod, Path.Combine(outputDir, "Windows.dll"), "TerrariaHooks.dll");
62 | File.Delete(hooksXNA);
63 | Repack(hooksFNA, extrasMod, Path.Combine(outputDir, "Mono.dll"), "TerrariaHooks.dll");
64 | File.Delete(hooksFNA);
65 | }
66 |
67 | static bool VerifyFile(out string path, params string[] paths) {
68 | path = Path.Combine(paths);
69 | if (!File.Exists(path) && !Directory.Exists(path)) {
70 | Console.Error.WriteLine($"Missing: {path}");
71 | return false;
72 | }
73 | return true;
74 | }
75 |
76 | static void GenHooks(string input, string output) {
77 | Console.WriteLine($"Hooking: {input} -> {output}");
78 |
79 | using (MonoModder mm = new MonoModder() {
80 | InputPath = input,
81 | OutputPath = output,
82 | ReadingMode = ReadingMode.Deferred,
83 |
84 | MissingDependencyThrow = false,
85 | }) {
86 | mm.Read();
87 | mm.MapDependencies();
88 |
89 | if (File.Exists(output))
90 | File.Delete(output);
91 |
92 | HookGenerator gen = new HookGenerator(mm, Path.GetFileName(output)) {
93 | HookPrivate = true,
94 | };
95 | gen.Generate();
96 | gen.OutputModule.Write(output);
97 | }
98 | }
99 |
100 | static void Repack(string input, string[] extras, string output, string name = null) {
101 | Console.Error.WriteLine($"Repacking: {input} -> {output}");
102 | if (name == null)
103 | name = Path.GetFileName(output);
104 |
105 | string outputTmp = Path.Combine(Path.GetDirectoryName(output), name);
106 | if (File.Exists(outputTmp))
107 | File.Delete(outputTmp);
108 |
109 | List args = new List();
110 | args.Add($"/out:{outputTmp}");
111 | args.Add(input);
112 | foreach (string dep in extras)
113 | if (!string.IsNullOrWhiteSpace(dep))
114 | args.Add(dep.Trim());
115 |
116 | RepackOptions options = new RepackOptions(args);
117 | ILRepack repack = new ILRepack(options);
118 | repack.Repack();
119 |
120 | if (output != outputTmp) {
121 | if (File.Exists(output))
122 | File.Delete(output);
123 | File.Move(outputTmp, output);
124 | }
125 | }
126 |
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __input
2 | Mono.dll
3 | Mono.pdb
4 | Windows.dll
5 | Windows.pdb
6 | tModLoaderServer_TerrariaHooks.exe
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.userosscache
15 | *.sln.docstates
16 |
17 | # User-specific files (MonoDevelop/Xamarin Studio)
18 | *.userprefs
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | bld/
28 | [Bb]in/
29 | [Oo]bj/
30 | [Ll]og/
31 |
32 | # Visual Studio 2015 cache/options directory
33 | .vs/
34 | # Uncomment if you have tasks that create the project's static files in wwwroot
35 | #wwwroot/
36 |
37 | # MSTest test Results
38 | [Tt]est[Rr]esult*/
39 | [Bb]uild[Ll]og.*
40 |
41 | # NUNIT
42 | *.VisualState.xml
43 | TestResult.xml
44 |
45 | # Build Results of an ATL Project
46 | [Dd]ebugPS/
47 | [Rr]eleasePS/
48 | dlldata.c
49 |
50 | # DNX
51 | project.lock.json
52 | project.fragment.lock.json
53 | artifacts/
54 |
55 | *_i.c
56 | *_p.c
57 | *_i.h
58 | *.ilk
59 | *.meta
60 | *.obj
61 | *.pch
62 | *.pdb
63 | *.pgc
64 | *.pgd
65 | *.rsp
66 | *.sbr
67 | *.tlb
68 | *.tli
69 | *.tlh
70 | *.tmp
71 | *.tmp_proj
72 | *.log
73 | *.vspscc
74 | *.vssscc
75 | .builds
76 | *.pidb
77 | *.svclog
78 | *.scc
79 |
80 | # Chutzpah Test files
81 | _Chutzpah*
82 |
83 | # Visual C++ cache files
84 | ipch/
85 | *.aps
86 | *.ncb
87 | *.opendb
88 | *.opensdf
89 | *.sdf
90 | *.cachefile
91 | *.VC.db
92 | *.VC.VC.opendb
93 |
94 | # Visual Studio profiler
95 | *.psess
96 | *.vsp
97 | *.vspx
98 | *.sap
99 |
100 | # TFS 2012 Local Workspace
101 | $tf/
102 |
103 | # Guidance Automation Toolkit
104 | *.gpState
105 |
106 | # ReSharper is a .NET coding add-in
107 | _ReSharper*/
108 | *.[Rr]e[Ss]harper
109 | *.DotSettings.user
110 |
111 | # JustCode is a .NET coding add-in
112 | .JustCode
113 |
114 | # TeamCity is a build add-in
115 | _TeamCity*
116 |
117 | # DotCover is a Code Coverage Tool
118 | *.dotCover
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | #*.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignoreable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | node_modules/
203 | orleans.codegen.cs
204 |
205 | # Since there are multiple workflows, uncomment next line to ignore bower_components
206 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
207 | #bower_components/
208 |
209 | # RIA/Silverlight projects
210 | Generated_Code/
211 |
212 | # Backup & report files from converting an old project file
213 | # to a newer Visual Studio version. Backup files are not needed,
214 | # because we have git ;-)
215 | _UpgradeReport_Files/
216 | Backup*/
217 | UpgradeLog*.XML
218 | UpgradeLog*.htm
219 |
220 | # SQL Server files
221 | *.mdf
222 | *.ldf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 |
238 | # Visual Studio 6 build log
239 | *.plg
240 |
241 | # Visual Studio 6 workspace options file
242 | *.opt
243 |
244 | # Visual Studio LightSwitch build output
245 | **/*.HTMLClient/GeneratedArtifacts
246 | **/*.DesktopClient/GeneratedArtifacts
247 | **/*.DesktopClient/ModelManifest.xml
248 | **/*.Server/GeneratedArtifacts
249 | **/*.Server/ModelManifest.xml
250 | _Pvt_Extensions
251 |
252 | # Paket dependency manager
253 | .paket/paket.exe
254 | paket-files/
255 |
256 | # FAKE - F# Make
257 | .fake/
258 |
259 | # JetBrains Rider
260 | .idea/
261 | *.sln.iml
262 |
263 | # CodeRush
264 | .cr/
265 |
266 | # Python Tools for Visual Studio (PTVS)
267 | __pycache__/
268 | *.pyc
--------------------------------------------------------------------------------
/TerrariaHooks/TerrariaHooksManager.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 | using Mono.Cecil.Cil;
3 | using MonoMod.RuntimeDetour;
4 | using MonoMod.RuntimeDetour.HookGen;
5 | using MonoMod.Utils;
6 | using Newtonsoft.Json;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Diagnostics;
10 | using System.IO;
11 | using System.Linq;
12 | using System.Reflection;
13 | using System.Runtime.ExceptionServices;
14 | using System.Text;
15 | using Terraria;
16 | using Terraria.ModLoader;
17 | using TerrariaHooks;
18 |
19 | namespace TerrariaHooks {
20 | static partial class TerrariaHooksManager {
21 |
22 | static bool Initialized = false;
23 |
24 | static DetourModManager Manager;
25 |
26 | public static void Init() {
27 | if (Initialized)
28 | return;
29 | Initialized = true;
30 |
31 | Manager = new DetourModManager();
32 | Manager.Ignored.Add(Assembly.GetExecutingAssembly());
33 |
34 | // Load the cecil module generator.
35 | // Adding this more than once shouldn't hurt.
36 | HookEndpointManager.OnGenerateCecilModule += GenerateCecilModule;
37 |
38 | // Some mods might forget to undo their hooks.
39 | HookOnUnloadContent = new Hook(
40 | typeof(Mod).GetMethod("UnloadContent", BindingFlags.NonPublic | BindingFlags.Instance),
41 | typeof(TerrariaHooksManager).GetMethod("OnUnloadContent", BindingFlags.NonPublic | BindingFlags.Static)
42 | );
43 |
44 | // All of our own hooks need to be undone last.
45 | MethodBase m_Unload = typeof(ModLoader).GetMethod("Unload", BindingFlags.NonPublic | BindingFlags.Static);
46 | if (m_Unload != null) {
47 | HookOnUnloadAll = new Hook(
48 | m_Unload,
49 | typeof(TerrariaHooksManager).GetMethod("OnUnloadAll", BindingFlags.NonPublic | BindingFlags.Static)
50 | );
51 | } else {
52 | /* The unofficial tML x64 form is a hot mess of tML 0.10 and 0.11 pre-beta.
53 | * As such, mods are only unloaded when they're reloaded.
54 | * Luckily, ModContnet.Unload runs right after unloading all mods.
55 | * Even more luckily, it pretty much shares the same signature as the old Unload method.
56 | */
57 | MethodBase m_UnloadContent =
58 | typeof(Mod).Assembly.GetType("Terraria.ModLoader.ModContent")
59 | ?.GetMethod("Unload", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
60 | if (m_UnloadContent != null) {
61 | HookOnUnloadAll = new Hook(
62 | m_UnloadContent,
63 | typeof(TerrariaHooksManager).GetMethod("OnUnloadAll", BindingFlags.NonPublic | BindingFlags.Static)
64 | );
65 | } else {
66 | throw new Exception("Incompatible tML version: Can't find unload hook point");
67 | }
68 | }
69 |
70 | // Try to hook the logger to avoid logging "silent" exceptions thrown by TerrariaHooks.
71 | MethodBase m_LogSilentException =
72 | typeof(Mod).Assembly.GetType("Terraria.ModLoader.ModCompile+<>c")
73 | ?.GetMethod("b__15_0", BindingFlags.NonPublic | BindingFlags.Instance);
74 | if (m_LogSilentException != null) {
75 | HookOnLogSilentException = new Hook(
76 | m_LogSilentException,
77 | typeof(TerrariaHooksManager).GetMethod("OnLogSilentException", BindingFlags.NonPublic | BindingFlags.Static)
78 | );
79 | }
80 | }
81 |
82 | internal static void Dispose() {
83 | if (!Initialized)
84 | return;
85 | Initialized = false;
86 |
87 | Manager.Dispose();
88 |
89 | HookEndpointManager.OnGenerateCecilModule -= GenerateCecilModule;
90 |
91 | HookOnUnloadContent.Dispose();
92 | HookOnUnloadAll.Dispose();
93 | HookOnLogSilentException?.Dispose();
94 | }
95 |
96 | static Hook HookOnUnloadContent;
97 | static void OnUnloadContent(Action orig, Mod mod) {
98 | orig(mod);
99 | Manager.Unload(mod.Code);
100 | }
101 |
102 | static Hook HookOnUnloadAll;
103 | static void OnUnloadAll(Action orig) {
104 | orig();
105 |
106 | // Dispose the context.
107 | Dispose();
108 | }
109 |
110 | static Hook HookOnLogSilentException;
111 | static void OnLogSilentException(Action