diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs index c6afeb3e..9c638ba5 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs @@ -2,6 +2,8 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.StorableLists; +using LBPUnion.ProjectLighthouse.StorableLists.Stores; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; @@ -53,13 +55,13 @@ public class FriendsController : ControllerBase blockedUsers.Add(blockedUser.UserId); } - UserFriendStore? friendStore = Redis.GetUserFriendStore(user.UserId); - if (friendStore == null) friendStore = Redis.CreateUserFriendStore(user.UserId); + UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId); + if (friendStore == null) friendStore = UserFriendStore.CreateUserFriendData(user.UserId); friendStore.FriendIds = friends.Select(u => u.UserId).ToList(); friendStore.BlockedIds = blockedUsers; - - Redis.UpdateFriendStore(friendStore); + + UserFriendStore.UpdateFriendData(friendStore); string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); @@ -77,7 +79,7 @@ public class FriendsController : ControllerBase User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; - UserFriendStore? friendStore = Redis.GetUserFriendStore(user.UserId); + UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId); if (friendStore == null) return this.Ok(LbpSerializer.BlankElement("myFriends")); diff --git a/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs index 5336e43b..541312dc 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs @@ -40,7 +40,7 @@ public class RoomVisualizerController : ControllerBase #if !DEBUG return this.NotFound(); #else - RoomHelper.Rooms.DeleteAll(); + RoomHelper.Rooms.RemoveAll(); return this.Redirect("/debug/roomVisualizer"); #endif } diff --git a/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs b/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs index a5426d88..1b98743e 100644 --- a/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs +++ b/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs @@ -7,21 +7,5 @@ namespace LBPUnion.ProjectLighthouse.Extensions; [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] public static class RedisCollectionExtensions { - public static void DeleteAll(this IRedisCollection collection, Func predicate) - { - foreach (T item in collection) - { - if (!predicate.Invoke(item)) continue; - - collection.DeleteSync(item); - } - } - - public static void DeleteAll(this IRedisCollection collection) - { - foreach (T item in collection) - { - collection.DeleteSync(item); - } - } + } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/RoomHelper.cs b/ProjectLighthouse/Helpers/RoomHelper.cs index 84e98c8f..74412e5a 100644 --- a/ProjectLighthouse/Helpers/RoomHelper.cs +++ b/ProjectLighthouse/Helpers/RoomHelper.cs @@ -5,18 +5,18 @@ using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.StorableLists; +using LBPUnion.ProjectLighthouse.StorableLists.Stores; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Match; using LBPUnion.ProjectLighthouse.Types.Profiles; -using Redis.OM; -using Redis.OM.Searching; namespace LBPUnion.ProjectLighthouse.Helpers; public class RoomHelper { - public static readonly IRedisCollection Rooms = Redis.GetRooms(); + public static readonly StorableList Rooms = RoomStore.GetRooms(); public static readonly RoomSlot PodSlot = new() { @@ -169,7 +169,7 @@ public class RoomHelper }; CleanupRooms(room.HostId, room); - lock(Rooms) Rooms.Insert(room); + lock(Rooms) Rooms.Add(room); Logger.LogInfo($"Created room (id: {room.RoomId}) for host {room.HostId}", LogArea.Match); return room; @@ -201,7 +201,6 @@ public class RoomHelper [SuppressMessage("ReSharper", "InvertIf")] public static void CleanupRooms(int? hostId = null, Room? newRoom = null, Database? database = null) { -// return; lock(Rooms) { int roomCountBeforeCleanup = Rooms.Count(); @@ -220,7 +219,7 @@ public class RoomHelper if (hostId != null) try { - Rooms.DeleteAll(r => r.HostId == hostId); + Rooms.RemoveAll(r => r.HostId == hostId); } catch { @@ -236,8 +235,8 @@ public class RoomHelper foreach (int newRoomPlayer in newRoom.PlayerIds) room.PlayerIds.RemoveAll(p => p == newRoomPlayer); } - Rooms.DeleteAll(r => r.PlayerIds.Count == 0); // Remove empty rooms - Rooms.DeleteAll(r => r.PlayerIds.Count > 4); // Remove obviously bogus rooms + Rooms.RemoveAll(r => r.PlayerIds.Count == 0); // Remove empty rooms + Rooms.RemoveAll(r => r.PlayerIds.Count > 4); // Remove obviously bogus rooms int roomCountAfterCleanup = Rooms.Count(); diff --git a/ProjectLighthouse/Redis.cs b/ProjectLighthouse/Redis.cs deleted file mode 100644 index 79b53131..00000000 --- a/ProjectLighthouse/Redis.cs +++ /dev/null @@ -1,77 +0,0 @@ -#nullable enable -using System; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Extensions; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Match; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Redis.OM; -using Redis.OM.Contracts; -using Redis.OM.Searching; - -namespace LBPUnion.ProjectLighthouse; - -public static class Redis -{ - private static readonly RedisConnectionProvider provider; - - static Redis() - { - provider = new RedisConnectionProvider(ServerConfiguration.Instance.RedisConnectionString); - } - - private static bool initialized; - public static async Task Initialize() - { - if (initialized) throw new InvalidOperationException("Redis has already been initialized."); - - IRedisConnection connection = getConnection(); - - string pong = (await connection.ExecuteAsync("PING")).ToString(CultureInfo.InvariantCulture); - if (pong != "PONG") - { - Logger.LogError("Could not ping, ping returned " + pong, LogArea.Redis); - return; - } - - await connection.RecreateIndexAsync(typeof(Room)); - await connection.RecreateIndexAsync(typeof(UserFriendStore)); - - initialized = true; - Logger.LogSuccess("Initialized Redis.", LogArea.Redis); - } - - private static IRedisConnection getConnection() - { - Logger.LogDebug("Getting a Redis connection", LogArea.Redis); - return provider.Connection; - } - - public static IRedisCollection GetRooms() => provider.RedisCollection(); - - private static IRedisCollection userFriendStoreCollection => provider.RedisCollection(); - - public static UserFriendStore? GetUserFriendStore(int userId) => - userFriendStoreCollection.FirstOrDefault(s => s.UserId == userId); - - public static UserFriendStore CreateUserFriendStore(int userId) - { - UserFriendStore friendStore = new() - { - UserId = userId, - }; - - userFriendStoreCollection.Insert(friendStore); - return friendStore; - } - - public static void UpdateFriendStore(UserFriendStore friendStore) - { - userFriendStoreCollection.UpdateSync(friendStore); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/StartupTasks.cs b/ProjectLighthouse/StartupTasks.cs index 6958a1c2..f9a3881d 100644 --- a/ProjectLighthouse/StartupTasks.cs +++ b/ProjectLighthouse/StartupTasks.cs @@ -4,6 +4,7 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging.Loggers; using LBPUnion.ProjectLighthouse.Startup; +using LBPUnion.ProjectLighthouse.StorableLists; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.EntityFrameworkCore; @@ -77,7 +78,7 @@ public static class StartupTasks RoomHelper.StartCleanupThread(); Logger.LogInfo("Initializing Redis...", LogArea.Startup); - Redis.Initialize().Wait(); + RedisDatabase.Initialize().Wait(); stopwatch.Stop(); Logger.LogSuccess($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LogArea.Startup); diff --git a/ProjectLighthouse/StorableLists/NormalStorableList.cs b/ProjectLighthouse/StorableLists/NormalStorableList.cs new file mode 100644 index 00000000..bba3e62d --- /dev/null +++ b/ProjectLighthouse/StorableLists/NormalStorableList.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public class NormalStorableList : StorableList +{ + private readonly List list; + public NormalStorableList(List normalCollection) : base(normalCollection) + { + this.list = normalCollection; + } + + public override void Add(T item) + { + this.list.Add(item); + } + + public override Task AddAsync(T item) + { + this.list.Add(item); + return Task.CompletedTask; + } + + public override void RemoveAll(Predicate predicate) + { + this.list.RemoveAll(predicate); + } + + public override Task RemoveAllAsync(Predicate predicate) + { + this.list.RemoveAll(predicate); + return Task.CompletedTask; + } + + public override Task RemoveAllAsync() + { + this.list.RemoveAll(_ => true); + return Task.CompletedTask; + } + + public override void RemoveAll() + { + this.list.RemoveAll(_ => true); + } + + public override void Remove(T item) + { + this.list.Remove(item); + } + + public override Task RemoveAsync(T item) + { + this.list.Remove(item); + return Task.CompletedTask; + } + public override void Update(T item) {} + public override Task UpdateAsync(T item) => Task.CompletedTask; +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/RedisDatabase.cs b/ProjectLighthouse/StorableLists/RedisDatabase.cs new file mode 100644 index 00000000..d686b8cb --- /dev/null +++ b/ProjectLighthouse/StorableLists/RedisDatabase.cs @@ -0,0 +1,65 @@ +#nullable enable +using System; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Match; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Redis.OM; +using Redis.OM.Contracts; +using Redis.OM.Searching; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public static class RedisDatabase +{ + private static readonly RedisConnectionProvider provider; + + static RedisDatabase() + { + provider = new RedisConnectionProvider(ServerConfiguration.Instance.RedisConnectionString); + } + + public static bool Initialized { get; private set; } + public static async Task Initialize() + { + if (Initialized) throw new InvalidOperationException("Redis has already been initialized."); + + try + { + IRedisConnection connection = getConnection(); + + string pong = (await connection.ExecuteAsync("PING")).ToString(CultureInfo.InvariantCulture); + if (pong != "PONG") + { + Logger.LogError("Could not ping, ping returned " + pong, + LogArea.Redis); + return; + } + + await connection.RecreateIndexAsync(typeof(Room)); + await connection.RecreateIndexAsync(typeof(UserFriendData)); + } + catch(Exception e) + { + Logger.LogError("Could not initialize Redis:\n" + e, LogArea.Redis); + return; + } + + Initialized = true; + Logger.LogSuccess("Initialized Redis.", LogArea.Redis); + } + + private static IRedisConnection getConnection() + { + Logger.LogDebug("Getting a Redis connection", LogArea.Redis); + return provider.Connection; + } + + public static IRedisCollection UserFriendStoreCollection => provider.RedisCollection(); + + internal static IRedisCollection GetRooms() => provider.RedisCollection(); +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/RedisStorableList.cs b/ProjectLighthouse/StorableLists/RedisStorableList.cs new file mode 100644 index 00000000..c6938f28 --- /dev/null +++ b/ProjectLighthouse/StorableLists/RedisStorableList.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Redis.OM.Searching; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public class RedisStorableList : StorableList +{ + private readonly IRedisCollection redisNormalCollection; + public RedisStorableList(IRedisCollection normalCollection) : base(normalCollection) + { + this.redisNormalCollection = normalCollection; + } + + public override Task AddAsync(T item) => this.redisNormalCollection.InsertAsync(item); + public override void Add(T item) + { + this.redisNormalCollection.Insert(item); + } + + public override Task RemoveAsync(T item) => this.redisNormalCollection.Delete(item); + public override void Remove(T item) + { + this.redisNormalCollection.DeleteSync(item); + } + + public override Task UpdateAsync(T item) => this.redisNormalCollection.Update(item); + public override void Update(T item) + { + this.redisNormalCollection.UpdateSync(item); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/StorableList.cs b/ProjectLighthouse/StorableLists/StorableList.cs new file mode 100644 index 00000000..ebd34d28 --- /dev/null +++ b/ProjectLighthouse/StorableLists/StorableList.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public abstract class StorableList : IEnumerable +{ + private protected readonly IEnumerable NormalCollection; + protected StorableList(IEnumerable normalCollection) + { + this.NormalCollection = normalCollection; + } + + public abstract void Add(T item); + public abstract Task AddAsync(T item); + public abstract void Remove(T item); + public abstract Task RemoveAsync(T item); + public abstract void Update(T item); + public abstract Task UpdateAsync(T item); + + public virtual void RemoveAll(Predicate predicate) + { + foreach (T item in this.NormalCollection) + { + if (!predicate.Invoke(item)) continue; + + this.Remove(item); + } + } + + public virtual async Task RemoveAllAsync(Predicate predicate) + { + foreach (T item in this.NormalCollection) + { + if (!predicate.Invoke(item)) continue; + + await this.RemoveAsync(item); + } + } + + public virtual void RemoveAll() + { + foreach (T item in this.NormalCollection) + { + this.Remove(item); + } + } + + public virtual async Task RemoveAllAsync() + { + foreach (T item in this.NormalCollection) + { + await this.RemoveAsync(item); + } + } + + public IEnumerator GetEnumerator() => this.NormalCollection.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/Stores/RoomStore.cs b/ProjectLighthouse/StorableLists/Stores/RoomStore.cs new file mode 100644 index 00000000..bcc8676e --- /dev/null +++ b/ProjectLighthouse/StorableLists/Stores/RoomStore.cs @@ -0,0 +1,21 @@ +#nullable enable +using System.Collections.Generic; +using LBPUnion.ProjectLighthouse.Types.Match; + +namespace LBPUnion.ProjectLighthouse.StorableLists.Stores; + +public static class RoomStore +{ + private static List? rooms; + + public static StorableList GetRooms() + { + if (RedisDatabase.Initialized) + { + return new RedisStorableList(RedisDatabase.GetRooms()); + } + + rooms ??= new List(); + return new NormalStorableList(rooms); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs b/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs new file mode 100644 index 00000000..2884691a --- /dev/null +++ b/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs @@ -0,0 +1,40 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.StorableLists.Stores; + +public static class UserFriendStore +{ + private static List? friendDataStore; + + private static StorableList getStorableFriendData() + { + if (RedisDatabase.Initialized) + { + return new RedisStorableList(RedisDatabase.UserFriendStoreCollection); + } + + friendDataStore ??= new List(); + return new NormalStorableList(friendDataStore); + } + + public static UserFriendData? GetUserFriendData(int userId) => getStorableFriendData().FirstOrDefault(s => s.UserId == userId); + + public static UserFriendData CreateUserFriendData(int userId) + { + UserFriendData friendData = new() + { + UserId = userId, + }; + + getStorableFriendData().Add(friendData); + return friendData; + } + + public static void UpdateFriendData(UserFriendData friendData) + { + getStorableFriendData().Update(friendData); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/UserFriendStore.cs b/ProjectLighthouse/Types/UserFriendData.cs similarity index 95% rename from ProjectLighthouse/Types/UserFriendStore.cs rename to ProjectLighthouse/Types/UserFriendData.cs index d71fe7bf..ee0224cf 100644 --- a/ProjectLighthouse/Types/UserFriendStore.cs +++ b/ProjectLighthouse/Types/UserFriendData.cs @@ -6,7 +6,7 @@ namespace LBPUnion.ProjectLighthouse.Types; [SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] [Document(StorageType = StorageType.Json)] -public class UserFriendStore +public class UserFriendData { private int userId; public int UserId {