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)
maddie480 committed Jul 11, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent c0efca9 commit cc0609b
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
@@ -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;
}

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

namespace Celeste.Mod.CollabUtils2.Entities {
/// <summary>
@@ -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;
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");
2 changes: 1 addition & 1 deletion LobbyHelper.cs
Original file line number Diff line number Diff line change
@@ -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);
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

0 comments on commit cc0609b

Please sign in to comment.