diff --git a/LabApi/Features/Stores/CustomDataStore.cs b/LabApi/Features/Stores/CustomDataStore.cs index 8677c738..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; @@ -11,7 +12,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. @@ -25,7 +26,6 @@ public abstract class CustomDataStore protected CustomDataStore(Player owner) { Owner = owner; - InternalOnInstanceCreated(); } /// @@ -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(); @@ -53,37 +50,105 @@ public static TStore GetOrAdd(Player player) store = (TStore)Activator.CreateInstance(type, player); playerStores[player] = store; + store.InternalOnInstanceCreated(); return (TStore)store; } /// - /// Called when a new instance of the is created. + /// Gets the for the specified if it exists. /// - protected virtual void OnInstanceCreated() { } + /// 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; + } /// - /// Called when an instance of the is going to be destroyed. + /// Tries to get the for the specified . /// - protected virtual void OnInstanceDestroyed() { } + /// 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 . /// /// The to destroy the for. /// The type of the . - internal static void Destroy(Player player) + /// Whether the was successfully destroyed. + public static bool Destroy(Player player) where TStore : CustomDataStore { if (!StoreInstances.TryGetValue(typeof(TStore), out Dictionary? playerStores)) - return; + return false; if (!playerStores.TryGetValue(player, out CustomDataStore? store)) - return; + return false; store.Destroy(); + return true; + } + + /// + /// 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. + /// + /// 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. + /// + protected virtual void OnInstanceCreated() { } + + /// + /// Called when an instance of the is going to be destroyed. + /// + protected virtual void OnInstanceDestroyed() { } + /// /// Destroys all instances of the for the specified type. /// @@ -102,7 +167,7 @@ internal static void DestroyAll() /// /// Destroys this instance of the . /// - internal void Destroy() + private void Destroy() { OnInstanceDestroyed(); StoreInstances[this.GetType()].Remove(Owner); @@ -127,10 +192,21 @@ 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 TStore GetOrAdd(Player player) => GetOrAdd(player); + + /// + 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); + + /// + 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 57add4ba..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; @@ -10,44 +9,29 @@ 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 Dictionary Handlers = new(); /// /// 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 + /// Once registered, the store will be automatically created for all players when they join the server. + public static bool RegisterStore() + where TStore : 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) + Type type = typeof(TStore); + if (Handlers.ContainsKey(type)) 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; + StoreHandler handler = new() + { + AddPlayer = player => CustomDataStore.GetOrAdd(player), + RemovePlayer = player => CustomDataStore.Destroy(player), + DestroyAll = CustomDataStore.DestroyAll + }; - 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) - return false; - - destroyAllMethod = destroyAllMethod.MakeGenericMethod(type); - DestroyAllMethods.Add(type, destroyAllMethod); - - RegisteredStores.Add(type); + Handlers.Add(type, handler); return true; } @@ -55,34 +39,26 @@ 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); - - if (DestroyAllMethods.TryGetValue(type, out MethodInfo? method)) - method.Invoke(null, null); + Type type = typeof(TStore); + 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); - - internal static bool IsRegistered() => IsRegistered(typeof(T)); } \ No newline at end of file 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 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.