Skip to content

Commit

Permalink
Merge pull request #788 from Viv-0/fixEntityIDforReal
Browse files Browse the repository at this point in the history
Fixing EntityID duplication on Global objects
  • Loading branch information
maddie480 authored Jul 21, 2024
2 parents 5370422 + 9fddb5f commit d08681b
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 34 deletions.
2 changes: 1 addition & 1 deletion Celeste.Mod.mm/Mod/Everest/Everest.Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ internal static void ProcessAssembly(EverestModuleMetadata meta, Assembly asm, T

ctor = type.GetConstructor(new Type[] { typeof(EntityData), typeof(Vector2), typeof(EntityID) });
if (ctor != null) {
loader = (level, levelData, offset, entityData) => (Entity) ctor.Invoke(new object[] { entityData, offset, new EntityID(levelData.Name, entityData.ID + (patch_Level._isLoadingTriggers ? 10000000 : 0)) });
loader = (level, levelData, offset, entityData) => (Entity) ctor.Invoke(new object[] { entityData, offset, (entityData as patch_EntityData).EntityID });
goto RegisterEntityLoader;
}

Expand Down
4 changes: 4 additions & 0 deletions Celeste.Mod.mm/Patches/EntityData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ class patch_EntityData : EntityData {
return orig_Has(key);
}

public EntityID EntityID;
internal void InitializeEntityID(string LevelName) {
EntityID = new EntityID(string.IsNullOrWhiteSpace(LevelName) ? EntityID.None.Level : LevelName, ID + (patch_LevelData._isRegisteringTriggers ? 10000000 : 0));
}
}
}
50 changes: 18 additions & 32 deletions Celeste.Mod.mm/Patches/Level.cs
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,6 @@ private bool CheckForErrors() {

private bool _IsInDoNotLoadIncreased(LevelData level, EntityData entity) => Session.DoNotLoad.Contains(new EntityID(level.Name, entity.ID + 20000000));

[ThreadStatic]
internal static bool _isLoadingTriggers;
}

