Skip to content

Commit 8699c1b

Browse files
authored
Merge pull request #121 from Wartori54/ftl-rewrite-support
Add support for the ftl rewrite
2 parents 4df3c03 + 4e1dbd2 commit 8699c1b

File tree

1 file changed

+79
-35
lines changed

1 file changed

+79
-35
lines changed

LazyLoadingHandler.cs

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Collections.Generic;
1010
using System.IO;
1111
using System.Linq;
12+
using System.Reflection;
1213

1314
namespace Celeste.Mod.CollabUtils2 {
1415
// This class allows turning on lazy loading on a map pack by dropping a CollabUtils2LazyLoading.txt file at its root.
@@ -45,15 +46,31 @@ public class PrefixesExcludedFromLazyLoading {
4546
private static bool preloadingTextures = false;
4647
private static ILHook hookOnTextureSafe;
4748
private static ILHook hookOnContentCrawl;
49+
private static EventInfo shouldForceLazyLoadEvent;
50+
private static EventInfo onLazyLoadEvent;
51+
private static Delegate shouldForceLazyLoadDelegate;
52+
private static Delegate onLazyLoadDelegate;
4853

4954
public static void Load() {
5055
hookOnContentCrawl = new ILHook(typeof(Everest.Content).GetMethod("Crawl"), registerLazyLoadingModsOnLoad);
51-
IL.Monocle.VirtualTexture.Preload += turnOnLazyLoadingSelectively;
56+
shouldForceLazyLoadEvent = typeof(Everest.Events).GetNestedType("VirtualTexture")?
57+
.GetEvent("ShouldForceLazyLoad");
58+
if (shouldForceLazyLoadEvent != null) {
59+
shouldForceLazyLoadDelegate = Delegate.CreateDelegate(shouldForceLazyLoadEvent.EventHandlerType, typeof(LazyLoadingHandler), "turnOnLazyLoadingSelectively2");
60+
shouldForceLazyLoadEvent.AddMethod.Invoke(null, new object[] { shouldForceLazyLoadDelegate });
61+
62+
onLazyLoadEvent = typeof(Everest.Events).GetNestedType("VirtualTexture")
63+
.GetEvent("OnLazyLoad");
64+
onLazyLoadDelegate = Delegate.CreateDelegate(onLazyLoadEvent.EventHandlerType, typeof(LazyLoadingHandler), "onTextureLazyLoadInner");
65+
onLazyLoadEvent.AddMethod.Invoke(null, new object[] { onLazyLoadDelegate });
66+
} else {
67+
IL.Monocle.VirtualTexture.Preload += turnOnLazyLoadingSelectivelyHook;
68+
hookOnTextureSafe = new ILHook(typeof(VirtualTexture).GetMethod("get_Texture_Safe"), lazyLoadTexturesOnAccess);
69+
On.Monocle.VirtualTexture.Reload += onTextureLazyLoadHook;
70+
}
5271
On.Celeste.LevelLoader.ctor += lazilyLoadTextures;
53-
On.Monocle.VirtualTexture.Reload += onTextureLazyLoad;
5472
Everest.Events.Level.OnExit += saveNewLazilyLoadedPaths;
5573

56-
hookOnTextureSafe = new ILHook(typeof(VirtualTexture).GetMethod("get_Texture_Safe"), lazyLoadTexturesOnAccess);
5774

5875
// check all mods that were registered before us.
5976
foreach (ModContent modContent in Everest.Content.Mods) {
@@ -65,9 +82,14 @@ public static void Unload() {
6582
hookOnContentCrawl?.Dispose();
6683
hookOnContentCrawl = null;
6784

68-
IL.Monocle.VirtualTexture.Preload -= turnOnLazyLoadingSelectively;
85+
if (shouldForceLazyLoadEvent != null) {
86+
shouldForceLazyLoadEvent.RemoveMethod.Invoke(null, new object[] { shouldForceLazyLoadDelegate });
87+
onLazyLoadEvent.RemoveMethod.Invoke(null, new object[] { onLazyLoadDelegate });
88+
}
89+
90+
IL.Monocle.VirtualTexture.Preload -= turnOnLazyLoadingSelectivelyHook;
6991
On.Celeste.LevelLoader.ctor -= lazilyLoadTextures;
70-
On.Monocle.VirtualTexture.Reload -= onTextureLazyLoad;
92+
On.Monocle.VirtualTexture.Reload -= onTextureLazyLoadHook;
7193
Everest.Events.Level.OnExit -= saveNewLazilyLoadedPaths;
7294

7395
hookOnTextureSafe?.Dispose();
@@ -168,41 +190,56 @@ private static void fillInTexturesFromCache(string key, Stream input) {
168190
}
169191

170192
// this turns on or off lazy loading based on which texture is being loaded.
171-
private static void turnOnLazyLoadingSelectively(ILContext il) {
193+
private static void turnOnLazyLoadingSelectivelyHook(ILContext il) {
172194
ILCursor cursor = new ILCursor(il);
173195
cursor.GotoNext(MoveType.After, instr => instr.MatchCallvirt<CoreModuleSettings>("get_LazyLoading"));
174196

175197
cursor.Emit(OpCodes.Ldarg_0);
176-
cursor.EmitDelegate<Func<bool, VirtualTexture, bool>>((orig, self) => {
177-
// don't do anything if lazy loading is actually turned on or for textures with (somehow) no name.
178-
if (orig || self.Name == null)
179-
return orig;
198+
cursor.EmitDelegate<Func<bool, VirtualTexture, bool>>(turnOnLazyLoadingSelectively1);
199+
}
180200

181-
string name = self.Name;
182-
if (lazilyLoadedTextures.Contains(name)) {
183-
Logger.Log(LogLevel.Debug, "CollabUtils2/LazyLoadingHandler", name + " was skipped and will be lazily loaded later");
184-
185-
// look for maps that use this, so that we can fill out texturesPerMap as we go through all textures.
186-
foreach (KeyValuePair<string, HashSet<string>> mapGraphics in pathsPerMap) {
187-
if (mapGraphics.Value.Contains(name)) {
188-
Logger.Log("CollabUtils2/LazyLoadingHandler", name + " is associated to map " + mapGraphics.Key);
189-
190-
// associate the (non-loaded) texture to the map so that it can be loaded more easily later.
191-
if (!texturesPerMap.TryGetValue(mapGraphics.Key, out HashSet<VirtualTexture> list)) {
192-
list = new HashSet<VirtualTexture>();
193-
texturesPerMap[mapGraphics.Key] = list;
194-
}
195-
list.Add(self);
201+
private static bool turnOnLazyLoadingSelectively1(bool orig, VirtualTexture self) {
202+
bool? ret = turnOnLazyLoadingSelectivelyInner(self);
203+
if (ret == null) return orig;
204+
return ret.Value;
205+
}
206+
207+
private static bool turnOnLazyLoadingSelectively2(VirtualTexture self) {
208+
bool? ret = turnOnLazyLoadingSelectivelyInner(self);
209+
// The newer implementation does not allow force not lazy loading a texture in the callback (just call Reload on the VirtualTexture to ensure that)
210+
// so treat null as false
211+
return ret == null ? false : ret.Value;
212+
}
213+
214+
private static bool? turnOnLazyLoadingSelectivelyInner(VirtualTexture self) {
215+
// don't do anything if lazy loading is actually turned on or for textures with (somehow) no name.
216+
if (self.Name == null)
217+
return null; // null means keep original behaviour
218+
219+
string name = self.Name;
220+
if (lazilyLoadedTextures.Contains(name)) {
221+
Logger.Log(LogLevel.Debug, "CollabUtils2/LazyLoadingHandler", name + " was skipped and will be lazily loaded later");
222+
223+
// look for maps that use this, so that we can fill out texturesPerMap as we go through all textures.
224+
foreach (KeyValuePair<string, HashSet<string>> mapGraphics in pathsPerMap) {
225+
if (mapGraphics.Value.Contains(name)) {
226+
Logger.Log("CollabUtils2/LazyLoadingHandler", name + " is associated to map " + mapGraphics.Key);
227+
228+
// associate the (non-loaded) texture to the map so that it can be loaded more easily later.
229+
if (!texturesPerMap.TryGetValue(mapGraphics.Key, out HashSet<VirtualTexture> list)) {
230+
list = new HashSet<VirtualTexture>();
231+
texturesPerMap[mapGraphics.Key] = list;
196232
}
233+
list.Add(self);
197234
}
198-
199-
// this triggers lazy loading: preload of the texture, but not actually load it in video RAM.
200-
return true;
201235
}
202236

203-
// this disables lazy loading, and the game will actually load the texture.
204-
return false;
205-
});
237+
// this triggers lazy loading: preload of the texture, but not actually load it in video RAM.
238+
return true;
239+
}
240+
241+
// this disables lazy loading, and the game will actually load the texture.
242+
return false;
206243
}
207244

208245
private static void lazilyLoadTextures(On.Celeste.LevelLoader.orig_ctor orig, LevelLoader self, Session session, Vector2? startPosition) {
@@ -256,10 +293,18 @@ private static void lazyLoadTexturesOnAccess(ILContext il) {
256293
});
257294
}
258295

259-
private static void onTextureLazyLoad(On.Monocle.VirtualTexture.orig_Reload orig, VirtualTexture self) {
296+
private static void onTextureLazyLoadHook(On.Monocle.VirtualTexture.orig_Reload orig, VirtualTexture self) {
260297
// this is actually called on every texture load, so we need to check if this is a lazy load or not
298+
if (!preloadingTextures) {
299+
onTextureLazyLoadInner(self);
300+
}
301+
302+
orig(self);
303+
}
304+
305+
private static void onTextureLazyLoadInner(VirtualTexture self) {
261306
string name = self.Name;
262-
if (!preloadingTextures && lazilyLoadedTextures.Contains(name)) {
307+
if (lazilyLoadedTextures.Contains(name)) {
263308
string currentMap = (Engine.Scene as Level)?.Session?.Area.GetSID();
264309

265310
Logger.Log(LogLevel.Debug, "CollabUtils2/LazyLoadingHandler", name + " was lazily loaded by Everest! It will be associated to map " + currentMap + ".");
@@ -280,10 +325,9 @@ private static void onTextureLazyLoad(On.Monocle.VirtualTexture.orig_Reload orig
280325
texturesForThisMap.Add(self);
281326
}
282327
}
283-
284-
orig(self);
285328
}
286329

330+
287331
private static void saveNewLazilyLoadedPaths(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) {
288332
writeNewPaths(session.Area.GetSID());
289333
}

0 commit comments

Comments
 (0)