From fe5fb32e1e80ad2e4e65654f704781c9a58b642c Mon Sep 17 00:00:00 2001 From: x3rt Date: Sat, 19 Apr 2025 06:54:26 -0600 Subject: [PATCH 1/9] minimal performance improvement --- LabApi/Features/Stores/CustomDataStore.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 8677c738..0015d41c 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -39,9 +39,6 @@ public static TStore GetOrAdd(Player player) { Type type = typeof(TStore); - if (!CustomDataStoreManager.IsRegistered()) - CustomDataStoreManager.RegisterStore(); - if (!StoreInstances.TryGetValue(type, out Dictionary? playerStores)) { playerStores = new Dictionary(); @@ -51,6 +48,9 @@ public static TStore GetOrAdd(Player player) if (playerStores.TryGetValue(player, out CustomDataStore? store)) return (TStore)store; + if (!CustomDataStoreManager.IsRegistered()) + CustomDataStoreManager.RegisterStore(); + store = (TStore)Activator.CreateInstance(type, player); playerStores[player] = store; From a2760fbb724cf303ad21c46153521e5b08f2dc3d Mon Sep 17 00:00:00 2001 From: x3rt Date: Wed, 30 Apr 2025 17:46:24 -0600 Subject: [PATCH 2/9] remove reflection --- .../Features/Stores/CustomDataStoreManager.cs | 53 +++++++------------ LabApi/Features/Stores/StoreHandler.cs | 13 +++++ 2 files changed, 31 insertions(+), 35 deletions(-) create mode 100644 LabApi/Features/Stores/StoreHandler.cs diff --git a/LabApi/Features/Stores/CustomDataStoreManager.cs b/LabApi/Features/Stores/CustomDataStoreManager.cs index 57add4ba..83ff4a1c 100644 --- a/LabApi/Features/Stores/CustomDataStoreManager.cs +++ b/LabApi/Features/Stores/CustomDataStoreManager.cs @@ -10,10 +10,8 @@ namespace LabApi.Features.Stores; /// public static class CustomDataStoreManager { - private static readonly List RegisteredStores = new (); - private static readonly Dictionary GetOrAddMethods = new (); - private static readonly Dictionary DestroyMethods = new (); - private static readonly Dictionary DestroyAllMethods = new (); + private static readonly HashSet RegisteredStores = new(); + private static readonly Dictionary Handlers = new(); /// /// Registers a custom data store. @@ -24,29 +22,17 @@ public static bool RegisterStore() where T : CustomDataStore { Type type = typeof(T); - if (RegisteredStores.Contains(type)) return false; - - MethodInfo? getOrAddMethod = typeof(CustomDataStore).GetMethod(nameof(CustomDataStore.GetOrAdd), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - if (getOrAddMethod == null) - return false; - - getOrAddMethod = getOrAddMethod.MakeGenericMethod(type); - GetOrAddMethods.Add(type, getOrAddMethod); - - MethodInfo? destroyMethod = typeof(CustomDataStore).GetMethod(nameof(CustomDataStore.Destroy), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - if (destroyMethod == null) - return false; - - destroyMethod = destroyMethod.MakeGenericMethod(type); - DestroyMethods.Add(type, destroyMethod); - - MethodInfo? destroyAllMethod = typeof(CustomDataStore).GetMethod(nameof(CustomDataStore.DestroyAll), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - if (destroyAllMethod == null) + if (RegisteredStores.Contains(type)) return false; - destroyAllMethod = destroyAllMethod.MakeGenericMethod(type); - DestroyAllMethods.Add(type, destroyAllMethod); + StoreHandler handler = new() + { + AddPlayer = player => CustomDataStore.GetOrAdd(player), + RemovePlayer = CustomDataStore.Destroy, + DestroyAll = CustomDataStore.DestroyAll + }; + Handlers.Add(type, handler); RegisteredStores.Add(type); return true; @@ -60,26 +46,23 @@ public static void UnregisterStore() where T : CustomDataStore { Type type = typeof(T); - - if (DestroyAllMethods.TryGetValue(type, out MethodInfo? method)) - method.Invoke(null, null); + if (Handlers.TryGetValue(type, out StoreHandler? handler)) + handler.DestroyAll(); - DestroyAllMethods.Remove(type); RegisteredStores.Remove(type); - GetOrAddMethods.Remove(type); - DestroyMethods.Remove(type); + Handlers.Remove(type); } internal static void AddPlayer(Player player) { - foreach (Type? storeType in RegisteredStores) - GetOrAddMethods[storeType].Invoke(null, new object[] { player }); + foreach (StoreHandler? handler in Handlers.Values) + handler.AddPlayer(player); } - internal static void RemovePlayer(Player player) + internal static void RemovePlayer(Player player) { - foreach (Type? storeType in RegisteredStores) - DestroyMethods[storeType].Invoke(null, new object[] { player }); + foreach (StoreHandler? handler in Handlers.Values) + handler.RemovePlayer(player); } internal static bool IsRegistered(Type type) => RegisteredStores.Contains(type); diff --git a/LabApi/Features/Stores/StoreHandler.cs b/LabApi/Features/Stores/StoreHandler.cs new file mode 100644 index 00000000..14666b58 --- /dev/null +++ b/LabApi/Features/Stores/StoreHandler.cs @@ -0,0 +1,13 @@ +using System; +using LabApi.Features.Wrappers; + +namespace LabApi.Features.Stores; + +internal class StoreHandler +{ + internal required Action AddPlayer { get; init; } + + internal required Action RemovePlayer { get; init; } + + internal required Action DestroyAll { get; init; } +} \ No newline at end of file From 61c4c9b65bd643d6f30dd45990566efafc28e03f Mon Sep 17 00:00:00 2001 From: x3rt Date: Wed, 30 Apr 2025 17:51:36 -0600 Subject: [PATCH 3/9] Stop registering store in GetOrAdd --- LabApi/Features/Stores/CustomDataStore.cs | 3 --- LabApi/Features/Stores/CustomDataStoreManager.cs | 9 +-------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 0015d41c..64fd2808 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -48,9 +48,6 @@ public static TStore GetOrAdd(Player player) if (playerStores.TryGetValue(player, out CustomDataStore? store)) return (TStore)store; - if (!CustomDataStoreManager.IsRegistered()) - CustomDataStoreManager.RegisterStore(); - store = (TStore)Activator.CreateInstance(type, player); playerStores[player] = store; diff --git a/LabApi/Features/Stores/CustomDataStoreManager.cs b/LabApi/Features/Stores/CustomDataStoreManager.cs index 83ff4a1c..86f618e9 100644 --- a/LabApi/Features/Stores/CustomDataStoreManager.cs +++ b/LabApi/Features/Stores/CustomDataStoreManager.cs @@ -10,7 +10,6 @@ namespace LabApi.Features.Stores; /// public static class CustomDataStoreManager { - private static readonly HashSet RegisteredStores = new(); private static readonly Dictionary Handlers = new(); /// @@ -22,7 +21,7 @@ public static bool RegisterStore() where T : CustomDataStore { Type type = typeof(T); - if (RegisteredStores.Contains(type)) + if (Handlers.ContainsKey(type)) return false; StoreHandler handler = new() @@ -33,7 +32,6 @@ public static bool RegisterStore() }; Handlers.Add(type, handler); - RegisteredStores.Add(type); return true; } @@ -49,7 +47,6 @@ public static void UnregisterStore() if (Handlers.TryGetValue(type, out StoreHandler? handler)) handler.DestroyAll(); - RegisteredStores.Remove(type); Handlers.Remove(type); } @@ -64,8 +61,4 @@ internal static void RemovePlayer(Player player) foreach (StoreHandler? handler in Handlers.Values) handler.RemovePlayer(player); } - - internal static bool IsRegistered(Type type) => RegisteredStores.Contains(type); - - internal static bool IsRegistered() => IsRegistered(typeof(T)); } \ No newline at end of file From 23a658485906b7cd41bc78aa5e585fdac5ad2241 Mon Sep 17 00:00:00 2001 From: x3rt Date: Wed, 30 Apr 2025 18:10:36 -0600 Subject: [PATCH 4/9] rename typeparam to TStore --- .../Features/Stores/CustomDataStoreManager.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStoreManager.cs b/LabApi/Features/Stores/CustomDataStoreManager.cs index 86f618e9..9a9c5b54 100644 --- a/LabApi/Features/Stores/CustomDataStoreManager.cs +++ b/LabApi/Features/Stores/CustomDataStoreManager.cs @@ -15,20 +15,20 @@ public static class CustomDataStoreManager /// /// Registers a custom data store. /// - /// The type of the custom data store. + /// The type of the custom data store. /// Whether the store was successfully registered. - public static bool RegisterStore() - where T : CustomDataStore + public static bool RegisterStore() + where TStore : CustomDataStore { - Type type = typeof(T); + Type type = typeof(TStore); if (Handlers.ContainsKey(type)) return false; StoreHandler handler = new() { - AddPlayer = player => CustomDataStore.GetOrAdd(player), - RemovePlayer = CustomDataStore.Destroy, - DestroyAll = CustomDataStore.DestroyAll + AddPlayer = player => CustomDataStore.GetOrAdd(player), + RemovePlayer = CustomDataStore.Destroy, + DestroyAll = CustomDataStore.DestroyAll }; Handlers.Add(type, handler); @@ -39,11 +39,11 @@ public static bool RegisterStore() /// /// Unregisters a custom data store. /// - /// The type of the custom data store. - public static void UnregisterStore() - where T : CustomDataStore + /// The type of the custom data store. + public static void UnregisterStore() + where TStore : CustomDataStore { - Type type = typeof(T); + Type type = typeof(TStore); if (Handlers.TryGetValue(type, out StoreHandler? handler)) handler.DestroyAll(); From ead72960c915264fb0d30ef9e92e5960b38d362e Mon Sep 17 00:00:00 2001 From: x3rt Date: Wed, 30 Apr 2025 18:12:03 -0600 Subject: [PATCH 5/9] move InternalOnInstanceCreated out of ctor --- LabApi/Features/Stores/CustomDataStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 64fd2808..296dac78 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -25,7 +25,6 @@ public abstract class CustomDataStore protected CustomDataStore(Player owner) { Owner = owner; - InternalOnInstanceCreated(); } /// @@ -50,6 +49,7 @@ public static TStore GetOrAdd(Player player) store = (TStore)Activator.CreateInstance(type, player); playerStores[player] = store; + store.InternalOnInstanceCreated(); return (TStore)store; } @@ -99,7 +99,7 @@ internal static void DestroyAll() /// /// Destroys this instance of the . /// - internal void Destroy() + private void Destroy() { OnInstanceDestroyed(); StoreInstances[this.GetType()].Remove(Owner); From 0ccde137147e6ec52a83f94984fc9316a693c9c9 Mon Sep 17 00:00:00 2001 From: x3rt Date: Wed, 30 Apr 2025 18:20:46 -0600 Subject: [PATCH 6/9] add GetAll method --- LabApi/Features/Stores/CustomDataStore.cs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 296dac78..c6e28a43 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -53,6 +53,20 @@ public static TStore GetOrAdd(Player player) return (TStore)store; } + + /// + /// Gets all instances of the for the specified type. + /// + /// The type of the . + /// An of all instances of the . + public static IEnumerable<(Player Player, TStore Store)> GetAll() + where TStore : CustomDataStore + { + if (!StoreInstances.TryGetValue(typeof(TStore), out Dictionary? playerStores)) + return Enumerable.Empty<(Player, TStore)>(); + + return playerStores.Select(entry => (entry.Key, (TStore)entry.Value)); + } /// /// Called when a new instance of the is created. @@ -124,10 +138,9 @@ protected CustomDataStore(Player owner) { } - /// - /// Gets the for the specified . - /// - /// The to get the for. - /// The for the specified . + /// public static TStore Get(Player player) => GetOrAdd(player); + + /// + public static IEnumerable<(Player Player, TStore Store)> GetAll() => CustomDataStore.GetAll(); } \ No newline at end of file From c1527c66123096ed417707f62e7471de635f660d Mon Sep 17 00:00:00 2001 From: Cyn <181435937+xCynDev@users.noreply.github.com> Date: Fri, 2 May 2025 00:49:52 -0700 Subject: [PATCH 7/9] Add CustomDataStore.Exists, alongside helper method on Player (#99) --- LabApi/Features/Stores/CustomDataStore.cs | 20 ++++++++++++++++++++ LabApi/Features/Wrappers/Players/Player.cs | 11 +++++++++++ 2 files changed, 31 insertions(+) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index c6e28a43..19bf2542 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -54,6 +54,23 @@ public static TStore GetOrAdd(Player player) return (TStore)store; } + /// + /// Checks if the for the specified exists. + /// + /// The to check the for. + /// The type of the + /// True if the exists for the specified , false if not. + public static bool Exists(Player player) + where TStore : CustomDataStore + { + Type type = typeof(TStore); + + if (!StoreInstances.TryGetValue(type, out Dictionary? playerStores)) + return false; + + return playerStores.ContainsKey(player); + } + /// /// Gets all instances of the for the specified type. /// @@ -143,4 +160,7 @@ protected CustomDataStore(Player owner) /// public static IEnumerable<(Player Player, TStore Store)> GetAll() => CustomDataStore.GetAll(); + + /// + public static bool Exists(Player player) => Exists(player); } \ No newline at end of file diff --git a/LabApi/Features/Wrappers/Players/Player.cs b/LabApi/Features/Wrappers/Players/Player.cs index 929cb526..de6d196f 100644 --- a/LabApi/Features/Wrappers/Players/Player.cs +++ b/LabApi/Features/Wrappers/Players/Player.cs @@ -1503,6 +1503,17 @@ public TStore GetDataStore() { return CustomDataStore.GetOrAdd(this); } + + /// + /// Checks if the exists on the player. + /// + /// The type of the + /// True if the exists on the player, false if not. + public bool HasDataStore() + where TStore : CustomDataStore + { + return CustomDataStore.Exists(this); + } /// /// Handles the creation of a player in the server. From d8f64aa4974d25c3cdacf1cd4c83ba8f3c05bf57 Mon Sep 17 00:00:00 2001 From: x3rt Date: Thu, 29 May 2025 14:42:22 -0600 Subject: [PATCH 8/9] Add Regular Get, and public Destroy --- LabApi/Features/Stores/CustomDataStore.cs | 74 +++++++++++++------ .../Features/Stores/CustomDataStoreManager.cs | 4 +- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 19bf2542..7468ba0d 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -11,7 +11,7 @@ namespace LabApi.Features.Stores; /// public abstract class CustomDataStore { - private static readonly Dictionary> StoreInstances = new (); + private static readonly Dictionary> StoreInstances = new(); /// /// Gets the that this instance is associated with. @@ -53,7 +53,46 @@ public static TStore GetOrAdd(Player player) return (TStore)store; } - + + /// + /// Gets the for the specified if it exists. + /// + /// The to get the for. + /// The type of the . + /// The for the specified or null if it doesn't exist. + public static TStore? Get(Player player) + where TStore : CustomDataStore + { + Type type = typeof(TStore); + + if (!StoreInstances.TryGetValue(type, out Dictionary? playerStores)) + return null; + + if (playerStores.TryGetValue(player, out CustomDataStore? store)) + return (TStore)store; + + return null; + } + + /// + /// Destroys the for the specified . + /// + /// The to destroy the for. + /// The type of the . + /// Whether the was successfully destroyed. + public static bool Destroy(Player player) + where TStore : CustomDataStore + { + if (!StoreInstances.TryGetValue(typeof(TStore), out Dictionary? playerStores)) + return false; + + if (!playerStores.TryGetValue(player, out CustomDataStore? store)) + return false; + + store.Destroy(); + return true; + } + /// /// Checks if the for the specified exists. /// @@ -70,7 +109,7 @@ public static bool Exists(Player player) return playerStores.ContainsKey(player); } - + /// /// Gets all instances of the for the specified type. /// @@ -95,23 +134,6 @@ protected virtual void OnInstanceCreated() { } /// protected virtual void OnInstanceDestroyed() { } - /// - /// Destroys the for the specified . - /// - /// The to destroy the for. - /// The type of the . - internal static void Destroy(Player player) - where TStore : CustomDataStore - { - if (!StoreInstances.TryGetValue(typeof(TStore), out Dictionary? playerStores)) - return; - - if (!playerStores.TryGetValue(player, out CustomDataStore? store)) - return; - - store.Destroy(); - } - /// /// Destroys all instances of the for the specified type. /// @@ -156,11 +178,17 @@ protected CustomDataStore(Player owner) } /// - public static TStore Get(Player player) => GetOrAdd(player); - + public static TStore GetOrAdd(Player player) => GetOrAdd(player); + + /// + public static TStore? Get(Player player) => Get(player); + + /// + public static bool Destroy(Player player) => Destroy(player); + /// public static IEnumerable<(Player Player, TStore Store)> GetAll() => CustomDataStore.GetAll(); - + /// public static bool Exists(Player player) => Exists(player); } \ No newline at end of file diff --git a/LabApi/Features/Stores/CustomDataStoreManager.cs b/LabApi/Features/Stores/CustomDataStoreManager.cs index 9a9c5b54..61397ee9 100644 --- a/LabApi/Features/Stores/CustomDataStoreManager.cs +++ b/LabApi/Features/Stores/CustomDataStoreManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; using LabApi.Features.Wrappers; namespace LabApi.Features.Stores; @@ -17,6 +16,7 @@ public static class CustomDataStoreManager /// /// The type of the custom data store. /// Whether the store was successfully registered. + /// Once registered, the store will be automatically created for all players when they join the server. public static bool RegisterStore() where TStore : CustomDataStore { @@ -27,7 +27,7 @@ public static bool RegisterStore() StoreHandler handler = new() { AddPlayer = player => CustomDataStore.GetOrAdd(player), - RemovePlayer = CustomDataStore.Destroy, + RemovePlayer = player => CustomDataStore.Destroy(player), DestroyAll = CustomDataStore.DestroyAll }; From 1fc197c86d267a275261bca91258f147340d163a Mon Sep 17 00:00:00 2001 From: x3rt Date: Thu, 29 May 2025 14:58:10 -0600 Subject: [PATCH 9/9] Add TryGet --- LabApi/Features/Stores/CustomDataStore.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 7468ba0d..8e05948d 100644 --- a/LabApi/Features/Stores/CustomDataStore.cs +++ b/LabApi/Features/Stores/CustomDataStore.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using LabApi.Features.Wrappers; using NorthwoodLib.Pools; @@ -74,6 +75,20 @@ public static TStore GetOrAdd(Player player) return null; } + /// + /// Tries to get the for the specified . + /// + /// The to get the for. + /// The for the specified . + /// The type of the . + /// Whether the was successfully retrieved. + public static bool TryGet(Player player, [NotNullWhen(true)] out TStore? store) + where TStore : CustomDataStore + { + store = Get(player); + return store != null; + } + /// /// Destroys the for the specified . /// @@ -183,6 +198,9 @@ protected CustomDataStore(Player owner) /// public static TStore? Get(Player player) => Get(player); + /// + public static bool TryGet(Player player, [NotNullWhen(true)] out TStore? store) => TryGet(player, out store); + /// public static bool Destroy(Player player) => Destroy(player);