99using System . Collections . Generic ;
1010using System . IO ;
1111using System . Linq ;
12+ using System . Reflection ;
1213
1314namespace 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