1- using LabExtended . Utilities ;
1+ using LabExtended . Extensions ;
2+ using LabExtended . Utilities ;
23using LabExtended . Utilities . Update ;
34
45using Mirror ;
@@ -61,6 +62,31 @@ public class StorageInstance
6162 /// </summary>
6263 public IReadOnlyDictionary < string , StorageValue > Lookup => lookup ;
6364
65+ /// <summary>
66+ /// Gets called when an existing value is saved.
67+ /// </summary>
68+ public event Action < StorageValue > ? Saved ;
69+
70+ /// <summary>
71+ /// Gets called when a new value is added.
72+ /// </summary>
73+ public event Action < StorageValue > ? Added ;
74+
75+ /// <summary>
76+ /// Gets called when an existing value is removed.
77+ /// </summary>
78+ public event Action < StorageValue > ? Removed ;
79+
80+ /// <summary>
81+ /// Gets called when an existing value is changed.
82+ /// </summary>
83+ public event Action < StorageValue > ? Changed ;
84+
85+ /// <summary>
86+ /// Gets called when the value of a new value is loaded from disk.
87+ /// </summary>
88+ public event Action < StorageValue > ? Loaded ;
89+
6490 /// <summary>
6591 /// Retrieves an existing value of the specified type by name, or adds a new value created by the provided
6692 /// factory function.
@@ -242,6 +268,7 @@ public bool Add(StorageValue value)
242268 values . Add ( value ) ;
243269 lookup . Add ( value . Name , value ) ;
244270
271+ Added ? . InvokeSafe ( value ) ;
245272 return true ;
246273 }
247274
@@ -267,6 +294,8 @@ public bool Remove(StorageValue value, bool deleteFile = false)
267294 if ( ! lookup . Remove ( value . Name ) )
268295 return false ;
269296
297+ values . Remove ( value ) ;
298+
270299 if ( deleteFile && File . Exists ( value . Path ) )
271300 File . Delete ( value . Path ) ;
272301
@@ -276,9 +305,69 @@ public bool Remove(StorageValue value, bool deleteFile = false)
276305 value . IsDirty = false ;
277306 value . Path = string . Empty ;
278307
308+ Removed ? . InvokeSafe ( value ) ;
279309 return true ;
280310 }
281311
312+ /// <summary>
313+ /// Removes all items from the collection and optionally deletes associated files and directories.
314+ /// </summary>
315+ /// <remarks>This method clears the internal collection and lookup structures. If <paramref
316+ /// name="deleteFiles"/> is <see langword="true"/>, it also attempts to delete all files and directories in the
317+ /// associated path. Any errors encountered during file or directory deletion are ignored.</remarks>
318+ /// <param name="deleteFiles">A value indicating whether to delete files and directories associated with the items. If <see
319+ /// langword="true"/>, all files and directories in the specified path are deleted. Defaults to <see
320+ /// langword="true"/>.</param>
321+ /// <returns>The total number of items, files, and directories that were successfully removed or deleted.</returns>
322+ public int RemoveAll ( bool deleteFiles = true )
323+ {
324+ var count = 0 ;
325+
326+ foreach ( var value in values . ToList ( ) )
327+ {
328+ if ( ! Remove ( value , deleteFiles ) )
329+ continue ;
330+
331+ count ++ ;
332+ }
333+
334+ values . Clear ( ) ;
335+ lookup . Clear ( ) ;
336+
337+ if ( ! deleteFiles )
338+ return count ;
339+
340+ foreach ( var directory in Directory . GetDirectories ( Path ) )
341+ {
342+ try
343+ {
344+ Directory . Delete ( directory , true ) ;
345+
346+ count ++ ;
347+ }
348+ catch
349+ {
350+ // Ignore
351+ }
352+ }
353+
354+ foreach ( var file in Directory . GetFiles ( Path ) )
355+ {
356+ try
357+ {
358+ File . Delete ( file ) ;
359+
360+ count ++ ;
361+ }
362+ catch
363+ {
364+ // Ignore
365+ }
366+ }
367+
368+ return count ;
369+ }
370+
282371 /// <summary>
283372 /// Initializes the necessary components and sets up file watching and update handling.
284373 /// </summary>
@@ -303,6 +392,7 @@ public virtual void Initialize()
303392 } ;
304393
305394 watcher . Changed += Internal_FileChanged ;
395+ watcher . Deleted += Internal_FileRemoved ;
306396
307397 updateComponent = PlayerUpdateComponent . Create ( ) ;
308398 updateComponent . OnFixedUpdate += Internal_Update ;
@@ -374,10 +464,18 @@ private void Internal_ReadFile(StorageValue value, bool isRemote)
374464 reader . SetBuffer ( segment ) ;
375465
376466 value . IsDirty = false ;
467+ value . dirtyRetries = 0 ;
468+
377469 value . ReadValue ( reader ) ;
378470
471+ Loaded ? . InvokeSafe ( value ) ;
472+
379473 if ( isRemote )
474+ {
380475 value . OnChanged ( ) ;
476+
477+ Changed ? . InvokeSafe ( value ) ;
478+ }
381479 }
382480 catch ( Exception ex )
383481 {
@@ -410,6 +508,7 @@ private void Internal_WriteDirty()
410508 if ( value . dirtyRetries > MaxRetries )
411509 {
412510 value . IsDirty = false ;
511+ value . dirtyRetries = 0 ;
413512
414513 ApiLog . Warn ( "StorageManager" , $ "Dirty value of &3{ value . ValuePath } &r will be discarded due to exceeding maximum retry count!") ;
415514 return ;
@@ -430,7 +529,12 @@ private void Internal_WriteDirty()
430529 fileStream . Write ( writer . buffer , 0 , writer . Position ) ;
431530
432531 value . IsDirty = false ;
532+ value . dirtyRetries = 0 ;
533+ value . LastSaveTime = UnityEngine . Time . realtimeSinceStartup ;
534+
433535 value . OnSaved ( ) ;
536+
537+ Saved ? . InvokeSafe ( value ) ;
434538 }
435539 catch ( Exception ex )
436540 {
@@ -442,6 +546,7 @@ private void Internal_WriteDirty()
442546 else
443547 {
444548 value . IsDirty = false ;
549+ value . dirtyRetries = 0 ;
445550
446551 ApiLog . Warn ( "StorageManager" , $ "Value &3{ value . ValuePath } &r did not write any data into the buffer!") ;
447552 }
@@ -463,6 +568,17 @@ private void Internal_FileChanged(object _, FileSystemEventArgs args)
463568 Internal_ReadFile ( targetValue , true ) ;
464569 }
465570
571+ private void Internal_FileRemoved ( object _ , FileSystemEventArgs args )
572+ {
573+ var fullPath = System . IO . Path . GetFullPath ( args . FullPath ) ;
574+ var targetValue = values . FirstOrDefault ( x => x . Path == fullPath ) ;
575+
576+ if ( targetValue is null )
577+ return ;
578+
579+ Remove ( targetValue , true ) ;
580+ }
581+
466582 private bool Internal_CheckGuard ( FileSystemEventArgs args )
467583 {
468584 var isActive = WriteGuard > 0
0 commit comments