Skip to content

Commit 0690c1b

Browse files
committed
More StorageAPI events and fixes
1 parent 8d39ee6 commit 0690c1b

2 files changed

Lines changed: 122 additions & 30 deletions

File tree

LabExtended/Core/Storage/StorageInstance.cs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using LabExtended.Utilities;
1+
using LabExtended.Extensions;
2+
using LabExtended.Utilities;
23
using LabExtended.Utilities.Update;
34

45
using 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

LabExtended/Core/Storage/StorageValue.cs

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,7 @@ public string ValuePath
4141
/// <summary>
4242
/// Gets or sets whether this value is dirty and needs to be saved.
4343
/// </summary>
44-
public bool IsDirty
45-
{
46-
get => field;
47-
set
48-
{
49-
if (value != field)
50-
{
51-
if (value)
52-
{
53-
LastChangeTime = UnityEngine.Time.realtimeSinceStartup;
54-
}
55-
else
56-
{
57-
dirtyRetries = 0;
58-
59-
LastSaveTime = UnityEngine.Time.realtimeSinceStartup;
60-
}
61-
}
62-
}
63-
}
44+
public bool IsDirty { get; set; }
6445

6546
/// <summary>
6647
/// Gets the time, in seconds, of the most recent change (from <see cref="UnityEngine.Time.realtimeSinceStartup"/>).
@@ -124,17 +105,12 @@ public bool SetField<T>(ref T field, T value)
124105
if (field == null && value == null)
125106
return false;
126107

127-
if ((field is null && value != null)
128-
|| (field != null && value == null)
129-
|| (field != null && value != null && !field.Equals(value)))
130-
{
131-
field = value;
108+
field = value;
132109

133-
IsDirty = true;
134-
return true;
135-
}
110+
IsDirty = true;
136111

137-
return false;
112+
LastChangeTime = UnityEngine.Time.realtimeSinceStartup;
113+
return true;
138114
}
139115

140116
/// <summary>

0 commit comments

Comments
 (0)