Skip to content

Commit

Permalink
Stop using HookHelper.HookCoroutine
Browse files Browse the repository at this point in the history
(cherry picked from commit d98d300)
  • Loading branch information
maddie480 committed Jul 11, 2024
1 parent c0efca9 commit cc0609b
Show file tree
Hide file tree
Showing 5 changed files with 12 additions and 46 deletions.
4 changes: 3 additions & 1 deletion Entities/MiniHeartDoor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ public class MiniHeartDoor : HeartGemDoor {
internal static void Load() {
hookOnHeartCount = new Hook(typeof(HeartGemDoor).GetMethod("get_HeartGems"),
typeof(MiniHeartDoor).GetMethod(nameof(getCollectedHeartGems), BindingFlags.NonPublic | BindingFlags.Static));
hookOnDoorRoutine = HookHelper.HookCoroutine("Celeste.HeartGemDoor", "Routine", modDoorRoutine);
hookOnDoorRoutine = new ILHook(
typeof(HeartGemDoor).GetMethod("Routine", BindingFlags.NonPublic | BindingFlags.Instance).GetStateMachineTarget(),
modDoorRoutine);
IL.Celeste.HeartGemDoor.DrawInterior += modDoorColor;
}

Expand Down
9 changes: 7 additions & 2 deletions Entities/StrawberryHooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections;
using System.Linq;
using System.Reflection;

namespace Celeste.Mod.CollabUtils2.Entities {
/// <summary>
Expand All @@ -23,8 +24,12 @@ static class StrawberryHooks {

internal static void Load() {
IL.Celeste.Strawberry.Added += modStrawberrySprite;
collectRoutineHook = HookHelper.HookCoroutine("Celeste.Strawberry", "CollectRoutine", modStrawberrySound);
playerDeathRoutineHook = HookHelper.HookCoroutine("Celeste.PlayerDeadBody", "DeathRoutine", modDeathSound);
collectRoutineHook = new ILHook(
typeof(Strawberry).GetMethod("CollectRoutine", BindingFlags.NonPublic | BindingFlags.Instance).GetStateMachineTarget(),
modStrawberrySound);
playerDeathRoutineHook = new ILHook(
typeof(PlayerDeadBody).GetMethod("DeathRoutine", BindingFlags.NonPublic | BindingFlags.Instance).GetStateMachineTarget(),
modDeathSound);
Everest.Events.Level.OnCreatePauseMenuButtons += onCreatePauseMenuButtons;
On.Celeste.Player.Added += Player_Added;
On.Celeste.SaveData.AddStrawberry_AreaKey_EntityID_bool += onSaveDataAddStrawberry;
Expand Down
41 changes: 0 additions & 41 deletions HookHelper.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,9 @@
using Mono.Cecil;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;
using System;
using System.Reflection;
using MonoMod.Utils;
using Mono.Cecil.Cil;

namespace Celeste.Mod.CollabUtils2 {
public static class HookHelper {
/// <summary>
/// Utility method to patch "coroutine" kinds of methods with IL.
/// Those methods' code reside in a compiler-generated method, and IL.Celeste.* do not allow manipulating them directly.
/// </summary>
/// <param name="manipulator">Method taking care of the patching</param>
/// <returns>The IL hook if the actual code was found, null otherwise</returns>
public static ILHook HookCoroutine(string typeName, string methodName, ILContext.Manipulator manipulator) {
// get the Celeste.exe module definition Everest loaded for us
ModuleDefinition celeste = Everest.Relinker.SharedRelinkModuleMap["Celeste.Mod.mm"];

// get the type
TypeDefinition type = celeste.GetType(typeName);
if (type == null)
return null;

// the "coroutine" method is actually a nested type tracking the coroutine's state
// (to make it restart from where it stopped when MoveNext() is called).
// what we see in ILSpy and what we want to hook is actually the MoveNext() method in this nested type.
foreach (TypeDefinition nest in type.NestedTypes) {
if (nest.Name.StartsWith("<" + methodName + ">d__")) {
// check that this nested type contains a MoveNext() method
MethodDefinition method = nest.FindMethod("System.Boolean MoveNext()");
if (method == null)
return null;

// we found it! let's convert it into basic System.Reflection stuff and hook it.
Logger.Log("CollabUtils2/HookHelper", $"Building IL hook for method {method.FullName} in order to mod {typeName}.{methodName}()");
Type reflectionType = typeof(Player).Assembly.GetType(typeName);
Type reflectionNestedType = reflectionType.GetNestedType(nest.Name, BindingFlags.NonPublic);
MethodBase moveNextMethod = reflectionNestedType.GetMethod("MoveNext", BindingFlags.NonPublic | BindingFlags.Instance);
return new ILHook(moveNextMethod, manipulator);
}
}

return null;
}

public static FieldReference FindReferenceToThisInCoroutine(ILCursor cursor) {
// coroutines are cursed and references to "this" are actually references to this.<>4__this
cursor.GotoNext(instr => instr.OpCode == OpCodes.Ldfld && (instr.Operand as FieldReference).Name == "<>4__this");
Expand Down
2 changes: 1 addition & 1 deletion LobbyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ internal static void Load() {
On.Celeste.Player.Update += onPlayerUpdate;

// hiding collab maps from chapter select
hookOnLevelSetSwitch = HookHelper.HookCoroutine(typeof(OuiHelper_ChapterSelect_LevelSet).FullName, "Enter", modLevelSetSwitch);
hookOnLevelSetSwitch = new ILHook(typeof(OuiHelper_ChapterSelect_LevelSet).GetMethod("Enter").GetStateMachineTarget(), modLevelSetSwitch);
hookMapSearchReloadItems = new ILHook(typeof(OuiMapSearch).GetMethod("ReloadItems", BindingFlags.NonPublic | BindingFlags.Instance), modMapSearch);
hookMapListReloadItems = new ILHook(typeof(OuiMapList).GetMethod("ReloadItems", BindingFlags.NonPublic | BindingFlags.Instance), modMapListReloadItems);
hookMapListCreateMenu = new ILHook(typeof(OuiMapList).GetMethod("CreateMenu", BindingFlags.NonPublic | BindingFlags.Instance), modMapListCreateMenu);
Expand Down
2 changes: 1 addition & 1 deletion everest.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
- Name: CollabUtils2
Version: 1.10.9
Version: 1.10.10
DLL: bin/Release/net452/CollabUtils2.dll
Dependencies:
- Name: Everest
Expand Down

0 comments on commit cc0609b

Please sign in to comment.