11using System . Collections . Generic ;
2- using System . Linq ;
2+ using System . IO ;
33
44namespace Celeste . Mod . SpeedrunTool . SaveLoad . Utils ;
55internal static class GraphicResourcesHandler {
@@ -8,13 +8,9 @@ public static void Add(VirtualAsset asset) {
88 VirtualAssetsHandler . Add ( asset ) ;
99 }
1010
11- public static void Add ( Atlas atlas ) {
12- AtlasHandler . Add ( atlas ) ;
13- }
14-
1511 internal static void AddSaveLoadAction ( ) {
16- AtlasHandler . AddAction ( ) ;
1712 VirtualAssetsHandler . ReloadVirtualAssets ( ) ;
13+ AtlasHandler . AddAction ( ) ;
1814 }
1915
2016 private static class MemoryLeakHandler {
@@ -36,14 +32,14 @@ private static void Handle() {
3632
3733 private static class VirtualAssetsHandler {
3834
39- private static readonly List < VirtualAsset > VirtualAssets = new ( ) ; // added and cleared when loading, so no need to be individual
35+ private static readonly List < VirtualAsset > VirtualAssets = [ ] ; // added and cleared when loading, so no need to be individual
4036
4137 internal static void Add ( VirtualAsset asset ) {
4238 VirtualAssets . Add ( asset ) ;
4339 }
4440 internal static void ReloadVirtualAssets ( ) {
4541 SaveLoadAction . InternalSafeAdd (
46- loadState : ( _ , _ ) => {
42+ loadState : static ( _ , _ ) => {
4743 List < VirtualAsset > list = new List < VirtualAsset > ( VirtualAssets ) ;
4844 // if load too frequently and switching between different slots, then collection might be modified? idk
4945 // so we avoid the crash in this way
@@ -65,12 +61,8 @@ internal static void ReloadVirtualAssets() {
6561
6662 VirtualAssets . Clear ( ) ;
6763 } ,
68- clearState : ( ) => {
69- VirtualAssets . Clear ( ) ;
70- } ,
71- preCloneEntities : ( ) => {
72- VirtualAssets . Clear ( ) ;
73- }
64+ clearState : VirtualAssets . Clear ,
65+ preCloneEntities : VirtualAssets . Clear
7466 ) ;
7567 }
7668 }
@@ -79,254 +71,18 @@ private static class AtlasHandler {
7971
8072 // This Fixes: WaveDashPresentation 中存档, 结束, 再读档, 会崩溃
8173
82- /* 每个存档带有信息: 它存档时持有的 atlas, 以及截止到存档时所有在它的历史上本应释放但没释放的 atlas
83- * 外加 CurrentGame 带有: 它所对应的那个存档的全部信息, 外加读档后新增加的本应释放的 atlas
84- *
85- * 每次清除存档时, 尝试释放这个存档持有的且在其他某个存档/CurrentGame 上本应释放的 atlas
86- * 每次读档时, 将 CurrentGame 的历史回退到读取的档上
87- * 每次存档时, 将 CurrentGame 的历史存入, 持有 atlas
88- * 每次 Dispose 但被阻碍时, 存入 CurrentGame
89- */
90-
91- private static Holder CurrentSlot => new Holder ( SaveSlotsManager . SlotName , isSlot : true ) ;
92-
93- private static readonly Holder CurrentGame = new Holder ( $ "SpeedrunTool/{ nameof ( AtlasHandler ) } /CurrentGame", isSlot : false ) ;
94-
95- [ Load ]
96- private static void Load ( ) {
97- On . Monocle . Atlas . Dispose += Atlas_Dispose ;
98- }
99-
100- [ Unload ]
101- private static void Unload ( ) {
102- On . Monocle . Atlas . Dispose -= Atlas_Dispose ;
103- }
104-
105- private static void Atlas_Dispose ( On . Monocle . Atlas . orig_Dispose orig , Atlas self ) {
106- bool b = false ;
107- if ( BipartiteGraph . HasDisposeBarrier ( self ) ) {
108- BipartiteGraph . AddDisposeEdge ( self , CurrentGame ) ;
109- b = BipartiteGraph . HasDisposeBarrier ( self ) ; // check again. since CurrentGame can be the only barrier
110- }
111- if ( b ) {
112- Logger . Info ( $ "SpeedrunTool/{ nameof ( AtlasHandler ) } ", $ "{ self . DataPath } was to be disposed but stopped by us.") ;
113- }
114- else {
115- Logger . Info ( $ "SpeedrunTool/{ nameof ( AtlasHandler ) } ", $ "Dispose { self . DataPath } ") ;
116- orig ( self ) ;
117- }
118- }
119-
120- internal static void SafeDispose ( Atlas atlas ) {
121- // not safe. Just make it easier to debug
122- atlas ? . Dispose ( ) ;
123- }
124-
125- internal static void Add ( Atlas atlas ) {
126- BipartiteGraph . AddHoldEdge ( atlas , CurrentSlot ) ;
127- }
128-
12974 internal static void AddAction ( ) {
13075 SaveLoadAction . InternalSafeAdd (
131- saveState : ( _ , _ ) => {
132- BipartiteGraph . AddTo ( CurrentGame , CurrentSlot ) ;
133- } ,
134- loadState : ( _ , _ ) => {
135- BipartiteGraph . Clone ( CurrentSlot , CurrentGame ) ;
136- } ,
137- clearState : ( ) => {
138- BipartiteGraph . ClearAndDispose ( CurrentSlot ) ;
139- }
140- ) ;
141- }
142-
143- private static void DebugLog ( ) {
144- BipartiteGraph . Log ( ) ;
145- }
146-
147-
148- internal static class BipartiteGraph {
149- internal enum State { Hold , Dispose } // Hold: don't dispose; Dispose: wanna dispose but failed to
150-
151- private static readonly HashSet < Atlas > VerticesAtlas = new HashSet < Atlas > ( ) ;
152-
153- private static readonly HashSet < Holder > VerticesHolder = new HashSet < Holder > ( ) ;
154-
155- private static readonly Dictionary < Atlas , HashSet < Holder > > Atlas2Hold = new ( ) ; // only contains Hold edges
156-
157- private static readonly Dictionary < Atlas , HashSet < Holder > > Atlas2Dispose = new ( ) ; // only contains Dispose edges
158-
159- private static readonly Dictionary < Holder , HashSet < Atlas > > Hold2Atlas = new ( ) ; // only contains Hold edges
160-
161- private static readonly Dictionary < Holder , HashSet < Atlas > > Dispose2Atlas = new ( ) ; // only contains Dispose edges
162-
163- private static void SafeAdd < TKey , TValue > ( Dictionary < TKey , HashSet < TValue > > dict , TKey key , TValue value ) {
164- if ( dict . TryGetValue ( key , out HashSet < TValue > list ) ) {
165- list . Add ( value ) ;
166- }
167- else {
168- dict [ key ] = [ value ] ;
169- }
170- }
171-
172- private static bool SafeRemove < TKey , TValue > ( Dictionary < TKey , HashSet < TValue > > dict , TKey key , TValue value ) {
173- bool b = false ;
174- if ( dict . TryGetValue ( key , out HashSet < TValue > list ) ) {
175- b = list . Remove ( value ) ;
176- if ( list . IsNullOrEmpty ( ) ) {
177- dict . Remove ( key ) ;
178- }
179- }
180- return b ;
181- }
182-
183- private static void SafeAdd < TKey , TValue > ( Dictionary < TKey , HashSet < TValue > > dict1 , Dictionary < TValue , HashSet < TKey > > dict2 , TKey key , TValue value ) {
184- SafeAdd ( dict1 , key , value ) ;
185- SafeAdd ( dict2 , value , key ) ;
186- }
187-
188- private static bool SafeRemove < TKey , TValue > ( Dictionary < TKey , HashSet < TValue > > dict1 , Dictionary < TValue , HashSet < TKey > > dict2 , TKey key , TValue value ) {
189- bool b1 = SafeRemove ( dict1 , key , value ) ;
190- bool b2 = SafeRemove ( dict2 , value , key ) ;
191- return b1 || b2 ;
192- }
193-
194- private static bool SafeRemove < TKey , TValue > ( Dictionary < TKey , HashSet < TValue > > dict1 , Dictionary < TValue , HashSet < TKey > > dict2 , TKey key ) {
195- if ( dict1 . TryGetValue ( key , out HashSet < TValue > list ) ) {
196- foreach ( TValue value in list ) {
197- SafeRemove ( dict2 , value , key ) ;
198- }
199- dict1 . Remove ( key ) ;
200- return true ;
201- }
202- return false ;
203- }
204-
205- internal static void AddHoldEdge ( Atlas atlas , Holder holder ) {
206- VerticesAtlas . Add ( atlas ) ;
207- VerticesHolder . Add ( holder ) ;
208- SafeAdd ( Atlas2Hold , Hold2Atlas , atlas , holder ) ;
209- SafeRemove ( Atlas2Dispose , Dispose2Atlas , atlas , holder ) ;
210- }
211-
212- internal static void AddDisposeEdge ( Atlas atlas , Holder holder ) {
213- VerticesAtlas . Add ( atlas ) ;
214- VerticesHolder . Add ( holder ) ;
215- SafeRemove ( Atlas2Hold , Hold2Atlas , atlas , holder ) ;
216- SafeAdd ( Atlas2Dispose , Dispose2Atlas , atlas , holder ) ;
217- }
218-
219- internal static void Clear ( Atlas atlas ) {
220- VerticesAtlas . Remove ( atlas ) ;
221- SafeRemove ( Atlas2Hold , Hold2Atlas , atlas ) ;
222- SafeRemove ( Atlas2Dispose , Dispose2Atlas , atlas ) ;
223- }
224-
225- internal static void Clear ( Holder holder ) {
226- VerticesHolder . Remove ( holder ) ;
227- SafeRemove ( Hold2Atlas , Atlas2Hold , holder ) ;
228- SafeRemove ( Dispose2Atlas , Atlas2Dispose , holder ) ;
229- }
230-
231- internal static void ClearAndDispose ( Holder holder ) {
232- VerticesHolder . Remove ( holder ) ;
233- SafeRemove ( Hold2Atlas , Atlas2Hold , holder ) ;
234- HashSet < Atlas > unholding = [ .. VerticesAtlas . Where ( x => ! Atlas2Hold . ContainsKey ( x ) ) ] ;
235- foreach ( Atlas atlas in unholding . Where ( Atlas2Dispose . ContainsKey ) ) {
236- SafeDispose ( atlas ) ;
237- }
238- foreach ( Atlas atlas2 in unholding ) {
239- Clear ( atlas2 ) ;
240- }
241- SafeRemove ( Dispose2Atlas , Atlas2Dispose , holder ) ;
242- HashSet < Holder > holders = [ .. VerticesHolder . Where ( x => ! Hold2Atlas . ContainsKey ( x ) && ! Dispose2Atlas . ContainsKey ( x ) ) ] ;
243- foreach ( Holder holder2 in holders ) {
244- Clear ( holder2 ) ;
245- }
246- }
247-
248- internal static void Clone ( Holder from , Holder to ) {
249- ClearAndDispose ( to ) ;
250- if ( Dispose2Atlas . TryGetValue ( from , out HashSet < Atlas > list2 ) ) {
251- foreach ( Atlas atlas in list2 ) {
252- AddDisposeEdge ( atlas , to ) ;
253- }
254- }
255- if ( Hold2Atlas . TryGetValue ( from , out HashSet < Atlas > list1 ) ) {
256- foreach ( Atlas atlas in list1 ) {
257- AddHoldEdge ( atlas , to ) ;
258- }
259- }
260- }
261-
262- internal static void AddTo ( Holder from , Holder to ) {
263- if ( Dispose2Atlas . TryGetValue ( from , out HashSet < Atlas > list2 ) ) {
264- foreach ( Atlas atlas in list2 ) {
265- AddDisposeEdge ( atlas , to ) ;
266- }
267- }
268- if ( Hold2Atlas . TryGetValue ( from , out HashSet < Atlas > list1 ) ) {
269- foreach ( Atlas atlas in list1 ) {
270- AddHoldEdge ( atlas , to ) ;
271- }
272- }
273- }
274-
275-
276- internal static bool HasDisposeBarrier ( Atlas atlas ) {
277- return Atlas2Hold . ContainsKey ( atlas ) ;
278- }
279-
280- internal static void Log ( ) {
281- #if DEBUG
282- System . Text . StringBuilder sb = new ( ) ;
283- sb . Append ( "BipartiteGraph Structure:" ) ;
284- foreach ( Atlas atlas in VerticesAtlas ) {
285- sb . Append ( $ "\n { atlas . DataPath } ") ;
286- if ( Atlas2Hold . TryGetValue ( atlas , out HashSet < Holder > list1 ) ) {
287- sb . Append ( $ " | hold by -> ") ;
288- foreach ( Holder holder in list1 ) {
289- sb . Append ( $ " { holder . Name } , ") ;
290- }
291- }
292- if ( Atlas2Dispose . TryGetValue ( atlas , out HashSet < Holder > list2 ) ) {
293- sb . Append ( $ " | dispose by -> ") ;
294- foreach ( Holder holder in list2 ) {
295- sb . Append ( $ " { holder . Name } , ") ;
296- }
297- }
298- }
299- sb . Append ( '\n ' ) ;
300- foreach ( Holder holder in VerticesHolder ) {
301- sb . Append ( $ "\n { holder . Name } ") ;
302- if ( Hold2Atlas . TryGetValue ( holder , out HashSet < Atlas > list1 ) ) {
303- sb . Append ( $ " | hold -> ") ;
304- foreach ( Atlas atlas in list1 ) {
305- sb . Append ( $ " { atlas . DataPath } , ") ;
306- }
307- }
308- if ( Dispose2Atlas . TryGetValue ( holder , out HashSet < Atlas > list2 ) ) {
309- sb . Append ( $ " | dispose -> ") ;
310- foreach ( Atlas atlas in list2 ) {
311- sb . Append ( $ " { atlas . DataPath } , ") ;
76+ loadState : static ( _ , level ) => {
77+ if ( level . Tracker . GetEntitiesTrackIfNeeded < WaveDashPresentation > ( ) is { } list && list . IsNotNullOrEmpty ( ) ) {
78+ foreach ( WaveDashPresentation presentation in list ) {
79+ presentation . Gfx ? . Dispose ( ) ;
80+ presentation . Gfx = Atlas . FromAtlas ( Path . Combine ( "Graphics" , "Atlases" , "WaveDashing" ) , Atlas . AtlasDataFormat . Packer ) ;
81+ presentation . loading = false ;
31282 }
31383 }
31484 }
315- Logger . Debug ( $ "SpeedrunTool/{ nameof ( Holder ) } ", sb . ToString ( ) ) ;
316- #endif
317- }
318- }
319-
320- internal struct Holder ( string name , bool isSlot ) {
321- public enum HolderType { Slot , CurrentRunningGame }
322-
323- public string Name = name ;
324-
325- public HolderType Type = isSlot ? HolderType . Slot : HolderType . CurrentRunningGame ;
326-
327- public override readonly string ToString ( ) {
328- return $ "{ nameof ( Holder ) } [{ ( Type == HolderType . Slot ? Name : "CurrentGame" ) } ]";
329- }
85+ ) ;
33086 }
33187 }
33288}
0 commit comments