diff --git a/ArmaRAMDb_x64.dll b/ArmaRAMDb_x64.dll index b13775a..a03db35 100644 Binary files a/ArmaRAMDb_x64.dll and b/ArmaRAMDb_x64.dll differ diff --git a/ArmaRAMDb_x64.so b/ArmaRAMDb_x64.so index 98d5c33..af0d378 100644 Binary files a/ArmaRAMDb_x64.so and b/ArmaRAMDb_x64.so differ diff --git a/addons/db/XEH_preInit_server.sqf b/addons/db/XEH_preInit_server.sqf index 84f2529..e863952 100644 --- a/addons/db/XEH_preInit_server.sqf +++ b/addons/db/XEH_preInit_server.sqf @@ -1 +1,6 @@ -#include "script_component.hpp" \ No newline at end of file +#include "script_component.hpp" + +addMissionEventHandler ["Ended", { + "ArmaRAMDb" callExtension ["save", [true]]; + INFO("Mission ended - forced save with backup"); +}]; \ No newline at end of file diff --git a/extension/bin/Release/net8.0/linux-x64/publish/ArmaRAMDb_x64.so b/extension/bin/Release/net8.0/linux-x64/publish/ArmaRAMDb_x64.so index 98d5c33..af0d378 100644 Binary files a/extension/bin/Release/net8.0/linux-x64/publish/ArmaRAMDb_x64.so and b/extension/bin/Release/net8.0/linux-x64/publish/ArmaRAMDb_x64.so differ diff --git a/extension/bin/Release/net8.0/win-x64/publish/ArmaRAMDb_x64.dll b/extension/bin/Release/net8.0/win-x64/publish/ArmaRAMDb_x64.dll index b13775a..a03db35 100644 Binary files a/extension/bin/Release/net8.0/win-x64/publish/ArmaRAMDb_x64.dll and b/extension/bin/Release/net8.0/win-x64/publish/ArmaRAMDb_x64.dll differ diff --git a/extension/src/Main.cs b/extension/src/Main.cs index 73b3f21..4b75bfc 100644 --- a/extension/src/Main.cs +++ b/extension/src/Main.cs @@ -19,6 +19,9 @@ namespace ArmaRAMDb { private const string ARDB_VERSION = "1.0.0"; public const int ARDB_BUFFERSIZE = 10240; +#pragma warning disable CA2211 // Non-constant fields should not be visible + public static RAMDb db; +#pragma warning restore CA2211 // Non-constant fields should not be visible public static readonly string DEFAULT_ARDB_PATH = $"@ramdb{Path.DirectorySeparatorChar}ArmaRAMDb.ardb"; public static string ARDB_LOGFOLDER { get; private set; } = $"{Path.DirectorySeparatorChar}@ramdb{Path.DirectorySeparatorChar}logs"; public static bool ARDB_DEBUG {get; private set; } = false; @@ -81,12 +84,13 @@ namespace ArmaRAMDb Log($"Config file found! Context Mode: {ARDB_CONTEXTLOG}! Debug Mode: {ARDB_DEBUG}! " + $"Auto Backup: {RAMDb.AutoBackupEnabled} (every {RAMDb.BackupFrequencyMinutes} min, keep {RAMDb.MaxBackupsToKeep})", "action"); + + db ??= new RAMDb(); // First, load any existing RDB file - var db = new RAMDb(); if (File.Exists(Path.Combine(Environment.CurrentDirectory, DEFAULT_ARDB_PATH))) { - db.ImportFromArdb(); + db.ImportFromBinary(); ARDB_ISLOADED = true; Log("Existing ARDB data loaded during initialization", "action"); } @@ -321,7 +325,7 @@ namespace ArmaRAMDb WriteOutput(output, "Data loaded"); return 100; case "save": - Save(args); + Save(args, argc); WriteOutput(output, "Data saved"); return 100; case "set": @@ -384,23 +388,23 @@ namespace ArmaRAMDb Marshal.Copy(bytes, 0, (nint)output, bytes.Length); } - private static void Save(List args) + private static void Save(List args, int argc) { - var db = new RAMDb(); + db ??= new RAMDb(); // Convert string to boolean properly - bool createBackup = args.Count > 0 && + bool createBackup = argc > 0 && (args[0].Trim('"').Equals("true", StringComparison.CurrentCultureIgnoreCase) || args[0].Trim('"') == "1"); - db.ExportToArdb(createBackup); + db.ExportToBinary(createBackup); Log($"Data saved to ARDB{(createBackup ? " with backup" : "")}", "action"); } private static void Load() { - var db = new RAMDb(); + db ??= new RAMDb(); - db.ImportFromArdb(); + db.ImportFromBinary(); ARDB_ISLOADED = true; Log("Data loaded from ARDB", "action"); } @@ -442,7 +446,7 @@ namespace ArmaRAMDb await HashStore.HashGet(STEAMID, args[0].Trim('"'), args[1].Trim('"'), _uniqueID, args[2].Trim('"'), args[3].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for HashGet operation", "warning"); + Log($"Unexpected argument count: {argc} for HashGet operation", "warning"); break; } }); @@ -465,7 +469,7 @@ namespace ArmaRAMDb await HashStore.HashGet(args[0].Trim('"'), args[1].Trim('"'), args[2].Trim('"'), _uniqueID, args[3].Trim('"'), args[4].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for HashGetId operation", "warning"); + Log($"Unexpected argument count: {argc} for HashGetId operation", "warning"); break; } }); @@ -488,7 +492,7 @@ namespace ArmaRAMDb await HashStore.HashGetAllAsync(STEAMID, args[0].Trim('"'), _uniqueID, args[1].Trim('"'), args[2].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for HashGetAll operation", "warning"); + Log($"Unexpected argument count: {argc} for HashGetAll operation", "warning"); break; } }); @@ -511,7 +515,7 @@ namespace ArmaRAMDb await HashStore.HashGetAllAsync(args[0].Trim('"'), args[1].Trim('"'), _uniqueID, args[2], args[3].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for HashGetAllId operation", "warning"); + Log($"Unexpected argument count: {argc} for HashGetAllId operation", "warning"); break; } }); @@ -555,7 +559,7 @@ namespace ArmaRAMDb await HashStore.HashSetAsync(STEAMID, args[0].Trim('"'), args[1].Trim('"')); break; default: - Main.Log($"Invalid argument count: {argc} for HashSet operation", "warning"); + Log($"Invalid argument count: {argc} for HashSet operation", "warning"); break; } }); @@ -579,7 +583,7 @@ namespace ArmaRAMDb await HashStore.HashSetAsync(args[0].Trim('"'), args[1].Trim('"'), args[2].Trim('"')); break; default: - Main.Log($"Invalid argument count: {argc} for HashSetId operation", "warning"); + Log($"Invalid argument count: {argc} for HashSetId operation", "warning"); break; } }); @@ -601,7 +605,7 @@ namespace ArmaRAMDb await ListStore.ListAddAsync(args[0].Trim('"'), args[1].Trim('"')); break; default: - Main.Log($"Invalid argument count: {argc} for ListAdd operation", "warning"); + Log($"Invalid argument count: {argc} for ListAdd operation", "warning"); break; } }); @@ -643,7 +647,7 @@ namespace ArmaRAMDb await ListStore.ListIndexAsync(args[0].Trim('"'), args[1].Trim('"'), args[2].Trim('"'), _uniqueID, args[3].Trim('"'), args[4].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for ListIdx operation", "warning"); + Log($"Unexpected argument count: {argc} for ListIdx operation", "warning"); break; } }); @@ -666,7 +670,7 @@ namespace ArmaRAMDb await ListStore.ListRangeAsync(args[0].Trim('"'), args[1].Trim('"'), args[2].Trim('"'), args[3].Trim('"'), _uniqueID, args[4].Trim('"'), args[5].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for ListRng operation", "warning"); + Log($"Unexpected argument count: {argc} for ListRng operation", "warning"); break; } }); @@ -709,7 +713,7 @@ namespace ArmaRAMDb await KeyValueStore.GetAsync(args[0].Trim('"'), args[1].Trim('"'), _uniqueID, args[2].Trim('"'), args[3].Trim('"')); break; default: - Main.Log($"Unexpected argument count: {argc} for Get operation", "warning"); + Log($"Unexpected argument count: {argc} for Get operation", "warning"); break; } }); @@ -727,7 +731,7 @@ namespace ArmaRAMDb private static string HandleListBackupsOperation() { - var db = new RAMDb(); + db ??= new RAMDb(); var backups = db.ListBackups(); if (backups.Count > 0) @@ -752,7 +756,7 @@ namespace ArmaRAMDb if (args.Count < 1) return false; string backupFileName = args[0].Trim('"'); - var db = new RAMDb(); + db ??= new RAMDb(); var backups = db.ListBackups(); // Find the full path based on filename @@ -774,7 +778,7 @@ namespace ArmaRAMDb if (args.Count < 1) return false; string backupFileName = args[0].Trim('"'); - var db = new RAMDb(); + db ??= new RAMDb(); var backups = db.ListBackups(); // Find the full path based on filename diff --git a/extension/src/RAMDb.cs b/extension/src/RAMDb.cs index 8c7f7ff..4268197 100644 --- a/extension/src/RAMDb.cs +++ b/extension/src/RAMDb.cs @@ -4,7 +4,7 @@ using System.Collections.Concurrent; namespace ArmaRAMDb #pragma warning restore IDE0130 // Namespace does not match folder structure { - internal class RAMDb(string ardbPath = null) : IDisposable + public class RAMDb(string ardbPath = null) : IDisposable { private readonly string _ardbPath = Path.Combine(Environment.CurrentDirectory, ardbPath ?? Main.DEFAULT_ARDB_PATH); public static readonly ConcurrentDictionary _keyValues = new(); @@ -16,7 +16,7 @@ namespace ArmaRAMDb public static int MaxBackupsToKeep { get; set; } = 10; private static Timer _backupTimer; - public void ImportFromArdb() + public void ImportFromBinary() { try { @@ -100,7 +100,7 @@ namespace ArmaRAMDb } } - public void ExportToArdb(bool createBackup = false) + public void ExportToBinary(bool createBackup = false) { try { @@ -111,7 +111,7 @@ namespace ArmaRAMDb { writer.Write(1); - WriteDataToBinaryWriter(writer); + Utils.WriteDataToBinaryWriter(writer); } if (createBackup) @@ -126,7 +126,7 @@ namespace ArmaRAMDb using (var stream = new FileStream(backupPath, FileMode.Create)) using (var writer = new BinaryWriter(stream)) { - WriteDataToBinaryWriter(writer); + Utils.WriteDataToBinaryWriter(writer); } Main.Log($"Created backup at: {backupPath}", "debug"); @@ -140,44 +140,6 @@ namespace ArmaRAMDb } } - private static void WriteDataToBinaryWriter(BinaryWriter writer) - { - // Write KeyValues - writer.Write(_keyValues.Count); - foreach (var pair in _keyValues) - { - writer.Write(pair.Key); - writer.Write(pair.Value); - } - - // Write HashTables - writer.Write(_hashTables.Count); - foreach (var table in _hashTables) - { - writer.Write(table.Key); - writer.Write(table.Value.Count); - - foreach (var entry in table.Value) - { - writer.Write(entry.Key); - writer.Write(entry.Value); - } - } - - // Write Lists - writer.Write(_lists.Count); - foreach (var list in _lists) - { - writer.Write(list.Key); - writer.Write(list.Value.Count); - - foreach (var item in list.Value) - { - writer.Write(item); - } - } - } - public List ListBackups() { string backupDirectory = Path.Combine(Path.GetDirectoryName(_ardbPath), "backups"); @@ -211,7 +173,7 @@ namespace ArmaRAMDb _hashTables.Clear(); _lists.Clear(); - ReadDataFromBinaryReader(reader); + Utils.ReadDataFromBinaryReader(reader); Main.Log($"Restored from backup: {backupPath}", "info"); return true; @@ -225,62 +187,6 @@ namespace ArmaRAMDb return false; } - private static void ReadDataFromBinaryReader(BinaryReader reader) - { - // Read KeyValues - int keyValueCount = reader.ReadInt32(); - for (int i = 0; i < keyValueCount; i++) - { - string key = reader.ReadString(); - string value = reader.ReadString(); - _keyValues.TryAdd(key, value); - Main.Log($"Loaded key-value: {key} = {value[..Math.Min(50, value.Length)]}...", "debug"); - } - - // Read HashTables - int tableCount = reader.ReadInt32(); - for (int i = 0; i < tableCount; i++) - { - string tableName = reader.ReadString(); - Main.Log($"Loading table: {tableName}", "debug"); - - var concurrentDict = new ConcurrentDictionary(); - int entryCount = reader.ReadInt32(); - - for (int j = 0; j < entryCount; j++) - { - string key = reader.ReadString(); - string value = reader.ReadString(); - concurrentDict.TryAdd(key, value); - Main.Log($"Loaded entry: {key} = {value[..Math.Min(50, value.Length)]}...", "debug"); - } - - _hashTables.TryAdd(tableName, concurrentDict); - } - - // Read Lists - int listCount = reader.ReadInt32(); - for (int i = 0; i < listCount; i++) - { - string listName = reader.ReadString(); - Main.Log($"Loading list: {listName}", "debug"); - - var items = new List(); - int itemCount = reader.ReadInt32(); - - for (int j = 0; j < itemCount; j++) - { - string value = reader.ReadString(); - items.Add(value); - Main.Log($"Loaded item: {value[..Math.Min(50, value.Length)]}...", "debug"); - } - - _lists.TryAdd(listName, items); - } - - Main.Log("ARDB import complete", "debug"); - } - public static void InitializeAutoBackup() { if (AutoBackupEnabled) @@ -299,8 +205,7 @@ namespace ArmaRAMDb { try { - var db = new RAMDb(); - db.ExportToArdb(true); + Main.db.ExportToBinary(true); ManageBackupRotation(); @@ -316,8 +221,7 @@ namespace ArmaRAMDb { try { - var db = new RAMDb(); - var backups = db.ListBackups(); + var backups = Main.db.ListBackups(); if (backups.Count > MaxBackupsToKeep) { @@ -336,8 +240,17 @@ namespace ArmaRAMDb public void Dispose() { - _backupTimer?.Dispose(); - ExportToArdb(createBackup: true); + try + { + ExportToBinary(true); + _backupTimer?.Dispose(); + Main.Log("RAMDb disposed with final save and backup", "action"); + } + catch (Exception ex) + { + Main.Log($"Error during final save on disposal: {ex.Message}", "error"); + } + GC.SuppressFinalize(this); } } } \ No newline at end of file diff --git a/extension/src/Utils.cs b/extension/src/Utils.cs index 55b7073..e3bdc6b 100644 --- a/extension/src/Utils.cs +++ b/extension/src/Utils.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Collections.Concurrent; #pragma warning disable IDE0130 // Namespace does not match folder structure namespace ArmaRAMDb @@ -44,6 +45,100 @@ namespace ArmaRAMDb return DateTimeOffset.Now.ToUnixTimeMilliseconds(); } + public static void WriteDataToBinaryWriter(BinaryWriter writer) + { + // Write KeyValues + writer.Write(RAMDb._keyValues.Count); + foreach (var pair in RAMDb._keyValues) + { + writer.Write(pair.Key); + writer.Write(pair.Value); + } + + // Write HashTables + writer.Write(RAMDb._hashTables.Count); + foreach (var table in RAMDb._hashTables) + { + writer.Write(table.Key); + writer.Write(table.Value.Count); + + foreach (var entry in table.Value) + { + writer.Write(entry.Key); + writer.Write(entry.Value); + } + } + + // Write Lists + writer.Write(RAMDb._lists.Count); + foreach (var list in RAMDb._lists) + { + writer.Write(list.Key); + writer.Write(list.Value.Count); + + foreach (var item in list.Value) + { + writer.Write(item); + } + } + } + + public static void ReadDataFromBinaryReader(BinaryReader reader) + { + // Read KeyValues + int keyValueCount = reader.ReadInt32(); + for (int i = 0; i < keyValueCount; i++) + { + string key = reader.ReadString(); + string value = reader.ReadString(); + RAMDb._keyValues.TryAdd(key, value); + Main.Log($"Loaded key-value: {key} = {value[..Math.Min(50, value.Length)]}...", "debug"); + } + + // Read HashTables + int tableCount = reader.ReadInt32(); + for (int i = 0; i < tableCount; i++) + { + string tableName = reader.ReadString(); + Main.Log($"Loading table: {tableName}", "debug"); + + var concurrentDict = new ConcurrentDictionary(); + int entryCount = reader.ReadInt32(); + + for (int j = 0; j < entryCount; j++) + { + string key = reader.ReadString(); + string value = reader.ReadString(); + concurrentDict.TryAdd(key, value); + Main.Log($"Loaded entry: {key} = {value[..Math.Min(50, value.Length)]}...", "debug"); + } + + RAMDb._hashTables.TryAdd(tableName, concurrentDict); + } + + // Read Lists + int listCount = reader.ReadInt32(); + for (int i = 0; i < listCount; i++) + { + string listName = reader.ReadString(); + Main.Log($"Loading list: {listName}", "debug"); + + var items = new List(); + int itemCount = reader.ReadInt32(); + + for (int j = 0; j < itemCount; j++) + { + string value = reader.ReadString(); + items.Add(value); + Main.Log($"Loaded item: {value[..Math.Min(50, value.Length)]}...", "debug"); + } + + RAMDb._lists.TryAdd(listName, items); + } + + Main.Log("ARDB import complete", "debug"); + } + public static void CheckByteCount(string uniqueId, string data, string function, string entity, bool call, int bufferSize) { if (Encoding.UTF8.GetByteCount(data) <= bufferSize)