22using System . Collections . Generic ;
33using System . IO ;
44using System . Linq ;
5+ using System . Reflection ;
56using System . Threading ;
67using System . Threading . Tasks ;
78using IPA . Utilities ;
89using ModestTree ;
9- using SiraUtil . Affinity ;
10+ using MonoMod . RuntimeDetour ;
1011using SongCore . Utilities ;
12+ using Zenject ;
1113
12- namespace SongCore . Patches . BeatmapLevelCache
14+ namespace SongCore . Hooks . BeatmapLevelCache
1315{
1416 /// <summary>
15- /// These patches implement a way to cache beatmap data when selecting levels for later use by the game or mods.
17+ /// This implements a way to cache beatmap data when selecting levels for later use by the game or mods.
1618 /// The execution flow is as follows:
1719 /// <list>
1820 /// <item><see cref="LevelCollectionTableView.didSelectLevelEvent"/> fires.</item>
@@ -29,25 +31,48 @@ namespace SongCore.Patches.BeatmapLevelCache
2931 /// </list>
3032 /// Ultimately, it ensures the cache is ready and prevents race conditions.
3133 /// </summary>
32- internal class BeatmapDataCachePatches : IAffinity
34+ internal class BeatmapDataCacheHooks : IInitializable , IDisposable
3335 {
3436 private readonly CustomLevelLoader _customLevelLoader ;
3537 private readonly BeatmapLevelsModel _beatmapLevelsModel ;
3638 private readonly BeatmapLevelsEntitlementModel _beatmapLevelsEntitlementModel ;
3739 private readonly BeatmapDataLoader _beatmapDataLoader ;
3840 private readonly BeatmapLevelCache _beatmapLevelCache ;
39- private readonly EventProxyPatches _eventProxyPatches ;
41+ private readonly EventProxyHooks _eventProxyHooks ;
4042
43+ private Hook _readAllTextFromPathAsyncHook = null ! ;
44+ private Hook _replaceDidSelectLevelEventHook = null ! ;
45+ private Hook _replaceDidChangeContentEventHook = null ! ;
46+ private Hook _createbeatmapKeyHook = null ! ;
47+ private Hook _loadBeatmapDataAsyncHook = null ! ;
4148 private CancellationToken _cancellationToken ;
4249
43- private BeatmapDataCachePatches ( CustomLevelLoader customLevelLoader , BeatmapLevelsModel beatmapLevelsModel , BeatmapLevelsEntitlementModel beatmapLevelsEntitlementModel , BeatmapDataLoader beatmapDataLoader , BeatmapLevelCache beatmapLevelCache , EventProxyPatches eventProxyPatches )
50+ private BeatmapDataCacheHooks ( CustomLevelLoader customLevelLoader , BeatmapLevelsModel beatmapLevelsModel , BeatmapLevelsEntitlementModel beatmapLevelsEntitlementModel , BeatmapDataLoader beatmapDataLoader , BeatmapLevelCache beatmapLevelCache , EventProxyHooks eventProxyHooks )
4451 {
4552 _customLevelLoader = customLevelLoader ;
4653 _beatmapLevelsModel = beatmapLevelsModel ;
4754 _beatmapLevelsEntitlementModel = beatmapLevelsEntitlementModel ;
4855 _beatmapDataLoader = beatmapDataLoader ;
4956 _beatmapLevelCache = beatmapLevelCache ;
50- _eventProxyPatches = eventProxyPatches ;
57+ _eventProxyHooks = eventProxyHooks ;
58+ }
59+
60+ public void Initialize ( )
61+ {
62+ _readAllTextFromPathAsyncHook = new Hook ( typeof ( BeatmapLevelDataUtils ) . GetMethod ( nameof ( BeatmapLevelDataUtils . ReadAllTextFromPathAsync ) ) ! , LogReadAllTextFromPathAsync , true ) ;
63+ _replaceDidSelectLevelEventHook = new Hook ( typeof ( LevelCollectionViewController ) . GetMethod ( nameof ( LevelCollectionViewController . DidActivate ) , BindingFlags . Instance | BindingFlags . NonPublic ) ! , ReplaceDidSelectLevelEvent , true ) ;
64+ _replaceDidChangeContentEventHook = new Hook ( typeof ( StandardLevelDetailViewController ) . GetMethod ( nameof ( StandardLevelDetailViewController . DidActivate ) , BindingFlags . Instance | BindingFlags . NonPublic ) ! , ReplaceDidChangeContentEvent , true ) ;
65+ _createbeatmapKeyHook = new Hook ( typeof ( StandardLevelDetailView ) . GetMethod ( nameof ( StandardLevelDetailView . CreateBeatmapKey ) , BindingFlags . Instance | BindingFlags . NonPublic ) ! , SetBeatmapLevelCacheBeatmapKey , true ) ;
66+ _loadBeatmapDataAsyncHook = new Hook ( typeof ( BeatmapDataLoader ) . GetMethod ( nameof ( BeatmapDataLoader . LoadBeatmapDataAsync ) ) ! , LoadBeatmapDataWithCacheAsync , true ) ;
67+ }
68+
69+ public void Dispose ( )
70+ {
71+ _readAllTextFromPathAsyncHook . Dispose ( ) ;
72+ _replaceDidSelectLevelEventHook . Dispose ( ) ;
73+ _replaceDidChangeContentEventHook . Dispose ( ) ;
74+ _createbeatmapKeyHook . Dispose ( ) ;
75+ _loadBeatmapDataAsyncHook . Dispose ( ) ;
5176 }
5277
5378 private async Task < IBeatmapLevelData ? > InitializeBeatmapLevelCacheAsync ( BeatmapLevel beatmapLevel , CancellationToken cancellationToken )
@@ -130,7 +155,7 @@ private async void HandleDidSelectLevel(LevelCollectionTableView levelCollection
130155 _beatmapLevelCache . Init ( beatmapLevel , InitializeBeatmapLevelCacheAsync ) ;
131156 _cancellationToken = _beatmapLevelCache . CancellationTokenSource ! . Token ;
132157
133- await InvokeEventAsync ( levelCollectionTableView , beatmapLevel , _eventProxyPatches . LevelCollectionTableViewDidSelectLevelDelegate ! , _cancellationToken ) ;
158+ await InvokeEventAsync ( levelCollectionTableView , beatmapLevel , _eventProxyHooks . LevelCollectionTableViewDidSelectLevelDelegate ! , _cancellationToken ) ;
134159 }
135160
136161 private async void HandleDidSelectLevel ( LevelCollectionViewController levelCollectionViewController , BeatmapLevel beatmapLevel )
@@ -139,7 +164,7 @@ private async void HandleDidSelectLevel(LevelCollectionViewController levelColle
139164
140165 Assert . That ( beatmapLevel == _beatmapLevelCache . BeatmapLevel ) ;
141166
142- await InvokeEventAsync ( levelCollectionViewController , beatmapLevel , _eventProxyPatches . LevelCollectionViewControllerDidSelectLevelDelegate ! , _cancellationToken ) ;
167+ await InvokeEventAsync ( levelCollectionViewController , beatmapLevel , _eventProxyHooks . LevelCollectionViewControllerDidSelectLevelDelegate ! , _cancellationToken ) ;
143168 }
144169
145170 private async void HandleDidChangeContent ( StandardLevelDetailViewController ? standardLevelDetailViewController , StandardLevelDetailViewController . ContentType contentType )
@@ -151,46 +176,54 @@ private async void HandleDidChangeContent(StandardLevelDetailViewController? sta
151176 Assert . That ( standardLevelDetailViewController . beatmapLevel == _beatmapLevelCache . BeatmapLevel ) ;
152177 }
153178
154- await InvokeEventAsync ( standardLevelDetailViewController , contentType , _eventProxyPatches . StandardLevelDetailViewControllerDidChangeContentDelegate ! , _cancellationToken ) ;
179+ await InvokeEventAsync ( standardLevelDetailViewController , contentType , _eventProxyHooks . StandardLevelDetailViewControllerDidChangeContentDelegate ! , _cancellationToken ) ;
155180 }
156181
157- [ AffinityPatch ( typeof ( BeatmapLevelDataUtils ) , nameof ( BeatmapLevelDataUtils . ReadAllTextFromPathAsync ) ) ]
158- private void LogReadAllTextFromPathAsync ( string path )
182+ private Task < string ? > LogReadAllTextFromPathAsync ( Func < string , CancellationToken , Task < string ? > > original , string path , CancellationToken cancellationToken )
159183 {
160184 Plugin . Log . Debug ( $ "ReadAllTextFromPathAsync { _beatmapLevelCache . BeatmapKey . ToString ( ) } { path } ") ;
185+ return original ( path , cancellationToken ) ;
161186 }
162187
163- [ AffinityPatch ( typeof ( LevelCollectionViewController ) , nameof ( LevelCollectionViewController . DidActivate ) ) ]
164- [ AffinityPrefix ]
165- private void ReplaceDidSelectLevelEvent ( LevelCollectionViewController __instance , bool firstActivation )
188+ private void ReplaceDidSelectLevelEvent ( Action < LevelCollectionViewController , bool , bool , bool > original , LevelCollectionViewController instance , bool firstActivation , bool addedToHierarchy , bool screenSystemEnabling )
166189 {
167190 if ( ! firstActivation )
168191 {
192+ original ( instance , firstActivation , addedToHierarchy , screenSystemEnabling ) ;
169193 return ;
170194 }
171195
172- ref var didSelectLevelViewControllerEvent = ref Accessors . ViewControllerDidSelectLevelEventAccessor ( ref __instance ) ;
196+ ref var didSelectLevelViewControllerEvent = ref Accessors . ViewControllerDidSelectLevelEventAccessor ( ref instance ) ;
173197 didSelectLevelViewControllerEvent = HandleDidSelectLevel ;
174198
175- ref var didSelectLevelViewEvent = ref Accessors . TableViewDidSelectLevelEventAccessor ( ref __instance . _levelCollectionTableView ) ;
199+ ref var didSelectLevelViewEvent = ref Accessors . TableViewDidSelectLevelEventAccessor ( ref instance . _levelCollectionTableView ) ;
176200 didSelectLevelViewEvent = HandleDidSelectLevel ;
201+
202+ original ( instance , firstActivation , addedToHierarchy , screenSystemEnabling ) ;
177203 }
178204
179- [ AffinityPatch ( typeof ( StandardLevelDetailViewController ) , nameof ( StandardLevelDetailViewController . DidActivate ) ) ]
180- [ AffinityPrefix ]
181- private void ReplaceDidChangeContentEvent ( StandardLevelDetailViewController __instance , bool firstActivation )
205+ private void ReplaceDidChangeContentEvent ( Action < StandardLevelDetailViewController , bool , bool , bool > original , StandardLevelDetailViewController instance , bool firstActivation , bool addedToHierarchy , bool screenSystemEnabling )
182206 {
183207 if ( ! firstActivation )
184208 {
209+ original ( instance , firstActivation , addedToHierarchy , screenSystemEnabling ) ;
185210 return ;
186211 }
187212
188- ref var didChangeContentEvent = ref Accessors . DidChangeContentEventAccessor ( ref __instance ) ;
213+ ref var didChangeContentEvent = ref Accessors . DidChangeContentEventAccessor ( ref instance ) ;
189214 didChangeContentEvent = HandleDidChangeContent ;
215+
216+ original ( instance , firstActivation , addedToHierarchy , screenSystemEnabling ) ;
217+ }
218+
219+ private BeatmapKey SetBeatmapLevelCacheBeatmapKey ( Func < StandardLevelDetailView , BeatmapKey > original , StandardLevelDetailView instance )
220+ {
221+ var result = original ( instance ) ;
222+ SetBeatmapLevelCacheBeatmapKeyAsync ( result ) ;
223+ return result ;
190224 }
191225
192- [ AffinityPatch ( typeof ( StandardLevelDetailView ) , nameof ( StandardLevelDetailView . CreateBeatmapKey ) ) ]
193- private async void SetBeatmapLevelCacheBeatmapKeyAsync ( BeatmapKey __result )
226+ private async void SetBeatmapLevelCacheBeatmapKeyAsync ( BeatmapKey beatmapKey )
194227 {
195228 Plugin . Log . Debug ( "Attempting to set beatmap level cache beatmap key" ) ;
196229
@@ -206,44 +239,39 @@ private async void SetBeatmapLevelCacheBeatmapKeyAsync(BeatmapKey __result)
206239
207240 Assert . That ( beatmapLevelData == _beatmapLevelCache . BeatmapLevelData ) ;
208241
209- if ( ! _beatmapLevelCache . DifficultyMatches ( __result ) )
242+ if ( ! _beatmapLevelCache . DifficultyMatches ( beatmapKey ) )
210243 {
211- Plugin . Log . Debug ( $ "Setting beatmap level cache beatmap key to { __result } ") ;
244+ Plugin . Log . Debug ( $ "Setting beatmap level cache beatmap key to { beatmapKey } ") ;
212245
213246 _beatmapLevelCache . InvalidateDifficulty ( ) ;
214- _beatmapLevelCache . BeatmapKey = __result ;
247+ _beatmapLevelCache . BeatmapKey = beatmapKey ;
215248 }
216249
217250 tcs . TrySetResult ( true ) ;
218251 }
219252
220- [ AffinityPatch ( typeof ( BeatmapDataLoader ) , nameof ( BeatmapDataLoader . LoadBeatmapDataAsync ) ) ]
221- [ AffinityPrefix ]
222- private bool LoadBeatmapDataWithCacheAsync ( ref Task < IReadonlyBeatmapData ? > __result , IBeatmapLevelData beatmapLevelData , BeatmapKey beatmapKey , float startBpm , bool loadingForDesignatedEnvironment , IEnvironmentInfo ? targetEnvironmentInfo , IEnvironmentInfo ? originalEnvironmentInfo , BeatmapLevelDataVersion beatmapLevelDataVersion , GameplayModifiers ? gameplayModifiers , PlayerSpecificSettings ? playerSpecificSettings , bool enableBeatmapDataCaching )
253+ private Task < IReadonlyBeatmapData ? > LoadBeatmapDataWithCacheAsync ( Func < BeatmapDataLoader , IBeatmapLevelData , BeatmapKey , float , bool , IEnvironmentInfo ? , IEnvironmentInfo ? , BeatmapLevelDataVersion , GameplayModifiers ? , PlayerSpecificSettings ? , bool , Task < IReadonlyBeatmapData ? > > original , BeatmapDataLoader instance , IBeatmapLevelData beatmapLevelData , BeatmapKey beatmapKey , float startBpm , bool loadingForDesignatedEnvironment , IEnvironmentInfo ? targetEnvironmentInfo , IEnvironmentInfo ? originalEnvironmentInfo , BeatmapLevelDataVersion beatmapLevelDataVersion , GameplayModifiers ? gameplayModifiers , PlayerSpecificSettings ? playerSpecificSettings , bool enableBeatmapDataCaching )
223254 {
224- Assert . That ( UnityGame . OnMainThread ) ;
255+ Assert . That ( UnityGame . OnMainThread , "This method must be called on the main thread." ) ;
225256
226257 var request = new BeatmapDataRequest ( beatmapLevelData , beatmapKey , startBpm , loadingForDesignatedEnvironment , targetEnvironmentInfo , originalEnvironmentInfo , beatmapLevelDataVersion , gameplayModifiers , playerSpecificSettings , enableBeatmapDataCaching ) ;
227258
228259 if ( ! _beatmapLevelCache . LevelMatches ( beatmapLevelData ) )
229260 {
230261 Plugin . Log . Debug ( "Level data changed, returning original method" ) ;
231- return true ;
262+ return original ( instance , beatmapLevelData , beatmapKey , startBpm , loadingForDesignatedEnvironment , targetEnvironmentInfo , originalEnvironmentInfo , beatmapLevelDataVersion , gameplayModifiers , playerSpecificSettings , enableBeatmapDataCaching ) ;
232263 }
233264
234265 if ( _beatmapLevelCache . BeatmapDataRequest ? . Equals ( request ) == true )
235266 {
236267 Plugin . Log . Debug ( "Returning stored beatmap data task" ) ;
237- __result = _beatmapLevelCache . BeatmapDataLoadingTask ! ;
238- return false ;
268+ return _beatmapLevelCache . BeatmapDataLoadingTask ! ;
239269 }
240270
241271 Plugin . Log . Debug ( "Starting new beatmap data request" ) ;
242272
243273 _beatmapLevelCache . BeatmapDataRequest = request ;
244- __result = _beatmapLevelCache . BeatmapDataLoadingTask = request . Start ( _beatmapDataLoader ) ;
245-
246- return false ;
274+ return _beatmapLevelCache . BeatmapDataLoadingTask = request . Start ( original , _beatmapDataLoader ) ;
247275 }
248276 }
249277}
0 commit comments