public static class LevelExt {
Expand Down Expand Up @@ -686,7 +684,8 @@ public static void PatchLevelLoader(ILContext context, CustomAttribute attrib) {
m_LoadStrings_Add.DeclaringType = t_LoadStrings;
m_LoadStrings_ctor.DeclaringType = t_LoadStrings;

FieldReference f_isLoadingTriggers = context.Method.DeclaringType.FindField("_isLoadingTriggers")!;

FieldDefinition f_EntityData_EntityID = MonoModRule.Modder.Module.GetType("Celeste.EntityData").Resolve().FindField("EntityID");
MethodReference m_IsInDoNotLoadIncreased = context.Method.DeclaringType.FindMethod("_IsInDoNotLoadIncreased")!;

ILCursor cursor = new ILCursor(context);
Expand All @@ -711,36 +710,23 @@ public static void PatchLevelLoader(ILContext context, CustomAttribute attrib) {
cursor.Index++;
}

// Reset to apply trigger loading patches

// Reset to apply EntityID fix patches - replaces the isLoadingTriggers patch
cursor.Index = 0;
int v_levelData = -1;
cursor.GotoNext(MoveType.Before, instr => instr.MatchLdloc(out v_levelData), instr => instr.MatchLdfld("Celeste.LevelData", "Triggers"));
// set global flag _isLoadingTriggers to true
cursor.EmitLdcI4(1);
cursor.EmitStsfld(f_isLoadingTriggers);
int v_entityData = -1;
cursor.GotoNext(instr => instr.MatchLdloc(out v_entityData), instr => instr.MatchLdfld("Celeste.EntityData", "ID"));
ILLabel continueLabel = null;
cursor.GotoNext(MoveType.After, instr => instr.MatchBrtrue(out continueLabel));
// add
// || _IsInDoNotLoadIncreased(levelData, trigger)
// to if condition for continue to handle triggers that already add 10000000 to their DoNotLoad entry
cursor.EmitLdarg0();
cursor.EmitLdloc(v_levelData);
cursor.EmitLdloc(v_entityData);
cursor.EmitCall(m_IsInDoNotLoadIncreased);
cursor.EmitBrtrue(continueLabel);
cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdloc(out _), instr => instr.MatchLdfld("Celeste.LevelData", "FgDecals"));
Instruction oldFinallyEnd = cursor.Next;
// set _isLoadingTriggers to false
cursor.EmitLdcI4(0);
Instruction newFinallyEnd = cursor.Prev;
cursor.EmitStsfld(f_isLoadingTriggers);
// fix end of finally block
foreach (ExceptionHandler handler in context.Body.ExceptionHandlers.Where(handler => handler.HandlerEnd == oldFinallyEnd)) {
handler.HandlerEnd = newFinallyEnd;
break;
}
// First instance of call EntityID.ctor is referenced to ldloca.s 19 = entityID (in Entities loop), we want to add `entityID = entity.EntityID` after it
// We also don't want to break mod parity by replacing instruction content
cursor.GotoNext(MoveType.After, i => i.MatchLdloc(18)); // this is the only way i found that the gotoNext works. someone could easily clean this up in the future.
cursor.Index++; // checking against call System.Void Celeste.EntityID::.ctor(System.String, System.Int32) from the MonoModRule class didn't work.
cursor.EmitLdloc(17); // emits entity
cursor.EmitLdfld(f_EntityData_EntityID);
cursor.EmitStloc(19); // stores to entityID
// Second instance of call EntityID.ctor is referenced to ldloca.s 48 = entityID3 (in Triggers loop), we want to add entityID3 = trigger.EntityID` after it
// We also don't want to break mod parity by replacing instruction content
cursor.GotoNext(MoveType.After, i => i.MatchLdloc(47));
cursor.Index++;
cursor.EmitLdloc(46); // emits trigger
cursor.EmitLdfld(f_EntityData_EntityID);
cursor.EmitStloc(48); // stores to entityID3

// Reset to apply entity patches
cursor.Index = 0;
Expand Down
40 changes: 39 additions & 1 deletion Celeste.Mod.mm/Patches/LevelData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
using MonoMod.Utils;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Celeste;

namespace Celeste {
public class patch_LevelData : LevelData {

[ThreadStatic]
internal static bool _isRegisteringTriggers;

public Vector2? DefaultSpawn;

public patch_LevelData(BinaryPacker.Element data) : base(data) {
Expand All @@ -24,6 +29,7 @@ public patch_LevelData(BinaryPacker.Element data) : base(data) {
[PatchLevelDataBerryTracker]
[PatchLevelDataDecalLoader]
[PatchLevelDataSpawnpointLoader]
[PatchLevelDataTriggerIDOffset]
public extern void orig_ctor(BinaryPacker.Element data);

[MonoModConstructor]
Expand All @@ -40,7 +46,7 @@ private void CheckForDefaultSpawn(BinaryPacker.Element spawn, Vector2 coords) {
// Optimise the method
[MonoModReplace]
private EntityData CreateEntityData(BinaryPacker.Element entity) {
EntityData entityData = new() {
patch_EntityData entityData = new() {
Name = entity.Name,
Level = this
};
Expand All @@ -51,6 +57,7 @@ private EntityData CreateEntityData(BinaryPacker.Element entity) {
{
case "id":
entityData.ID = (int) value;
entityData.InitializeEntityID(this.Name);
break;
case "x":
entityData.Position.X = Convert.ToSingle(value, CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -124,6 +131,9 @@ class PatchLevelDataDecalLoader : Attribute { }
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelDataSpawnpointLoader))]
class PatchLevelDataSpawnpointLoaderAttribute : Attribute { }

[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelDataTriggerIDOffset))]
class PatchLevelDataTriggerIDOffsetAttribute : Attribute { }

static partial class MonoModRules {

public static void PatchLevelDataBerryTracker(MethodDefinition method, CustomAttribute attrib) {
Expand Down Expand Up @@ -259,5 +269,33 @@ public static void PatchLevelDataSpawnpointLoader(ILContext context, CustomAttri
cursor.Emit(OpCodes.Callvirt, m_LevelDataCheckForDefaultSpawn);
cursor.Emit(OpCodes.Ldloc, v_spawnCoords);
}

public static void PatchLevelDataTriggerIDOffset(ILContext context, CustomAttribute attrib) {
FieldDefinition f_LevelData__isRegisteringTriggers = context.Method.DeclaringType.FindField("_isRegisteringTriggers");

ILCursor cursor = new(context);
ILLabel oldLeave = null, endOfIfTriggers = null;

cursor.GotoNext(i => i.MatchStloc(10));
cursor.GotoPrev(MoveType.After, i => i.MatchBrfalse(out endOfIfTriggers));
cursor.EmitLdcI4(1);
cursor.EmitStsfld(f_LevelData__isRegisteringTriggers);
cursor.GotoNext(i => i.MatchLeave(out oldLeave));
ILCursor clone = cursor.Clone();
cursor.GotoNext(i => i.MatchLdloc(7), i => true, i => i.MatchLdstr("bgdecals"));
Instruction oldFinallyEnd = cursor.Next;
ILLabel newLeave = cursor.MarkLabel();
cursor.EmitLdcI4(0);
Instruction newFinallyEnd = cursor.Prev;
cursor.EmitStsfld(f_LevelData__isRegisteringTriggers);
cursor.EmitBr(oldLeave);

foreach (ExceptionHandler handler in context.Body.ExceptionHandlers.Where(handler => handler.HandlerEnd == oldFinallyEnd)) {
handler.HandlerEnd = newFinallyEnd;
break;
}

clone.Next.Operand = newLeave;
}
}
}

0 comments on commit d08681b

Please sign in to comment.