diff --git a/artifacts/exe/Firefly b/artifacts/exe/Firefly index a6e9ea5..1c3f953 100644 Binary files a/artifacts/exe/Firefly and b/artifacts/exe/Firefly differ diff --git a/artifacts/exe/Firefly.dbg b/artifacts/exe/Firefly.dbg index 2321fd4..2fc0bba 100644 Binary files a/artifacts/exe/Firefly.dbg and b/artifacts/exe/Firefly.dbg differ diff --git a/artifacts/exe/Firefly.exe b/artifacts/exe/Firefly.exe index 035aeb5..cb1a294 100644 Binary files a/artifacts/exe/Firefly.exe and b/artifacts/exe/Firefly.exe differ diff --git a/artifacts/exe/Firefly.pdb b/artifacts/exe/Firefly.pdb index 6ac887d..b61d466 100644 Binary files a/artifacts/exe/Firefly.pdb and b/artifacts/exe/Firefly.pdb differ diff --git a/artifacts/exe/Firefly.xml b/artifacts/exe/Firefly.xml index db4c865..40c85ab 100644 --- a/artifacts/exe/Firefly.xml +++ b/artifacts/exe/Firefly.xml @@ -320,6 +320,11 @@ The key to execute the action on The action to execute + + + Displays Firefly ASCII art in the console + + Handles the AUTH command which authenticates a client. diff --git a/artifacts/native/Firefly.dll b/artifacts/native/Firefly.dll index 5a4a6b3..d1f5909 100644 Binary files a/artifacts/native/Firefly.dll and b/artifacts/native/Firefly.dll differ diff --git a/artifacts/native/Firefly.pdb b/artifacts/native/Firefly.pdb index f362799..35b8891 100644 Binary files a/artifacts/native/Firefly.pdb and b/artifacts/native/Firefly.pdb differ diff --git a/artifacts/native/Firefly.so b/artifacts/native/Firefly.so index bfbd90c..eca2573 100644 Binary files a/artifacts/native/Firefly.so and b/artifacts/native/Firefly.so differ diff --git a/artifacts/native/Firefly.so.dbg b/artifacts/native/Firefly.so.dbg index 4fda6ad..da8df45 100644 Binary files a/artifacts/native/Firefly.so.dbg and b/artifacts/native/Firefly.so.dbg differ diff --git a/artifacts/native/Firefly.xml b/artifacts/native/Firefly.xml index db4c865..40c85ab 100644 --- a/artifacts/native/Firefly.xml +++ b/artifacts/native/Firefly.xml @@ -320,6 +320,11 @@ The key to execute the action on The action to execute + + + Displays Firefly ASCII art in the console + + Handles the AUTH command which authenticates a client. diff --git a/src/BackupSystem.cs b/src/BackupSystem.cs index f4adf8d..fd05d4a 100644 --- a/src/BackupSystem.cs +++ b/src/BackupSystem.cs @@ -8,6 +8,13 @@ namespace Firefly public partial class Firefly { #region Backup System + // Track if data has been modified since last backup + private static volatile bool dataModified = false; + // Debounce period in milliseconds for data modification backups + private static readonly int dataModificationBackupDelayMs = 5000; + // Timer for debouncing data modification backups + private static System.Timers.Timer? modificationBackupTimer; + static void InitializeBackupSystem() { // Create backup directory if it doesn't exist @@ -33,7 +40,21 @@ namespace Firefly backupTimer.AutoReset = true; backupTimer.Start(); + // Set up data modification backup timer + modificationBackupTimer = new System.Timers.Timer(dataModificationBackupDelayMs); + modificationBackupTimer.Elapsed += (sender, e) => + { + if (dataModified) + { + BackupData(); + dataModified = false; + } + modificationBackupTimer.Stop(); // Stop timer after backup + }; + modificationBackupTimer.AutoReset = false; + Console.WriteLine($"Automatic backup system initialized (every {autoBackupIntervalMinutes} minutes)"); + Console.WriteLine($"Data modification backup system initialized (after {dataModificationBackupDelayMs/1000} seconds of inactivity)"); Console.WriteLine($"Keeping up to {maxBackupFiles} backup files"); // Register backup on exit @@ -47,6 +68,22 @@ namespace Firefly }; } + // Call this method whenever data is modified + internal static void MarkDataAsModified() + { + if (!backupsEnabled) + return; + + dataModified = true; + + // Reset and start the timer to debounce multiple rapid changes + if (modificationBackupTimer != null) + { + modificationBackupTimer.Stop(); + modificationBackupTimer.Start(); + } + } + static void LoadDataFromBackup() { if (!backupsEnabled) diff --git a/src/HashOperations.cs b/src/HashOperations.cs index e53161c..6210cb6 100644 --- a/src/HashOperations.cs +++ b/src/HashOperations.cs @@ -27,10 +27,11 @@ namespace Firefly { var hash = GetOrCreateHash(key); bool isNewField = hash.TryAdd(field, value); + if (!isNewField) - { hash[field] = value; - } + + MarkDataAsModified(); return Encoding.UTF8.GetBytes($":{(isNewField ? 1 : 0)}\r\n"); } @@ -91,10 +92,11 @@ namespace Firefly { bool removed = hash.TryRemove(field, out _); + if (removed) + MarkDataAsModified(); + if (hash.IsEmpty) - { HashStoreRemove(key); - } return Encoding.UTF8.GetBytes($":{(removed ? 1 : 0)}\r\n"); } @@ -197,6 +199,8 @@ namespace Firefly return Encoding.UTF8.GetBytes($"-ERR internal error: {ex.Message}\r\n"); } + MarkDataAsModified(); + return Encoding.UTF8.GetBytes("+OK\r\n"); } @@ -220,7 +224,12 @@ namespace Firefly } } - return hashStoreShards[shardIndex].GetOrAdd(key, _ => new ConcurrentDictionary()); + var hash = hashStoreShards[shardIndex].GetOrAdd(key, _ => new ConcurrentDictionary()); + + if (hash.IsEmpty) + MarkDataAsModified(); + + return hash; } /// @@ -254,7 +263,12 @@ namespace Firefly private static bool HashStoreRemove(string key) { int shardIndex = GetShardIndex(key); - return hashStoreShards[shardIndex].TryRemove(key, out _); + bool result = hashStoreShards[shardIndex].TryRemove(key, out _); + + if (result) + MarkDataAsModified(); + + return result; } #endregion #endregion diff --git a/src/ListOperations.cs b/src/ListOperations.cs index b9ba45d..8368922 100644 --- a/src/ListOperations.cs +++ b/src/ListOperations.cs @@ -548,6 +548,13 @@ namespace Firefly { throw new InvalidOperationException($"Key '{key}' already exists with a different type"); } + + var newList = new List(); + if (listStoreShards[shardIndex].TryAdd(key, newList)) + { + MarkDataAsModified(); + return newList; + } } return listStoreShards[shardIndex].GetOrAdd(key, _ => []); @@ -587,7 +594,12 @@ namespace Firefly listStoreLocks[shardIndex].EnterWriteLock(); try { - return listStoreShards[shardIndex].TryRemove(key, out _); + bool result = listStoreShards[shardIndex].TryRemove(key, out _); + + if (result) + MarkDataAsModified(); + + return result; } finally { @@ -608,6 +620,8 @@ namespace Firefly { var list = GetOrCreateList(key); action(list); + + MarkDataAsModified(); } finally { diff --git a/src/ServerManager.cs b/src/ServerManager.cs index 9b28f5c..bb79d37 100644 --- a/src/ServerManager.cs +++ b/src/ServerManager.cs @@ -9,6 +9,9 @@ namespace Firefly #region Server Management static async Task StartServerAsync() { + // Display Firefly ASCII art + DisplayFireflyArt(); + // Set up server on specified port var listener = new TcpListener(IPAddress.Parse(bindAddress), serverPort); listener.Start(); @@ -305,6 +308,38 @@ namespace Firefly } } } + + /// + /// Displays Firefly ASCII art in the console + /// + static void DisplayFireflyArt() + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine( +" ,,_\n" + +" zd$$??=\n" + +" z$$P? F:`c, _\n" + +" d$$, `c'cc$$i ,cd$?R\n" + +" $$$$ cud$,?$$$i ,=P'2?z '\n" + +" $` ` ?$$$,?$$$. ,-''`>, bzP\n" + +" 'cLdb,?$$,?$$$ ,h' 'I$'J$P\n" + +" ... `?$$$,`$$,`$$h $$PxrF'd$'\n" + +" d$PP``?-,`?$$,?$h`$$,,$$'$F44'\n" + +" ?,,_`=4c,?=,`?hu?$`?L4$'? '\n" + +" ```?==``=-`` ```-`'_,,,,\n" + +" .ccu?m?e?JC,-,\"=?\n" + +" ```=='?'\n"); + + Console.WriteLine(@" + ███████╗██╗██████╗ ███████╗███████╗██╗ ██╗ ██╗ + ██╔════╝██║██╔══██╗██╔════╝██╔════╝██║ ╚██╗ ██╔╝ + █████╗ ██║██████╔╝█████╗ █████╗ ██║ ╚████╔╝ + ██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║ ╚██╔╝ + ██║ ██║██║ ██║███████╗██║ ███████╗██║ + ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝ +"); + Console.ResetColor(); + } #endregion } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/StringOperations.cs b/src/StringOperations.cs index aad07f1..cdc584a 100644 --- a/src/StringOperations.cs +++ b/src/StringOperations.cs @@ -88,6 +88,9 @@ namespace Firefly int shardIndex = GetShardIndex(key); stringStoreShards[shardIndex][key] = value; + + MarkDataAsModified(); + return true; } @@ -111,7 +114,12 @@ namespace Firefly private static bool StringStoreRemove(string key) { int shardIndex = GetShardIndex(key); - return stringStoreShards[shardIndex].TryRemove(key, out _); + bool result = stringStoreShards[shardIndex].TryRemove(key, out _); + + if (result) + MarkDataAsModified(); + + return result; } #endregion #endregion