Skip to content

Commit

Permalink
Merge pull request #79 from KomiksPL/master
Browse files Browse the repository at this point in the history
Updated the il2cpp side to be compatible with Il2cppInterop.
  • Loading branch information
loukylor authored Nov 12, 2023
2 parents b358cd3 + d0a04d5 commit 65bbe27
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 44 deletions.
1 change: 0 additions & 1 deletion docs/credits.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Here is the list of persons that have helped in any way to the creation of Melon
- Sinai: Creator of CppExplorer and MonoExplorer
- MintLily: Creator of XSOverlayTitleHider and GamePriority
- loukylor: Modder and editor of the wiki
- KosmicShovel: Modder and *barely an editor of the wiki*
- SirCoolness: Developer of Android support

### Outsiders
Expand Down
2 changes: 1 addition & 1 deletion docs/games/btd6.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Bloons Tower Defense 6

> The BTD6 mods and community are mostly found at the [1330 Studios Discord Server](https://discord.1330studios.com/) and on the [BTD6 Mods & Discussion Discord Server](https://discord.gg/SeRH8xQ5mQ)
> The BTD6 mods and community are found at the [BTD6 Mods & Discussion Discord Server](https://discord.gg/SeRH8xQ5mQ)
10 changes: 10 additions & 0 deletions docs/modders/MelonPlugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,13 @@ Fortunately, the `MelonBase` class comes with a static `RegisterSorted` method t

It is also possible to unregister Melons.<br>
Unregistering a Melon will deactive it by unsubscribing it from all Melon Events, unpatching all its Harmony patches, and by calling the `OnDeinitializeMelon` callback.

### Harmony Patching

Harmony patches work a little different with plugins, as you need to manually tell Harmony to patch your methods.

First off, you need to add the `HarmonyDontPatchAll` attribute to your assembly (`[assembly: HarmonyDontPatchAll]`) in order to stop MelonLoader from trying to do your patches for you.

In order to get Harmony to patch your methods, you either need to use [manual patching](https://melonwiki.xyz/#/modders/patching?id=patching-manually), or call `HarmonyInstance.PatchAll(MelonAssembly.Assembly);` in your `OnInitializeMelon` override.

After doing those steps, you should be able to patch normally.
27 changes: 13 additions & 14 deletions docs/modders/il2cppdifferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ MelonLoader have some "small" things that doesn't work the exact same as if you

> If you find something that doesn't seems natural with Il2Cpp and that isn't listed here, please ping _loukylor#0001_ on the [MelonLoader Discord](https://discord.gg/2Wn3N2P).
### Il2CppAssemblyUnhollower Generated Names
### Il2cppInterop Generated Names

?> You may ignore this section if your game is not obfuscated

Il2CppAssemblyUnhollower is what is used to generate proxy mono assemblies from Il2Cpp code. It will automatically assign auto-generated to obfuscated names.
Il2cppInterop is what is used to generate proxy mono assemblies from Il2Cpp code. It will automatically assign auto-generated to obfuscated names.

The names are generated following certain rules:
For fields and properties:
Expand All @@ -34,20 +34,20 @@ For classes:

### Custom Components / Il2Cpp Type Inheritance

> For more info, please check [Il2CppAssemblyUnhollower's readme on github](https://github.com/knah/Il2CppAssemblyUnhollower#class-injection)
> For more info, please check [Il2CppInterop's readme on github](https://github.com/BepInEx/Il2CppInterop/blob/master/Documentation/Class-Injection.md)
When making a class inheriting from an Il2Cpp type, we have to follow these 4 rules:
- Inherit from a non-abstract Il2Cpp class
- Have a constructor taking an IntPtr and passing it to a base constructor (called by the Il2Cpp side)
- Register the class before using it by adding the `MelonLoader.RegisterTypeInIl2Cpp` attribute to the class or using `UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<T>()`
- If you need to instantiate it from the mono-side, you need to have a constructor calling `UnhollowerRuntimeLib.ClassInjector.DerivedConstructorPointer<T>()` and `UnhollowerRuntimeLib.ClassInjector.DerivedConstructorBody(this)`
- Register the class before using it by adding the `MelonLoader.RegisterTypeInIl2Cpp` attribute to the class or using `Il2CppInterop.Runtime.Injection.ClassInjector.RegisterTypeInIl2Cpp<T>()`
- If you need to instantiate it from the mono-side, you need to have a constructor calling `Il2CppInterop.Runtime.Injection.ClassInjector.DerivedConstructorPointer<T>()` and `Il2CppInterop.Runtime.Injection.ClassInjector.DerivedConstructorBody(this)`

Note that `MelonLoader.RegisterTypeInIl2Cpp` will register all parent types if added to a child class. It is good practice to add the attribute to every custom injected class however.

Here is a very basic example:
```cs
// You must reference `UnhollowerBaseLib.dll` for this to work
using UnhollowerRuntimeLib;
// You must reference `Il2cppInterop.Runtime.dll` for this to work
using Il2CppInterop.Runtime;

[RegisterTypeInIl2Cpp]
class MyCustomComponent : MonoBehaviour
Expand Down Expand Up @@ -122,12 +122,10 @@ MelonCoroutines.Stop(routine);
### Usage of Il2Cpp Types

In case you want to run an Il2Cpp method taking a type, you may want to use `.GetType()`.<br/>
`.GetType()` would actually returns the Mono type, and not the original Il2Cpp type. To do so, we need to replace it with `.GetIl2CppType()` or `UnhollowerRuntimeLib.Il2CppType.Of<T>()`.
`.GetType()` would actually returns the Mono type, and not the original Il2Cpp type. To do so, we need to replace it with `Il2CppInterop.Runtime.Il2CppType.Of<T>()`.
```cs
Resources.FindObjectsOfTypeAll(Camera.GetIl2CppType());

// You must reference `UnhollowerBaseLib.dll` for this.
using UnhollowerRuntimeLib;
// You must reference `Il2cppInterop.Runtime.dll` for this.
using Il2CppInterop.Runtime;

Resources.FindObjectsOfTypeAll(Il2CppType.Of<Camera>());
```
Expand All @@ -137,6 +135,7 @@ Resources.FindObjectsOfTypeAll<Camera>();
```

### Casting Il2Cpp Types
!> This code is subject to change and become easier with the upcoming updates

In a standard Mono game, casting is quite easy.<br/>
Let's say we have a class `MyChildClass`, which inherit from `MyParentClass`.<br />
Expand All @@ -148,9 +147,9 @@ MyChildClass childInstanceCasted = childInstance as MyChildClass;
// or
MyChildClass childInstanceCasted = (MyChildClass) childInstance;
```
We can't do that with Il2Cpp objects. We have to use some methods from Il2CppAssemblyUnhollower:
We can't do that with Il2Cpp objects. We have to use some methods from Il2CppInterop:
```cs
using UnhollowerBaseLib;
using Il2CppInterop.Runtime;
// ...
MyChildClass childInstanceCasted = childInstance.TryCast<MyChildClass>();
// or
Expand Down
4 changes: 2 additions & 2 deletions docs/modders/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ However, compared to Unity, you have to reference all the game and Unity assembl
For games using the [Mono runtime](https://www.mono-project.com), all the game/Unity assemblies can be found in `[Game Directory]\[Game Name]_data\Managed\`.<br>
For games using the IL2CPP runtime, all the game/Unity assemblies can be found in `[Game Directory]\MelonLoader\Managed\` (make sure you have ran the game with MelonLoader at least once!).

Since IL2CPP converts all game assemblies to C++, MelonLoader is using [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower), an IL2CPP proxy assembly generator which allows us to use IL2CPP assemblies from C#.
Since IL2CPP converts all game assemblies to C++, MelonLoader is using [Il2CppInterop](https://github.com/BepInEx/Il2CppInterop), an IL2CPP proxy assembly generator which allows us to use IL2CPP assemblies from C#.
Before we can use any assemblies generated by the Unhollower, it's required to reference the following assemblies first:
- `il2cppmscorlib`
- `UnhollowerBaseLib`
- `Il2cppInterop`

At this point, you're ready to make your first functional Melon.

Expand Down
52 changes: 26 additions & 26 deletions docs/modders/xrefscanning.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ Note that on Il2Cpp games Xref Scanning cannot find virtual method calls, delega

### Finding Methods Calls Within a Method

First, ensure you have `UnhollowerBaseLib.dll` referenced. It can be found in the `MelonLoader/Managed` folder.<br>
Now we can use `UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase)` to scan our method (`methodBase`) of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<UnhollowerRuntimeLib.XrefScans.XrefInstance>`, which we can loop through.<br>
First, ensure you have `Il2CppInterop.Common.dll` referenced. It can be found in the `MelonLoader/net6` folder.<br>
Now we can use `Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase)` to scan our method (`methodBase`) of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<Il2CppInterop.Common.XrefScans.XrefInstance>`, which we can loop through.<br>
Here's what your code should look like so far:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{

}
Expand All @@ -29,8 +29,8 @@ foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
With this set up, we can now call `TryResolve()` on the `XrefInstance` to get the `MethodBase` representing a method called in the scanned method.<br>
The final code should look like this:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{
MethodBase calledMethod = instance.TryResolve();

Expand All @@ -43,13 +43,13 @@ Note that `TryResolve` may not always return a `MethodBase`. So, make sure to ha

Doing this is almost identical to finding method calls within a method. The only difference is using `UsedBy` instead of `XrefScan`.

First, ensure you have `UnhollowerBaseLib.dll` referenced. It can be found in the `MelonLoader/Managed` folder.<br>
Now we can use `UnhollowerRuntimeLib.XrefScans.XrefScanner.UsedBy(methodBase)` to scan our method of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<UnhollowerRuntimeLib.XrefScans.XrefInstance>`, which we can loop through.<br>
First, ensure you have `Il2CppInterop.Common.dll` referenced. It can be found in the `MelonLoader/net6` folder.<br>
Now we can use `Il2CppInterop.Common.XrefScans.XrefScanner.UsedBy(methodBase)` to scan our method of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<Il2CppInterop.Common.XrefScans.XrefInstance>`, which we can loop through.<br>
Here's what your code should look like so far:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.UsedBy(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.UsedBy(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{

}
Expand All @@ -58,8 +58,8 @@ foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
With this set up, we can now call `TryResolve()` on the `XrefInstance` to get the `MethodBase` representing a method that used the scanned method.<br>
The final code should look like this:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.UsedBy(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.UsedBy(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{
MethodBase calledMethod = instance.TryResolve();

Expand All @@ -72,26 +72,26 @@ Note that `TryResolve` may not always return a `MethodBase`. So, make sure to ha

The first steps of doing this are very similar to finding methods that use a method and finding methods called within a method.

First, ensure you have `UnhollowerBaseLib.dll` referenced. It can be found in the `MelonLoader/Managed` folder.<br>
Now we can use `UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase)` to scan our method of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<UnhollowerRuntimeLib.XrefScans.XrefInstance>`, which we can loop through.<br>
First, ensure you have `Il2CppInterop.Common.dll` referenced. It can be found in the `MelonLoader/net6` folder.<br>
Now we can use `Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase)` to scan our method of choice in the form of a `MethodBase`.<br>
This will return an `IEnumerable<Il2CppInterop.Common.XrefScans.XrefInstance>`, which we can loop through.<br>
Here's what your code should look like so far:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{

}
```

Now this is where finding strings splits.<br>
To find the strings, we must ensure that the `UnhollowerRuntimeLib.XrefScans.XrefType` of our instance is `Global`.<br>
To find the strings, we must ensure that the `Il2CppInterop.Common.XrefScans.XrefType` of our instance is `Global`.<br>
This can be done like so:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{
if (instance.Type == UnhollowerRuntimeLib.XrefScans.XrefType.Global)
if (instance.Type == Il2CppInterop.Common.XrefScans.XrefType.Global)
{

}
Expand All @@ -107,10 +107,10 @@ If we did not make sure our instance type was global, `ReadAsObject()` would ret
If we now call `ToString()` on the instance, we can get the strings used in the scanned method.<br>
This looks like this:
```cs
var instances = UnhollowerRuntimeLib.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (UnhollowerRuntimeLib.XrefScans.XrefInstance instance in instances)
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.XrefScan(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{
if (instance.Type == UnhollowerRuntimeLib.XrefScans.XrefType.Global)
if (instance.Type == Il2CppInterop.Common.XrefScans.XrefType.Global)
{
Il2CppSystem.Object methodObject = instance.ReadAsObject();
string usedString = methodObject.ToString();
Expand Down

0 comments on commit 65bbe27

Please sign in to comment.