From 35f50f5f8c637efec20194bcae17cd80f76e558a Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 18 Jan 2022 23:09:02 -0500 Subject: [PATCH] The Great Formatting of 2022 --- .../AuthenticationTests.cs | 91 ++- .../DatabaseTests.cs | 25 +- .../MatchTests.cs | 71 +-- .../SlotTests.cs | 119 ++-- .../UploadTests.cs | 83 ++- .../AdminTests.cs | 79 ++- .../AuthenticationTests.cs | 171 +++-- .../LighthouseWebTest.cs | 61 +- .../RegisterTests.cs | 93 ++- .../DatabaseFactAttribute.cs | 34 +- ProjectLighthouse.Tests/FileTypeTests.cs | 89 ++- .../LighthouseServerTest.cs | 129 ++-- ProjectLighthouse.Tests/SerializerTests.cs | 63 +- .../ClientConfigurationController.cs | 79 ++- .../Controllers/CollectionController.cs | 183 +++--- .../Controllers/CommentController.cs | 103 ++- .../Controllers/DeveloperController.cs | 15 +- .../Controllers/EnterLevelController.cs | 201 +++--- .../Controllers/FriendsController.cs | 141 ++-- .../Controllers/LevelTagsController.cs | 33 +- .../Controllers/ListController.cs | 403 ++++++------ .../Controllers/LoginController.cs | 237 ++++--- .../Controllers/MatchController.cs | 191 +++--- .../Controllers/MessageController.cs | 103 ++- .../Controllers/NewsController.cs | 59 +- .../Controllers/PhotosController.cs | 234 ++++--- .../Controllers/PublishController.cs | 273 ++++---- .../Controllers/ResourcesController.cs | 129 ++-- .../Controllers/ReviewController.cs | 549 ++++++++-------- .../Controllers/ScoreController.cs | 252 ++++---- .../Controllers/SearchController.cs | 65 +- .../Controllers/SlotsController.cs | 601 +++++++++--------- .../Controllers/StatisticsController.cs | 59 +- .../Controllers/StoreController.cs | 17 +- .../Controllers/UserController.cs | 319 +++++----- .../Website/Admin/AdminSlotController.cs | 89 ++- .../Website/Admin/AdminUserController.cs | 45 +- .../Website/Debug/RoomVisualizerController.cs | 5 - .../ExternalAuth/AuthenticationController.cs | 137 ++-- .../ExternalAuth/AutoApprovalController.cs | 93 ++- .../Controllers/Website/SlotPageController.cs | 107 ++-- .../Controllers/Website/UserPageController.cs | 59 +- ProjectLighthouse/Database.cs | 547 ++++++++-------- .../FakeRemoteIPAddressMiddleware.cs | 27 +- ProjectLighthouse/Helpers/BinaryHelper.cs | 95 ++- ProjectLighthouse/Helpers/CollectionHelper.cs | 25 +- .../Helpers/DeniedAuthenticationHelper.cs | 51 +- ProjectLighthouse/Helpers/EulaHelper.cs | 9 +- .../Helpers/Extensions/ExceptionExtensions.cs | 33 +- .../Helpers/Extensions/RequestExtensions.cs | 17 +- .../Helpers/Extensions/StringExtensions.cs | 9 +- ProjectLighthouse/Helpers/FileHelper.cs | 175 +++-- ProjectLighthouse/Helpers/FriendHelper.cs | 15 +- .../Helpers/GameVersionHelper.cs | 177 +++--- ProjectLighthouse/Helpers/HashHelper.cs | 139 ++-- ProjectLighthouse/Helpers/InfluxHelper.cs | 63 +- .../Helpers/LastContactHelper.cs | 39 +- .../Helpers/MaintenanceHelper.cs | 98 ++- ProjectLighthouse/Helpers/MatchHelper.cs | 115 ++-- ProjectLighthouse/Helpers/RoomHelper.cs | 311 +++++---- ProjectLighthouse/Helpers/StatisticsHelper.cs | 17 +- ProjectLighthouse/Helpers/TimeHelper.cs | 11 +- ProjectLighthouse/Helpers/TimestampHelper.cs | 13 +- ProjectLighthouse/Helpers/VersionHelper.cs | 121 ++-- .../Logging/AspNetToKettuLogger.cs | 25 +- .../Logging/AspNetToKettuLoggerProvider.cs | 19 +- ProjectLighthouse/Logging/InfluxLogger.cs | 25 +- .../Logging/LighthouseFileLogger.cs | 37 +- ProjectLighthouse/Logging/LoggerLevels.cs | 125 ++-- ProjectLighthouse/Logging/NullScope.cs | 21 +- .../Maintenance/Commands/CreateUserCommand.cs | 71 +-- .../Maintenance/Commands/DeleteUserCommand.cs | 59 +- .../Commands/MakeUserAdminCommand.cs | 61 +- .../Commands/ResetPasswordCommand.cs | 67 +- .../Commands/WipeTokensForUserCommand.cs | 59 +- ProjectLighthouse/Maintenance/ICommand.cs | 19 +- .../Maintenance/IMaintenanceJob.cs | 13 +- .../CleanupBrokenPhotosMaintenanceJob.cs | 141 ++-- .../CleanupUnusedLocationsMaintenanceJob.cs | 39 +- .../DeleteAllTokensMaintenanceJob.cs | 25 +- ...lBrokenPlayerRequirementsMaintenanceJob.cs | 39 +- .../Pages/Admin/AdminBanUserPage.cshtml.cs | 4 +- .../Pages/Admin/AdminPanelUsersPage.cshtml.cs | 4 +- .../ExternalAuth/AuthenticationPage.cshtml.cs | 41 +- ...anageUserApprovedIpAddressesPage.cshtml.cs | 27 +- ProjectLighthouse/Pages/LandingPage.cshtml.cs | 49 +- .../Pages/Layouts/BaseLayout.cshtml.cs | 67 +- ProjectLighthouse/Pages/LoginForm.cshtml.cs | 129 ++-- ProjectLighthouse/Pages/LogoutPage.cshtml.cs | 25 +- .../Pages/PasswordResetPage.cshtml.cs | 73 ++- .../Pages/PasswordResetRequiredPage.cshtml.cs | 27 +- ProjectLighthouse/Pages/PhotosPage.cshtml.cs | 56 +- .../Pages/RegisterForm.cshtml.cs | 119 ++-- ProjectLighthouse/Pages/SlotPage.cshtml.cs | 27 +- ProjectLighthouse/Pages/SlotsPage.cshtml | 4 +- ProjectLighthouse/Pages/SlotsPage.cshtml.cs | 56 +- ProjectLighthouse/Pages/UserPage.cshtml | 4 +- ProjectLighthouse/Pages/UserPage.cshtml.cs | 59 +- ProjectLighthouse/Program.cs | 149 +++-- .../Serialization/JsonOutputFormatter.cs | 13 +- .../Serialization/LbpSerializer.cs | 45 +- .../Serialization/XmlOutputFormatter.cs | 13 +- ProjectLighthouse/Startup.cs | 281 ++++---- ProjectLighthouse/TestStartup.cs | 21 +- .../Types/AuthenticationAttempt.cs | 23 +- .../Types/Categories/Category.cs | 113 ++-- .../Types/Categories/CategoryWithUser.cs | 5 +- .../Types/Categories/CustomCategory.cs | 60 +- .../Types/Categories/DatabaseCategory.cs | 29 +- .../Types/Categories/HeartedCategory.cs | 23 +- .../Types/Categories/NewestLevelsCategory.cs | 25 +- .../Types/Categories/QueueCategory.cs | 25 +- .../Types/Categories/TeamPicksCategory.cs | 25 +- ProjectLighthouse/Types/DeletedBy.cs | 23 +- ProjectLighthouse/Types/Files/LbpFile.cs | 49 +- ProjectLighthouse/Types/Files/LbpFileType.cs | 27 +- ProjectLighthouse/Types/GameToken.cs | 33 +- ProjectLighthouse/Types/GameVersion.cs | 29 +- ProjectLighthouse/Types/HeartedProfile.cs | 33 +- .../Types/Levels/HeartedLevel.cs | 25 +- ProjectLighthouse/Types/Levels/LevelTags.cs | 169 +++-- ProjectLighthouse/Types/Levels/QueuedLevel.cs | 25 +- ProjectLighthouse/Types/Levels/RatedLevel.cs | 29 +- ProjectLighthouse/Types/Levels/Slot.cs | 481 +++++++------- ProjectLighthouse/Types/Levels/SlotType.cs | 21 +- .../Types/Levels/VisitedLevel.cs | 33 +- ProjectLighthouse/Types/LoginData.cs | 67 +- ProjectLighthouse/Types/LoginResult.cs | 31 +- ProjectLighthouse/Types/Match/CreateRoom.cs | 57 +- ProjectLighthouse/Types/Match/FindBestRoom.cs | 37 +- .../Types/Match/FindBestRoomResponse.cs | 23 +- ProjectLighthouse/Types/Match/IMatchData.cs | 9 +- ProjectLighthouse/Types/Match/NatType.cs | 13 +- ProjectLighthouse/Types/Match/Player.cs | 21 +- ProjectLighthouse/Types/Match/Room.cs | 65 +- ProjectLighthouse/Types/Match/RoomSlot.cs | 11 +- ProjectLighthouse/Types/Match/RoomState.cs | 49 +- .../Types/Match/UpdateMyPlayerData.cs | 13 +- .../Types/Match/UpdatePlayersInRoom.cs | 13 +- ProjectLighthouse/Types/News/NewsEntry.cs | 45 +- ProjectLighthouse/Types/News/NewsImage.cs | 17 +- ProjectLighthouse/Types/PageNavigationItem.cs | 21 +- ProjectLighthouse/Types/Photo.cs | 129 ++-- ProjectLighthouse/Types/PhotoSubject.cs | 61 +- ProjectLighthouse/Types/Platform.cs | 13 +- .../Types/Profiles/ClientsConnected.cs | 41 +- ProjectLighthouse/Types/Profiles/Comment.cs | 59 +- .../Types/Profiles/LastContact.cs | 15 +- ProjectLighthouse/Types/Profiles/Location.cs | 31 +- ProjectLighthouse/Types/Profiles/NPData.cs | 25 +- ProjectLighthouse/Types/Profiles/Pins.cs | 19 +- ProjectLighthouse/Types/ResourceList.cs | 15 +- .../Types/Reviews/RatedReview.cs | 27 +- ProjectLighthouse/Types/Reviews/Review.cs | 146 +++-- ProjectLighthouse/Types/Score.cs | 101 ++- .../Types/Settings/PrivacySettings.cs | 25 +- .../Types/Settings/ServerSettings.cs | 207 +++--- .../Types/Settings/ServerStatics.cs | 47 +- ProjectLighthouse/Types/User.cs | 470 +++++++------- .../Types/UserApprovedIpAddress.cs | 19 +- ProjectLighthouse/Types/WebToken.cs | 17 +- README.md | 20 +- 162 files changed, 6609 insertions(+), 6809 deletions(-) diff --git a/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs index 05f0c413..912c3e1a 100644 --- a/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs @@ -6,62 +6,61 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests +namespace ProjectLighthouse.Tests.GameApiTests; + +public class AuthenticationTests : LighthouseServerTest { - public class AuthenticationTests : LighthouseServerTest + [Fact] + public async Task ShouldReturnErrorOnNoPostData() { - [Fact] - public async Task ShouldReturnErrorOnNoPostData() - { - HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!); - Assert.False(response.IsSuccessStatusCode); - #if NET6_0_OR_GREATER - Assert.True(response.StatusCode == HttpStatusCode.BadRequest); - #else + HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!); + Assert.False(response.IsSuccessStatusCode); + #if NET6_0_OR_GREATER + Assert.True(response.StatusCode == HttpStatusCode.BadRequest); + #else Assert.True(response.StatusCode == HttpStatusCode.NotAcceptable); - #endif - } + #endif + } - [DatabaseFact] - public async Task ShouldReturnWithValidData() - { - HttpResponseMessage response = await this.AuthenticateResponse(); - Assert.True(response.IsSuccessStatusCode); - string responseContent = await response.Content.ReadAsStringAsync(); - Assert.Contains("MM_AUTH=", responseContent); - Assert.Contains(ServerStatics.ServerName, responseContent); - } + [DatabaseFact] + public async Task ShouldReturnWithValidData() + { + HttpResponseMessage response = await this.AuthenticateResponse(); + Assert.True(response.IsSuccessStatusCode); + string responseContent = await response.Content.ReadAsStringAsync(); + Assert.Contains("MM_AUTH=", responseContent); + Assert.Contains(ServerStatics.ServerName, responseContent); + } - [DatabaseFact] - public async Task CanSerializeBack() - { - LoginResult loginResult = await this.Authenticate(); + [DatabaseFact] + public async Task CanSerializeBack() + { + LoginResult loginResult = await this.Authenticate(); - Assert.NotNull(loginResult); - Assert.NotNull(loginResult.AuthTicket); - Assert.NotNull(loginResult.LbpEnvVer); + Assert.NotNull(loginResult); + Assert.NotNull(loginResult.AuthTicket); + Assert.NotNull(loginResult.LbpEnvVer); - Assert.Contains("MM_AUTH=", loginResult.AuthTicket); - Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer); - } + Assert.Contains("MM_AUTH=", loginResult.AuthTicket); + Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer); + } - [DatabaseFact] - public async Task CanUseToken() - { - LoginResult loginResult = await this.Authenticate(); + [DatabaseFact] + public async Task CanUseToken() + { + LoginResult loginResult = await this.Authenticate(); - HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/1", loginResult.AuthTicket); - string responseContent = await response.Content.ReadAsStringAsync(); + HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/1", loginResult.AuthTicket); + string responseContent = await response.Content.ReadAsStringAsync(); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - } + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + } - [DatabaseFact] - public async Task ShouldReturnForbiddenWhenNotAuthenticated() - { - HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce"); - Assert.False(response.IsSuccessStatusCode); - Assert.True(response.StatusCode == HttpStatusCode.Forbidden); - } + [DatabaseFact] + public async Task ShouldReturnForbiddenWhenNotAuthenticated() + { + HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce"); + Assert.False(response.IsSuccessStatusCode); + Assert.True(response.StatusCode == HttpStatusCode.Forbidden); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs index 86b26ccd..5aa82220 100644 --- a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs @@ -6,23 +6,22 @@ using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests +namespace ProjectLighthouse.Tests.GameApiTests; + +public class DatabaseTests : LighthouseServerTest { - public class DatabaseTests : LighthouseServerTest + [DatabaseFact] + public async Task CanCreateUserTwice() { - [DatabaseFact] - public async Task CanCreateUserTwice() - { - await using Database database = new(); - int rand = new Random().Next(); + await using Database database = new(); + int rand = new Random().Next(); - User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken()); - User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken()); + User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken()); + User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken()); - Assert.NotNull(userA); - Assert.NotNull(userB); + Assert.NotNull(userA); + Assert.NotNull(userB); - await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user - } + await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs b/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs index c7e83feb..d5ac8e69 100644 --- a/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs @@ -8,55 +8,54 @@ using LBPUnion.ProjectLighthouse.Tests; using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests +namespace ProjectLighthouse.Tests.GameApiTests; + +public class MatchTests : LighthouseServerTest { - public class MatchTests : LighthouseServerTest + private static readonly SemaphoreSlim semaphore = new(1, 1); + + [DatabaseFact] + public async Task ShouldRejectEmptyData() { - private static readonly SemaphoreSlim semaphore = new(1, 1); + LoginResult loginResult = await this.Authenticate(); + await semaphore.WaitAsync(); - [DatabaseFact] - public async Task ShouldRejectEmptyData() - { - LoginResult loginResult = await this.Authenticate(); - await semaphore.WaitAsync(); + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty(), loginResult.AuthTicket); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty(), loginResult.AuthTicket); + semaphore.Release(); + Assert.False(result.IsSuccessStatusCode); + } - semaphore.Release(); - Assert.False(result.IsSuccessStatusCode); - } + [DatabaseFact] + public async Task ShouldReturnOk() + { + LoginResult loginResult = await this.Authenticate(); + await semaphore.WaitAsync(); - [DatabaseFact] - public async Task ShouldReturnOk() - { - LoginResult loginResult = await this.Authenticate(); - await semaphore.WaitAsync(); + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest + ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest - ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); + semaphore.Release(); + Assert.True(result.IsSuccessStatusCode); + } - semaphore.Release(); - Assert.True(result.IsSuccessStatusCode); - } + [DatabaseFact] + public async Task ShouldIncrementPlayerCount() + { + LoginResult loginResult = await this.Authenticate(new Random().Next()); - [DatabaseFact] - public async Task ShouldIncrementPlayerCount() - { - LoginResult loginResult = await this.Authenticate(new Random().Next()); + await semaphore.WaitAsync(); - await semaphore.WaitAsync(); + int oldPlayerCount = await StatisticsHelper.RecentMatches(); - int oldPlayerCount = await StatisticsHelper.RecentMatches(); + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest + ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest - ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); + Assert.True(result.IsSuccessStatusCode); - Assert.True(result.IsSuccessStatusCode); + int playerCount = await StatisticsHelper.RecentMatches(); - int playerCount = await StatisticsHelper.RecentMatches(); - - semaphore.Release(); - Assert.Equal(oldPlayerCount + 1, playerCount); - } + semaphore.Release(); + Assert.Equal(oldPlayerCount + 1, playerCount); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs b/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs index 568a9ed4..dcd4779d 100644 --- a/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs @@ -9,85 +9,84 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Profiles; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests +namespace ProjectLighthouse.Tests.GameApiTests; + +public class SlotTests : LighthouseServerTest { - public class SlotTests : LighthouseServerTest + [DatabaseFact] + public async Task ShouldOnlyShowUsersLevels() { - [DatabaseFact] - public async Task ShouldOnlyShowUsersLevels() + await using Database database = new(); + + Random r = new(); + + User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); + User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); + + Location l = new() { - await using Database database = new(); + X = 0, + Y = 0, + }; + database.Locations.Add(l); + await database.SaveChangesAsync(); - Random r = new(); + Slot slotA = new() + { + Creator = userA, + CreatorId = userA.UserId, + Name = "slotA", + Location = l, + LocationId = l.Id, + ResourceCollection = "", + }; - User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); - User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); + Slot slotB = new() + { + Creator = userB, + CreatorId = userB.UserId, + Name = "slotB", + Location = l, + LocationId = l.Id, + ResourceCollection = "", + }; - Location l = new() - { - X = 0, - Y = 0, - }; - database.Locations.Add(l); - await database.SaveChangesAsync(); + database.Slots.Add(slotA); + database.Slots.Add(slotB); - Slot slotA = new() - { - Creator = userA, - CreatorId = userA.UserId, - Name = "slotA", - Location = l, - LocationId = l.Id, - ResourceCollection = "", - }; - - Slot slotB = new() - { - Creator = userB, - CreatorId = userB.UserId, - Name = "slotB", - Location = l, - LocationId = l.Id, - ResourceCollection = "", - }; - - database.Slots.Add(slotA); - database.Slots.Add(slotB); - - await database.SaveChangesAsync(); + await database.SaveChangesAsync(); // XmlSerializer serializer = new(typeof(Slot)); // Slot slot = (Slot)serializer.Deserialize(new StringReader(bodyString)); - LoginResult loginResult = await this.Authenticate(); + LoginResult loginResult = await this.Authenticate(); - HttpResponseMessage respMessageA = await this.AuthenticatedRequest - ($"LITTLEBIGPLANETPS3_XML/slots/by?u={userA.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket); - HttpResponseMessage respMessageB = await this.AuthenticatedRequest - ($"LITTLEBIGPLANETPS3_XML/slots/by?u={userB.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket); + HttpResponseMessage respMessageA = await this.AuthenticatedRequest + ($"LITTLEBIGPLANETPS3_XML/slots/by?u={userA.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket); + HttpResponseMessage respMessageB = await this.AuthenticatedRequest + ($"LITTLEBIGPLANETPS3_XML/slots/by?u={userB.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket); - Assert.True(respMessageA.IsSuccessStatusCode); - Assert.True(respMessageB.IsSuccessStatusCode); + Assert.True(respMessageA.IsSuccessStatusCode); + Assert.True(respMessageB.IsSuccessStatusCode); - string respA = await respMessageA.Content.ReadAsStringAsync(); - string respB = await respMessageB.Content.ReadAsStringAsync(); + string respA = await respMessageA.Content.ReadAsStringAsync(); + string respB = await respMessageB.Content.ReadAsStringAsync(); - Assert.False(string.IsNullOrEmpty(respA)); - Assert.False(string.IsNullOrEmpty(respB)); + Assert.False(string.IsNullOrEmpty(respA)); + Assert.False(string.IsNullOrEmpty(respB)); - Assert.NotEqual(respA, respB); - Assert.DoesNotContain(respA, "slotB"); - Assert.DoesNotContain(respB, "slotA"); + Assert.NotEqual(respA, respB); + Assert.DoesNotContain(respA, "slotB"); + Assert.DoesNotContain(respB, "slotA"); - // Cleanup + // Cleanup - database.Slots.Remove(slotA); - database.Slots.Remove(slotB); + database.Slots.Remove(slotA); + database.Slots.Remove(slotB); - await database.RemoveUser(userA); - await database.RemoveUser(userB); + await database.RemoveUser(userA); + await database.RemoveUser(userB); - await database.SaveChangesAsync(); - } + await database.SaveChangesAsync(); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs b/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs index faac8aa9..8f788617 100644 --- a/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs @@ -6,54 +6,53 @@ using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Tests; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests +namespace ProjectLighthouse.Tests.GameApiTests; + +public class UploadTests : LighthouseServerTest { - public class UploadTests : LighthouseServerTest + public UploadTests() { - public UploadTests() - { - string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r"); - if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true); - } + string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r"); + if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true); + } - [Fact] - public async Task ShouldNotAcceptScript() - { - HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff"); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - Assert.False(response.IsSuccessStatusCode); - } + [Fact] + public async Task ShouldNotAcceptScript() + { + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff"); + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + Assert.False(response.IsSuccessStatusCode); + } - [Fact] - public async Task ShouldNotAcceptFarc() - { - HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc"); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - Assert.False(response.IsSuccessStatusCode); - } + [Fact] + public async Task ShouldNotAcceptFarc() + { + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc"); + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + Assert.False(response.IsSuccessStatusCode); + } - [Fact] - public async Task ShouldNotAcceptGarbage() - { - HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin"); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - Assert.False(response.IsSuccessStatusCode); - } + [Fact] + public async Task ShouldNotAcceptGarbage() + { + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin"); + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + Assert.False(response.IsSuccessStatusCode); + } - [Fact] - public async Task ShouldAcceptTexture() - { - HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex"); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - Assert.True(response.IsSuccessStatusCode); - } + [Fact] + public async Task ShouldAcceptTexture() + { + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex"); + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + Assert.True(response.IsSuccessStatusCode); + } - [Fact] - public async Task ShouldAcceptLevel() - { - HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl"); - Assert.False(response.StatusCode == HttpStatusCode.Forbidden); - Assert.True(response.IsSuccessStatusCode); - } + [Fact] + public async Task ShouldAcceptLevel() + { + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl"); + Assert.False(response.StatusCode == HttpStatusCode.Forbidden); + Assert.True(response.IsSuccessStatusCode); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs index 83d2118b..f76fb47d 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs @@ -7,58 +7,57 @@ using LBPUnion.ProjectLighthouse.Types; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests +namespace ProjectLighthouse.Tests.WebsiteTests; + +public class AdminTests : LighthouseWebTest { - public class AdminTests : LighthouseWebTest + public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]"; + + [DatabaseFact] + public async Task ShouldShowAdminPanelButtonWhenAdmin() { - public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]"; + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); - [DatabaseFact] - public async Task ShouldShowAdminPanelButtonWhenAdmin() + WebToken webToken = new() { - await using Database database = new(); - Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; - WebToken webToken = new() - { - UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), - }; + database.WebTokens.Add(webToken); + user.IsAdmin = true; + await database.SaveChangesAsync(); - database.WebTokens.Add(webToken); - user.IsAdmin = true; - await database.SaveChangesAsync(); + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + this.Driver.Navigate().Refresh(); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); - this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); - this.Driver.Navigate().Refresh(); + Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); + } - Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); - } + [DatabaseFact] + public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); - [DatabaseFact] - public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin() + WebToken webToken = new() { - await using Database database = new(); - Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; - WebToken webToken = new() - { - UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), - }; + database.WebTokens.Add(webToken); + user.IsAdmin = false; + await database.SaveChangesAsync(); - database.WebTokens.Add(webToken); - user.IsAdmin = false; - await database.SaveChangesAsync(); + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + this.Driver.Navigate().Refresh(); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/"); - this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); - this.Driver.Navigate().Refresh(); - - Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); - } + Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs index 598969d9..1b482075 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs @@ -9,98 +9,97 @@ using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests +namespace ProjectLighthouse.Tests.WebsiteTests; + +public class AuthenticationTests : LighthouseWebTest { - public class AuthenticationTests : LighthouseWebTest + [DatabaseFact] + public async Task ShouldLoginWithPassword() { - [DatabaseFact] - public async Task ShouldLoginWithPassword() + await using Database database = new(); + Random random = new(); + + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password))); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + this.Driver.FindElement(By.Id("password")).SendKeys(password); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.NotNull(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldNotLoginWithNoPassword() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,")); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.Null(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldNotLoginWithWrongPassword() + { + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + + this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); + this.Driver.FindElement(By.Id("password")).SendKeys("nah man"); + + this.Driver.FindElement(By.Id("submit")).Click(); + + WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); + Assert.Null(webToken); + + await database.RemoveUser(user); + } + + [DatabaseFact] + public async Task ShouldLoginWithInjectedCookie() + { + const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b"; + + await using Database database = new(); + Random random = new(); + User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + + WebToken webToken = new() { - await using Database database = new(); - Random random = new(); + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password))); + database.WebTokens.Add(webToken); + await database.SaveChangesAsync(); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); + INavigation navigation = this.Driver.Navigate(); - this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); - this.Driver.FindElement(By.Id("password")).SendKeys(password); + navigation.GoToUrl(this.BaseAddress + "/"); + this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); + Assert.Throws(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath))); + navigation.Refresh(); + Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username); - this.Driver.FindElement(By.Id("submit")).Click(); - - WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); - Assert.NotNull(webToken); - - await database.RemoveUser(user); - } - - [DatabaseFact] - public async Task ShouldNotLoginWithNoPassword() - { - await using Database database = new(); - Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,")); - - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); - - this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); - - this.Driver.FindElement(By.Id("submit")).Click(); - - WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); - Assert.Null(webToken); - - await database.RemoveUser(user); - } - - [DatabaseFact] - public async Task ShouldNotLoginWithWrongPassword() - { - await using Database database = new(); - Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); - - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); - - this.Driver.FindElement(By.Id("text")).SendKeys(user.Username); - this.Driver.FindElement(By.Id("password")).SendKeys("nah man"); - - this.Driver.FindElement(By.Id("submit")).Click(); - - WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId); - Assert.Null(webToken); - - await database.RemoveUser(user); - } - - [DatabaseFact] - public async Task ShouldLoginWithInjectedCookie() - { - const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b"; - - await using Database database = new(); - Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); - - WebToken webToken = new() - { - UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), - }; - - database.WebTokens.Add(webToken); - await database.SaveChangesAsync(); - - INavigation navigation = this.Driver.Navigate(); - - navigation.GoToUrl(this.BaseAddress + "/"); - this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken)); - Assert.Throws(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath))); - navigation.Refresh(); - Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username); - - await database.RemoveUser(user); - } + await database.RemoveUser(user); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs b/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs index dc96a596..7e2352c6 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs @@ -7,44 +7,43 @@ using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests +namespace ProjectLighthouse.Tests.WebsiteTests; + +[Collection(nameof(LighthouseWebTest))] +public class LighthouseWebTest : IDisposable { - [Collection(nameof(LighthouseWebTest))] - public class LighthouseWebTest : IDisposable + public readonly string BaseAddress; + + public readonly IWebDriver Driver; + public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); + + public LighthouseWebTest() { - public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); - public readonly string BaseAddress; + this.WebHost.Start(); - public readonly IWebDriver Driver; + IServerAddressesFeature? serverAddressesFeature = this.WebHost.ServerFeatures.Get(); + if (serverAddressesFeature == null) throw new ArgumentNullException(); - public LighthouseWebTest() + this.BaseAddress = serverAddressesFeature.Addresses.First(); + + ChromeOptions chromeOptions = new(); + if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) { - this.WebHost.Start(); - - IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get(); - if (serverAddressesFeature == null) throw new ArgumentNullException(); - - this.BaseAddress = serverAddressesFeature.Addresses.First(); - - ChromeOptions chromeOptions = new(); - if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false")) - { - chromeOptions.AddArgument("headless"); - chromeOptions.AddArgument("no-sandbox"); - chromeOptions.AddArgument("disable-dev-shm-usage"); - Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); - } - - this.Driver = new ChromeDriver(chromeOptions); + chromeOptions.AddArgument("headless"); + chromeOptions.AddArgument("no-sandbox"); + chromeOptions.AddArgument("disable-dev-shm-usage"); + Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled."); } - public void Dispose() - { - this.Driver.Close(); - this.Driver.Dispose(); - this.WebHost.Dispose(); + this.Driver = new ChromeDriver(chromeOptions); + } - GC.SuppressFinalize(this); - } + public void Dispose() + { + this.Driver.Close(); + this.Driver.Dispose(); + this.WebHost.Dispose(); + + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs b/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs index 2c3cf855..42d8463d 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs @@ -9,76 +9,75 @@ using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests +namespace ProjectLighthouse.Tests.WebsiteTests; + +public class RegisterTests : LighthouseWebTest { - public class RegisterTests : LighthouseWebTest + [DatabaseFact] + public async Task ShouldRegister() { - [DatabaseFact] - public async Task ShouldRegister() - { - await using Database database = new(); + await using Database database = new(); - string username = "unitTestUser" + new Random().Next(); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); - this.Driver.FindElement(By.Id("text")).SendKeys(username); + this.Driver.FindElement(By.Id("text")).SendKeys(username); - this.Driver.FindElement(By.Id("password")).SendKeys(password); - this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); - this.Driver.FindElement(By.Id("submit")).Click(); + this.Driver.FindElement(By.Id("submit")).Click(); - User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); - Assert.NotNull(user); + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.NotNull(user); - await database.RemoveUser(user); - } + await database.RemoveUser(user); + } - [DatabaseFact] - public async Task ShouldNotRegisterWithMismatchingPasswords() - { - await using Database database = new(); + [DatabaseFact] + public async Task ShouldNotRegisterWithMismatchingPasswords() + { + await using Database database = new(); - string username = "unitTestUser" + new Random().Next(); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); - this.Driver.FindElement(By.Id("text")).SendKeys(username); + this.Driver.FindElement(By.Id("text")).SendKeys(username); - this.Driver.FindElement(By.Id("password")).SendKeys(password); - this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a"); + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a"); - this.Driver.FindElement(By.Id("submit")).Click(); + this.Driver.FindElement(By.Id("submit")).Click(); - User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); - Assert.Null(user); - } + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.Null(user); + } - [DatabaseFact] - public async Task ShouldNotRegisterWithTakenUsername() - { - await using Database database = new(); + [DatabaseFact] + public async Task ShouldNotRegisterWithTakenUsername() + { + await using Database database = new(); - string username = "unitTestUser" + new Random().Next(); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string username = "unitTestUser" + new Random().Next(); + string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); - await database.CreateUser(username, HashHelper.BCryptHash(password)); - User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); - Assert.NotNull(user); + await database.CreateUser(username, HashHelper.BCryptHash(password)); + User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); + Assert.NotNull(user); - this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); + this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); - this.Driver.FindElement(By.Id("text")).SendKeys(username); + this.Driver.FindElement(By.Id("text")).SendKeys(username); - this.Driver.FindElement(By.Id("password")).SendKeys(password); - this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); + this.Driver.FindElement(By.Id("password")).SendKeys(password); + this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password); - this.Driver.FindElement(By.Id("submit")).Click(); + this.Driver.FindElement(By.Id("submit")).Click(); - Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource); - } + Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs index 3089bdc8..e9e177f8 100644 --- a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs +++ b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs @@ -2,28 +2,22 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.EntityFrameworkCore; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests -{ - public sealed class DatabaseFactAttribute : FactAttribute - { - private static readonly object migrateLock = new(); +namespace LBPUnion.ProjectLighthouse.Tests; - public DatabaseFactAttribute() - { - ServerSettings.Instance = new ServerSettings(); - ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; - if (!ServerStatics.DbConnected) +public sealed class DatabaseFactAttribute : FactAttribute +{ + private static readonly object migrateLock = new(); + + public DatabaseFactAttribute() + { + ServerSettings.Instance = new ServerSettings(); + ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + if (!ServerStatics.DbConnected) this.Skip = "Database not available"; + else + lock(migrateLock) { - this.Skip = "Database not available"; + using Database database = new(); + database.Database.Migrate(); } - else - { - lock(migrateLock) - { - using Database database = new(); - database.Database.Migrate(); - } - } - } } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/FileTypeTests.cs b/ProjectLighthouse.Tests/FileTypeTests.cs index b7229aa2..aa0452de 100644 --- a/ProjectLighthouse.Tests/FileTypeTests.cs +++ b/ProjectLighthouse.Tests/FileTypeTests.cs @@ -4,58 +4,57 @@ using System.Text; using LBPUnion.ProjectLighthouse.Types.Files; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace LBPUnion.ProjectLighthouse.Tests; + +public class FileTypeTests { - public class FileTypeTests + [Fact] + public void ShouldRecognizeLevel() { - [Fact] - public void ShouldRecognizeLevel() - { - LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl")); - Assert.True(file.FileType == LbpFileType.Level); - } + LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl")); + Assert.True(file.FileType == LbpFileType.Level); + } - [Fact] - public void ShouldRecognizeScript() - { - LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff")); - Assert.True(file.FileType == LbpFileType.Script); - } + [Fact] + public void ShouldRecognizeScript() + { + LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff")); + Assert.True(file.FileType == LbpFileType.Script); + } - [Fact] - public void ShouldRecognizeTexture() - { - LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex")); - Assert.True(file.FileType == LbpFileType.Texture); - } + [Fact] + public void ShouldRecognizeTexture() + { + LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex")); + Assert.True(file.FileType == LbpFileType.Texture); + } - [Fact] - public void ShouldRecognizeFileArchive() - { - LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); - Assert.True(file.FileType == LbpFileType.FileArchive); - } + [Fact] + public void ShouldRecognizeFileArchive() + { + LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); + Assert.True(file.FileType == LbpFileType.FileArchive); + } - [Fact] - public void ShouldNotRecognizeFileArchiveAsScript() - { - LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); - Assert.False(file.FileType == LbpFileType.Script); - Assert.True(file.FileType == LbpFileType.FileArchive); - } + [Fact] + public void ShouldNotRecognizeFileArchiveAsScript() + { + LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); + Assert.False(file.FileType == LbpFileType.Script); + Assert.True(file.FileType == LbpFileType.FileArchive); + } - [Fact] - public void ShouldRecognizeNothingAsUnknown() - { - LbpFile file = new(Array.Empty()); - Assert.True(file.FileType == LbpFileType.Unknown); - } + [Fact] + public void ShouldRecognizeNothingAsUnknown() + { + LbpFile file = new(Array.Empty()); + Assert.True(file.FileType == LbpFileType.Unknown); + } - [Fact] - public void ShouldRecognizeGarbageAsUnknown() - { - LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900")); - Assert.True(file.FileType == LbpFileType.Unknown); - } + [Fact] + public void ShouldRecognizeGarbageAsUnknown() + { + LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900")); + Assert.True(file.FileType == LbpFileType.Unknown); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/LighthouseServerTest.cs b/ProjectLighthouse.Tests/LighthouseServerTest.cs index 4e79d578..5d9b6033 100644 --- a/ProjectLighthouse.Tests/LighthouseServerTest.cs +++ b/ProjectLighthouse.Tests/LighthouseServerTest.cs @@ -11,89 +11,84 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Tests +namespace LBPUnion.ProjectLighthouse.Tests; + +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public class LighthouseServerTest { - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public class LighthouseServerTest + public readonly HttpClient Client; + public readonly TestServer Server; + + public LighthouseServerTest() { - public readonly HttpClient Client; - public readonly TestServer Server; + this.Server = new TestServer(new WebHostBuilder().UseStartup()); + this.Client = this.Server.CreateClient(); + } + public async Task AuthenticateResponse(int number = -1, bool createUser = true) + { + if (number == -1) number = new Random().Next(); - public LighthouseServerTest() + const string username = "unitTestUser"; + if (createUser) { - this.Server = new TestServer(new WebHostBuilder().UseStartup()); - this.Client = this.Server.CreateClient(); - } - public async Task AuthenticateResponse(int number = -1, bool createUser = true) - { - if (number == -1) - { - number = new Random().Next(); - } - - const string username = "unitTestUser"; - if (createUser) - { - await using Database database = new(); - if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null) - await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}")); - } - - string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}"; - - HttpResponseMessage response = await this.Client.PostAsync - ($"/LITTLEBIGPLANETPS3_XML/login?titleID={GameVersionHelper.LittleBigPlanet2TitleIds[0]}", new StringContent(stringContent)); - return response; + await using Database database = new(); + if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null) + await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}")); } - public async Task Authenticate(int number = 0) - { - HttpResponseMessage response = await this.AuthenticateResponse(number); + string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}"; - string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync()); + HttpResponseMessage response = await this.Client.PostAsync + ($"/LITTLEBIGPLANETPS3_XML/login?titleID={GameVersionHelper.LittleBigPlanet2TitleIds[0]}", new StringContent(stringContent)); + return response; + } - XmlSerializer serializer = new(typeof(LoginResult)); - return (LoginResult)serializer.Deserialize(new StringReader(responseContent))!; - } + public async Task Authenticate(int number = 0) + { + HttpResponseMessage response = await this.AuthenticateResponse(number); - public Task AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get); + string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync()); - public Task AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method) - { - using HttpRequestMessage requestMessage = new(method, endpoint); - requestMessage.Headers.Add("Cookie", mmAuth); + XmlSerializer serializer = new(typeof(LoginResult)); + return (LoginResult)serializer.Deserialize(new StringReader(responseContent))!; + } - return this.Client.SendAsync(requestMessage); - } + public Task AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get); - public async Task UploadFileEndpointRequest(string filePath) - { - byte[] bytes = await File.ReadAllBytesAsync(filePath); - string hash = HashHelper.Sha1Hash(bytes).ToLower(); + public Task AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method) + { + using HttpRequestMessage requestMessage = new(method, endpoint); + requestMessage.Headers.Add("Cookie", mmAuth); - return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes)); - } + return this.Client.SendAsync(requestMessage); + } - public async Task UploadFileRequest(string endpoint, string filePath) - => await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); + public async Task UploadFileEndpointRequest(string filePath) + { + byte[] bytes = await File.ReadAllBytesAsync(filePath); + string hash = HashHelper.Sha1Hash(bytes).ToLower(); - public async Task UploadDataRequest(string endpoint, byte[] data) - => await this.Client.PostAsync(endpoint, new ByteArrayContent(data)); + return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes)); + } - public async Task AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth) - { - using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint); - requestMessage.Headers.Add("Cookie", mmAuth); - requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath)); - return await this.Client.SendAsync(requestMessage); - } + public async Task UploadFileRequest(string endpoint, string filePath) + => await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); - public async Task AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth) - { - using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint); - requestMessage.Headers.Add("Cookie", mmAuth); - requestMessage.Content = new ByteArrayContent(data); - return await this.Client.SendAsync(requestMessage); - } + public async Task UploadDataRequest(string endpoint, byte[] data) => await this.Client.PostAsync(endpoint, new ByteArrayContent(data)); + + public async Task AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth) + { + using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint); + requestMessage.Headers.Add("Cookie", mmAuth); + requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath)); + return await this.Client.SendAsync(requestMessage); + } + + public async Task AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth) + { + using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint); + requestMessage.Headers.Add("Cookie", mmAuth); + requestMessage.Content = new ByteArrayContent(data); + return await this.Client.SendAsync(requestMessage); } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/SerializerTests.cs b/ProjectLighthouse.Tests/SerializerTests.cs index 39a12ce4..65dc15aa 100644 --- a/ProjectLighthouse.Tests/SerializerTests.cs +++ b/ProjectLighthouse.Tests/SerializerTests.cs @@ -2,42 +2,41 @@ using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Serialization; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests +namespace LBPUnion.ProjectLighthouse.Tests; + +public class SerializerTests { - public class SerializerTests + [Fact] + public void BlankElementWorks() { - [Fact] - public void BlankElementWorks() - { - Assert.Equal("", LbpSerializer.BlankElement("test")); - } + Assert.Equal("", LbpSerializer.BlankElement("test")); + } - [Fact] - public void StringElementWorks() - { - Assert.Equal("asd", LbpSerializer.StringElement("test", "asd")); - Assert.Equal("asd", LbpSerializer.StringElement(new KeyValuePair("test", "asd"))); - } + [Fact] + public void StringElementWorks() + { + Assert.Equal("asd", LbpSerializer.StringElement("test", "asd")); + Assert.Equal("asd", LbpSerializer.StringElement(new KeyValuePair("test", "asd"))); + } - [Fact] - public void TaggedStringElementWorks() - { - Assert.Equal("asd", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar")); - Assert.Equal - ( - "asd", - LbpSerializer.TaggedStringElement(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) - ); - } + [Fact] + public void TaggedStringElementWorks() + { + Assert.Equal("asd", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar")); + Assert.Equal + ( + "asd", + LbpSerializer.TaggedStringElement(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) + ); + } - [Fact] - public void ElementsWorks() - { - Assert.Equal - ( - "asdbar", - LbpSerializer.Elements(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) - ); - } + [Fact] + public void ElementsWorks() + { + Assert.Equal + ( + "asdbar", + LbpSerializer.Elements(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/ClientConfigurationController.cs b/ProjectLighthouse/Controllers/ClientConfigurationController.cs index 349f4d71..b7a3c477 100644 --- a/ProjectLighthouse/Controllers/ClientConfigurationController.cs +++ b/ProjectLighthouse/Controllers/ClientConfigurationController.cs @@ -3,46 +3,45 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/plain")] +public class ClientConfigurationController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/plain")] - public class ClientConfigurationController : ControllerBase + [HttpGet("network_settings.nws")] + [SuppressMessage("ReSharper", "StringLiteralTypo")] + public IActionResult NetworkSettings() { - [HttpGet("network_settings.nws")] - [SuppressMessage("ReSharper", "StringLiteralTypo")] - public IActionResult NetworkSettings() - { - HostString hostname = this.Request.Host; - return this.Ok - ( - "ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels false\nAllowModeratedPoppetItems false\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\n" + - $"TelemetryServer {hostname}\n" + - $"CDNHostName {hostname}\n" + - $"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" + - $"AllowOnlineCreate {ServerSettings.Instance.VitaCreateMode.ToString().ToLower()}\n" - ); - } - - [HttpGet("t_conf")] - [Produces("text/json")] - public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]"); - - [HttpGet("farc_hashes")] - public IActionResult FarcHashes() => this.Ok(); - - [HttpGet("privacySettings")] - [Produces("text/xml")] - public IActionResult PrivacySettings() - { - PrivacySettings ps = new() - { - LevelVisibility = "all", - ProfileVisibility = "all", - }; - - return this.Ok(ps.Serialize()); - } + HostString hostname = this.Request.Host; + return this.Ok + ( + "ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels false\nAllowModeratedPoppetItems false\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\n" + + $"TelemetryServer {hostname}\n" + + $"CDNHostName {hostname}\n" + + $"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" + + $"AllowOnlineCreate {ServerSettings.Instance.VitaCreateMode.ToString().ToLower()}\n" + ); } -} + + [HttpGet("t_conf")] + [Produces("text/json")] + public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]"); + + [HttpGet("farc_hashes")] + public IActionResult FarcHashes() => this.Ok(); + + [HttpGet("privacySettings")] + [Produces("text/xml")] + public IActionResult PrivacySettings() + { + PrivacySettings ps = new() + { + LevelVisibility = "all", + ProfileVisibility = "all", + }; + + return this.Ok(ps.Serialize()); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/CollectionController.cs b/ProjectLighthouse/Controllers/CollectionController.cs index d6c182a0..4925d80d 100644 --- a/ProjectLighthouse/Controllers/CollectionController.cs +++ b/ProjectLighthouse/Controllers/CollectionController.cs @@ -11,110 +11,109 @@ using LBPUnion.ProjectLighthouse.Types.Categories; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class CollectionController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class CollectionController : ControllerBase + private readonly Database database; + + public CollectionController(Database database) { - private readonly Database database; + this.database = database; + } - public CollectionController(Database database) - { - this.database = database; - } + [HttpGet("user/{username}/playlists")] + public IActionResult GetUserPlaylists(string username) => this.Ok(); - [HttpGet("user/{username}/playlists")] - public IActionResult GetUserPlaylists(string username) => this.Ok(); + [HttpGet("searches")] + [HttpGet("genres")] + public async Task GenresAndSearches() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - [HttpGet("searches")] - [HttpGet("genres")] - public async Task GenresAndSearches() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + string categoriesSerialized = CollectionHelper.Categories.Aggregate + ( + string.Empty, + (current, category) => + { + string serialized; - string categoriesSerialized = CollectionHelper.Categories.Aggregate + if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user); + else serialized = category.Serialize(this.database); + + return current + serialized; + } + ); + + return this.Ok + ( + LbpSerializer.TaggedStringElement ( - string.Empty, - (current, category) => + "categories", + categoriesSerialized, + new Dictionary { - string serialized; - - if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user); - else serialized = category.Serialize(this.database); - - return current + serialized; + { + "hint", "" + }, + { + "hint_start", 1 + }, + { + "total", CollectionHelper.Categories.Count + }, } - ); + ) + ); + } - return this.Ok - ( - LbpSerializer.TaggedStringElement - ( - "categories", - categoriesSerialized, - new Dictionary - { - { - "hint", "" - }, - { - "hint_start", 1 - }, - { - "total", CollectionHelper.Categories.Count - }, - } - ) - ); - } + [HttpGet("searches/{endpointName}")] + public async Task GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - [HttpGet("searches/{endpointName}")] - public async Task GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize) + Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); + if (category == null) return this.NotFound(); + + Logger.Log("Found category " + category, LoggerLevelCategory.Instance); + + List slots; + int totalSlots; + + if (category is CategoryWithUser categoryWithUser) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); - if (category == null) return this.NotFound(); - - Logger.Log("Found category " + category, LoggerLevelCategory.Instance); - - List slots; - int totalSlots; - - if (category is CategoryWithUser categoryWithUser) - { - slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList(); - totalSlots = categoryWithUser.GetTotalSlots(this.database, user); - } - else - { - slots = category.GetSlots(this.database, pageStart, pageSize).ToList(); - totalSlots = category.GetTotalSlots(this.database); - } - - string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ( - "results", - slotsSerialized, - new Dictionary - { - { - "total", totalSlots - }, - { - "hint_start", pageStart + pageSize - }, - } - ) - ); + slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList(); + totalSlots = categoryWithUser.GetTotalSlots(this.database, user); } + else + { + slots = category.GetSlots(this.database, pageStart, pageSize).ToList(); + totalSlots = category.GetTotalSlots(this.database); + } + + string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok + ( + LbpSerializer.TaggedStringElement + ( + "results", + slotsSerialized, + new Dictionary + { + { + "total", totalSlots + }, + { + "hint_start", pageStart + pageSize + }, + } + ) + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index c4e83394..0e470ecc 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -11,73 +11,72 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class CommentController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class CommentController : ControllerBase + private readonly Database database; + public CommentController(Database database) { - private readonly Database database; - public CommentController(Database database) - { - this.database = database; - } + this.database = database; + } - [HttpGet("userComments/{username}")] - public async Task GetComments(string username) - { - List comments = await this.database.Comments.Include - (c => c.Target) - .Include(c => c.Poster) - .Where(c => c.Target.Username == username) - .OrderByDescending(c => c.Timestamp) - .ToListAsync(); + [HttpGet("userComments/{username}")] + public async Task GetComments(string username) + { + List comments = await this.database.Comments.Include + (c => c.Target) + .Include(c => c.Poster) + .Where(c => c.Target.Username == username) + .OrderByDescending(c => c.Timestamp) + .ToListAsync(); - string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize()); - return this.Ok(LbpSerializer.StringElement("comments", outputXml)); - } + string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize()); + return this.Ok(LbpSerializer.StringElement("comments", outputXml)); + } - [HttpPost("postUserComment/{username}")] - public async Task PostComment(string username) - { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + [HttpPost("postUserComment/{username}")] + public async Task PostComment(string username) + { + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - XmlSerializer serializer = new(typeof(Comment)); - Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString)); + XmlSerializer serializer = new(typeof(Comment)); + Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString)); - User? poster = await this.database.UserFromGameRequest(this.Request); - if (poster == null) return this.StatusCode(403, ""); + User? poster = await this.database.UserFromGameRequest(this.Request); + if (poster == null) return this.StatusCode(403, ""); - User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (comment == null || target == null) return this.BadRequest(); + User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (comment == null || target == null) return this.BadRequest(); - comment.PosterUserId = poster.UserId; - comment.TargetUserId = target.UserId; + comment.PosterUserId = poster.UserId; + comment.TargetUserId = target.UserId; - comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); + comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); - this.database.Comments.Add(comment); - await this.database.SaveChangesAsync(); - return this.Ok(); - } + this.database.Comments.Add(comment); + await this.database.SaveChangesAsync(); + return this.Ok(); + } - [HttpPost("deleteUserComment/{username}")] - public async Task DeleteComment([FromQuery] int commentId, string username) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + [HttpPost("deleteUserComment/{username}")] + public async Task DeleteComment([FromQuery] int commentId, string username) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); - if (comment == null) return this.NotFound(); + Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); + if (comment == null) return this.NotFound(); - if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, ""); + if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, ""); - this.database.Comments.Remove(comment); - await this.database.SaveChangesAsync(); + this.database.Comments.Remove(comment); + await this.database.SaveChangesAsync(); - return this.Ok(); - } + return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/DeveloperController.cs b/ProjectLighthouse/Controllers/DeveloperController.cs index 0ec94875..80c6881d 100644 --- a/ProjectLighthouse/Controllers/DeveloperController.cs +++ b/ProjectLighthouse/Controllers/DeveloperController.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +public class DeveloperController : Controller { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - public class DeveloperController : Controller - { - [HttpGet("/developer_videos")] - public IActionResult DeveloperVideos() => this.Ok(); - } + [HttpGet("/developer_videos")] + public IActionResult DeveloperVideos() => this.Ok(); } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/EnterLevelController.cs b/ProjectLighthouse/Controllers/EnterLevelController.cs index 8d45be74..c5504ff5 100644 --- a/ProjectLighthouse/Controllers/EnterLevelController.cs +++ b/ProjectLighthouse/Controllers/EnterLevelController.cs @@ -7,123 +7,122 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers -{ - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] // [Produces("text/plain")] - public class EnterLevelController : ControllerBase +public class EnterLevelController : ControllerBase +{ + private readonly Database database; + + public EnterLevelController(Database database) { - private readonly Database database; + this.database = database; + } - public EnterLevelController(Database database) + [HttpPost("play/user/{slotId}")] + public async Task PlayLevel(int slotId) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.StatusCode(403, ""); + + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + GameVersion gameVersion = token.GameVersion; + + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId); + VisitedLevel? v; + if (!visited.Any()) { - this.database = database; - } - - [HttpPost("play/user/{slotId}")] - public async Task PlayLevel(int slotId) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); - - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - GameVersion gameVersion = token.GameVersion; - - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId); - VisitedLevel? v; - if (!visited.Any()) - { - switch (gameVersion) - { - case GameVersion.LittleBigPlanet2: - slot.PlaysLBP2Unique++; - break; - case GameVersion.LittleBigPlanet3: - slot.PlaysLBP3Unique++; - break; - case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVitaUnique++; - break; - default: return this.BadRequest(); - } - - v = new VisitedLevel(); - v.SlotId = slotId; - v.UserId = user.UserId; - this.database.VisitedLevels.Add(v); - } - else - { - v = await visited.FirstOrDefaultAsync(); - } - - if (v == null) return this.NotFound(); - switch (gameVersion) { case GameVersion.LittleBigPlanet2: - slot.PlaysLBP2++; - v.PlaysLBP2++; + slot.PlaysLBP2Unique++; break; case GameVersion.LittleBigPlanet3: - slot.PlaysLBP3++; - v.PlaysLBP3++; + slot.PlaysLBP3Unique++; break; case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVita++; - v.PlaysLBPVita++; + slot.PlaysLBPVitaUnique++; break; - case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException(); - case GameVersion.Unknown: - default: - return this.BadRequest(); + default: return this.BadRequest(); } - await this.database.SaveChangesAsync(); - - return this.Ok(); + v = new VisitedLevel(); + v.SlotId = slotId; + v.UserId = user.UserId; + this.database.VisitedLevels.Add(v); } - - // Only used in LBP1 - [HttpGet("enterLevel/{id:int}")] - public async Task EnterLevel(int id) + else { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId); - VisitedLevel? v; - if (!visited.Any()) - { - slot.PlaysLBP1Unique++; - - v = new VisitedLevel(); - v.SlotId = id; - v.UserId = user.UserId; - this.database.VisitedLevels.Add(v); - } - else - { - v = await visited.FirstOrDefaultAsync(); - } - - if (v == null) return this.NotFound(); - - slot.PlaysLBP1++; - v.PlaysLBP1++; - - await this.database.SaveChangesAsync(); - - return this.Ok(); + v = await visited.FirstOrDefaultAsync(); } + + if (v == null) return this.NotFound(); + + switch (gameVersion) + { + case GameVersion.LittleBigPlanet2: + slot.PlaysLBP2++; + v.PlaysLBP2++; + break; + case GameVersion.LittleBigPlanet3: + slot.PlaysLBP3++; + v.PlaysLBP3++; + break; + case GameVersion.LittleBigPlanetVita: + slot.PlaysLBPVita++; + v.PlaysLBPVita++; + break; + case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException(); + case GameVersion.Unknown: + default: + return this.BadRequest(); + } + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + // Only used in LBP1 + [HttpGet("enterLevel/{id:int}")] + public async Task EnterLevel(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId); + VisitedLevel? v; + if (!visited.Any()) + { + slot.PlaysLBP1Unique++; + + v = new VisitedLevel(); + v.SlotId = id; + v.UserId = user.UserId; + this.database.VisitedLevels.Add(v); + } + else + { + v = await visited.FirstOrDefaultAsync(); + } + + if (v == null) return this.NotFound(); + + slot.PlaysLBP1++; + v.PlaysLBP1++; + + await this.database.SaveChangesAsync(); + + return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/FriendsController.cs b/ProjectLighthouse/Controllers/FriendsController.cs index b96964bd..794396c5 100644 --- a/ProjectLighthouse/Controllers/FriendsController.cs +++ b/ProjectLighthouse/Controllers/FriendsController.cs @@ -11,88 +11,87 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +public class FriendsController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - public class FriendsController : ControllerBase + private readonly Database database; + + public FriendsController(Database database) { - private readonly Database database; + this.database = database; + } - public FriendsController(Database database) + [HttpPost("npdata")] + public async Task NPData() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + XmlSerializer serializer = new(typeof(NPData)); + NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString)); + if (npData == null) return this.BadRequest(); + + List friends = new(); + foreach (string friendName in npData.Friends) { - this.database = database; + User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName); + if (friend == null) continue; + + friends.Add(friend); } - [HttpPost("npdata")] - public async Task NPData() + List blockedUsers = new(); + foreach (string blockedUserName in npData.BlockedUsers) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName); + if (blockedUser == null) continue; - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(NPData)); - NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString)); - if (npData == null) return this.BadRequest(); - - List friends = new(); - foreach (string friendName in npData.Friends) - { - User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName); - if (friend == null) continue; - - friends.Add(friend); - } - - List blockedUsers = new(); - foreach (string blockedUserName in npData.BlockedUsers) - { - User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName); - if (blockedUser == null) continue; - - blockedUsers.Add(blockedUser.UserId); - } - - if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId)) - { - FriendHelper.FriendIdsByUserId.Remove(user.UserId); - FriendHelper.BlockedIdsByUserId.Remove(user.UserId); - } - - FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray()); - FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray()); - - string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); - - return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized))); + blockedUsers.Add(blockedUser.UserId); } - [HttpGet("myFriends")] - public async Task MyFriends() + if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId)) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); - - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null) - return this.Ok(LbpSerializer.BlankElement("myFriends")); - - string friends = ""; - foreach (int friendId in friendIds) - { - User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId); - if (friend == null) continue; - - friends += friend.Serialize(gameToken.GameVersion); - } - - return this.Ok(LbpSerializer.StringElement("myFriends", friends)); + FriendHelper.FriendIdsByUserId.Remove(user.UserId); + FriendHelper.BlockedIdsByUserId.Remove(user.UserId); } + + FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray()); + FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray()); + + string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); + + return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized))); + } + + [HttpGet("myFriends")] + public async Task MyFriends() + { + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + + if (userAndToken == null) return this.StatusCode(403, ""); + + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; + + if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null) + return this.Ok(LbpSerializer.BlankElement("myFriends")); + + string friends = ""; + foreach (int friendId in friendIds) + { + User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId); + if (friend == null) continue; + + friends += friend.Serialize(gameToken.GameVersion); + } + + return this.Ok(LbpSerializer.StringElement("myFriends", friends)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LevelTagsController.cs b/ProjectLighthouse/Controllers/LevelTagsController.cs index 1795a868..7c5370fa 100644 --- a/ProjectLighthouse/Controllers/LevelTagsController.cs +++ b/ProjectLighthouse/Controllers/LevelTagsController.cs @@ -2,26 +2,25 @@ using System; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/tags")] +[Produces("text/plain")] +public class LevelTagsController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/tags")] - [Produces("text/plain")] - public class LevelTagsController : ControllerBase + [HttpGet] + public IActionResult Get() { - [HttpGet] - public IActionResult Get() + string[] tags = Enum.GetNames(typeof(LevelTags)); + + int i = 0; + foreach (string tag in tags) { - string[] tags = Enum.GetNames(typeof(LevelTags)); - - int i = 0; - foreach (string tag in tags) - { - tags[i] = $"TAG_{tag.Replace("_", "-")}"; - i++; - } - - return this.Ok(string.Join(",", tags)); + tags[i] = $"TAG_{tag.Replace("_", "-")}"; + i++; } + + return this.Ok(string.Join(",", tags)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/ListController.cs b/ProjectLighthouse/Controllers/ListController.cs index 8accb958..289b2d83 100644 --- a/ProjectLighthouse/Controllers/ListController.cs +++ b/ProjectLighthouse/Controllers/ListController.cs @@ -9,209 +9,208 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class ListController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class ListController : ControllerBase + private readonly Database database; + public ListController(Database database) { - private readonly Database database; - public ListController(Database database) - { - this.database = database; - } - - #region Levels - - #region Level Queue (lolcatftw) - - [HttpGet("slots/lolcatftw/{username}")] - public async Task GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - GameVersion gameVersion = token.GameVersion; - - IEnumerable queuedLevels = this.database.QueuedLevels.Include(q => q.User) - .Include(q => q.Slot) - .Include(q => q.Slot.Location) - .Include(q => q.Slot.Creator) - .Where(q => q.Slot.GameVersion <= gameVersion) - .Where(q => q.User.Username == username) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); - - string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username)) - ); - } - - [HttpPost("lolcatftw/add/user/{id:int}")] - public async Task AddQueuedLevel(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - await this.database.QueueLevel(user, slot); - - return this.Ok(); - } - - [HttpPost("lolcatftw/remove/user/{id:int}")] - public async Task RemoveQueuedLevel(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - await this.database.UnqueueLevel(user, slot); - - return this.Ok(); - } - - [HttpPost("lolcatftw/clear")] - public async Task ClearQueuedLevels() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId)); - - await this.database.SaveChangesAsync(); - - return this.Ok(); - } - - #endregion - - #region Hearted Levels - - [HttpGet("favouriteSlots/{username}")] - public async Task GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - GameVersion gameVersion = token.GameVersion; - - IEnumerable heartedLevels = this.database.HeartedLevels.Include(q => q.User) - .Include(q => q.Slot) - .Include(q => q.Slot.Location) - .Include(q => q.Slot.Creator) - .Where(q => q.Slot.GameVersion <= gameVersion) - .Where(q => q.User.Username == username) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); - - string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username)) - ); - } - - [HttpPost("favourite/slot/user/{id:int}")] - public async Task AddFavouriteSlot(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - await this.database.HeartLevel(user, slot); - - return this.Ok(); - } - - [HttpPost("unfavourite/slot/user/{id:int}")] - public async Task RemoveFavouriteSlot(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - await this.database.UnheartLevel(user, slot); - - return this.Ok(); - } - - #endregion - - #endregion Levels - - #region Users - - [HttpGet("favouriteUsers/{username}")] - public async Task GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - IEnumerable heartedProfiles = this.database.HeartedProfiles.Include - (q => q.User) - .Include(q => q.HeartedUser) - .Include(q => q.HeartedUser.Location) - .Where(q => q.User.Username == username) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); - - string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion)); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username)) - ); - } - - [HttpPost("favourite/user/{username}")] - public async Task AddFavouriteUser(string username) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (heartedUser == null) return this.NotFound(); - - await this.database.HeartUser(user, heartedUser); - - return this.Ok(); - } - - [HttpPost("unfavourite/user/{username}")] - public async Task RemoveFavouriteUser(string username) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (heartedUser == null) return this.NotFound(); - - await this.database.UnheartUser(user, heartedUser); - - return this.Ok(); - } - - #endregion - + this.database = database; } + + #region Levels + + #region Level Queue (lolcatftw) + + [HttpGet("slots/lolcatftw/{username}")] + public async Task GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + GameVersion gameVersion = token.GameVersion; + + IEnumerable queuedLevels = this.database.QueuedLevels.Include(q => q.User) + .Include(q => q.Slot) + .Include(q => q.Slot.Location) + .Include(q => q.Slot.Creator) + .Where(q => q.Slot.GameVersion <= gameVersion) + .Where(q => q.User.Username == username) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + .AsEnumerable(); + + string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); + + return this.Ok + ( + LbpSerializer.TaggedStringElement + ("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username)) + ); + } + + [HttpPost("lolcatftw/add/user/{id:int}")] + public async Task AddQueuedLevel(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + await this.database.QueueLevel(user, slot); + + return this.Ok(); + } + + [HttpPost("lolcatftw/remove/user/{id:int}")] + public async Task RemoveQueuedLevel(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + await this.database.UnqueueLevel(user, slot); + + return this.Ok(); + } + + [HttpPost("lolcatftw/clear")] + public async Task ClearQueuedLevels() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId)); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + #endregion + + #region Hearted Levels + + [HttpGet("favouriteSlots/{username}")] + public async Task GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + GameVersion gameVersion = token.GameVersion; + + IEnumerable heartedLevels = this.database.HeartedLevels.Include(q => q.User) + .Include(q => q.Slot) + .Include(q => q.Slot.Location) + .Include(q => q.Slot.Creator) + .Where(q => q.Slot.GameVersion <= gameVersion) + .Where(q => q.User.Username == username) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + .AsEnumerable(); + + string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); + + return this.Ok + ( + LbpSerializer.TaggedStringElement + ("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username)) + ); + } + + [HttpPost("favourite/slot/user/{id:int}")] + public async Task AddFavouriteSlot(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + await this.database.HeartLevel(user, slot); + + return this.Ok(); + } + + [HttpPost("unfavourite/slot/user/{id:int}")] + public async Task RemoveFavouriteSlot(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + await this.database.UnheartLevel(user, slot); + + return this.Ok(); + } + + #endregion + + #endregion Levels + + #region Users + + [HttpGet("favouriteUsers/{username}")] + public async Task GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + IEnumerable heartedProfiles = this.database.HeartedProfiles.Include + (q => q.User) + .Include(q => q.HeartedUser) + .Include(q => q.HeartedUser.Location) + .Where(q => q.User.Username == username) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + .AsEnumerable(); + + string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion)); + + return this.Ok + ( + LbpSerializer.TaggedStringElement + ("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username)) + ); + } + + [HttpPost("favourite/user/{username}")] + public async Task AddFavouriteUser(string username) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (heartedUser == null) return this.NotFound(); + + await this.database.HeartUser(user, heartedUser); + + return this.Ok(); + } + + [HttpPost("unfavourite/user/{username}")] + public async Task RemoveFavouriteUser(string username) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (heartedUser == null) return this.NotFound(); + + await this.database.UnheartUser(user, heartedUser); + + return this.Ok(); + } + + #endregion + } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs index a8695b17..04a9d7df 100644 --- a/ProjectLighthouse/Controllers/LoginController.cs +++ b/ProjectLighthouse/Controllers/LoginController.cs @@ -11,144 +11,143 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers -{ - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/login")] - [Produces("text/xml")] - public class LoginController : ControllerBase - { - private readonly Database database; +namespace LBPUnion.ProjectLighthouse.Controllers; - public LoginController(Database database) +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/login")] +[Produces("text/xml")] +public class LoginController : ControllerBase +{ + private readonly Database database; + + public LoginController(Database database) + { + this.database = database; + } + + [HttpPost] + public async Task Login([FromQuery] string? titleId) + { + titleId ??= ""; + + string body = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + LoginData? loginData; + try { - this.database = database; + loginData = LoginData.CreateFromString(body); + } + catch + { + loginData = null; } - [HttpPost] - public async Task Login([FromQuery] string? titleId) + if (loginData == null) { - titleId ??= ""; + Logger.Log("loginData was null, rejecting login", LoggerLevelLogin.Instance); + return this.BadRequest(); + } - string body = await new StreamReader(this.Request.Body).ReadToEndAsync(); + IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress; + if (remoteIpAddress == null) + { + Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance); + return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever + } - LoginData? loginData; - try + string ipAddress = remoteIpAddress.ToString(); + + // Get an existing token from the IP & username + GameToken? token = await this.database.GameTokens.Include + (t => t.User) + .FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == loginData.Username && !t.Used); + + if (token == null) // If we cant find an existing token, try to generate a new one + { + token = await this.database.AuthenticateUser(loginData, ipAddress, titleId); + if (token == null) { - loginData = LoginData.CreateFromString(body); + Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance); + return this.StatusCode(403, ""); // If not, then 403. } - catch + } + + User? user = await this.database.UserFromGameToken(token, true); + + if (user == null || user.Banned) + { + Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance); + return this.StatusCode(403, ""); + } + + if (ServerSettings.Instance.UseExternalAuth) + { + if (ServerSettings.Instance.BlockDeniedUsers) { - loginData = null; - } - - if (loginData == null) - { - Logger.Log("loginData was null, rejecting login", LoggerLevelLogin.Instance); - return this.BadRequest(); - } - - IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress; - if (remoteIpAddress == null) - { - Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever - } - - string ipAddress = remoteIpAddress.ToString(); - - // Get an existing token from the IP & username - GameToken? token = await this.database.GameTokens.Include - (t => t.User) - .FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == loginData.Username && !t.Used); - - if (token == null) // If we cant find an existing token, try to generate a new one - { - token = await this.database.AuthenticateUser(loginData, ipAddress, titleId); - if (token == null) + string ipAddressAndName = $"{token.UserLocation}|{user.Username}"; + if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3) { - Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); // If not, then 403. + this.database.AuthenticationAttempts.RemoveRange + (this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId)); + + DeniedAuthenticationHelper.AddAttempt(ipAddressAndName); + + await this.database.SaveChangesAsync(); + Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance); + return this.StatusCode(403, ""); } } - User? user = await this.database.UserFromGameToken(token, true); - - if (user == null || user.Banned) - { - Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); - } - - if (ServerSettings.Instance.UseExternalAuth) - { - if (ServerSettings.Instance.BlockDeniedUsers) - { - string ipAddressAndName = $"{token.UserLocation}|{user.Username}"; - if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3) - { - this.database.AuthenticationAttempts.RemoveRange - (this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId)); - - DeniedAuthenticationHelper.AddAttempt(ipAddressAndName); - - await this.database.SaveChangesAsync(); - Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); - } - } - - if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress)) - { - token.Approved = true; - } - else - { - AuthenticationAttempt authAttempt = new() - { - GameToken = token, - GameTokenId = token.TokenId, - Timestamp = TimestampHelper.Timestamp, - IPAddress = ipAddress, - Platform = token.GameVersion == GameVersion.LittleBigPlanetVita ? Platform.Vita : Platform.PS3, // TODO: properly identify RPCS3 - }; - - this.database.AuthenticationAttempts.Add(authAttempt); - } - } - else + if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress)) { token.Approved = true; } - - await this.database.SaveChangesAsync(); - - if (!token.Approved) + else { - Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); - } - - Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client ({titleId})", LoggerLevelLogin.Instance); - // After this point we are now considering this session as logged in. - - // We just logged in with the token. Mark it as used so someone else doesnt try to use it, - // and so we don't pick the same token up when logging in later. - token.Used = true; - - await this.database.SaveChangesAsync(); - - // Create a new room on LBP2/3/Vita - if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion); - - return this.Ok - ( - new LoginResult + AuthenticationAttempt authAttempt = new() { - AuthTicket = "MM_AUTH=" + token.UserToken, - LbpEnvVer = ServerStatics.ServerName, - }.Serialize() - ); + GameToken = token, + GameTokenId = token.TokenId, + Timestamp = TimestampHelper.Timestamp, + IPAddress = ipAddress, + Platform = token.GameVersion == GameVersion.LittleBigPlanetVita ? Platform.Vita : Platform.PS3, // TODO: properly identify RPCS3 + }; + + this.database.AuthenticationAttempts.Add(authAttempt); + } } + else + { + token.Approved = true; + } + + await this.database.SaveChangesAsync(); + + if (!token.Approved) + { + Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance); + return this.StatusCode(403, ""); + } + + Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client ({titleId})", LoggerLevelLogin.Instance); + // After this point we are now considering this session as logged in. + + // We just logged in with the token. Mark it as used so someone else doesnt try to use it, + // and so we don't pick the same token up when logging in later. + token.Used = true; + + await this.database.SaveChangesAsync(); + + // Create a new room on LBP2/3/Vita + if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion); + + return this.Ok + ( + new LoginResult + { + AuthTicket = "MM_AUTH=" + token.UserToken, + LbpEnvVer = ServerStatics.ServerName, + }.Serialize() + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index c897614c..b3a78d79 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -14,96 +14,115 @@ using LBPUnion.ProjectLighthouse.Types.Match; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers -{ - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class MatchController : ControllerBase - { - private readonly Database database; +namespace LBPUnion.ProjectLighthouse.Controllers; - public MatchController(Database database) +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class MatchController : ControllerBase +{ + private readonly Database database; + + public MatchController(Database database) + { + this.database = database; + } + + [HttpPost("match")] + [Produces("text/plain")] + public async Task Match() + { + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + + if (userAndToken == null) return this.StatusCode(403, ""); + + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; + + #region Parse match data + + // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] + + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest(); + + Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance); + + IMatchData? matchData; + try { - this.database = database; + matchData = MatchHelper.Deserialize(bodyString); + } + catch(Exception e) + { + Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance); + string[] lines = e.ToDetailedException().Split("\n"); + foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance); + + return this.BadRequest(); } - [HttpPost("match")] - [Produces("text/plain")] - public async Task Match() + if (matchData == null) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance); + return this.BadRequest(); + } - if (userAndToken == null) return this.StatusCode(403, ""); + Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; + #endregion - #region Parse match data + await LastContactHelper.SetLastContact(user, gameToken.GameVersion); - // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] + #region Process match data - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + if (matchData is UpdateMyPlayerData playerData) + { + MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation); + Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, true); - if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest(); + if (playerData.RoomState != null) + if (room != null && Equals(room.Host, user)) + room.State = (RoomState)playerData.RoomState; + } - Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance); + if (matchData is FindBestRoom && MatchHelper.UserLocations.Count > 1) + { + FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.UserLocation); - IMatchData? matchData; - try + if (response == null) return this.NotFound(); + + string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse)); + foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId); + + return this.Ok($"[{{\"StatusCode\":200}},{serialized}]"); + } + + if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) + { + List users = new(); + foreach (string playerUsername in createRoom.Players) { - matchData = MatchHelper.Deserialize(bodyString); - } - catch(Exception e) - { - Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance); - string[] lines = e.ToDetailedException().Split("\n"); - foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance); - - return this.BadRequest(); + User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (player != null) users.Add(player); + else return this.BadRequest(); } - if (matchData == null) - { - Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance); - return this.BadRequest(); - } + // Create a new one as requested + RoomHelper.CreateRoom(users, gameToken.GameVersion, createRoom.RoomSlot); + } - Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance); + if (matchData is UpdatePlayersInRoom updatePlayersInRoom) + { + Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user); - #endregion - - await LastContactHelper.SetLastContact(user, gameToken.GameVersion); - - #region Process match data - - if (matchData is UpdateMyPlayerData playerData) - { - MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation); - Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, true); - - if (playerData.RoomState != null) - if (room != null && Equals(room.Host, user)) - room.State = (RoomState)playerData.RoomState; - } - - if (matchData is FindBestRoom && MatchHelper.UserLocations.Count > 1) - { - FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.UserLocation); - - if (response == null) return this.NotFound(); - - string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse)); - foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId); - - return this.Ok($"[{{\"StatusCode\":200}},{serialized}]"); - } - - if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) + if (room != null) { List users = new(); - foreach (string playerUsername in createRoom.Players) + foreach (string playerUsername in updatePlayersInRoom.Players) { User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); // ReSharper disable once ConditionIsAlwaysTrueOrFalse @@ -111,33 +130,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers else return this.BadRequest(); } - // Create a new one as requested - RoomHelper.CreateRoom(users, gameToken.GameVersion, createRoom.RoomSlot); + room.Players = users; + RoomHelper.CleanupRooms(null, room); } - - if (matchData is UpdatePlayersInRoom updatePlayersInRoom) - { - Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user); - - if (room != null) - { - List users = new(); - foreach (string playerUsername in updatePlayersInRoom.Players) - { - User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (player != null) users.Add(player); - else return this.BadRequest(); - } - - room.Players = users; - RoomHelper.CleanupRooms(null, room); - } - } - - #endregion - - return this.Ok("[{\"StatusCode\":200}]"); } + + #endregion + + return this.Ok("[{\"StatusCode\":200}]"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index 98680ffb..46fd4520 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -8,36 +8,36 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/plain")] +public class MessageController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/plain")] - public class MessageController : ControllerBase + private readonly Database database; + + public MessageController(Database database) { - private readonly Database database; + this.database = database; + } - public MessageController(Database database) - { - this.database = database; - } + [HttpGet("eula")] + public async Task Eula() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - [HttpGet("eula")] - public async Task Eula() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}"); + } - return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}"); - } - - [HttpGet("announce")] - public async Task Announce() - { - #if !DEBUG - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - #else + [HttpGet("announce")] + public async Task Announce() + { + #if !DEBUG + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + #else (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); if (userAndToken == null) return this.StatusCode(403, ""); @@ -45,17 +45,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers // ReSharper disable once PossibleInvalidOperationException User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; - #endif + #endif - string announceText = ServerSettings.Instance.AnnounceText; + string announceText = ServerSettings.Instance.AnnounceText; - announceText = announceText.Replace("%user", user.Username); - announceText = announceText.Replace("%id", user.UserId.ToString()); + announceText = announceText.Replace("%user", user.Username); + announceText = announceText.Replace("%id", user.UserId.ToString()); - return this.Ok - ( - announceText + - #if DEBUG + return this.Ok + ( + announceText + + #if DEBUG "\n\n---DEBUG INFO---\n" + $"user.UserId: {user.UserId}\n" + $"token.Approved: {gameToken.Approved}\n" + @@ -63,27 +63,26 @@ namespace LBPUnion.ProjectLighthouse.Controllers $"token.UserLocation: {gameToken.UserLocation}\n" + $"token.GameVersion: {gameToken.GameVersion}\n" + "---DEBUG INFO---" + - #endif - "\n" - ); - } + #endif + "\n" + ); + } - [HttpGet("notification")] - public IActionResult Notification() => this.Ok(); - /// - /// Filters chat messages sent by a user. - /// The reponse sent is the text that will appear in-game. - /// - [HttpPost("filter")] - public async Task Filter() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + [HttpGet("notification")] + public IActionResult Notification() => this.Ok(); + /// + /// Filters chat messages sent by a user. + /// The reponse sent is the text that will appear in-game. + /// + [HttpPost("filter")] + public async Task Filter() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync(); + string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync(); - Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance); - return this.Ok(loggedText); - } + Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance); + return this.Ok(loggedText); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/NewsController.cs b/ProjectLighthouse/Controllers/NewsController.cs index ee0342d2..ab4a29a9 100644 --- a/ProjectLighthouse/Controllers/NewsController.cs +++ b/ProjectLighthouse/Controllers/NewsController.cs @@ -2,36 +2,35 @@ using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.News; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers -{ - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/news")] - [Produces("text/xml")] - public class NewsController : ControllerBase - { - [HttpGet] - public IActionResult Get() - { - string newsEntry = LbpSerializer.StringElement - ( - "item", - new NewsEntry - { - Category = "no_category", - Summary = "test summary", - Image = new NewsImage - { - Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe", - Alignment = "right", - }, - Id = 1, - Title = "Test Title", - Text = "Test Text", - Date = 1348755214000, - }.Serialize() - ); +namespace LBPUnion.ProjectLighthouse.Controllers; - return this.Ok(LbpSerializer.StringElement("news", newsEntry)); - } +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/news")] +[Produces("text/xml")] +public class NewsController : ControllerBase +{ + [HttpGet] + public IActionResult Get() + { + string newsEntry = LbpSerializer.StringElement + ( + "item", + new NewsEntry + { + Category = "no_category", + Summary = "test summary", + Image = new NewsImage + { + Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe", + Alignment = "right", + }, + Id = 1, + Title = "Test Title", + Text = "Test Text", + Date = 1348755214000, + }.Serialize() + ); + + return this.Ok(LbpSerializer.StringElement("news", newsEntry)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/PhotosController.cs b/ProjectLighthouse/Controllers/PhotosController.cs index 5e3de091..f00ef305 100644 --- a/ProjectLighthouse/Controllers/PhotosController.cs +++ b/ProjectLighthouse/Controllers/PhotosController.cs @@ -13,148 +13,142 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class PhotosController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class PhotosController : ControllerBase + private readonly Database database; + + public PhotosController(Database database) { - private readonly Database database; + this.database = database; + } - public PhotosController(Database database) + [HttpPost("uploadPhoto")] + public async Task UploadPhoto() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest(); + + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + XmlSerializer serializer = new(typeof(Photo)); + Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString)); + if (photo == null) return this.BadRequest(); + + foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId)) { - this.database = database; + if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded + if (p.MediumHash == photo.MediumHash) return this.Ok(); + if (p.SmallHash == photo.SmallHash) return this.Ok(); + if (p.PlanHash == photo.PlanHash) return this.Ok(); } - [HttpPost("uploadPhoto")] - public async Task UploadPhoto() + photo.CreatorId = user.UserId; + photo.Creator = user; + + if (photo.Subjects.Count > 4) return this.BadRequest(); + + foreach (PhotoSubject subject in photo.Subjects) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); - if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest(); + if (subject.User == null) continue; - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + subject.UserId = subject.User.UserId; + Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance); - XmlSerializer serializer = new(typeof(Photo)); - Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString)); - if (photo == null) return this.BadRequest(); - - foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId)) - { - if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded - if (p.MediumHash == photo.MediumHash) return this.Ok(); - if (p.SmallHash == photo.SmallHash) return this.Ok(); - if (p.PlanHash == photo.PlanHash) return this.Ok(); - } - - photo.CreatorId = user.UserId; - photo.Creator = user; - - if (photo.Subjects.Count > 4) - { - return this.BadRequest(); - } - - foreach (PhotoSubject subject in photo.Subjects) - { - subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); - - if (subject.User == null) continue; - - subject.UserId = subject.User.UserId; - Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance); - - this.database.PhotoSubjects.Add(subject); - } - - await this.database.SaveChangesAsync(); - - // Check for duplicate photo subjects - List subjectUserIds = new(4); - foreach (PhotoSubject subject in photo.Subjects) - { - if (subjectUserIds.Contains(subject.UserId)) - { - return this.BadRequest(); - } - subjectUserIds.Add(subject.UserId); - } - - photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray(); - - // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); - - Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance); - - this.database.Photos.Add(photo); - - await this.database.SaveChangesAsync(); - - return this.Ok(); + this.database.PhotoSubjects.Add(subject); } - [HttpGet("photos/user/{id:int}")] - public async Task SlotPhotos(int id) + await this.database.SaveChangesAsync(); + + // Check for duplicate photo subjects + List subjectUserIds = new(4); + foreach (PhotoSubject subject in photo.Subjects) { - List photos = await this.database.Photos.Include(p => p.Creator).Take(10).ToListAsync(); - string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id)); - return this.Ok(LbpSerializer.StringElement("photos", response)); + if (subjectUserIds.Contains(subject.UserId)) return this.BadRequest(); + + subjectUserIds.Add(subject.UserId); } - [HttpGet("photos/by")] - public async Task UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) - { - User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (userFromQuery == null) return this.NotFound(); + photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray(); - List photos = await this.database.Photos.Include - (p => p.Creator) - .Where(p => p.CreatorId == userFromQuery.UserId) - .OrderByDescending(s => s.Timestamp) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .ToListAsync(); - string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0)); - return this.Ok(LbpSerializer.StringElement("photos", response)); - } + // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); - [HttpGet("photos/with")] - public async Task UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) - { - User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (userFromQuery == null) return this.NotFound(); + Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance); - List photos = new(); - foreach (Photo photo in this.database.Photos.Include - (p => p.Creator)) photos.AddRange(photo.Subjects.Where(subject => subject.User.UserId == userFromQuery.UserId).Select(_ => photo)); + this.database.Photos.Add(photo); - string response = photos.OrderByDescending - (s => s.Timestamp) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0)); + await this.database.SaveChangesAsync(); - return this.Ok(LbpSerializer.StringElement("photos", response)); - } + return this.Ok(); + } - [HttpPost("deletePhoto/{id:int}")] - public async Task DeletePhoto(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + [HttpGet("photos/user/{id:int}")] + public async Task SlotPhotos(int id) + { + List photos = await this.database.Photos.Include(p => p.Creator).Take(10).ToListAsync(); + string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id)); + return this.Ok(LbpSerializer.StringElement("photos", response)); + } - Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); - if (photo == null) return this.NotFound(); - if (photo.CreatorId != user.UserId) return this.StatusCode(401, ""); + [HttpGet("photos/by")] + public async Task UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (userFromQuery == null) return this.NotFound(); - this.database.Photos.Remove(photo); - await this.database.SaveChangesAsync(); - return this.Ok(); - } + List photos = await this.database.Photos.Include + (p => p.Creator) + .Where(p => p.CreatorId == userFromQuery.UserId) + .OrderByDescending(s => s.Timestamp) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + .ToListAsync(); + string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0)); + return this.Ok(LbpSerializer.StringElement("photos", response)); + } + + [HttpGet("photos/with")] + public async Task UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (userFromQuery == null) return this.NotFound(); + + List photos = new(); + foreach (Photo photo in this.database.Photos.Include + (p => p.Creator)) photos.AddRange(photo.Subjects.Where(subject => subject.User.UserId == userFromQuery.UserId).Select(_ => photo)); + + string response = photos.OrderByDescending + (s => s.Timestamp) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + .Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0)); + + return this.Ok(LbpSerializer.StringElement("photos", response)); + } + + [HttpPost("deletePhoto/{id:int}")] + public async Task DeletePhoto(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); + if (photo == null) return this.NotFound(); + if (photo.CreatorId != user.UserId) return this.StatusCode(401, ""); + + this.database.Photos.Remove(photo); + await this.database.SaveChangesAsync(); + return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index ecf7a66d..46a62ce7 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -13,138 +13,113 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class PublishController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class PublishController : ControllerBase + private readonly Database database; + + public PublishController(Database database) { - private readonly Database database; + this.database = database; + } - public PublishController(Database database) + /// + /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded + /// + [HttpPost("startPublish")] + public async Task StartPublish() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); + + Slot? slot = await this.GetSlotFromBody(); + if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded + + if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest(); + + if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel; + + // Republish logic + if (slot.SlotId != 0) { - this.database = database; + Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + if (oldSlot == null) return this.NotFound(); + if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); } - /// - /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded - /// - [HttpPost("startPublish")] - public async Task StartPublish() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here - if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); + string resources = slot.Resources.Where + (hash => !FileHelper.ResourceExists(hash)) + .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); - Slot? slot = await this.GetSlotFromBody(); - if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded + return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user")); + } - if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest(); - - if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel; - - // Republish logic - if (slot.SlotId != 0) - { - Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); - if (oldSlot == null) return this.NotFound(); - if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); - } - - slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here - - string resources = slot.Resources.Where - (hash => !FileHelper.ResourceExists(hash)) - .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); - - return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user")); - } - - /// - /// Endpoint actually used to publish a level - /// - [HttpPost("publish")] - public async Task Publish() - { + /// + /// Endpoint actually used to publish a level + /// + [HttpPost("publish")] + public async Task Publish() + { // User user = await this.database.UserFromGameRequest(this.Request); - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); - if (userAndToken == null) return this.StatusCode(403, ""); + if (userAndToken == null) return this.StatusCode(403, ""); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; - if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); + if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest(); - Slot? slot = await this.GetSlotFromBody(); - if (slot?.Location == null) return this.BadRequest(); + Slot? slot = await this.GetSlotFromBody(); + if (slot?.Location == null) return this.BadRequest(); - // Republish logic - if (slot.SlotId != 0) - { - Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); - if (oldSlot == null) return this.NotFound(); + // Republish logic + if (slot.SlotId != 0) + { + Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + if (oldSlot == null) return this.NotFound(); - if (oldSlot.Location == null) throw new ArgumentNullException(); + if (oldSlot.Location == null) throw new ArgumentNullException(); - if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); + if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); - oldSlot.Location.X = slot.Location.X; - oldSlot.Location.Y = slot.Location.Y; + oldSlot.Location.X = slot.Location.X; + oldSlot.Location.Y = slot.Location.Y; - slot.CreatorId = oldSlot.CreatorId; - slot.LocationId = oldSlot.LocationId; - slot.SlotId = oldSlot.SlotId; + slot.CreatorId = oldSlot.CreatorId; + slot.LocationId = oldSlot.LocationId; + slot.SlotId = oldSlot.SlotId; - slot.PlaysLBP1 = oldSlot.PlaysLBP1; - slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete; - slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique; + slot.PlaysLBP1 = oldSlot.PlaysLBP1; + slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete; + slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique; - slot.PlaysLBP2 = oldSlot.PlaysLBP2; - slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete; - slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique; + slot.PlaysLBP2 = oldSlot.PlaysLBP2; + slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete; + slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique; - slot.PlaysLBP3 = oldSlot.PlaysLBP3; - slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete; - slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique; + slot.PlaysLBP3 = oldSlot.PlaysLBP3; + slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete; + slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique; - slot.PlaysLBPVita = oldSlot.PlaysLBPVita; - slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete; - slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique; + slot.PlaysLBPVita = oldSlot.PlaysLBPVita; + slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete; + slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique; - slot.FirstUploaded = oldSlot.FirstUploaded; - slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); - - slot.TeamPick = oldSlot.TeamPick; - - slot.GameVersion = gameToken.GameVersion; - - if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) - { - slot.MinimumPlayers = 1; - slot.MaximumPlayers = 4; - } - - this.database.Entry(oldSlot).CurrentValues.SetValues(slot); - await this.database.SaveChangesAsync(); - return this.Ok(oldSlot.Serialize()); - } - - //TODO: parse location in body - Location l = new() - { - X = slot.Location.X, - Y = slot.Location.Y, - }; - this.database.Locations.Add(l); - await this.database.SaveChangesAsync(); - slot.LocationId = l.Id; - slot.CreatorId = user.UserId; - slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); + slot.FirstUploaded = oldSlot.FirstUploaded; slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); + + slot.TeamPick = oldSlot.TeamPick; + slot.GameVersion = gameToken.GameVersion; if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) @@ -153,42 +128,66 @@ namespace LBPUnion.ProjectLighthouse.Controllers slot.MaximumPlayers = 4; } - this.database.Slots.Add(slot); + this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); - - return this.Ok(slot.Serialize()); + return this.Ok(oldSlot.Serialize()); } - [HttpPost("unpublish/{id:int}")] - public async Task Unpublish(int id) + //TODO: parse location in body + Location l = new() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + X = slot.Location.X, + Y = slot.Location.Y, + }; + this.database.Locations.Add(l); + await this.database.SaveChangesAsync(); + slot.LocationId = l.Id; + slot.CreatorId = user.UserId; + slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); + slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); + slot.GameVersion = gameToken.GameVersion; - Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - if (slot.Location == null) throw new ArgumentNullException(); - - if (slot.CreatorId != user.UserId) return this.StatusCode(403, ""); - - this.database.Locations.Remove(slot.Location); - this.database.Slots.Remove(slot); - - await this.database.SaveChangesAsync(); - - return this.Ok(); - } - - public async Task GetSlotFromBody() + if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Slot)); - Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString)); - - return slot; + slot.MinimumPlayers = 1; + slot.MaximumPlayers = 4; } + + this.database.Slots.Add(slot); + await this.database.SaveChangesAsync(); + + return this.Ok(slot.Serialize()); + } + + [HttpPost("unpublish/{id:int}")] + public async Task Unpublish(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + if (slot.Location == null) throw new ArgumentNullException(); + + if (slot.CreatorId != user.UserId) return this.StatusCode(403, ""); + + this.database.Locations.Remove(slot.Location); + this.database.Slots.Remove(slot); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + public async Task GetSlotFromBody() + { + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + XmlSerializer serializer = new(typeof(Slot)); + Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString)); + + return slot; } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/ResourcesController.cs b/ProjectLighthouse/Controllers/ResourcesController.cs index 1b645471..0abf1069 100644 --- a/ProjectLighthouse/Controllers/ResourcesController.cs +++ b/ProjectLighthouse/Controllers/ResourcesController.cs @@ -11,79 +11,78 @@ using LBPUnion.ProjectLighthouse.Types.Files; using Microsoft.AspNetCore.Mvc; using IOFile = System.IO.File; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Produces("text/xml")] +[Route("LITTLEBIGPLANETPS3_XML")] +public class ResourcesController : ControllerBase { - [ApiController] - [Produces("text/xml")] - [Route("LITTLEBIGPLANETPS3_XML")] - public class ResourcesController : ControllerBase + [HttpPost("showModerated")] + public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources")); + + [HttpPost("filterResources")] + [HttpPost("showNotUploaded")] + public async Task FilterResources() { - [HttpPost("showModerated")] - public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources")); + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - [HttpPost("filterResources")] - [HttpPost("showNotUploaded")] - public async Task FilterResources() + XmlSerializer serializer = new(typeof(ResourceList)); + ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString)); + + if (resourceList == null) return this.BadRequest(); + + string resources = resourceList.Resources.Where + (s => !FileHelper.ResourceExists(s)) + .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); + + return this.Ok(LbpSerializer.StringElement("resources", resources)); + } + + [ResponseCache(Duration = 86400)] + [HttpGet("/gameAssets/{hash}")] + [HttpGet("r/{hash}")] + public IActionResult GetResource(string hash) + { + string path = FileHelper.GetResourcePath(hash); + + if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream"); + + return this.NotFound(); + } + + // TODO: check if this is a valid hash + [HttpPost("upload/{hash}")] + public async Task UploadResource(string hash) + { + string assetsDirectory = FileHelper.ResourcePath; + string path = FileHelper.GetResourcePath(hash); + + FileHelper.EnsureDirectoryCreated(assetsDirectory); + if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded + + Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance); + LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader)); + + if (!FileHelper.IsFileSafe(file)) { - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(ResourceList)); - ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString)); - - if (resourceList == null) return this.BadRequest(); - - string resources = resourceList.Resources.Where - (s => !FileHelper.ResourceExists(s)) - .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); - - return this.Ok(LbpSerializer.StringElement("resources", resources)); + Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); + return this.UnprocessableEntity(); } - [ResponseCache(Duration = 86400)] - [HttpGet("/gameAssets/{hash}")] - [HttpGet("r/{hash}")] - public IActionResult GetResource(string hash) + string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower(); + if (calculatedHash != hash) { - string path = FileHelper.GetResourcePath(hash); - - if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream"); - - return this.NotFound(); + Logger.Log + ( + $"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})", + LoggerLevelResources.Instance + ); + return this.Conflict(); } - // TODO: check if this is a valid hash - [HttpPost("upload/{hash}")] - public async Task UploadResource(string hash) - { - string assetsDirectory = FileHelper.ResourcePath; - string path = FileHelper.GetResourcePath(hash); - - FileHelper.EnsureDirectoryCreated(assetsDirectory); - if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded - - Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance); - LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader)); - - if (!FileHelper.IsFileSafe(file)) - { - Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); - return this.UnprocessableEntity(); - } - - string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower(); - if (calculatedHash != hash) - { - Logger.Log - ( - $"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})", - LoggerLevelResources.Instance - ); - return this.Conflict(); - } - - Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); - await IOFile.WriteAllBytesAsync(path, file.Data); - return this.Ok(); - } + Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); + await IOFile.WriteAllBytesAsync(path, file.Data); + return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/ReviewController.cs b/ProjectLighthouse/Controllers/ReviewController.cs index 50234b25..bf741e27 100644 --- a/ProjectLighthouse/Controllers/ReviewController.cs +++ b/ProjectLighthouse/Controllers/ReviewController.cs @@ -13,319 +13,318 @@ using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/plain")] +public class ReviewController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/plain")] - public class ReviewController : ControllerBase + private readonly Database database; + + public ReviewController(Database database) { - private readonly Database database; + this.database = database; + } - public ReviewController(Database database) + // LBP1 rating + [HttpPost("rate/user/{slotId}")] + public async Task Rate(int slotId, [FromQuery] int rating) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.StatusCode(403, ""); + + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + if (ratedLevel == null) { - this.database = database; + ratedLevel = new RatedLevel(); + ratedLevel.SlotId = slotId; + ratedLevel.UserId = user.UserId; + ratedLevel.Rating = 0; + this.database.RatedLevels.Add(ratedLevel); } - // LBP1 rating - [HttpPost("rate/user/{slotId}")] - public async Task Rate(int slotId, [FromQuery] int rating) + ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + // LBP2 and beyond rating + [HttpPost("dpadrate/user/{slotId:int}")] + public async Task DPadRate(int slotId, [FromQuery] int rating) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.StatusCode(403, ""); + + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + if (ratedLevel == null) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); - - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); - if (ratedLevel == null) - { - ratedLevel = new RatedLevel(); - ratedLevel.SlotId = slotId; - ratedLevel.UserId = user.UserId; - ratedLevel.Rating = 0; - this.database.RatedLevels.Add(ratedLevel); - } - - ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0); - - await this.database.SaveChangesAsync(); - - return this.Ok(); + ratedLevel = new RatedLevel(); + ratedLevel.SlotId = slotId; + ratedLevel.UserId = user.UserId; + ratedLevel.RatingLBP1 = 0; + this.database.RatedLevels.Add(ratedLevel); } - // LBP2 and beyond rating - [HttpPost("dpadrate/user/{slotId:int}")] - public async Task DPadRate(int slotId, [FromQuery] int rating) + ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1); + + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); + if (review != null) review.Thumb = ratedLevel.Rating; + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [HttpPost("postReview/user/{slotId:int}")] + public async Task PostReview(int slotId) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); + Review? newReview = await this.GetReviewFromBody(); + if (newReview == null) return this.BadRequest(); + + if (review == null) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + review = new Review(); + review.SlotId = slotId; + review.ReviewerId = user.UserId; + review.DeletedBy = DeletedBy.None; + review.ThumbsUp = 0; + review.ThumbsDown = 0; + this.database.Reviews.Add(review); + } + review.Thumb = newReview.Thumb; + review.LabelCollection = newReview.LabelCollection; + review.Text = newReview.Text; + review.Deleted = false; + review.Timestamp = TimeHelper.UnixTimeMilliseconds(); - Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); - - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); - if (ratedLevel == null) - { - ratedLevel = new RatedLevel(); - ratedLevel.SlotId = slotId; - ratedLevel.UserId = user.UserId; - ratedLevel.RatingLBP1 = 0; - this.database.RatedLevels.Add(ratedLevel); - } - - ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1); - - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); - if (review != null) review.Thumb = ratedLevel.Rating; - - await this.database.SaveChangesAsync(); - - return this.Ok(); + // sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??) + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + if (ratedLevel == null) + { + ratedLevel = new RatedLevel(); + ratedLevel.SlotId = slotId; + ratedLevel.UserId = user.UserId; + ratedLevel.RatingLBP1 = 0; + this.database.RatedLevels.Add(ratedLevel); } - [HttpPost("postReview/user/{slotId:int}")] - public async Task PostReview(int slotId) + ratedLevel.Rating = newReview.Thumb; + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [HttpGet("reviewsFor/user/{slotId:int}")] + public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) + { + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + + if (userAndToken == null) return this.StatusCode(403, ""); + + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; + + GameVersion gameVersion = gameToken.GameVersion; + + Random rand = new(); + + Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync + (r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); + + VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync + (v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.BadRequest(); + + bool canNowReviewLevel = slot.CreatorId != user.UserId && visitedLevel != null && yourReview == null; + if (canNowReviewLevel) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync + (r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); - Review? newReview = await this.GetReviewFromBody(); - if (newReview == null) return this.BadRequest(); - - if (review == null) - { - review = new Review(); - review.SlotId = slotId; - review.ReviewerId = user.UserId; - review.DeletedBy = DeletedBy.None; - review.ThumbsUp = 0; - review.ThumbsDown = 0; - this.database.Reviews.Add(review); - } - review.Thumb = newReview.Thumb; - review.LabelCollection = newReview.LabelCollection; - review.Text = newReview.Text; - review.Deleted = false; - review.Timestamp = TimeHelper.UnixTimeMilliseconds(); - - // sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??) - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); - if (ratedLevel == null) - { - ratedLevel = new RatedLevel(); - ratedLevel.SlotId = slotId; - ratedLevel.UserId = user.UserId; - ratedLevel.RatingLBP1 = 0; - this.database.RatedLevels.Add(ratedLevel); - } - - ratedLevel.Rating = newReview.Thumb; - - await this.database.SaveChangesAsync(); - - return this.Ok(); + yourReview = new Review(); + yourReview.ReviewerId = user.UserId; + yourReview.Reviewer = user; + yourReview.Thumb = ratedLevel?.Rating == null ? 0 : ratedLevel.Rating; + yourReview.Slot = slot; + yourReview.SlotId = slotId; + yourReview.Deleted = false; + yourReview.DeletedBy = DeletedBy.None; + yourReview.Text = "You haven't reviewed this level yet. Edit this blank review to upload one!"; + yourReview.LabelCollection = ""; + yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds(); } - [HttpGet("reviewsFor/user/{slotId:int}")] - public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) - { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + IQueryable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion) + .Include(r => r.Reviewer) + .Include(r => r.Slot) + .OrderByDescending(r => r.ThumbsUp) + .ThenByDescending(_ => EF.Functions.Random()) + .Skip(pageStart - 1) + .Take(pageSize); - if (userAndToken == null) return this.StatusCode(403, ""); + IEnumerable prependedReviews; + if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level + // prepend the fake review to the top of the list to be easily edited + prependedReviews = reviews.ToList().Prepend(yourReview); + else prependedReviews = reviews.ToList(); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - GameVersion gameVersion = gameToken.GameVersion; - - Random rand = new(); - - Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync - (r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); - - VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync - (v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion); - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.BadRequest(); - - bool canNowReviewLevel = slot.CreatorId != user.UserId && visitedLevel != null && yourReview == null; - if (canNowReviewLevel) + string inner = prependedReviews.Aggregate + ( + string.Empty, + (current, review) => { - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync - (r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); + if (review == null) return current; - yourReview = new Review(); - yourReview.ReviewerId = user.UserId; - yourReview.Reviewer = user; - yourReview.Thumb = ratedLevel?.Rating == null ? 0 : ratedLevel.Rating; - yourReview.Slot = slot; - yourReview.SlotId = slotId; - yourReview.Deleted = false; - yourReview.DeletedBy = DeletedBy.None; - yourReview.Text = "You haven't reviewed this level yet. Edit this blank review to upload one!"; - yourReview.LabelCollection = ""; - yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds(); + return current + review.Serialize(); } + ); - IQueryable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion) - .Include(r => r.Reviewer) - .Include(r => r.Slot) - .OrderByDescending(r => r.ThumbsUp) - .ThenByDescending(_ => EF.Functions.Random()) - .Skip(pageStart - 1) - .Take(pageSize); - - IEnumerable prependedReviews; - if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level - // prepend the fake review to the top of the list to be easily edited - prependedReviews = reviews.ToList().Prepend(yourReview); - else prependedReviews = reviews.ToList(); - - string inner = prependedReviews.Aggregate - ( - string.Empty, - (current, review) => + string response = LbpSerializer.TaggedStringElement + ( + "reviews", + inner, + new Dictionary + { { - if (review == null) return current; - - return current + review.Serialize(); - } - ); - - string response = LbpSerializer.TaggedStringElement - ( - "reviews", - inner, - new Dictionary + "hint_start", pageStart + pageSize + }, { - { - "hint_start", pageStart + pageSize - }, - { - "hint", pageStart // not sure - }, - } - ); - return this.Ok(response); - } - - [HttpGet("reviewsBy/{username}")] - public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) - { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); - - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - GameVersion gameVersion = gameToken.GameVersion; - - IEnumerable reviews = this.database.Reviews.Where(r => r.Reviewer.Username == username && r.Slot.GameVersion <= gameVersion) - .Include(r => r.Reviewer) - .Include(r => r.Slot) - .OrderByDescending(r => r.Timestamp) - .Skip(pageStart - 1) - .Take(pageSize); - - string inner = reviews.Aggregate - ( - string.Empty, - (current, review) => - { - //RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId); - //RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); - return current + review.Serialize( /*, ratedReview*/); - } - ); - - string response = LbpSerializer.TaggedStringElement - ( - "reviews", - inner, - new Dictionary - { - { - "hint_start", pageStart - }, - { - "hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest - }, - } - ); - - return this.Ok(response); - } - - [HttpPost("rateReview/user/{slotId:int}/{username}")] - public async Task RateReview(int slotId, string username, [FromQuery] int rating = 0) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (reviewer == null) return this.StatusCode(400, ""); - - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); - if (review == null) return this.StatusCode(400, ""); - - RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); - if (ratedReview == null) - { - ratedReview = new RatedReview(); - ratedReview.ReviewId = review.ReviewId; - ratedReview.UserId = user.UserId; - ratedReview.Thumb = 0; - this.database.RatedReviews.Add(ratedReview); + "hint", pageStart // not sure + }, } + ); + return this.Ok(response); + } - int oldThumb = ratedReview.Thumb; - ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1); + [HttpGet("reviewsBy/{username}")] + public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) + { + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); - if (oldThumb != ratedReview.Thumb) + if (userAndToken == null) return this.StatusCode(403, ""); + + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; + + GameVersion gameVersion = gameToken.GameVersion; + + IEnumerable reviews = this.database.Reviews.Where(r => r.Reviewer.Username == username && r.Slot.GameVersion <= gameVersion) + .Include(r => r.Reviewer) + .Include(r => r.Slot) + .OrderByDescending(r => r.Timestamp) + .Skip(pageStart - 1) + .Take(pageSize); + + string inner = reviews.Aggregate + ( + string.Empty, + (current, review) => { - if (oldThumb == -1) review.ThumbsDown--; - else if (oldThumb == 1) review.ThumbsUp--; - - if (ratedReview.Thumb == -1) review.ThumbsDown++; - else if (ratedReview.Thumb == 1) review.ThumbsUp++; + //RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId); + //RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + return current + review.Serialize( /*, ratedReview*/); } + ); - await this.database.SaveChangesAsync(); + string response = LbpSerializer.TaggedStringElement + ( + "reviews", + inner, + new Dictionary + { + { + "hint_start", pageStart + }, + { + "hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest + }, + } + ); - return this.Ok(); - } + return this.Ok(response); + } - [HttpPost("deleteReview/user/{slotId:int}/{username}")] - public async Task DeleteReview(int slotId, string username) + [HttpPost("rateReview/user/{slotId:int}/{username}")] + public async Task RateReview(int slotId, string username, [FromQuery] int rating = 0) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (reviewer == null) return this.StatusCode(400, ""); + + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); + if (review == null) return this.StatusCode(400, ""); + + RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + if (ratedReview == null) { - User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (reviewer == null) return this.StatusCode(403, ""); - - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); - if (review == null) return this.StatusCode(403, ""); - - review.Deleted = true; - review.DeletedBy = DeletedBy.LevelAuthor; - - await this.database.SaveChangesAsync(); - return this.Ok(); + ratedReview = new RatedReview(); + ratedReview.ReviewId = review.ReviewId; + ratedReview.UserId = user.UserId; + ratedReview.Thumb = 0; + this.database.RatedReviews.Add(ratedReview); } - public async Task GetReviewFromBody() + int oldThumb = ratedReview.Thumb; + ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1); + + if (oldThumb != ratedReview.Thumb) { - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + if (oldThumb == -1) review.ThumbsDown--; + else if (oldThumb == 1) review.ThumbsUp--; - XmlSerializer serializer = new(typeof(Review)); - Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString)); - - return review; + if (ratedReview.Thumb == -1) review.ThumbsDown++; + else if (ratedReview.Thumb == 1) review.ThumbsUp++; } + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [HttpPost("deleteReview/user/{slotId:int}/{username}")] + public async Task DeleteReview(int slotId, string username) + { + User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (reviewer == null) return this.StatusCode(403, ""); + + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); + if (review == null) return this.StatusCode(403, ""); + + review.Deleted = true; + review.DeletedBy = DeletedBy.LevelAuthor; + + await this.database.SaveChangesAsync(); + return this.Ok(); + } + + public async Task GetReviewFromBody() + { + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + XmlSerializer serializer = new(typeof(Review)); + Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString)); + + return review; } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/ScoreController.cs b/ProjectLighthouse/Controllers/ScoreController.cs index 8b08c214..8f521fc5 100644 --- a/ProjectLighthouse/Controllers/ScoreController.cs +++ b/ProjectLighthouse/Controllers/ScoreController.cs @@ -11,155 +11,151 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class ScoreController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class ScoreController : ControllerBase + private readonly Database database; + + public ScoreController(Database database) { - private readonly Database database; + this.database = database; + } - public ScoreController(Database database) + [HttpPost("scoreboard/user/{id:int}")] + public async Task SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false) + { + (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + + if (userAndToken == null) return this.StatusCode(403, ""); + + // ReSharper disable once PossibleInvalidOperationException + User user = userAndToken.Value.Item1; + GameToken gameToken = userAndToken.Value.Item2; + + this.Request.Body.Position = 0; + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + XmlSerializer serializer = new(typeof(Score)); + Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString)); + if (score == null) return this.BadRequest(); + + score.SlotId = id; + + Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId); + if (slot == null) return this.BadRequest(); + + switch (gameToken.GameVersion) { - this.database = database; + case GameVersion.LittleBigPlanet1: + slot.PlaysLBP1Complete++; + break; + case GameVersion.LittleBigPlanet2: + slot.PlaysLBP2Complete++; + break; + case GameVersion.LittleBigPlanet3: + slot.PlaysLBP3Complete++; + break; + case GameVersion.LittleBigPlanetVita: + slot.PlaysLBPVitaComplete++; + break; } - [HttpPost("scoreboard/user/{id:int}")] - public async Task SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false) + IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection); + + if (existingScore.Any()) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); - - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - this.Request.Body.Position = 0; - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - - XmlSerializer serializer = new(typeof(Score)); - Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString)); - if (score == null) return this.BadRequest(); - - score.SlotId = id; - - Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId); - if (slot == null) return this.BadRequest(); - - switch (gameToken.GameVersion) - { - case GameVersion.LittleBigPlanet1: - slot.PlaysLBP1Complete++; - break; - case GameVersion.LittleBigPlanet2: - slot.PlaysLBP2Complete++; - break; - case GameVersion.LittleBigPlanet3: - slot.PlaysLBP3Complete++; - break; - case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVitaComplete++; - break; - } - - IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection); - - if (existingScore.Any()) - { - Score first = existingScore.First(s => s.SlotId == score.SlotId); - score.ScoreId = first.ScoreId; - score.Points = Math.Max(first.Points, score.Points); - this.database.Entry(first).CurrentValues.SetValues(score); - } - else - { - this.database.Scores.Add(score); - } - - await this.database.SaveChangesAsync(); - - string myRanking = this.GetScores(score.SlotId, score.Type, user); - - return this.Ok(myRanking); + Score first = existingScore.First(s => s.SlotId == score.SlotId); + score.ScoreId = first.ScoreId; + score.Points = Math.Max(first.Points, score.Points); + this.database.Entry(first).CurrentValues.SetValues(score); + } + else + { + this.database.Scores.Add(score); } - [HttpGet("friendscores/user/{slotId:int}/{type:int}")] - public IActionResult FriendScores(int slotId, int type) - //=> await TopScores(slotId, type); - => this.Ok(LbpSerializer.BlankElement("scores")); + await this.database.SaveChangesAsync(); - [HttpGet("topscores/user/{slotId:int}/{type:int}")] - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public async Task TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) - { - // Get username - User? user = await this.database.UserFromGameRequest(this.Request); + string myRanking = this.GetScores(score.SlotId, score.Type, user); - if (user == null) return this.StatusCode(403, ""); + return this.Ok(myRanking); + } - return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize)); - } + [HttpGet("friendscores/user/{slotId:int}/{type:int}")] + public IActionResult FriendScores(int slotId, int type) + //=> await TopScores(slotId, type); + => this.Ok(LbpSerializer.BlankElement("scores")); - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5) - { - // This is hella ugly but it technically assigns the proper rank to a score - // var needed for Anonymous type returned from SELECT - var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type) - .OrderByDescending(s => s.Points) - .ToList() - .Select - ( - (s, rank) => new - { - Score = s, - Rank = rank + 1, - } - ); + [HttpGet("topscores/user/{slotId:int}/{type:int}")] + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public async Task TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) + { + // Get username + User? user = await this.database.UserFromGameRequest(this.Request); - // Find your score, since even if you aren't in the top list your score is pinned - var myScore = rankedScores.Where - (rs => rs.Score.PlayerIdCollection.Contains(user.Username)) - .OrderByDescending(rs => rs.Score.Points) - .FirstOrDefault(); + if (user == null) return this.StatusCode(403, ""); - // Paginated viewing: if not requesting pageStart, get results around user - var pagedScores = rankedScores.Skip(pageStart != -1 || myScore == null ? pageStart - 1 : myScore.Rank - 3).Take(Math.Min(pageSize, 30)); + return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize)); + } - string serializedScores = pagedScores.Aggregate + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5) + { + // This is hella ugly but it technically assigns the proper rank to a score + // var needed for Anonymous type returned from SELECT + var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type) + .OrderByDescending(s => s.Points) + .ToList() + .Select ( - string.Empty, - (current, rs) => + (s, rank) => new { - rs.Score.Rank = rs.Rank; - return current + rs.Score.Serialize(); + Score = s, + Rank = rank + 1, } ); - string res; - if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores); - else - res = LbpSerializer.TaggedStringElement - ( - "scores", - serializedScores, - new Dictionary - { - { - "yourScore", myScore.Score.Points - }, - { - "yourRank", myScore.Rank - }, //This is the numerator of your position globally in the side menu. - { - "totalNumScores", rankedScores.Count() - }, // This is the denominator of your position globally in the side menu. - } - ); + // Find your score, since even if you aren't in the top list your score is pinned + var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Contains(user.Username)).OrderByDescending(rs => rs.Score.Points).FirstOrDefault(); - return res; - } + // Paginated viewing: if not requesting pageStart, get results around user + var pagedScores = rankedScores.Skip(pageStart != -1 || myScore == null ? pageStart - 1 : myScore.Rank - 3).Take(Math.Min(pageSize, 30)); + + string serializedScores = pagedScores.Aggregate + ( + string.Empty, + (current, rs) => + { + rs.Score.Rank = rs.Rank; + return current + rs.Score.Serialize(); + } + ); + + string res; + if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores); + else + res = LbpSerializer.TaggedStringElement + ( + "scores", + serializedScores, + new Dictionary + { + { + "yourScore", myScore.Score.Points + }, + { + "yourRank", myScore.Rank + }, //This is the numerator of your position globally in the side menu. + { + "totalNumScores", rankedScores.Count() + }, // This is the denominator of your position globally in the side menu. + } + ); + + return res; } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/SearchController.cs b/ProjectLighthouse/Controllers/SearchController.cs index 42b1d3c1..62e80e51 100644 --- a/ProjectLighthouse/Controllers/SearchController.cs +++ b/ProjectLighthouse/Controllers/SearchController.cs @@ -7,48 +7,47 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class SearchController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class SearchController : ControllerBase + private readonly Database database; + public SearchController(Database database) { - private readonly Database database; - public SearchController(Database database) - { - this.database = database; - } + this.database = database; + } - [HttpGet("slots/search")] - public async Task SearchSlots([FromQuery] string query, [FromQuery] int pageSize, [FromQuery] int pageStart) - { - if (query == null) return this.BadRequest(); + [HttpGet("slots/search")] + public async Task SearchSlots([FromQuery] string query, [FromQuery] int pageSize, [FromQuery] int pageStart) + { + if (query == null) return this.BadRequest(); - query = query.ToLower(); + query = query.ToLower(); - string[] keywords = query.Split(" "); + string[] keywords = query.Split(" "); - IQueryable dbQuery = this.database.Slots - .Include(s => s.Creator) - .Include(s => s.Location) - .Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable + IQueryable dbQuery = this.database.Slots + .Include(s => s.Creator) + .Include(s => s.Location) + .Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (string keyword in keywords) - dbQuery = dbQuery.Where - ( - s => s.Name.ToLower().Contains(keyword) || - s.Description.ToLower().Contains(keyword) || - s.Creator.Username.ToLower().Contains(keyword) || - s.SlotId.ToString().Equals(keyword) - ); + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (string keyword in keywords) + dbQuery = dbQuery.Where + ( + s => s.Name.ToLower().Contains(keyword) || + s.Description.ToLower().Contains(keyword) || + s.Creator.Username.ToLower().Contains(keyword) || + s.SlotId.ToString().Equals(keyword) + ); - List slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync(); + List slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync(); - string response = slots.Aggregate("", (current, slot) => current + slot.Serialize()); + string response = slots.Aggregate("", (current, slot) => current + slot.Serialize()); - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count())); - } + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count())); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 9d48f57e..a64a248b 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -11,345 +11,340 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class SlotsController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class SlotsController : ControllerBase + private readonly Database database; + public SlotsController(Database database) { - private readonly Database database; - public SlotsController(Database database) - { - this.database = database; - } + this.database = database; + } - [HttpGet("slots/by")] - public async Task SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + [HttpGet("slots/by")] + public async Task SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - GameVersion gameVersion = token.GameVersion; + GameVersion gameVersion = token.GameVersion; - User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u); - if (user == null) return this.NotFound(); + User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u); + if (user == null) return this.NotFound(); - string response = Enumerable.Aggregate - ( - this.database.Slots.Where(s => s.GameVersion <= gameVersion) - .Include(s => s.Creator) - .Include(s => s.Location) - .Where(s => s.Creator!.Username == user.Username) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)), - string.Empty, - (current, slot) => current + slot.Serialize() - ); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ( - "slots", - response, - new Dictionary - { - { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) - }, - { - "total", user.UsedSlots - }, - } - ) - ); - } - - [HttpGet("s/user/{id:int}")] - public async Task SUser(int id) - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - GameVersion gameVersion = token.GameVersion; - - Slot? slot = await this.database.Slots.Where(s => s.GameVersion <= gameVersion) + string response = Enumerable.Aggregate + ( + this.database.Slots.Where(s => s.GameVersion <= gameVersion) .Include(s => s.Creator) .Include(s => s.Location) - .FirstOrDefaultAsync(s => s.SlotId == id); - - if (slot == null) return this.NotFound(); - - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); - VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); - return this.Ok(slot.Serialize(ratedLevel, visitedLevel)); - } - - [HttpGet("slots/cool")] - public async Task Lbp1CoolSlots([FromQuery] int page) - { - const int pageSize = 30; - return await CoolSlots((page - 1) * pageSize, pageSize); - } - - [HttpGet("slots/lbp2cool")] - public async Task CoolSlots - ( - [FromQuery] int pageStart, - [FromQuery] int pageSize, - [FromQuery] string? gameFilterType = null, - [FromQuery] int? players = null, - [FromQuery] bool? move = null, - [FromQuery] int? page = null - ) - { - int _pageStart = pageStart; - if (page != null) _pageStart = (int)page * 30; - // bit of a better placeholder until we can track average user interaction with /stream endpoint - return await ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek"); - } - - [HttpGet("slots")] - public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - GameVersion gameVersion = token.GameVersion; - - IQueryable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) - .Include(s => s.Creator) - .Include(s => s.Location) - .OrderByDescending(s => s.FirstUploaded) + .Where(s => s.Creator!.Username == user.Username) .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); + .Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)), + string.Empty, + (current, slot) => current + slot.Serialize() + ); - return this.Ok + return this.Ok + ( + LbpSerializer.TaggedStringElement ( - LbpSerializer.TaggedStringElement - ( - "slots", - response, - new Dictionary + "slots", + response, + new Dictionary + { { - { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) - }, - { - "total", await StatisticsHelper.SlotCount() - }, - } - ) - ); - } + "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + }, + { + "total", user.UsedSlots + }, + } + ) + ); + } - [HttpGet("slots/mmpicks")] - public async Task TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + [HttpGet("s/user/{id:int}")] + public async Task SUser(int id) + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); - GameVersion gameVersion = token.GameVersion; + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - IQueryable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) - .Where(s => s.TeamPick) - .Include(s => s.Creator) - .Include(s => s.Location) - .OrderByDescending(s => s.LastUpdated) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); + GameVersion gameVersion = token.GameVersion; - return this.Ok + Slot? slot = await this.database.Slots.Where(s => s.GameVersion <= gameVersion) + .Include(s => s.Creator) + .Include(s => s.Location) + .FirstOrDefaultAsync(s => s.SlotId == id); + + if (slot == null) return this.NotFound(); + + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); + VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); + return this.Ok(slot.Serialize(ratedLevel, visitedLevel)); + } + + [HttpGet("slots/cool")] + public async Task Lbp1CoolSlots([FromQuery] int page) + { + const int pageSize = 30; + return await this.CoolSlots((page - 1) * pageSize, pageSize); + } + + [HttpGet("slots/lbp2cool")] + public async Task CoolSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string? gameFilterType = null, + [FromQuery] int? players = null, + [FromQuery] bool? move = null, + [FromQuery] int? page = null + ) + { + int _pageStart = pageStart; + if (page != null) _pageStart = (int)page * 30; + // bit of a better placeholder until we can track average user interaction with /stream endpoint + return await this.ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek"); + } + + [HttpGet("slots")] + public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + GameVersion gameVersion = token.GameVersion; + + IQueryable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) + .Include(s => s.Creator) + .Include(s => s.Location) + .OrderByDescending(s => s.FirstUploaded) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok + ( + LbpSerializer.TaggedStringElement ( - LbpSerializer.TaggedStringElement - ( - "slots", - response, - new Dictionary + "slots", + response, + new Dictionary + { { - { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) - }, - { - "total", await StatisticsHelper.MMPicksCount() - }, - } - ) - ); - } + "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + }, + { + "total", await StatisticsHelper.SlotCount() + }, + } + ) + ); + } - [HttpGet("slots/lbp2luckydip")] - public async Task LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); + [HttpGet("slots/mmpicks")] + public async Task TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - GameVersion gameVersion = token.GameVersion; + GameVersion gameVersion = token.GameVersion; - IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) - .Include(s => s.Creator) - .Include(s => s.Location) - .OrderBy(_ => EF.Functions.Random()) - .Take(Math.Min(pageSize, 30)); + IQueryable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) + .Where(s => s.TeamPick) + .Include(s => s.Creator) + .Include(s => s.Location) + .OrderByDescending(s => s.LastUpdated) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); - - return this.Ok + return this.Ok + ( + LbpSerializer.TaggedStringElement ( - LbpSerializer.TaggedStringElement - ( - "slots", - response, - new Dictionary + "slots", + response, + new Dictionary + { { - { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) - }, - { - "total", await StatisticsHelper.SlotCount() - }, - } - ) - ); - } - - [HttpGet("slots/thumbs")] - public async Task ThumbsSlots - ( - [FromQuery] int pageStart, - [FromQuery] int pageSize, - [FromQuery] string? gameFilterType = null, - [FromQuery] int? players = null, - [FromQuery] bool? move = null, - [FromQuery] string? dateFilterType = null - ) - { - Random rand = new(); - - IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) - .AsEnumerable() - .OrderByDescending(s => s.Thumbsup) - .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)); - - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); - - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); - } - - [HttpGet("slots/mostUniquePlays")] - public async Task MostUniquePlaysSlots - ( - [FromQuery] int pageStart, - [FromQuery] int pageSize, - [FromQuery] string? gameFilterType = null, - [FromQuery] int? players = null, - [FromQuery] bool? move = null, - [FromQuery] string? dateFilterType = null - ) - { - Random rand = new(); - - IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) - .AsEnumerable() - .OrderByDescending - ( - s => + "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + }, { - // probably not the best way to do this? - return GetGameFilter(gameFilterType) switch - { - GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, - GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, - GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique, - GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique, - _ => s.PlaysUnique, - }; - } - ) - .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)); + "total", await StatisticsHelper.MMPicksCount() + }, + } + ) + ); + } - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + [HttpGet("slots/lbp2luckydip")] + public async Task LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); - } + GameVersion gameVersion = token.GameVersion; - [HttpGet("slots/mostHearted")] - public async Task MostHeartedSlots + IEnumerable slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion) + .Include(s => s.Creator) + .Include(s => s.Location) + .OrderBy(_ => EF.Functions.Random()) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok ( - [FromQuery] int pageStart, - [FromQuery] int pageSize, - [FromQuery] string? gameFilterType = null, - [FromQuery] int? players = null, - [FromQuery] bool? move = null, - [FromQuery] string? dateFilterType = null - ) + LbpSerializer.TaggedStringElement + ( + "slots", + response, + new Dictionary + { + { + "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + }, + { + "total", await StatisticsHelper.SlotCount() + }, + } + ) + ); + } + + [HttpGet("slots/thumbs")] + public async Task ThumbsSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string? gameFilterType = null, + [FromQuery] int? players = null, + [FromQuery] bool? move = null, + [FromQuery] string? dateFilterType = null + ) + { + Random rand = new(); + + IEnumerable slots = this.FilterByRequest(gameFilterType, dateFilterType) + .AsEnumerable() + .OrderByDescending(s => s.Thumbsup) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + + [HttpGet("slots/mostUniquePlays")] + public async Task MostUniquePlaysSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string? gameFilterType = null, + [FromQuery] int? players = null, + [FromQuery] bool? move = null, + [FromQuery] string? dateFilterType = null + ) + { + Random rand = new(); + + IEnumerable slots = this.FilterByRequest(gameFilterType, dateFilterType) + .AsEnumerable() + .OrderByDescending + ( + s => + { + // probably not the best way to do this? + return this.GetGameFilter(gameFilterType) switch + { + GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, + GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, + GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique, + GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique, + _ => s.PlaysUnique, + }; + } + ) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + + [HttpGet("slots/mostHearted")] + public async Task MostHeartedSlots + ( + [FromQuery] int pageStart, + [FromQuery] int pageSize, + [FromQuery] string? gameFilterType = null, + [FromQuery] int? players = null, + [FromQuery] bool? move = null, + [FromQuery] string? dateFilterType = null + ) + { + Random rand = new(); + + IEnumerable slots = this.FilterByRequest(gameFilterType, dateFilterType) + .AsEnumerable() + .OrderByDescending(s => s.Hearts) + .ThenBy(_ => rand.Next()) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)); + + string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); + } + + public GameVersion GetGameFilter(string? gameFilterType) + { + return gameFilterType switch { - Random rand = new(); + "lbp1" => GameVersion.LittleBigPlanet1, + "lbp2" => GameVersion.LittleBigPlanet2, + "lbp3" => GameVersion.LittleBigPlanet3, + "both" => GameVersion.LittleBigPlanet2, // LBP2 default option + null => GameVersion.LittleBigPlanet1, + _ => GameVersion.Unknown, + }; + } - IEnumerable slots = FilterByRequest(gameFilterType, dateFilterType) - .AsEnumerable() - .OrderByDescending(s => s.Hearts) - .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)); + public IQueryable FilterByRequest(string? gameFilterType, string? dateFilterType) + { + string _dateFilterType = dateFilterType ?? ""; - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize()); - - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); - } - - public GameVersion GetGameFilter(string? gameFilterType) + long oldestTime = _dateFilterType switch { - return gameFilterType switch - { - "lbp1" => GameVersion.LittleBigPlanet1, - "lbp2" => GameVersion.LittleBigPlanet2, - "lbp3" => GameVersion.LittleBigPlanet3, - "both" => GameVersion.LittleBigPlanet2, // LBP2 default option - null => GameVersion.LittleBigPlanet1, - _ => GameVersion.Unknown, - }; - } + "thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(), + "thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(), + _ => 0, + }; - public IQueryable FilterByRequest(string? gameFilterType, string? dateFilterType) - { - string _dateFilterType = dateFilterType ?? ""; + GameVersion gameVersion = this.GetGameFilter(gameFilterType); - long oldestTime = _dateFilterType switch - { - "thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(), - "thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(), - _ => 0, - }; + IQueryable whereSlots; - GameVersion gameVersion = GetGameFilter(gameFilterType); + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (gameFilterType == "both") + // Get game versions less than the current version + // Needs support for LBP3 ("both" = LBP1+2) + whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); + else + // Get game versions exactly equal to gamefiltertype + whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); - IQueryable whereSlots; - - // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression - if (gameFilterType == "both") - { - // Get game versions less than the current version - // Needs support for LBP3 ("both" = LBP1+2) - whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime); - } - else - { - // Get game versions exactly equal to gamefiltertype - whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime); - } - - return whereSlots.Include(s => s.Creator).Include(s => s.Location); - } + return whereSlots.Include(s => s.Creator).Include(s => s.Location); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/StatisticsController.cs b/ProjectLighthouse/Controllers/StatisticsController.cs index eb945419..d6cbee75 100644 --- a/ProjectLighthouse/Controllers/StatisticsController.cs +++ b/ProjectLighthouse/Controllers/StatisticsController.cs @@ -3,37 +3,36 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/plain")] +public class StatisticsController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/plain")] - public class StatisticsController : ControllerBase + private readonly Database database; + public StatisticsController(Database database) { - private readonly Database database; - public StatisticsController(Database database) - { - this.database = database; - } - - [HttpGet("playersInPodCount")] - [HttpGet("totalPlayerCount")] - public async Task TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!); - - [HttpGet("planetStats")] - public async Task PlanetStats() - { - int totalSlotCount = await StatisticsHelper.SlotCount(); - int mmPicksCount = await StatisticsHelper.MMPicksCount(); - - return this.Ok - ( - LbpSerializer.StringElement - ("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount)) - ); - } - - [HttpGet("planetStats/totalLevelCount")] - public async Task TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString()); + this.database = database; } + + [HttpGet("playersInPodCount")] + [HttpGet("totalPlayerCount")] + public async Task TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!); + + [HttpGet("planetStats")] + public async Task PlanetStats() + { + int totalSlotCount = await StatisticsHelper.SlotCount(); + int mmPicksCount = await StatisticsHelper.MMPicksCount(); + + return this.Ok + ( + LbpSerializer.StringElement + ("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount)) + ); + } + + [HttpGet("planetStats/totalLevelCount")] + public async Task TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString()); } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/StoreController.cs b/ProjectLighthouse/Controllers/StoreController.cs index 7c68be02..614f5c11 100644 --- a/ProjectLighthouse/Controllers/StoreController.cs +++ b/ProjectLighthouse/Controllers/StoreController.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class StoreController : Controller { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class StoreController : Controller - { - [HttpGet("promotions")] - public IActionResult Promotions() => this.Ok(); - } + [HttpGet("promotions")] + public IActionResult Promotions() => this.Ok(); } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs index b87336a6..4cd47c08 100644 --- a/ProjectLighthouse/Controllers/UserController.cs +++ b/ProjectLighthouse/Controllers/UserController.cs @@ -12,181 +12,180 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers +namespace LBPUnion.ProjectLighthouse.Controllers; + +[ApiController] +[Route("LITTLEBIGPLANETPS3_XML/")] +[Produces("text/xml")] +public class UserController : ControllerBase { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class UserController : ControllerBase + private readonly Database database; + + public UserController(Database database) { - private readonly Database database; + this.database = database; + } - public UserController(Database database) + public async Task GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1) + { + User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username); + return user?.Serialize(gameVersion); + } + + [HttpGet("user/{username}")] + public async Task GetUser(string username) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + string? user = await this.GetSerializedUser(username, token.GameVersion); + if (user == null) return this.NotFound(); + + return this.Ok(user); + } + + [HttpGet("users")] + public async Task GetUserAlt([FromQuery] string[] u) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + List serializedUsers = new(); + foreach (string userId in u) serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion)); + + string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user); + + return this.Ok(LbpSerializer.StringElement("users", serialized)); + } + + [HttpPost("updateUser")] + public async Task UpdateUser() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + XmlReaderSettings settings = new() { - this.database = database; - } + Async = true, // this is apparently not default + }; - public async Task GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1) + bool locationChanged = false; + + // this is an absolute mess, but necessary because LBP only sends what changed + // + // example for changing profile card location: + // + // + // 1234 + // 1234 + // + // + // + // example for changing biography: + // + // biography stuff + // + // + // if you find a way to make it not stupid feel free to replace this + using (XmlReader reader = XmlReader.Create(this.Request.Body, settings)) { - User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username); - return user?.Serialize(gameVersion); - } - - [HttpGet("user/{username}")] - public async Task GetUser(string username) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - string? user = await this.GetSerializedUser(username, token.GameVersion); - if (user == null) return this.NotFound(); - - return this.Ok(user); - } - - [HttpGet("users")] - public async Task GetUserAlt([FromQuery] string[] u) - { - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - - List serializedUsers = new(); - foreach (string userId in u) serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion)); - - string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user); - - return this.Ok(LbpSerializer.StringElement("users", serialized)); - } - - [HttpPost("updateUser")] - public async Task UpdateUser() - { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - - XmlReaderSettings settings = new() - { - Async = true, // this is apparently not default - }; - - bool locationChanged = false; - - // this is an absolute mess, but necessary because LBP only sends what changed - // - // example for changing profile card location: - // - // - // 1234 - // 1234 - // - // - // - // example for changing biography: - // - // biography stuff - // - // - // if you find a way to make it not stupid feel free to replace this - using (XmlReader reader = XmlReader.Create(this.Request.Body, settings)) - { - List path = new(); // you can think of this as a file path in the XML, like -> -> - while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault - switch (reader.NodeType) - { - case XmlNodeType.Element: - path.Add(reader.Name); - break; - case XmlNodeType.Text: - switch (path[1]) + List path = new(); // you can think of this as a file path in the XML, like -> -> + while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault + switch (reader.NodeType) + { + case XmlNodeType.Element: + path.Add(reader.Name); + break; + case XmlNodeType.Text: + switch (path[1]) + { + case "biography": { - case "biography": - { - user.Biography = await reader.GetValueAsync(); - break; - } - case "location": - { - locationChanged = true; // if we're here then we're probably about to change the location. - // ReSharper disable once ConvertIfStatementToSwitchStatement - if (path[2] == "x") - user.Location.X = Convert.ToInt32 - (await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol - else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync()); - break; - } - case "icon": - { - user.IconHash = await reader.GetValueAsync(); - break; - } - case "planets": - { - user.PlanetHash = await reader.GetValueAsync(); - break; - } - case "yay2": - { - user.YayHash = await reader.GetValueAsync(); - break; - } - case "boo2": - { - user.BooHash = await reader.GetValueAsync(); - break; - } - case "meh2": - { - user.MehHash = await reader.GetValueAsync(); - break; - } + user.Biography = await reader.GetValueAsync(); + break; } + case "location": + { + locationChanged = true; // if we're here then we're probably about to change the location. + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (path[2] == "x") + user.Location.X = Convert.ToInt32 + (await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol + else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync()); + break; + } + case "icon": + { + user.IconHash = await reader.GetValueAsync(); + break; + } + case "planets": + { + user.PlanetHash = await reader.GetValueAsync(); + break; + } + case "yay2": + { + user.YayHash = await reader.GetValueAsync(); + break; + } + case "boo2": + { + user.BooHash = await reader.GetValueAsync(); + break; + } + case "meh2": + { + user.MehHash = await reader.GetValueAsync(); + break; + } + } - break; - case XmlNodeType.EndElement: - path.RemoveAt(path.Count - 1); - break; - } - } - - // the way location on a user card works is stupid and will not save with the way below as-is, so we do the following: - if (locationChanged) // only modify the database if we modify here - { - Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again - - if (l == null) throw new Exception("this shouldn't happen ever but we handle this"); - - // set the location in the database to the one we modified above - l.X = user.Location.X; - l.Y = user.Location.Y; - - // now both are in sync, and will update in the database. - } - - if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything - return this.Ok(); + break; + case XmlNodeType.EndElement: + path.RemoveAt(path.Count - 1); + break; + } } - [HttpPost("update_my_pins")] - public async Task UpdateMyPins() + // the way location on a user card works is stupid and will not save with the way below as-is, so we do the following: + if (locationChanged) // only modify the database if we modify here { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again - string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - Pins? pinJson = JsonSerializer.Deserialize(pinsString); - if (pinJson == null) return this.BadRequest(); + if (l == null) throw new Exception("this shouldn't happen ever but we handle this"); - // Sometimes the update gets called periodically as pin progress updates via playing, - // may not affect equipped profile pins however, so check before setting it. - string currentPins = user.Pins; - string newPins = string.Join(",", pinJson.ProfilePins); + // set the location in the database to the one we modified above + l.X = user.Location.X; + l.Y = user.Location.Y; - if (string.Equals(currentPins, newPins)) return this.Ok("[{\"StatusCode\":200}]"); - - user.Pins = newPins; - await this.database.SaveChangesAsync(); - - return this.Ok("[{\"StatusCode\":200}]"); + // now both are in sync, and will update in the database. } + + if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything + return this.Ok(); + } + + [HttpPost("update_my_pins")] + public async Task UpdateMyPins() + { + User? user = await this.database.UserFromGameRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + Pins? pinJson = JsonSerializer.Deserialize(pinsString); + if (pinJson == null) return this.BadRequest(); + + // Sometimes the update gets called periodically as pin progress updates via playing, + // may not affect equipped profile pins however, so check before setting it. + string currentPins = user.Pins; + string newPins = string.Join(",", pinJson.ProfilePins); + + if (string.Equals(currentPins, newPins)) return this.Ok("[{\"StatusCode\":200}]"); + + user.Pins = newPins; + await this.database.SaveChangesAsync(); + + return this.Ok("[{\"StatusCode\":200}]"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs index 4787f1a7..3f84078f 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs @@ -6,68 +6,67 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin +namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; + +[ApiController] +[Route("admin/slot/{id:int}")] +public class AdminSlotController : ControllerBase { - [ApiController] - [Route("admin/slot/{id:int}")] - public class AdminSlotController : ControllerBase + private readonly Database database; + + public AdminSlotController(Database database) { - private readonly Database database; + this.database = database; + } - public AdminSlotController(Database database) - { - this.database = database; - } + [HttpGet("teamPick")] + public async Task TeamPick([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); - [HttpGet("teamPick")] - public async Task TeamPick([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); + slot.TeamPick = true; - slot.TeamPick = true; + await this.database.SaveChangesAsync(); - await this.database.SaveChangesAsync(); + return this.Ok(); + } - return this.Ok(); - } + [HttpGet("removeTeamPick")] + public async Task RemoveTeamPick([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); - [HttpGet("removeTeamPick")] - public async Task RemoveTeamPick([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); + slot.TeamPick = false; - slot.TeamPick = false; + await this.database.SaveChangesAsync(); - await this.database.SaveChangesAsync(); + return this.Ok(); + } - return this.Ok(); - } + [HttpGet("delete")] + public async Task DeleteLevel([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); - [HttpGet("delete")] - public async Task DeleteLevel([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.Ok(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.Ok(); + if (slot.Location == null) throw new ArgumentNullException(); - if (slot.Location == null) throw new ArgumentNullException(); + this.database.Locations.Remove(slot.Location); + this.database.Slots.Remove(slot); - this.database.Locations.Remove(slot.Location); - this.database.Slots.Remove(slot); + await this.database.SaveChangesAsync(); - await this.database.SaveChangesAsync(); - - return this.Ok(); - } + return this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs index e92266fe..13f2a4d5 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs @@ -4,34 +4,33 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin +namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; + +[ApiController] +[Route("admin/user/{id:int}")] +public class AdminUserController : ControllerBase { - [ApiController] - [Route("admin/user/{id:int}")] - public class AdminUserController : ControllerBase + private readonly Database database; + + public AdminUserController(Database database) { - private readonly Database database; + this.database = database; + } - public AdminUserController(Database database) - { - this.database = database; - } + [HttpGet("unban")] + public async Task UnbanUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.NotFound(); - [HttpGet("unban")] - public async Task UnbanUser([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.NotFound(); + User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + ; + if (targetedUser == null) return this.NotFound(); - User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); - ; - if (targetedUser == null) return this.NotFound(); + targetedUser.Banned = false; + targetedUser.BannedReason = null; - targetedUser.Banned = false; - targetedUser.BannedReason = null; - - await this.database.SaveChangesAsync(); - return this.Redirect($"/user/{targetedUser.UserId}"); - } + await this.database.SaveChangesAsync(); + return this.Redirect($"/user/{targetedUser.UserId}"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs b/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs index 854f05c9..e4defbcb 100644 --- a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs +++ b/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs @@ -1,10 +1,5 @@ -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Controllers.Website.Debug; diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs b/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs index 045d4cd4..a33b7ca6 100644 --- a/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs +++ b/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs @@ -7,85 +7,84 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth +namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; + +[ApiController] +[Route("/authentication")] +public class AuthenticationController : ControllerBase { - [ApiController] - [Route("/authentication")] - public class AuthenticationController : ControllerBase + private readonly Database database; + + public AuthenticationController(Database database) { - private readonly Database database; + this.database = database; + } - public AuthenticationController(Database database) + [HttpGet("approve/{id:int}")] + public async Task Approve(int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include + (a => a.GameToken) + .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); + if (authAttempt == null) return this.NotFound(); + + if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, ""); + + authAttempt.GameToken.Approved = true; + this.database.AuthenticationAttempts.Remove(authAttempt); + + await this.database.SaveChangesAsync(); + + return this.Redirect("~/authentication"); + } + + [HttpGet("deny/{id:int}")] + public async Task Deny(int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include + (a => a.GameToken) + .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); + if (authAttempt == null) return this.NotFound(); + + if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, ""); + + this.database.GameTokens.Remove(authAttempt.GameToken); + this.database.AuthenticationAttempts.Remove(authAttempt); + + DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}"); + + await this.database.SaveChangesAsync(); + + return this.Redirect("~/authentication"); + } + + [HttpGet("denyAll")] + public async Task DenyAll() + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + List authAttempts = await this.database.AuthenticationAttempts.Include + (a => a.GameToken) + .Where(a => a.GameToken.UserId == user.UserId) + .ToListAsync(); + + foreach (AuthenticationAttempt authAttempt in authAttempts) { - this.database = database; - } - - [HttpGet("approve/{id:int}")] - public async Task Approve(int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); - - AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include - (a => a.GameToken) - .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); - if (authAttempt == null) return this.NotFound(); - - if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, ""); - - authAttempt.GameToken.Approved = true; - this.database.AuthenticationAttempts.Remove(authAttempt); - - await this.database.SaveChangesAsync(); - - return this.Redirect("~/authentication"); - } - - [HttpGet("deny/{id:int}")] - public async Task Deny(int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); - - AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include - (a => a.GameToken) - .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); - if (authAttempt == null) return this.NotFound(); - - if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, ""); - this.database.GameTokens.Remove(authAttempt.GameToken); this.database.AuthenticationAttempts.Remove(authAttempt); DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}"); - - await this.database.SaveChangesAsync(); - - return this.Redirect("~/authentication"); } - [HttpGet("denyAll")] - public async Task DenyAll() - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); + await this.database.SaveChangesAsync(); - List authAttempts = await this.database.AuthenticationAttempts.Include - (a => a.GameToken) - .Where(a => a.GameToken.UserId == user.UserId) - .ToListAsync(); - - foreach (AuthenticationAttempt authAttempt in authAttempts) - { - this.database.GameTokens.Remove(authAttempt.GameToken); - this.database.AuthenticationAttempts.Remove(authAttempt); - - DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}"); - } - - await this.database.SaveChangesAsync(); - - return this.Redirect("~/authentication"); - } + return this.Redirect("~/authentication"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs b/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs index f801b055..4d889285 100644 --- a/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs +++ b/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs @@ -4,64 +4,63 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth +namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; + +[ApiController] +[Route("/authentication")] +public class AutoApprovalController : ControllerBase { - [ApiController] - [Route("/authentication")] - public class AutoApprovalController : ControllerBase + private readonly Database database; + + public AutoApprovalController(Database database) { - private readonly Database database; + this.database = database; + } - public AutoApprovalController(Database database) + [HttpGet("autoApprove/{id:int}")] + public async Task AutoApprove([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); + + AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include + (a => a.GameToken) + .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); + + if (authAttempt == null) return this.BadRequest(); + if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login"); + + authAttempt.GameToken.Approved = true; + + UserApprovedIpAddress approvedIpAddress = new() { - this.database = database; - } + UserId = user.UserId, + User = user, + IpAddress = authAttempt.IPAddress, + }; - [HttpGet("autoApprove/{id:int}")] - public async Task AutoApprove([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); + this.database.UserApprovedIpAddresses.Add(approvedIpAddress); + this.database.AuthenticationAttempts.Remove(authAttempt); - AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include - (a => a.GameToken) - .FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id); + await this.database.SaveChangesAsync(); - if (authAttempt == null) return this.BadRequest(); - if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login"); + return this.Redirect("/authentication"); + } - authAttempt.GameToken.Approved = true; + [HttpGet("revokeAutoApproval/{id:int}")] + public async Task RevokeAutoApproval([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); - UserApprovedIpAddress approvedIpAddress = new() - { - UserId = user.UserId, - User = user, - IpAddress = authAttempt.IPAddress, - }; + UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id); + if (approvedIpAddress == null) return this.BadRequest(); + if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login"); - this.database.UserApprovedIpAddresses.Add(approvedIpAddress); - this.database.AuthenticationAttempts.Remove(authAttempt); + this.database.UserApprovedIpAddresses.Remove(approvedIpAddress); - await this.database.SaveChangesAsync(); + await this.database.SaveChangesAsync(); - return this.Redirect("/authentication"); - } - - [HttpGet("revokeAutoApproval/{id:int}")] - public async Task RevokeAutoApproval([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); - - UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id); - if (approvedIpAddress == null) return this.BadRequest(); - if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login"); - - this.database.UserApprovedIpAddresses.Remove(approvedIpAddress); - - await this.database.SaveChangesAsync(); - - return this.Redirect("/authentication/autoApprovals"); - } + return this.Redirect("/authentication/autoApprovals"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/SlotPageController.cs b/ProjectLighthouse/Controllers/Website/SlotPageController.cs index e69697c7..18fb14e3 100644 --- a/ProjectLighthouse/Controllers/Website/SlotPageController.cs +++ b/ProjectLighthouse/Controllers/Website/SlotPageController.cs @@ -10,81 +10,80 @@ using Microsoft.EntityFrameworkCore; // TODO: Clean up this file // - jvyden -namespace LBPUnion.ProjectLighthouse.Controllers.Website +namespace LBPUnion.ProjectLighthouse.Controllers.Website; + +[ApiController] +[Route("slot/{id:int}")] +public class SlotPageController : ControllerBase { - [ApiController] - [Route("slot/{id:int}")] - public class SlotPageController : ControllerBase + private readonly Database database; + + public SlotPageController(Database database) { - private readonly Database database; + this.database = database; + } - public SlotPageController(Database database) - { - this.database = database; - } + [HttpGet("heart")] + public async Task HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl) + { + if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - [HttpGet("heart")] - public async Task HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl) - { - if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (heartedSlot == null) return this.NotFound(); - Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (heartedSlot == null) return this.NotFound(); + await this.database.HeartLevel(user, heartedSlot); - await this.database.HeartLevel(user, heartedSlot); + return this.Redirect(callbackUrl); + } - return this.Redirect(callbackUrl); - } + [HttpGet("unheart")] + public async Task UnheartLevel([FromRoute] int id, [FromQuery] string? callbackUrl) + { + if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - [HttpGet("unheart")] - public async Task UnheartLevel([FromRoute] int id, [FromQuery] string? callbackUrl) - { - if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (heartedSlot == null) return this.NotFound(); - Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (heartedSlot == null) return this.NotFound(); + await this.database.UnheartLevel(user, heartedSlot); - await this.database.UnheartLevel(user, heartedSlot); + return this.Redirect(callbackUrl); + } - return this.Redirect(callbackUrl); - } + [HttpGet("queue")] + public async Task QueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl) + { + if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - [HttpGet("queue")] - public async Task QueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl) - { - if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (queuedSlot == null) return this.NotFound(); - Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (queuedSlot == null) return this.NotFound(); + await this.database.QueueLevel(user, queuedSlot); - await this.database.QueueLevel(user, queuedSlot); + return this.Redirect(callbackUrl); + } - return this.Redirect(callbackUrl); - } + [HttpGet("unqueue")] + public async Task UnqueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl) + { + if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - [HttpGet("unqueue")] - public async Task UnqueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl) - { - if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (queuedSlot == null) return this.NotFound(); - Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); - if (queuedSlot == null) return this.NotFound(); + await this.database.UnqueueLevel(user, queuedSlot); - await this.database.UnqueueLevel(user, queuedSlot); - - return this.Redirect(callbackUrl); - } + return this.Redirect(callbackUrl); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/UserPageController.cs b/ProjectLighthouse/Controllers/Website/UserPageController.cs index 43e37725..0f44d876 100644 --- a/ProjectLighthouse/Controllers/Website/UserPageController.cs +++ b/ProjectLighthouse/Controllers/Website/UserPageController.cs @@ -4,45 +4,44 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website +namespace LBPUnion.ProjectLighthouse.Controllers.Website; + +[ApiController] +[Route("user/{id:int}")] +public class UserPageController : ControllerBase { - [ApiController] - [Route("user/{id:int}")] - public class UserPageController : ControllerBase + private readonly Database database; + + public UserPageController(Database database) { - private readonly Database database; + this.database = database; + } - public UserPageController(Database database) - { - this.database = database; - } + [HttpGet("heart")] + public async Task HeartUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - [HttpGet("heart")] - public async Task HeartUser([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (heartedUser == null) return this.NotFound(); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); - if (heartedUser == null) return this.NotFound(); + await this.database.HeartUser(user, heartedUser); - await this.database.HeartUser(user, heartedUser); + return this.Redirect("~/user/" + id); + } - return this.Redirect("~/user/" + id); - } + [HttpGet("unheart")] + public async Task UnheartUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - [HttpGet("unheart")] - public async Task UnheartUser([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (heartedUser == null) return this.NotFound(); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); - if (heartedUser == null) return this.NotFound(); + await this.database.UnheartUser(user, heartedUser); - await this.database.UnheartUser(user, heartedUser); - - return this.Redirect("~/user/" + id); - } + return this.Redirect("~/user/" + id); } } \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 8f882808..844fd6e5 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -13,287 +13,284 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse +namespace LBPUnion.ProjectLighthouse; + +public class Database : DbContext { - public class Database : DbContext + public DbSet Users { get; set; } + public DbSet Locations { get; set; } + public DbSet Slots { get; set; } + public DbSet QueuedLevels { get; set; } + public DbSet HeartedLevels { get; set; } + public DbSet HeartedProfiles { get; set; } + public DbSet Comments { get; set; } + public DbSet GameTokens { get; set; } + public DbSet WebTokens { get; set; } + public DbSet Scores { get; set; } + public DbSet PhotoSubjects { get; set; } + public DbSet Photos { get; set; } + public DbSet LastContacts { get; set; } + public DbSet VisitedLevels { get; set; } + public DbSet RatedLevels { get; set; } + public DbSet AuthenticationAttempts { get; set; } + public DbSet Reviews { get; set; } + public DbSet RatedReviews { get; set; } + public DbSet UserApprovedIpAddresses { get; set; } + public DbSet CustomCategories { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); + + public async Task CreateUser(string username, string password) { - public DbSet Users { get; set; } - public DbSet Locations { get; set; } - public DbSet Slots { get; set; } - public DbSet QueuedLevels { get; set; } - public DbSet HeartedLevels { get; set; } - public DbSet HeartedProfiles { get; set; } - public DbSet Comments { get; set; } - public DbSet GameTokens { get; set; } - public DbSet WebTokens { get; set; } - public DbSet Scores { get; set; } - public DbSet PhotoSubjects { get; set; } - public DbSet Photos { get; set; } - public DbSet LastContacts { get; set; } - public DbSet VisitedLevels { get; set; } - public DbSet RatedLevels { get; set; } - public DbSet AuthenticationAttempts { get; set; } - public DbSet Reviews { get; set; } - public DbSet RatedReviews { get; set; } - public DbSet UserApprovedIpAddresses { get; set; } - public DbSet CustomCategories { get; set; } + if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash"); - protected override void OnConfiguring(DbContextOptionsBuilder options) - => options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); + User user; + if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user; - public async Task CreateUser(string username, string password) + Location l = new(); // store to get id after submitting + this.Locations.Add(l); // add to table + await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity + + user = new User { - if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash"); + Username = username, + Password = password, + LocationId = l.Id, + Biography = username + " hasn't introduced themselves yet.", + }; + this.Users.Add(user); - User user; - if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user; + await this.SaveChangesAsync(); - Location l = new(); // store to get id after submitting - this.Locations.Add(l); // add to table - await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity - - user = new User - { - Username = username, - Password = password, - LocationId = l.Id, - Biography = username + " hasn't introduced themselves yet.", - }; - this.Users.Add(user); - - await this.SaveChangesAsync(); - - return user; - } - - #nullable enable - public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "") - { - User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username); - if (user == null) return null; - - GameToken gameToken = new() - { - UserToken = HashHelper.GenerateAuthToken(), - User = user, - UserId = user.UserId, - UserLocation = userLocation, - GameVersion = GameVersionHelper.FromTitleId(titleId), - }; - - if (gameToken.GameVersion == GameVersion.Unknown) - { - Logger.Log($"Unknown GameVersion for TitleId {titleId}", LoggerLevelLogin.Instance); - return null; - } - - this.GameTokens.Add(gameToken); - await this.SaveChangesAsync(); - - return gameToken; - } - - #region Hearts & Queues - - public async Task HeartUser(User user, User heartedUser) - { - HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync - (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); - if (heartedProfile != null) return; - - this.HeartedProfiles.Add - ( - new HeartedProfile - { - HeartedUserId = heartedUser.UserId, - UserId = user.UserId, - } - ); - - await this.SaveChangesAsync(); - } - - public async Task UnheartUser(User user, User heartedUser) - { - HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync - (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); - if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile); - - await this.SaveChangesAsync(); - } - - public async Task HeartLevel(User user, Slot heartedSlot) - { - HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); - if (heartedLevel != null) return; - - this.HeartedLevels.Add - ( - new HeartedLevel - { - SlotId = heartedSlot.SlotId, - UserId = user.UserId, - } - ); - - await this.SaveChangesAsync(); - } - - public async Task UnheartLevel(User user, Slot heartedSlot) - { - HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); - if (heartedLevel != null) this.HeartedLevels.Remove(heartedLevel); - - await this.SaveChangesAsync(); - } - - public async Task QueueLevel(User user, Slot queuedSlot) - { - QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); - if (queuedLevel != null) return; - - this.QueuedLevels.Add - ( - new QueuedLevel - { - SlotId = queuedSlot.SlotId, - UserId = user.UserId, - } - ); - - await this.SaveChangesAsync(); - } - - public async Task UnqueueLevel(User user, Slot queuedSlot) - { - QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); - if (queuedLevel != null) this.QueuedLevels.Remove(queuedLevel); - - await this.SaveChangesAsync(); - } - - #endregion - - #region Game Token Shenanigans - - public async Task UserFromMMAuth(string authToken, bool allowUnapproved = false) - { - if (ServerStatics.IsUnitTesting) allowUnapproved = true; - GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken); - - if (token == null) return null; - if (!allowUnapproved && !token.Approved) return null; - - return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId); - } - - public async Task UserFromGameToken - (GameToken gameToken, bool allowUnapproved = false) - => await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved); - - public async Task UserFromGameRequest(HttpRequest request, bool allowUnapproved = false) - { - if (ServerStatics.IsUnitTesting) allowUnapproved = true; - if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; - - return await this.UserFromMMAuth(mmAuth, allowUnapproved); - } - - public async Task GameTokenFromRequest(HttpRequest request, bool allowUnapproved = false) - { - if (ServerStatics.IsUnitTesting) allowUnapproved = true; - if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; - - GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); - - if (token == null) return null; - if (!allowUnapproved && !token.Approved) return null; - - return token; - } - - public async Task<(User, GameToken)?> UserAndGameTokenFromRequest(HttpRequest request, bool allowUnapproved = false) - { - if (ServerStatics.IsUnitTesting) allowUnapproved = true; - if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; - - GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); - if (token == null) return null; - if (!allowUnapproved && !token.Approved) return null; - - User? user = await this.UserFromGameToken(token); - - if (user == null) return null; - - return (user, token); - } - - #endregion - - #region Web Token Shenanigans - - public User? UserFromLighthouseToken(string lighthouseToken) - { - WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken); - if (token == null) return null; - - return this.Users.Include(u => u.Location).FirstOrDefault(u => u.UserId == token.UserId); - } - - public User? UserFromWebRequest(HttpRequest request) - { - if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null; - - return this.UserFromLighthouseToken(lighthouseToken); - } - - public WebToken? WebTokenFromRequest(HttpRequest request) - { - if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null; - - return this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken); - } - - #endregion - - public async Task PhotoFromSubject(PhotoSubject subject) - => await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString())); - public async Task RemoveUser(User? user) - { - if (user == null) return; - - if (user.Location != null) this.Locations.Remove(user.Location); - LastContact? lastContact = await this.LastContacts.FirstOrDefaultAsync(l => l.UserId == user.UserId); - if (lastContact != null) this.LastContacts.Remove(lastContact); - - foreach (Slot slot in this.Slots.Where(s => s.CreatorId == user.UserId)) await this.RemoveSlot(slot, false); - - this.AuthenticationAttempts.RemoveRange(this.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId)); - this.HeartedProfiles.RemoveRange(this.HeartedProfiles.Where(h => h.UserId == user.UserId)); - this.PhotoSubjects.RemoveRange(this.PhotoSubjects.Where(s => s.UserId == user.UserId)); - this.HeartedLevels.RemoveRange(this.HeartedLevels.Where(h => h.UserId == user.UserId)); - this.VisitedLevels.RemoveRange(this.VisitedLevels.Where(v => v.UserId == user.UserId)); - this.RatedReviews.RemoveRange(this.RatedReviews.Where(r => r.UserId == user.UserId)); - this.QueuedLevels.RemoveRange(this.QueuedLevels.Where(q => q.UserId == user.UserId)); - this.RatedLevels.RemoveRange(this.RatedLevels.Where(r => r.UserId == user.UserId)); - this.GameTokens.RemoveRange(this.GameTokens.Where(t => t.UserId == user.UserId)); - this.WebTokens.RemoveRange(this.WebTokens.Where(t => t.UserId == user.UserId)); - this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId)); - this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId)); - this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId)); - - this.Users.Remove(user); - - await this.SaveChangesAsync(); - } - - public async Task RemoveSlot(Slot slot, bool saveChanges = true) - { - if (slot.Location != null) this.Locations.Remove(slot.Location); - this.Slots.Remove(slot); - - if (saveChanges) await this.SaveChangesAsync(); - } - #nullable disable + return user; } + + #nullable enable + public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "") + { + User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username); + if (user == null) return null; + + GameToken gameToken = new() + { + UserToken = HashHelper.GenerateAuthToken(), + User = user, + UserId = user.UserId, + UserLocation = userLocation, + GameVersion = GameVersionHelper.FromTitleId(titleId), + }; + + if (gameToken.GameVersion == GameVersion.Unknown) + { + Logger.Log($"Unknown GameVersion for TitleId {titleId}", LoggerLevelLogin.Instance); + return null; + } + + this.GameTokens.Add(gameToken); + await this.SaveChangesAsync(); + + return gameToken; + } + + #region Hearts & Queues + + public async Task HeartUser(User user, User heartedUser) + { + HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + if (heartedProfile != null) return; + + this.HeartedProfiles.Add + ( + new HeartedProfile + { + HeartedUserId = heartedUser.UserId, + UserId = user.UserId, + } + ); + + await this.SaveChangesAsync(); + } + + public async Task UnheartUser(User user, User heartedUser) + { + HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile); + + await this.SaveChangesAsync(); + } + + public async Task HeartLevel(User user, Slot heartedSlot) + { + HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); + if (heartedLevel != null) return; + + this.HeartedLevels.Add + ( + new HeartedLevel + { + SlotId = heartedSlot.SlotId, + UserId = user.UserId, + } + ); + + await this.SaveChangesAsync(); + } + + public async Task UnheartLevel(User user, Slot heartedSlot) + { + HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); + if (heartedLevel != null) this.HeartedLevels.Remove(heartedLevel); + + await this.SaveChangesAsync(); + } + + public async Task QueueLevel(User user, Slot queuedSlot) + { + QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); + if (queuedLevel != null) return; + + this.QueuedLevels.Add + ( + new QueuedLevel + { + SlotId = queuedSlot.SlotId, + UserId = user.UserId, + } + ); + + await this.SaveChangesAsync(); + } + + public async Task UnqueueLevel(User user, Slot queuedSlot) + { + QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); + if (queuedLevel != null) this.QueuedLevels.Remove(queuedLevel); + + await this.SaveChangesAsync(); + } + + #endregion + + #region Game Token Shenanigans + + public async Task UserFromMMAuth(string authToken, bool allowUnapproved = false) + { + if (ServerStatics.IsUnitTesting) allowUnapproved = true; + GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken); + + if (token == null) return null; + if (!allowUnapproved && !token.Approved) return null; + + return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId); + } + + public async Task UserFromGameToken + (GameToken gameToken, bool allowUnapproved = false) + => await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved); + + public async Task UserFromGameRequest(HttpRequest request, bool allowUnapproved = false) + { + if (ServerStatics.IsUnitTesting) allowUnapproved = true; + if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; + + return await this.UserFromMMAuth(mmAuth, allowUnapproved); + } + + public async Task GameTokenFromRequest(HttpRequest request, bool allowUnapproved = false) + { + if (ServerStatics.IsUnitTesting) allowUnapproved = true; + if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; + + GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); + + if (token == null) return null; + if (!allowUnapproved && !token.Approved) return null; + + return token; + } + + public async Task<(User, GameToken)?> UserAndGameTokenFromRequest(HttpRequest request, bool allowUnapproved = false) + { + if (ServerStatics.IsUnitTesting) allowUnapproved = true; + if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; + + GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); + if (token == null) return null; + if (!allowUnapproved && !token.Approved) return null; + + User? user = await this.UserFromGameToken(token); + + if (user == null) return null; + + return (user, token); + } + + #endregion + + #region Web Token Shenanigans + + public User? UserFromLighthouseToken(string lighthouseToken) + { + WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken); + if (token == null) return null; + + return this.Users.Include(u => u.Location).FirstOrDefault(u => u.UserId == token.UserId); + } + + public User? UserFromWebRequest(HttpRequest request) + { + if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null; + + return this.UserFromLighthouseToken(lighthouseToken); + } + + public WebToken? WebTokenFromRequest(HttpRequest request) + { + if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null; + + return this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken); + } + + #endregion + + public async Task PhotoFromSubject(PhotoSubject subject) + => await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString())); + public async Task RemoveUser(User? user) + { + if (user == null) return; + + if (user.Location != null) this.Locations.Remove(user.Location); + LastContact? lastContact = await this.LastContacts.FirstOrDefaultAsync(l => l.UserId == user.UserId); + if (lastContact != null) this.LastContacts.Remove(lastContact); + + foreach (Slot slot in this.Slots.Where(s => s.CreatorId == user.UserId)) await this.RemoveSlot(slot, false); + + this.AuthenticationAttempts.RemoveRange(this.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId)); + this.HeartedProfiles.RemoveRange(this.HeartedProfiles.Where(h => h.UserId == user.UserId)); + this.PhotoSubjects.RemoveRange(this.PhotoSubjects.Where(s => s.UserId == user.UserId)); + this.HeartedLevels.RemoveRange(this.HeartedLevels.Where(h => h.UserId == user.UserId)); + this.VisitedLevels.RemoveRange(this.VisitedLevels.Where(v => v.UserId == user.UserId)); + this.RatedReviews.RemoveRange(this.RatedReviews.Where(r => r.UserId == user.UserId)); + this.QueuedLevels.RemoveRange(this.QueuedLevels.Where(q => q.UserId == user.UserId)); + this.RatedLevels.RemoveRange(this.RatedLevels.Where(r => r.UserId == user.UserId)); + this.GameTokens.RemoveRange(this.GameTokens.Where(t => t.UserId == user.UserId)); + this.WebTokens.RemoveRange(this.WebTokens.Where(t => t.UserId == user.UserId)); + this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId)); + this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId)); + this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId)); + + this.Users.Remove(user); + + await this.SaveChangesAsync(); + } + + public async Task RemoveSlot(Slot slot, bool saveChanges = true) + { + if (slot.Location != null) this.Locations.Remove(slot.Location); + this.Slots.Remove(slot); + + if (saveChanges) await this.SaveChangesAsync(); + } + #nullable disable } \ No newline at end of file diff --git a/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs b/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs index 58018157..c17e4f82 100644 --- a/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs +++ b/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs @@ -2,23 +2,22 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace LBPUnion.ProjectLighthouse +namespace LBPUnion.ProjectLighthouse; + +public class FakeRemoteIPAddressMiddleware { - public class FakeRemoteIPAddressMiddleware + private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1"); + private readonly RequestDelegate next; + + public FakeRemoteIPAddressMiddleware(RequestDelegate next) { - private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1"); - private readonly RequestDelegate next; + this.next = next; + } - public FakeRemoteIPAddressMiddleware(RequestDelegate next) - { - this.next = next; - } + public async Task Invoke(HttpContext httpContext) + { + httpContext.Connection.RemoteIpAddress = this.fakeIpAddress; - public async Task Invoke(HttpContext httpContext) - { - httpContext.Connection.RemoteIpAddress = this.fakeIpAddress; - - await this.next(httpContext); - } + await this.next(httpContext); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/BinaryHelper.cs b/ProjectLighthouse/Helpers/BinaryHelper.cs index 3765ef46..8e44c4ff 100644 --- a/ProjectLighthouse/Helpers/BinaryHelper.cs +++ b/ProjectLighthouse/Helpers/BinaryHelper.cs @@ -6,60 +6,59 @@ using System.IO.Pipelines; using System.Text; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class BinaryHelper { - public static class BinaryHelper + public static string ReadString(BinaryReader reader) { - public static string ReadString(BinaryReader reader) + List readBytes = new(); + + byte readByte; + do readBytes.Add(readByte = reader.ReadByte()); + while (readByte != 0x00); + + return Encoding.UTF8.GetString(readBytes.ToArray()); + } + + public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) + { + byte readByte; + do readByte = reader.ReadByte(); + while (readByte != byteToReadTo); + } + + public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) + { + long oldPosition = reader.BaseStream.Position; + + if (reader.BaseStream.Length < count) return Array.Empty(); + + reader.BaseStream.Position = reader.BaseStream.Length - count; + byte[] data = reader.ReadBytes(count); + + if (restoreOldPosition) reader.BaseStream.Position = oldPosition; + return data; + } + + // Written with reference from + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 + // Surprisingly doesn't take seconds. (67ms for a 100kb file) + public static async Task ReadFromPipeReader(PipeReader reader) + { + List data = new(); + while (true) { - List readBytes = new(); + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; - byte readByte; - do readBytes.Add(readByte = reader.ReadByte()); - while (readByte != 0x00); + if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); - return Encoding.UTF8.GetString(readBytes.ToArray()); + reader.AdvanceTo(buffer.Start, buffer.End); + + if (readResult.IsCompleted) break; } - public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) - { - byte readByte; - do readByte = reader.ReadByte(); - while (readByte != byteToReadTo); - } - - public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) - { - long oldPosition = reader.BaseStream.Position; - - if (reader.BaseStream.Length < count) return Array.Empty(); - - reader.BaseStream.Position = reader.BaseStream.Length - count; - byte[] data = reader.ReadBytes(count); - - if (restoreOldPosition) reader.BaseStream.Position = oldPosition; - return data; - } - - // Written with reference from - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 - // Surprisingly doesn't take seconds. (67ms for a 100kb file) - public static async Task ReadFromPipeReader(PipeReader reader) - { - List data = new(); - while (true) - { - ReadResult readResult = await reader.ReadAsync(); - ReadOnlySequence buffer = readResult.Buffer; - - if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); - - reader.AdvanceTo(buffer.Start, buffer.End); - - if (readResult.IsCompleted) break; - } - - return data.ToArray(); - } + return data.ToArray(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/CollectionHelper.cs b/ProjectLighthouse/Helpers/CollectionHelper.cs index 6d950409..7a19065a 100644 --- a/ProjectLighthouse/Helpers/CollectionHelper.cs +++ b/ProjectLighthouse/Helpers/CollectionHelper.cs @@ -1,21 +1,20 @@ using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Types.Categories; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class CollectionHelper { - public static class CollectionHelper + public static readonly List Categories = new(); + + static CollectionHelper() { - public static readonly List Categories = new(); + Categories.Add(new TeamPicksCategory()); + Categories.Add(new NewestLevelsCategory()); + Categories.Add(new QueueCategory()); + Categories.Add(new HeartedCategory()); - static CollectionHelper() - { - Categories.Add(new TeamPicksCategory()); - Categories.Add(new NewestLevelsCategory()); - Categories.Add(new QueueCategory()); - Categories.Add(new HeartedCategory()); - - using Database database = new(); - foreach (DatabaseCategory category in database.CustomCategories) Categories.Add(new CustomCategory(category)); - } + using Database database = new(); + foreach (DatabaseCategory category in database.CustomCategories) Categories.Add(new CustomCategory(category)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs b/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs index 1dc990a0..3b805fa9 100644 --- a/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs +++ b/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs @@ -1,38 +1,37 @@ using System.Collections.Generic; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class DeniedAuthenticationHelper { - public static class DeniedAuthenticationHelper + public static readonly Dictionary IPAddressAndNameDeniedAt = new(); + public static readonly Dictionary AttemptsByIPAddressAndName = new(); + + public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0) { - public static readonly Dictionary IPAddressAndNameDeniedAt = new(); - public static readonly Dictionary AttemptsByIPAddressAndName = new(); + if (timestamp == 0) timestamp = TimestampHelper.Timestamp; - public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0) - { - if (timestamp == 0) timestamp = TimestampHelper.Timestamp; + if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName); + IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp); + } - if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName); - IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp); - } + public static bool RecentlyDenied(string ipAddressAndName) + { + if (!IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long timestamp)) return false; - public static bool RecentlyDenied(string ipAddressAndName) - { - if (!IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long timestamp)) return false; + return TimestampHelper.Timestamp < timestamp + 300; + } - return TimestampHelper.Timestamp < timestamp + 300; - } + public static void AddAttempt(string ipAddressAndName) + { + if (AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) AttemptsByIPAddressAndName.Remove(ipAddressAndName); + AttemptsByIPAddressAndName.Add(ipAddressAndName, attempts + 1); + } - public static void AddAttempt(string ipAddressAndName) - { - if (AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) AttemptsByIPAddressAndName.Remove(ipAddressAndName); - AttemptsByIPAddressAndName.Add(ipAddressAndName, attempts + 1); - } + public static int GetAttempts(string ipAddressAndName) + { + if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0; - public static int GetAttempts(string ipAddressAndName) - { - if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0; - - return attempts; - } + return attempts; } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/EulaHelper.cs b/ProjectLighthouse/Helpers/EulaHelper.cs index 03c91a38..3d9398c7 100644 --- a/ProjectLighthouse/Helpers/EulaHelper.cs +++ b/ProjectLighthouse/Helpers/EulaHelper.cs @@ -1,8 +1,8 @@ -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class EulaHelper { - public static class EulaHelper - { - public const string License = @" + public const string License = @" This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the @@ -15,5 +15,4 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see ."; - } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs b/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs index 5a119dbc..5b6e4623 100644 --- a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs @@ -3,26 +3,25 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions +namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; + +// https://stackoverflow.com/a/8039737 +public static class ExceptionExtensions { - // https://stackoverflow.com/a/8039737 - public static class ExceptionExtensions + public static string ToDetailedException(this Exception exception) { - public static string ToDetailedException(this Exception exception) - { - PropertyInfo[] properties = exception.GetType().GetProperties(); + PropertyInfo[] properties = exception.GetType().GetProperties(); - IEnumerable fields = properties.Select - ( - property => new - { - property.Name, - Value = property.GetValue(exception, null), - } - ) - .Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}"); + IEnumerable fields = properties.Select + ( + property => new + { + property.Name, + Value = property.GetValue(exception, null), + } + ) + .Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}"); - return string.Join("\n", fields); - } + return string.Join("\n", fields); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs b/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs index a31b359a..05208124 100644 --- a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs @@ -2,14 +2,13 @@ using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions -{ - // yoinked and adapted from https://stackoverflow.com/a/68641796 - public static class RequestExtensions - { - private static readonly Regex mobileCheck = new - ("Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled); +namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; - public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString()); - } +// yoinked and adapted from https://stackoverflow.com/a/68641796 +public static class RequestExtensions +{ + private static readonly Regex mobileCheck = new + ("Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled); + + public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString()); } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs index 72ef05af..fe2564ac 100644 --- a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs @@ -1,10 +1,9 @@ using System.IO; using System.Linq; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions +namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; + +public static class StringExtensions { - public static class StringExtensions - { - public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), "")); - } + public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), "")); } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index ce33cff1..08c4caf3 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -5,99 +5,98 @@ using System.Text; using LBPUnion.ProjectLighthouse.Types.Files; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class FileHelper { - public static class FileHelper + public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r"); + + public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash); + + public static bool IsFileSafe(LbpFile file) { - public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r"); + if (!ServerSettings.Instance.CheckForUnsafeFiles) return true; - public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash); + if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); - public static bool IsFileSafe(LbpFile file) + return file.FileType switch { - if (!ServerSettings.Instance.CheckForUnsafeFiles) return true; - - if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); - - return file.FileType switch - { - LbpFileType.FileArchive => false, - LbpFileType.Painting => true, - LbpFileType.Unknown => false, - LbpFileType.Texture => true, - LbpFileType.Script => false, - LbpFileType.Level => true, - LbpFileType.Voice => true, - LbpFileType.Plan => true, - LbpFileType.Jpeg => true, - LbpFileType.Png => true, - #if DEBUG + LbpFileType.FileArchive => false, + LbpFileType.Painting => true, + LbpFileType.Unknown => false, + LbpFileType.Texture => true, + LbpFileType.Script => false, + LbpFileType.Level => true, + LbpFileType.Voice => true, + LbpFileType.Plan => true, + LbpFileType.Jpeg => true, + LbpFileType.Png => true, + #if DEBUG _ => throw new ArgumentOutOfRangeException(nameof(file), $"Unhandled file type ({file.FileType}) in FileHelper.IsFileSafe()"), - #else - _ => false, - #endif - }; - } - - public static LbpFileType DetermineFileType(byte[] data) - { - if (data.Length == 0) return LbpFileType.Unknown; // Can't be anything if theres no data. - - using MemoryStream ms = new(data); - using BinaryReader reader = new(ms); - - // Determine if file is a FARC (File Archive). - // Needs to be done before anything else that determines the type by the header - // because this determines the type by the footer. - string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4)); - if (footer == "FARC") return LbpFileType.FileArchive; - - byte[] header = reader.ReadBytes(3); - - return Encoding.ASCII.GetString(header) switch - { - "PTG" => LbpFileType.Painting, - "TEX" => LbpFileType.Texture, - "FSH" => LbpFileType.Script, - "VOP" => LbpFileType.Voice, - "LVL" => LbpFileType.Level, - "PLN" => LbpFileType.Plan, - _ => determineFileTypePartTwoWeirdName(reader), - }; - } - - private static LbpFileType determineFileTypePartTwoWeirdName(BinaryReader reader) - { - reader.BaseStream.Position = 0; - - // Determine if file is JPEG/PNG - byte[] header = reader.ReadBytes(9); - - if (header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF && header[3] == 0xE0) return LbpFileType.Jpeg; - if (header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47) return LbpFileType.Png; - - return LbpFileType.Unknown; // Still unknown. - } - - public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); - - public static int ResourceSize(string hash) - { - try - { - return (int)new FileInfo(GetResourcePath(hash)).Length; - } - catch - { - return 0; - } - } - - public static void EnsureDirectoryCreated(string path) - { - if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path))); - } - - public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray(); + #else + _ => false, + #endif + }; } + + public static LbpFileType DetermineFileType(byte[] data) + { + if (data.Length == 0) return LbpFileType.Unknown; // Can't be anything if theres no data. + + using MemoryStream ms = new(data); + using BinaryReader reader = new(ms); + + // Determine if file is a FARC (File Archive). + // Needs to be done before anything else that determines the type by the header + // because this determines the type by the footer. + string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4)); + if (footer == "FARC") return LbpFileType.FileArchive; + + byte[] header = reader.ReadBytes(3); + + return Encoding.ASCII.GetString(header) switch + { + "PTG" => LbpFileType.Painting, + "TEX" => LbpFileType.Texture, + "FSH" => LbpFileType.Script, + "VOP" => LbpFileType.Voice, + "LVL" => LbpFileType.Level, + "PLN" => LbpFileType.Plan, + _ => determineFileTypePartTwoWeirdName(reader), + }; + } + + private static LbpFileType determineFileTypePartTwoWeirdName(BinaryReader reader) + { + reader.BaseStream.Position = 0; + + // Determine if file is JPEG/PNG + byte[] header = reader.ReadBytes(9); + + if (header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF && header[3] == 0xE0) return LbpFileType.Jpeg; + if (header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47) return LbpFileType.Png; + + return LbpFileType.Unknown; // Still unknown. + } + + public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); + + public static int ResourceSize(string hash) + { + try + { + return (int)new FileInfo(GetResourcePath(hash)).Length; + } + catch + { + return 0; + } + } + + public static void EnsureDirectoryCreated(string path) + { + if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path))); + } + + public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray(); } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/FriendHelper.cs b/ProjectLighthouse/Helpers/FriendHelper.cs index 41639c46..e5f0bf43 100644 --- a/ProjectLighthouse/Helpers/FriendHelper.cs +++ b/ProjectLighthouse/Helpers/FriendHelper.cs @@ -2,13 +2,12 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +[NotMapped] +[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] +public static class FriendHelper { - [NotMapped] - [SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] - public static class FriendHelper - { - public static readonly Dictionary FriendIdsByUserId = new(); - public static readonly Dictionary BlockedIdsByUserId = new(); - } + public static readonly Dictionary FriendIdsByUserId = new(); + public static readonly Dictionary BlockedIdsByUserId = new(); } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/GameVersionHelper.cs b/ProjectLighthouse/Helpers/GameVersionHelper.cs index 748b5cf7..a1104644 100644 --- a/ProjectLighthouse/Helpers/GameVersionHelper.cs +++ b/ProjectLighthouse/Helpers/GameVersionHelper.cs @@ -1,102 +1,101 @@ using System.Linq; using LBPUnion.ProjectLighthouse.Types; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public class GameVersionHelper { - public class GameVersionHelper + // https://www.serialstation.com/games/b89b4eb4-4e4c-4e54-b72b-f7f9dbfac125 + public static readonly string[] LittleBigPlanet1TitleIds = { - // https://www.serialstation.com/games/b89b4eb4-4e4c-4e54-b72b-f7f9dbfac125 - public static readonly string[] LittleBigPlanet1TitleIds = - { - "BCES00141", - "BCAS20091", - "BCUS98208", - "BCAS20078", - "BCJS70009", - "BCES00611", - "BCUS98148", - "BCAS20058", - "BCJS30018", - "UCAS40262", - "BCET70011", - "BCUS98199", - "BCJB95003", - "NPUA70045", - "NPEA00241", - "NPEA00147", - "NPHG00033", - "NPHG00035", - }; + "BCES00141", + "BCAS20091", + "BCUS98208", + "BCAS20078", + "BCJS70009", + "BCES00611", + "BCUS98148", + "BCAS20058", + "BCJS30018", + "UCAS40262", + "BCET70011", + "BCUS98199", + "BCJB95003", + "NPUA70045", + "NPEA00241", + "NPEA00147", + "NPHG00033", + "NPHG00035", + }; - // https://serialstation.com/games/35e69aba-1872-4fd7-9d39-11ce75924040 - public static readonly string[] LittleBigPlanet2TitleIds = - { - "BCUS98249", - "BCES01086", - "BCAS20113", - "BCJS70024", - "BCAS20201", - "BCUS98245", - "BCES01345", - "BCJS30058", - "BCUS98372", - "BCES00850", - "BCES01346", - "BCUS90260", - "BCET70023", - "BCES01694", - "NPUA80662", - }; + // https://serialstation.com/games/35e69aba-1872-4fd7-9d39-11ce75924040 + public static readonly string[] LittleBigPlanet2TitleIds = + { + "BCUS98249", + "BCES01086", + "BCAS20113", + "BCJS70024", + "BCAS20201", + "BCUS98245", + "BCES01345", + "BCJS30058", + "BCUS98372", + "BCES00850", + "BCES01346", + "BCUS90260", + "BCET70023", + "BCES01694", + "NPUA80662", + }; - // https://www.serialstation.com/games/b62d53d9-fdff-4463-8134-64b81e1cbd50 - // includes PS4 games - public static readonly string[] LittleBigPlanet3TitleIds = - { - "CUSA00063", - "CUSA00693", - "CUSA00473", - "CUSA00810", - "CUSA00473", - "CUSA01072", - "CUSA00738", - "PCJS50003", - "BCES02068", - "BCAS20322", - "BCJS30095", - "BCES01663", - "CUSA00063", - "BCUS98362", - "PCKS90007", - "PCAS00012", - "CUSA00601", - "CUSA00810", - "CUSA00762", - "PCAS20007", - "CUSA00473", - "CUSA01077", - "CUSA01304", - "NPUA81116", - }; + // https://www.serialstation.com/games/b62d53d9-fdff-4463-8134-64b81e1cbd50 + // includes PS4 games + public static readonly string[] LittleBigPlanet3TitleIds = + { + "CUSA00063", + "CUSA00693", + "CUSA00473", + "CUSA00810", + "CUSA00473", + "CUSA01072", + "CUSA00738", + "PCJS50003", + "BCES02068", + "BCAS20322", + "BCJS30095", + "BCES01663", + "CUSA00063", + "BCUS98362", + "PCKS90007", + "PCAS00012", + "CUSA00601", + "CUSA00810", + "CUSA00762", + "PCAS20007", + "CUSA00473", + "CUSA01077", + "CUSA01304", + "NPUA81116", + }; - public static readonly string[] LittleBigPlanetVitaTitleIds = - { - "PCSF00021", "PCSA00017", "PCSC00013", "PCSD00006", "PCSA00549", "PCSF00516", - }; + public static readonly string[] LittleBigPlanetVitaTitleIds = + { + "PCSF00021", "PCSA00017", "PCSC00013", "PCSD00006", "PCSA00549", "PCSF00516", + }; - public static readonly string[] LittleBigPlanetPSPTitleIds = - { - "NPWR00500", "UCAS40262", "UCES01264", "UCUS98744", "UCJS10107", - }; + public static readonly string[] LittleBigPlanetPSPTitleIds = + { + "NPWR00500", "UCAS40262", "UCES01264", "UCUS98744", "UCJS10107", + }; - public static GameVersion FromTitleId(string titleId) - { - if (LittleBigPlanet1TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet1; - if (LittleBigPlanet2TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet2; - if (LittleBigPlanet3TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet3; - if (LittleBigPlanetVitaTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetVita; - if (LittleBigPlanetPSPTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetPSP; + public static GameVersion FromTitleId(string titleId) + { + if (LittleBigPlanet1TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet1; + if (LittleBigPlanet2TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet2; + if (LittleBigPlanet3TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet3; + if (LittleBigPlanetVitaTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetVita; + if (LittleBigPlanetPSPTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetPSP; - return GameVersion.LittleBigPlanet1; - } + return GameVersion.LittleBigPlanet1; } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/HashHelper.cs b/ProjectLighthouse/Helpers/HashHelper.cs index 88012439..15dc3022 100644 --- a/ProjectLighthouse/Helpers/HashHelper.cs +++ b/ProjectLighthouse/Helpers/HashHelper.cs @@ -6,78 +6,77 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public static class HashHelper { - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static class HashHelper + private static readonly SHA1 sha1 = SHA1.Create(); + private static readonly SHA256 sha256 = SHA256.Create(); + private static readonly Random random = new(); + + /// + /// Generates a specified amount of random bytes in an array. + /// + /// The amount of bytes to generate. + /// The bytes generated + public static IEnumerable GenerateRandomBytes(int count) { - private static readonly SHA1 sha1 = SHA1.Create(); - private static readonly SHA256 sha256 = SHA256.Create(); - private static readonly Random random = new(); - - /// - /// Generates a specified amount of random bytes in an array. - /// - /// The amount of bytes to generate. - /// The bytes generated - public static IEnumerable GenerateRandomBytes(int count) - { - byte[] b = new byte[count]; - random.NextBytes(b); - - return b; - } - - /// - /// Generates a random SHA256 & BCrypted token - /// - /// The token as a string. - public static string GenerateAuthToken() - { - byte[] bytes = (byte[])GenerateRandomBytes(256); - - return BCryptHash(Sha256Hash(bytes)); - } - - public static async Task ComputeDigest(string path, string authCookie, Stream body, string digestKey) - { - MemoryStream memoryStream = new(); - - byte[] pathBytes = Encoding.UTF8.GetBytes(path); - byte[] cookieBytes = string.IsNullOrEmpty(authCookie) ? Array.Empty() : Encoding.UTF8.GetBytes(authCookie); - byte[] keyBytes = Encoding.UTF8.GetBytes(digestKey); - - await body.CopyToAsync(memoryStream); - - byte[] bodyBytes = memoryStream.ToArray(); - - using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); - sha1.AppendData(bodyBytes); - if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes); - sha1.AppendData(pathBytes); - sha1.AppendData(keyBytes); - - byte[] digestBytes = sha1.GetHashAndReset(); - string digestString = Convert.ToHexString(digestBytes).ToLower(); - - return digestString; - } - - #region Hash Functions - - public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); - - public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower(); - - public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str)); - - public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", ""); - - public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); - - public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); - - #endregion + byte[] b = new byte[count]; + random.NextBytes(b); + return b; } + + /// + /// Generates a random SHA256 & BCrypted token + /// + /// The token as a string. + public static string GenerateAuthToken() + { + byte[] bytes = (byte[])GenerateRandomBytes(256); + + return BCryptHash(Sha256Hash(bytes)); + } + + public static async Task ComputeDigest(string path, string authCookie, Stream body, string digestKey) + { + MemoryStream memoryStream = new(); + + byte[] pathBytes = Encoding.UTF8.GetBytes(path); + byte[] cookieBytes = string.IsNullOrEmpty(authCookie) ? Array.Empty() : Encoding.UTF8.GetBytes(authCookie); + byte[] keyBytes = Encoding.UTF8.GetBytes(digestKey); + + await body.CopyToAsync(memoryStream); + + byte[] bodyBytes = memoryStream.ToArray(); + + using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1); + sha1.AppendData(bodyBytes); + if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes); + sha1.AppendData(pathBytes); + sha1.AppendData(keyBytes); + + byte[] digestBytes = sha1.GetHashAndReset(); + string digestString = Convert.ToHexString(digestBytes).ToLower(); + + return digestString; + } + + #region Hash Functions + + public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); + + public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower(); + + public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str)); + + public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", ""); + + public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); + + public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); + + #endregion + } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/InfluxHelper.cs b/ProjectLighthouse/Helpers/InfluxHelper.cs index 166429fc..3876a2b5 100644 --- a/ProjectLighthouse/Helpers/InfluxHelper.cs +++ b/ProjectLighthouse/Helpers/InfluxHelper.cs @@ -6,45 +6,44 @@ using Kettu; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class InfluxHelper { - public static class InfluxHelper + public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken); + + public static async void Log() { - public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken); + using WriteApi writeApi = Client.GetWriteApi(); + PointData point = PointData.Measurement("lighthouse") + .Field("playerCount", await StatisticsHelper.RecentMatches()) + .Field("slotCount", await StatisticsHelper.SlotCount()); - public static async void Log() - { - using WriteApi writeApi = Client.GetWriteApi(); - PointData point = PointData.Measurement("lighthouse") - .Field("playerCount", await StatisticsHelper.RecentMatches()) - .Field("slotCount", await StatisticsHelper.SlotCount()); + writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); - writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); + writeApi.Flush(); + } - writeApi.Flush(); - } - - public static async Task StartLogging() - { - await Client.ReadyAsync(); - Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance); - Thread t = new - ( - delegate() + public static async Task StartLogging() + { + await Client.ReadyAsync(); + Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance); + Thread t = new + ( + delegate() + { + while (true) { - while (true) - { - #pragma warning disable CS4014 - Log(); - #pragma warning restore CS4014 + #pragma warning disable CS4014 + Log(); + #pragma warning restore CS4014 // Logger.Log("Logged.", LoggerLevelInflux.Instance); - Thread.Sleep(60000); - } + Thread.Sleep(60000); } - ); - t.IsBackground = true; - t.Name = "InfluxDB Logger"; - t.Start(); - } + } + ); + t.IsBackground = true; + t.Name = "InfluxDB Logger"; + t.Start(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/LastContactHelper.cs b/ProjectLighthouse/Helpers/LastContactHelper.cs index fa06e84b..cdc7a06d 100644 --- a/ProjectLighthouse/Helpers/LastContactHelper.cs +++ b/ProjectLighthouse/Helpers/LastContactHelper.cs @@ -5,31 +5,30 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class LastContactHelper { - public static class LastContactHelper + private static readonly Database database = new(); + + public static async Task SetLastContact(User user, GameVersion gameVersion) { - private static readonly Database database = new(); + LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); - public static async Task SetLastContact(User user, GameVersion gameVersion) + // below makes it not look like trash + // ReSharper disable once ConvertIfStatementToNullCoalescingExpression + if (lastContact == null) { - LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); - - // below makes it not look like trash - // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - if (lastContact == null) + lastContact = new LastContact { - lastContact = new LastContact - { - UserId = user.UserId, - }; - database.LastContacts.Add(lastContact); - } - - lastContact.Timestamp = TimestampHelper.Timestamp; - lastContact.GameVersion = gameVersion; - - await database.SaveChangesAsync(); + UserId = user.UserId, + }; + database.LastContacts.Add(lastContact); } + + lastContact.Timestamp = TimestampHelper.Timestamp; + lastContact.GameVersion = gameVersion; + + await database.SaveChangesAsync(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/MaintenanceHelper.cs b/ProjectLighthouse/Helpers/MaintenanceHelper.cs index a2794e57..7b50b49d 100644 --- a/ProjectLighthouse/Helpers/MaintenanceHelper.cs +++ b/ProjectLighthouse/Helpers/MaintenanceHelper.cs @@ -6,65 +6,61 @@ using System.Reflection; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Maintenance; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class MaintenanceHelper { - public static class MaintenanceHelper + + static MaintenanceHelper() { + Commands = getListOfInterfaceObjects(); + MaintenanceJobs = getListOfInterfaceObjects(); + } + public static List Commands { get; } - static MaintenanceHelper() + public static List MaintenanceJobs { get; } + + private static List getListOfInterfaceObjects() where T : class + { + return Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) + .Select(t => Activator.CreateInstance(t) as T) + .ToList()!; + } + + public static async Task RunCommand(string[] args) + { + if (args.Length < 1) + throw new Exception + ("This should never happen. " + "If it did, its because you tried to run a command before validating that the user actually wants to run one."); + + string baseCmd = args[0]; + args = args.Skip(1).ToArray(); + + IEnumerable suitableCommands = Commands.Where + (command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower())) + .Where(command => args.Length >= command.RequiredArgs()); + foreach (ICommand command in suitableCommands) { - Commands = getListOfInterfaceObjects(); - MaintenanceJobs = getListOfInterfaceObjects(); - } - public static List Commands { get; } - - public static List MaintenanceJobs { get; } - - private static List getListOfInterfaceObjects() where T : class - { - return Assembly.GetExecutingAssembly() - .GetTypes() - .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) - .Select(t => Activator.CreateInstance(t) as T) - .ToList()!; + Console.WriteLine("Running command " + command.Name()); + await command.Run(args); + return; } - public static async Task RunCommand(string[] args) - { - if (args.Length < 1) - throw new Exception - ( - "This should never happen. " + - "If it did, its because you tried to run a command before validating that the user actually wants to run one." - ); + Console.WriteLine("Command not found."); + } - string baseCmd = args[0]; - args = args.Skip(1).ToArray(); + public static async Task RunMaintenanceJob(string jobName) + { + IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName); + if (job == null) throw new ArgumentNullException(); - IEnumerable suitableCommands = Commands.Where - (command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower())) - .Where(command => args.Length >= command.RequiredArgs()); - foreach (ICommand command in suitableCommands) - { - Console.WriteLine("Running command " + command.Name()); - await command.Run(args); - return; - } + await RunMaintenanceJob(job); + } - Console.WriteLine("Command not found."); - } - - public static async Task RunMaintenanceJob(string jobName) - { - IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName); - if (job == null) throw new ArgumentNullException(); - - await RunMaintenanceJob(job); - } - - public static async Task RunMaintenanceJob(IMaintenanceJob job) - { - await job.Run(); - } + public static async Task RunMaintenanceJob(IMaintenanceJob job) + { + await job.Run(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/MatchHelper.cs b/ProjectLighthouse/Helpers/MatchHelper.cs index 5afa10e6..f04eb938 100644 --- a/ProjectLighthouse/Helpers/MatchHelper.cs +++ b/ProjectLighthouse/Helpers/MatchHelper.cs @@ -7,70 +7,69 @@ using System.Text.Json; using System.Text.RegularExpressions; using LBPUnion.ProjectLighthouse.Types.Match; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class MatchHelper { - public static class MatchHelper + public static readonly Dictionary UserLocations = new(); + public static readonly Dictionary?> UserRecentlyDivedIn = new(); + + public static void SetUserLocation(int userId, string location) { - public static readonly Dictionary UserLocations = new(); - public static readonly Dictionary?> UserRecentlyDivedIn = new(); + if (UserLocations.TryGetValue(userId, out string? _)) UserLocations.Remove(userId); + UserLocations.Add(userId, location); + } - public static void SetUserLocation(int userId, string location) + public static void AddUserRecentlyDivedIn(int userId, int otherUserId) + { + if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn)) UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List()); + + Debug.Assert(recentlyDivedIn != null, nameof(recentlyDivedIn) + " is null, somehow."); + + recentlyDivedIn.Add(otherUserId); + } + + public static bool DidUserRecentlyDiveInWith(int userId, int otherUserId) + { + if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn) || recentlyDivedIn == null) return false; + + return recentlyDivedIn.Contains(otherUserId); + } + + // This is the function used to show people how laughably awful LBP's protocol is. Beware. + public static IMatchData? Deserialize(string data) + { + string matchType = ""; + + int i = 1; + while (true) { - if (UserLocations.TryGetValue(userId, out string? _)) UserLocations.Remove(userId); - UserLocations.Add(userId, location); + if (data[i] == ',') break; + + matchType += data[i]; + i++; } - public static void AddUserRecentlyDivedIn(int userId, int otherUserId) + string matchData = $"{{{string.Concat(data.Skip(matchType.Length + 3).SkipLast(2))}}}"; // unfuck formatting so we can parse it as json + + // JSON does not like the hex value that location comes in (0x7f000001) so, convert it to int + matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{8}", m => Convert.ToInt32(m.Value, 16).ToString()); + // oh, but it gets better than that! LBP also likes to send hex values with an uneven amount of digits (0xa000064, 7 digits). in any case, we handle it here: + matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{7}", m => Convert.ToInt32(m.Value, 16).ToString()); + // i'm actually crying about it. + + return Deserialize(matchType, matchData); + } + + public static IMatchData? Deserialize(string matchType, string matchData) + { + return matchType switch { - if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn)) UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List()); - - Debug.Assert(recentlyDivedIn != null, nameof(recentlyDivedIn) + " is null, somehow."); - - recentlyDivedIn.Add(otherUserId); - } - - public static bool DidUserRecentlyDiveInWith(int userId, int otherUserId) - { - if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn) || recentlyDivedIn == null) return false; - - return recentlyDivedIn.Contains(otherUserId); - } - - // This is the function used to show people how laughably awful LBP's protocol is. Beware. - public static IMatchData? Deserialize(string data) - { - string matchType = ""; - - int i = 1; - while (true) - { - if (data[i] == ',') break; - - matchType += data[i]; - i++; - } - - string matchData = $"{{{string.Concat(data.Skip(matchType.Length + 3).SkipLast(2))}}}"; // unfuck formatting so we can parse it as json - - // JSON does not like the hex value that location comes in (0x7f000001) so, convert it to int - matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{8}", m => Convert.ToInt32(m.Value, 16).ToString()); - // oh, but it gets better than that! LBP also likes to send hex values with an uneven amount of digits (0xa000064, 7 digits). in any case, we handle it here: - matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{7}", m => Convert.ToInt32(m.Value, 16).ToString()); - // i'm actually crying about it. - - return Deserialize(matchType, matchData); - } - - public static IMatchData? Deserialize(string matchType, string matchData) - { - return matchType switch - { - "UpdateMyPlayerData" => JsonSerializer.Deserialize(matchData), - "UpdatePlayersInRoom" => JsonSerializer.Deserialize(matchData), - "CreateRoom" => JsonSerializer.Deserialize(matchData), - "FindBestRoom" => JsonSerializer.Deserialize(matchData), - _ => null, - }; - } + "UpdateMyPlayerData" => JsonSerializer.Deserialize(matchData), + "UpdatePlayersInRoom" => JsonSerializer.Deserialize(matchData), + "CreateRoom" => JsonSerializer.Deserialize(matchData), + "FindBestRoom" => JsonSerializer.Deserialize(matchData), + _ => null, + }; } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/RoomHelper.cs b/ProjectLighthouse/Helpers/RoomHelper.cs index e95907f3..7cca021b 100644 --- a/ProjectLighthouse/Helpers/RoomHelper.cs +++ b/ProjectLighthouse/Helpers/RoomHelper.cs @@ -8,184 +8,177 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Match; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public class RoomHelper { - public class RoomHelper + public static readonly List Rooms = new(); + + public static readonly RoomSlot PodSlot = new() { - public static readonly List Rooms = new(); + SlotType = SlotType.Pod, + SlotId = 0, + }; - public static readonly RoomSlot PodSlot = new() + private static int roomIdIncrement; + + internal static int RoomIdIncrement => roomIdIncrement++; + + public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, string? location) + { + if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP) { - SlotType = SlotType.Pod, - SlotId = 0, - }; - - private static int roomIdIncrement; - - internal static int RoomIdIncrement => roomIdIncrement++; - - public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, string? location) - { - if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP) - { - Logger.Log($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in", LoggerLevelMatch.Instance); - return null; - } - - bool anyRoomsLookingForPlayers; - List rooms; - - lock(Rooms) - { - anyRoomsLookingForPlayers = Rooms.Any(r => r.IsLookingForPlayers); - rooms = anyRoomsLookingForPlayers ? Rooms.Where(r => anyRoomsLookingForPlayers && r.IsLookingForPlayers).ToList() : Rooms; - } - - rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList(); - - foreach (Room room in rooms) - // Look for rooms looking for players before moving on to rooms that are idle. - { - if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.Host.UserId)) continue; - - Dictionary relevantUserLocations = new(); - - // Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it - bool allPlayersHaveLocations = room.Players.All - ( - p => - { - bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value); - - if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value); - return gotValue; - } - ); - - // If we don't have all locations then the game won't know how to communicate. Thus, it's not a valid room. - if (!allPlayersHaveLocations) continue; - - // If we got here then it should be a valid room. - - FindBestRoomResponse response = new(); - response.RoomId = room.RoomId; - - response.Players = new List(); - response.Locations = new List(); - foreach (User player in room.Players) - { - response.Players.Add - ( - new Player - { - MatchingRes = 0, - User = player, - } - ); - - response.Locations.Add(relevantUserLocations.GetValueOrDefault(player.UserId)); // Already validated to exist - } - - if (user != null) - { - response.Players.Add - ( - new Player - { - MatchingRes = 1, - User = user, - } - ); - } - - if (location == null) - { - response.Locations.Add(location); - } - - response.Slots = new List> - { - new() - { - (int)room.Slot.SlotType, - room.Slot.SlotId, - }, - }; - - Logger.Log($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LoggerLevelMatch.Instance); - - return response; - } - + Logger.Log($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in", LoggerLevelMatch.Instance); return null; } - public static Room CreateRoom(User user, GameVersion roomVersion, RoomSlot? slot = null) - => CreateRoom - ( - new List - { - user, - }, - roomVersion, - slot - ); - public static Room CreateRoom(List users, GameVersion roomVersion, RoomSlot? slot = null) + bool anyRoomsLookingForPlayers; + List rooms; + + lock(Rooms) { - Room room = new() + anyRoomsLookingForPlayers = Rooms.Any(r => r.IsLookingForPlayers); + rooms = anyRoomsLookingForPlayers ? Rooms.Where(r => anyRoomsLookingForPlayers && r.IsLookingForPlayers).ToList() : Rooms; + } + + rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList(); + + foreach (Room room in rooms) + // Look for rooms looking for players before moving on to rooms that are idle. + { + if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.Host.UserId)) continue; + + Dictionary relevantUserLocations = new(); + + // Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it + bool allPlayersHaveLocations = room.Players.All + ( + p => + { + bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value); + + if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value); + return gotValue; + } + ); + + // If we don't have all locations then the game won't know how to communicate. Thus, it's not a valid room. + if (!allPlayersHaveLocations) continue; + + // If we got here then it should be a valid room. + + FindBestRoomResponse response = new(); + response.RoomId = room.RoomId; + + response.Players = new List(); + response.Locations = new List(); + foreach (User player in room.Players) { - RoomId = RoomIdIncrement, - Players = users, - State = RoomState.Idle, - Slot = slot ?? PodSlot, - RoomVersion = roomVersion, + response.Players.Add + ( + new Player + { + MatchingRes = 0, + User = player, + } + ); + + response.Locations.Add(relevantUserLocations.GetValueOrDefault(player.UserId)); // Already validated to exist + } + + if (user != null) + response.Players.Add + ( + new Player + { + MatchingRes = 1, + User = user, + } + ); + + if (location == null) response.Locations.Add(location); + + response.Slots = new List> + { + new() + { + (int)room.Slot.SlotType, + room.Slot.SlotId, + }, }; - CleanupRooms(room.Host, room); - lock(Rooms) Rooms.Add(room); - Logger.Log($"Created room (id: {room.RoomId}) for host {room.Host.Username} (id: {room.Host.UserId})", LoggerLevelMatch.Instance); + Logger.Log($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LoggerLevelMatch.Instance); - return room; + return response; } - public static Room? FindRoomByUser(User user, GameVersion roomVersion, bool createIfDoesNotExist = false) - { - lock(Rooms) + return null; + } + + public static Room CreateRoom(User user, GameVersion roomVersion, RoomSlot? slot = null) + => CreateRoom + ( + new List { - foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player))) return room; - } - - return createIfDoesNotExist ? CreateRoom(user, roomVersion) : null; - } - - [SuppressMessage("ReSharper", "InvertIf")] - public static void CleanupRooms(User? host = null, Room? newRoom = null) + user, + }, + roomVersion, + slot + ); + public static Room CreateRoom(List users, GameVersion roomVersion, RoomSlot? slot = null) + { + Room room = new() { - lock(Rooms) - { - // Delete old rooms based on host - if (host != null) - try - { - Rooms.RemoveAll(r => r.Host == host); - } - catch - { - // TODO: detect the room that failed and remove it - } + RoomId = RoomIdIncrement, + Players = users, + State = RoomState.Idle, + Slot = slot ?? PodSlot, + RoomVersion = roomVersion, + }; - // Remove players in this new room from other rooms - if (newRoom != null) - foreach (Room room in Rooms) - { - if (room == newRoom) continue; + CleanupRooms(room.Host, room); + lock(Rooms) Rooms.Add(room); + Logger.Log($"Created room (id: {room.RoomId}) for host {room.Host.Username} (id: {room.Host.UserId})", LoggerLevelMatch.Instance); - foreach (User newRoomPlayer in newRoom.Players) room.Players.RemoveAll(p => p == newRoomPlayer); - } + return room; + } - Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms - Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms - } + public static Room? FindRoomByUser(User user, GameVersion roomVersion, bool createIfDoesNotExist = false) + { + lock(Rooms) + foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player))) + return room; + + return createIfDoesNotExist ? CreateRoom(user, roomVersion) : null; + } + + [SuppressMessage("ReSharper", "InvertIf")] + public static void CleanupRooms(User? host = null, Room? newRoom = null) + { + lock(Rooms) + { + // Delete old rooms based on host + if (host != null) + try + { + Rooms.RemoveAll(r => r.Host == host); + } + catch + { + // TODO: detect the room that failed and remove it + } + + // Remove players in this new room from other rooms + if (newRoom != null) + foreach (Room room in Rooms) + { + if (room == newRoom) continue; + + foreach (User newRoomPlayer in newRoom.Players) room.Players.RemoveAll(p => p == newRoomPlayer); + } + + Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms + Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms } } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/StatisticsHelper.cs b/ProjectLighthouse/Helpers/StatisticsHelper.cs index 97091d22..c460c547 100644 --- a/ProjectLighthouse/Helpers/StatisticsHelper.cs +++ b/ProjectLighthouse/Helpers/StatisticsHelper.cs @@ -2,18 +2,17 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class StatisticsHelper { - public static class StatisticsHelper - { - private static readonly Database database = new(); + private static readonly Database database = new(); - public static async Task RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync(); + public static async Task RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync(); - public static async Task SlotCount() => await database.Slots.CountAsync(); + public static async Task SlotCount() => await database.Slots.CountAsync(); - public static async Task MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick); + public static async Task MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick); - public static async Task PhotoCount() => await database.Photos.CountAsync(); - } + public static async Task PhotoCount() => await database.Photos.CountAsync(); } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs index 77d8757d..6f034855 100644 --- a/ProjectLighthouse/Helpers/TimeHelper.cs +++ b/ProjectLighthouse/Helpers/TimeHelper.cs @@ -1,12 +1,11 @@ using System; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class TimeHelper { - public static class TimeHelper - { - public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); - public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); - } + public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); + public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); } // 1397109686193 diff --git a/ProjectLighthouse/Helpers/TimestampHelper.cs b/ProjectLighthouse/Helpers/TimestampHelper.cs index 456f33ab..646ad24f 100644 --- a/ProjectLighthouse/Helpers/TimestampHelper.cs +++ b/ProjectLighthouse/Helpers/TimestampHelper.cs @@ -1,11 +1,10 @@ using System; -namespace LBPUnion.ProjectLighthouse.Helpers -{ - public static class TimestampHelper - { - public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; +namespace LBPUnion.ProjectLighthouse.Helpers; - public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; - } +public static class TimestampHelper +{ + public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + + public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/VersionHelper.cs b/ProjectLighthouse/Helpers/VersionHelper.cs index 4d25b32c..3a806a5c 100644 --- a/ProjectLighthouse/Helpers/VersionHelper.cs +++ b/ProjectLighthouse/Helpers/VersionHelper.cs @@ -5,79 +5,78 @@ using Kettu; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Helpers +namespace LBPUnion.ProjectLighthouse.Helpers; + +public static class VersionHelper { - public static class VersionHelper + static VersionHelper() { - static VersionHelper() + try { - try - { - CommitHash = readManifestFile("gitVersion.txt"); - Branch = readManifestFile("gitBranch.txt"); + CommitHash = readManifestFile("gitVersion.txt"); + Branch = readManifestFile("gitBranch.txt"); - string remotesFile = readManifestFile("gitRemotes.txt"); + string remotesFile = readManifestFile("gitRemotes.txt"); - string[] lines = remotesFile.Split('\n'); + string[] lines = remotesFile.Split('\n'); - // line[0] line[1] line[2] - // origin git@github.com:LBPUnion/project-lighthouse.git (fetch) + // line[0] line[1] line[2] + // origin git@github.com:LBPUnion/project-lighthouse.git (fetch) - // linq is a serious and painful catastrophe but its useful so i'm gonna keep using it - Remotes = lines.Select(line => line.Split("\t")[1]).ToArray(); + // linq is a serious and painful catastrophe but its useful so i'm gonna keep using it + Remotes = lines.Select(line => line.Split("\t")[1]).ToArray(); - CommitsOutOfDate = readManifestFile("gitUnpushed.txt").Split('\n').Length; + CommitsOutOfDate = readManifestFile("gitUnpushed.txt").Split('\n').Length; - CanCheckForUpdates = true; - } - catch - { - Logger.Log - ( - "Project Lighthouse was built incorrectly. Please make sure git is available when building. " + - "Because of this, you will not be notified of updates.", - LoggerLevelStartup.Instance - ); - CommitHash = "invalid"; - Branch = "invalid"; - CanCheckForUpdates = false; - } - - if (IsDirty) - { - Logger.Log - ( - "This is a modified version of Project Lighthouse. " + - "Please make sure you are properly disclosing the source code to any users who may be using this instance.", - LoggerLevelStartup.Instance - ); - CanCheckForUpdates = false; - } + CanCheckForUpdates = true; + } + catch + { + Logger.Log + ( + "Project Lighthouse was built incorrectly. Please make sure git is available when building. " + + "Because of this, you will not be notified of updates.", + LoggerLevelStartup.Instance + ); + CommitHash = "invalid"; + Branch = "invalid"; + CanCheckForUpdates = false; } - private static string readManifestFile(string fileName) + if (IsDirty) { - using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}"); - using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null.")); - - return reader.ReadToEnd().Trim(); + Logger.Log + ( + "This is a modified version of Project Lighthouse. " + + "Please make sure you are properly disclosing the source code to any users who may be using this instance.", + LoggerLevelStartup.Instance + ); + CanCheckForUpdates = false; } - - public static string CommitHash { get; set; } - public static string Branch { get; set; } - public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash} {Build}"; - public static bool IsDirty => CommitHash.EndsWith("-dirty") || CommitsOutOfDate != 1 || CommitHash == "invalid" || Branch == "invalid"; - public static int CommitsOutOfDate { get; set; } - public static bool CanCheckForUpdates { get; set; } - public static string[] Remotes { get; set; } - - public const string Build = - #if DEBUG - "Debug"; - #elif RELEASE - "Release"; - #else - "Unknown"; - #endif } + + private static string readManifestFile(string fileName) + { + using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}"); + using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null.")); + + return reader.ReadToEnd().Trim(); + } + + public static string CommitHash { get; set; } + public static string Branch { get; set; } + public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash} {Build}"; + public static bool IsDirty => CommitHash.EndsWith("-dirty") || CommitsOutOfDate != 1 || CommitHash == "invalid" || Branch == "invalid"; + public static int CommitsOutOfDate { get; set; } + public static bool CanCheckForUpdates { get; set; } + public static string[] Remotes { get; set; } + + public const string Build = + #if DEBUG + "Debug"; + #elif RELEASE + "Release"; + #else + "Unknown"; + #endif } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs index 0aa4f817..113af70a 100644 --- a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs +++ b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs @@ -3,22 +3,21 @@ using Kettu; using LBPUnion.ProjectLighthouse.Helpers.Extensions; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging +namespace LBPUnion.ProjectLighthouse.Logging; + +public class AspNetToKettuLogger : ILogger { - public class AspNetToKettuLogger : ILogger + public IDisposable BeginScope(TState state) => NullScope.Instance; + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - public IDisposable BeginScope(TState state) => NullScope.Instance; - public bool IsEnabled(LogLevel logLevel) => true; + LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); + Logger.Log(state.ToString(), loggerLevel); + if (exception == null) return; - Logger.Log(state.ToString(), loggerLevel); - if (exception == null) return; - - string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n"); - foreach (string line in lines) Logger.Log(line, loggerLevel); - } + string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n"); + foreach (string line in lines) Logger.Log(line, loggerLevel); } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs b/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs index 84039bfe..ea1fd976 100644 --- a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs +++ b/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs @@ -1,16 +1,15 @@ using System; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging -{ - [ProviderAlias("Kettu")] - public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable - { - public void Dispose() - { - GC.SuppressFinalize(this); - } +namespace LBPUnion.ProjectLighthouse.Logging; - public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger(); +[ProviderAlias("Kettu")] +public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable +{ + public void Dispose() + { + GC.SuppressFinalize(this); } + + public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger(); } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/InfluxLogger.cs b/ProjectLighthouse/Logging/InfluxLogger.cs index 02c881f6..85c88a75 100644 --- a/ProjectLighthouse/Logging/InfluxLogger.cs +++ b/ProjectLighthouse/Logging/InfluxLogger.cs @@ -4,24 +4,23 @@ using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Logging +namespace LBPUnion.ProjectLighthouse.Logging; + +public class InfluxLogger : LoggerBase { - public class InfluxLogger : LoggerBase + public override bool AllowMultiple => false; + + public override void Send(LoggerLine line) { - public override bool AllowMultiple => false; + string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; - public override void Send(LoggerLine line) - { - string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; + string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}"; + string content = line.LineData; - string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}"; - string content = line.LineData; + using WriteApi writeApi = InfluxHelper.Client.GetWriteApi(); - using WriteApi writeApi = InfluxHelper.Client.GetWriteApi(); + PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content); - PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content); - - writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); - } + writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs index cc276f5b..4e881771 100644 --- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs +++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs @@ -4,29 +4,28 @@ using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers.Extensions; -namespace LBPUnion.ProjectLighthouse.Logging +namespace LBPUnion.ProjectLighthouse.Logging; + +public class LighthouseFileLogger : LoggerBase { - public class LighthouseFileLogger : LoggerBase + private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); + public override bool AllowMultiple => false; + + public override void Send(LoggerLine line) { - private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); - public override bool AllowMultiple => false; + FileHelper.EnsureDirectoryCreated(logsDirectory); - public override void Send(LoggerLine line) + string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; + + string contentFile = $"{channel}{line.LineData}\n"; + string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n"; + + try { - FileHelper.EnsureDirectoryCreated(logsDirectory); - - string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; - - string contentFile = $"{channel}{line.LineData}\n"; - string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n"; - - try - { - File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile); - File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); - } - catch(IOException) {} // windows, ya goofed - + File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile); + File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); } + catch(IOException) {} // windows, ya goofed + } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs index c8b2e458..e7175fc3 100644 --- a/ProjectLighthouse/Logging/LoggerLevels.cs +++ b/ProjectLighthouse/Logging/LoggerLevels.cs @@ -1,81 +1,80 @@ using Kettu; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging +namespace LBPUnion.ProjectLighthouse.Logging; + +public class LoggerLevelStartup : LoggerLevel { - public class LoggerLevelStartup : LoggerLevel - { - public static readonly LoggerLevelStartup Instance = new(); - public override string Name => "Startup"; - } + public static readonly LoggerLevelStartup Instance = new(); + public override string Name => "Startup"; +} - public class LoggerLevelDatabase : LoggerLevel - { - public static readonly LoggerLevelDatabase Instance = new(); - public override string Name => "Database"; - } +public class LoggerLevelDatabase : LoggerLevel +{ + public static readonly LoggerLevelDatabase Instance = new(); + public override string Name => "Database"; +} - public class LoggerLevelHttp : LoggerLevel - { - public static readonly LoggerLevelHttp Instance = new(); - public override string Name => "HTTP"; - } +public class LoggerLevelHttp : LoggerLevel +{ + public static readonly LoggerLevelHttp Instance = new(); + public override string Name => "HTTP"; +} - public class LoggerLevelFilter : LoggerLevel - { - public static readonly LoggerLevelFilter Instance = new(); - public override string Name => "Filter"; - } +public class LoggerLevelFilter : LoggerLevel +{ + public static readonly LoggerLevelFilter Instance = new(); + public override string Name => "Filter"; +} - public class LoggerLevelLogin : LoggerLevel - { - public static readonly LoggerLevelLogin Instance = new(); - public override string Name => "Login"; - } +public class LoggerLevelLogin : LoggerLevel +{ + public static readonly LoggerLevelLogin Instance = new(); + public override string Name => "Login"; +} - public class LoggerLevelResources : LoggerLevel - { - public static readonly LoggerLevelResources Instance = new(); - public override string Name => "Resources"; - } +public class LoggerLevelResources : LoggerLevel +{ + public static readonly LoggerLevelResources Instance = new(); + public override string Name => "Resources"; +} - public class LoggerLevelMatch : LoggerLevel - { - public static readonly LoggerLevelMatch Instance = new(); - public override string Name => "Match"; - } +public class LoggerLevelMatch : LoggerLevel +{ + public static readonly LoggerLevelMatch Instance = new(); + public override string Name => "Match"; +} - public class LoggerLevelPhotos : LoggerLevel - { - public static readonly LoggerLevelPhotos Instance = new(); - public override string Name => "Photos"; - } +public class LoggerLevelPhotos : LoggerLevel +{ + public static readonly LoggerLevelPhotos Instance = new(); + public override string Name => "Photos"; +} - public class LoggerLevelConfig : LoggerLevel - { - public static readonly LoggerLevelConfig Instance = new(); - public override string Name => "Config"; - } +public class LoggerLevelConfig : LoggerLevel +{ + public static readonly LoggerLevelConfig Instance = new(); + public override string Name => "Config"; +} - public class LoggerLevelInflux : LoggerLevel - { - public static readonly LoggerLevelInflux Instance = new(); - public override string Name => "Influx"; - } +public class LoggerLevelInflux : LoggerLevel +{ + public static readonly LoggerLevelInflux Instance = new(); + public override string Name => "Influx"; +} - public class LoggerLevelAspNet : LoggerLevel - { +public class LoggerLevelAspNet : LoggerLevel +{ - public LoggerLevelAspNet(LogLevel level) - { - this.Channel = level.ToString(); - } - public override string Name => "AspNet"; - } - - public class LoggerLevelCategory : LoggerLevel + public LoggerLevelAspNet(LogLevel level) { - public static readonly LoggerLevelCategory Instance = new(); - public override string Name => "Category"; + this.Channel = level.ToString(); } + public override string Name => "AspNet"; +} + +public class LoggerLevelCategory : LoggerLevel +{ + public static readonly LoggerLevelCategory Instance = new(); + public override string Name => "Category"; } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/NullScope.cs b/ProjectLighthouse/Logging/NullScope.cs index 5d790f78..bbfcf284 100644 --- a/ProjectLighthouse/Logging/NullScope.cs +++ b/ProjectLighthouse/Logging/NullScope.cs @@ -1,17 +1,16 @@ using System; -namespace LBPUnion.ProjectLighthouse.Logging +namespace LBPUnion.ProjectLighthouse.Logging; + +public class NullScope : IDisposable { - public class NullScope : IDisposable + + private NullScope() + {} + public static NullScope Instance { get; } = new(); + + public void Dispose() { - - private NullScope() - {} - public static NullScope Instance { get; } = new(); - - public void Dispose() - { - GC.SuppressFinalize(this); - } + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs index 36846952..43f479df 100644 --- a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs @@ -7,48 +7,47 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; + +[UsedImplicitly] +public class CreateUserCommand : ICommand { - [UsedImplicitly] - public class CreateUserCommand : ICommand + private readonly Database _database = new(); + + public async Task Run(string[] args) { - private readonly Database _database = new(); + string onlineId = args[0]; + string password = args[1]; - public async Task Run(string[] args) + password = HashHelper.Sha256Hash(password); + + User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId); + if (user == null) { - string onlineId = args[0]; - string password = args[1]; + user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password)); + Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance); - password = HashHelper.Sha256Hash(password); + user.PasswordResetRequired = true; + Logger.Log("This user will need to reset their password when they log in.", LoggerLevelLogin.Instance); - User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId); - if (user == null) - { - user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password)); - Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance); - - user.PasswordResetRequired = true; - Logger.Log("This user will need to reset their password when they log in.", LoggerLevelLogin.Instance); - - await this._database.SaveChangesAsync(); - Logger.Log("Database changes saved.", LoggerLevelDatabase.Instance); - } - else - { - Logger.Log("A user with this username already exists.", LoggerLevelLogin.Instance); - } + await this._database.SaveChangesAsync(); + Logger.Log("Database changes saved.", LoggerLevelDatabase.Instance); + } + else + { + Logger.Log("A user with this username already exists.", LoggerLevelLogin.Instance); } - - public string Name() => "Create New User"; - - public string[] Aliases() - => new[] - { - "useradd", "adduser", "newuser", "createUser", - }; - - public string Arguments() => " "; - - public int RequiredArgs() => 2; } + + public string Name() => "Create New User"; + + public string[] Aliases() + => new[] + { + "useradd", "adduser", "newuser", "createUser", + }; + + public string Arguments() => " "; + + public int RequiredArgs() => 2; } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs index 740c5c99..fc7c522f 100644 --- a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs @@ -5,36 +5,35 @@ using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands -{ - [UsedImplicitly] - public class DeleteUserCommand : ICommand - { - private readonly Database database = new(); - public string Name() => "Delete User"; - public string[] Aliases() - => new[] - { - "deleteUser", "wipeUser", - }; - public string Arguments() => ""; - public int RequiredArgs() => 1; - public async Task Run(string[] args) - { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); - if (user == null) - try - { - user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); - if (user == null) throw new Exception(); - } - catch - { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); - return; - } +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; - await this.database.RemoveUser(user); - } +[UsedImplicitly] +public class DeleteUserCommand : ICommand +{ + private readonly Database database = new(); + public string Name() => "Delete User"; + public string[] Aliases() + => new[] + { + "deleteUser", "wipeUser", + }; + public string Arguments() => ""; + public int RequiredArgs() => 1; + public async Task Run(string[] args) + { + User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); + if (user == null) + try + { + user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); + if (user == null) throw new Exception(); + } + catch + { + Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + return; + } + + await this.database.RemoveUser(user); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs b/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs index df22f928..ec526e1d 100644 --- a/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs @@ -5,41 +5,40 @@ using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; + +[UsedImplicitly] +public class MakeUserAdminCommand : ICommand { - [UsedImplicitly] - public class MakeUserAdminCommand : ICommand - { - private readonly Database database = new(); + private readonly Database database = new(); - public string Name() => "Make User Admin"; - public string[] Aliases() - => new[] - { - "makeAdmin", - }; - public string Arguments() => ""; - public int RequiredArgs() => 1; - - public async Task Run(string[] args) + public string Name() => "Make User Admin"; + public string[] Aliases() + => new[] { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); - if (user == null) - try - { - user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); - if (user == null) throw new Exception(); - } - catch - { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); - return; - } + "makeAdmin", + }; + public string Arguments() => ""; + public int RequiredArgs() => 1; - user.IsAdmin = true; - await this.database.SaveChangesAsync(); + public async Task Run(string[] args) + { + User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); + if (user == null) + try + { + user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); + if (user == null) throw new Exception(); + } + catch + { + Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + return; + } - Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin."); - } + user.IsAdmin = true; + await this.database.SaveChangesAsync(); + + Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin."); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs b/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs index 1f22afa5..ef922afe 100644 --- a/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs @@ -6,44 +6,43 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; + +[UsedImplicitly] +public class ResetPasswordCommand : ICommand { - [UsedImplicitly] - public class ResetPasswordCommand : ICommand - { - private readonly Database database = new(); - public string Name() => "Reset Password"; - public string[] Aliases() - => new[] - { - "setPassword", "resetPassword", "passwd", "password", - }; - public string Arguments() => " "; - public int RequiredArgs() => 2; - - public async Task Run(string[] args) + private readonly Database database = new(); + public string Name() => "Reset Password"; + public string[] Aliases() + => new[] { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); - if (user == null) - try - { - user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); - if (user == null) throw new Exception(); - } - catch - { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); - return; - } - string password = args[1]; - if (password.Length != 64) password = HashHelper.Sha256Hash(password); + "setPassword", "resetPassword", "passwd", "password", + }; + public string Arguments() => " "; + public int RequiredArgs() => 2; - user.Password = HashHelper.BCryptHash(password); - user.PasswordResetRequired = true; + public async Task Run(string[] args) + { + User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); + if (user == null) + try + { + user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); + if (user == null) throw new Exception(); + } + catch + { + Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + return; + } + string password = args[1]; + if (password.Length != 64) password = HashHelper.Sha256Hash(password); - await this.database.SaveChangesAsync(); + user.Password = HashHelper.BCryptHash(password); + user.PasswordResetRequired = true; - Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset."); - } + await this.database.SaveChangesAsync(); + + Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset."); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs index d9ec15c8..646963df 100644 --- a/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs +++ b/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs @@ -5,41 +5,40 @@ using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; + +public class WipeTokensForUserCommand : ICommand { - public class WipeTokensForUserCommand : ICommand - { - private readonly Database database = new(); + private readonly Database database = new(); - public string Name() => "Wipe tokens for user"; - public string[] Aliases() - => new[] - { - "wipeTokens", "wipeToken", "deleteTokens", "deleteToken", "removeTokens", "removeToken", - }; - public string Arguments() => ""; - public int RequiredArgs() => 1; - public async Task Run(string[] args) + public string Name() => "Wipe tokens for user"; + public string[] Aliases() + => new[] { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); - if (user == null) - try - { - user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); - if (user == null) throw new Exception(); - } - catch - { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); - return; - } + "wipeTokens", "wipeToken", "deleteTokens", "deleteToken", "removeTokens", "removeToken", + }; + public string Arguments() => ""; + public int RequiredArgs() => 1; + public async Task Run(string[] args) + { + User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); + if (user == null) + try + { + user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0])); + if (user == null) throw new Exception(); + } + catch + { + Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + return; + } - this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == user.UserId)); - this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == user.UserId)); + this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == user.UserId)); + this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == user.UserId)); - await this.database.SaveChangesAsync(); + await this.database.SaveChangesAsync(); - Console.WriteLine($"Deleted all tokens for {user.Username} (id: {user.UserId})."); - } + Console.WriteLine($"Deleted all tokens for {user.Username} (id: {user.UserId})."); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/ICommand.cs b/ProjectLighthouse/Maintenance/ICommand.cs index a12f26a8..03ce547b 100644 --- a/ProjectLighthouse/Maintenance/ICommand.cs +++ b/ProjectLighthouse/Maintenance/ICommand.cs @@ -1,19 +1,18 @@ using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Maintenance +namespace LBPUnion.ProjectLighthouse.Maintenance; + +public interface ICommand { - public interface ICommand - { - public string FirstAlias => this.Aliases()[0]; - public Task Run(string[] args); + public string FirstAlias => this.Aliases()[0]; + public Task Run(string[] args); - public string Name(); + public string Name(); - public string[] Aliases(); + public string[] Aliases(); - public string Arguments(); + public string Arguments(); - public int RequiredArgs(); - } + public int RequiredArgs(); } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/IMaintenanceJob.cs b/ProjectLighthouse/Maintenance/IMaintenanceJob.cs index 4abd1dcb..545e7d33 100644 --- a/ProjectLighthouse/Maintenance/IMaintenanceJob.cs +++ b/ProjectLighthouse/Maintenance/IMaintenanceJob.cs @@ -1,13 +1,12 @@ using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Maintenance +namespace LBPUnion.ProjectLighthouse.Maintenance; + +public interface IMaintenanceJob { - public interface IMaintenanceJob - { - public Task Run(); + public Task Run(); - public string Name(); + public string Name(); - public string Description(); - } + public string Description(); } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs index 252ee93f..8b607db9 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs +++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs @@ -7,86 +7,85 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Files; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs +namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; + +public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob { - public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob + private readonly Database database = new(); + public string Name() => "Cleanup Broken Photos"; + public string Description() => "Deletes all photos that have missing assets or invalid photo subjects."; + + [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] + public async Task Run() { - private readonly Database database = new(); - public string Name() => "Cleanup Broken Photos"; - public string Description() => "Deletes all photos that have missing assets or invalid photo subjects."; - - [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] - public async Task Run() + foreach (Photo photo in this.database.Photos) { - foreach (Photo photo in this.database.Photos) + bool hashNullOrEmpty = false; + bool noHashesExist = false; + bool largeHashIsInvalidFile = false; + bool tooManyPhotoSubjects = false; + bool duplicatePhotoSubjects = false; + + hashNullOrEmpty = string.IsNullOrEmpty + (photo.LargeHash) || + string.IsNullOrEmpty(photo.MediumHash) || + string.IsNullOrEmpty(photo.SmallHash) || + string.IsNullOrEmpty(photo.PlanHash); + if (hashNullOrEmpty) goto removePhoto; + + List hashes = new() { - bool hashNullOrEmpty = false; - bool noHashesExist = false; - bool largeHashIsInvalidFile = false; - bool tooManyPhotoSubjects = false; - bool duplicatePhotoSubjects = false; + photo.LargeHash, + photo.MediumHash, + photo.SmallHash, + photo.PlanHash, + }; - hashNullOrEmpty = string.IsNullOrEmpty - (photo.LargeHash) || - string.IsNullOrEmpty(photo.MediumHash) || - string.IsNullOrEmpty(photo.SmallHash) || - string.IsNullOrEmpty(photo.PlanHash); - if (hashNullOrEmpty) goto removePhoto; + noHashesExist = FileHelper.ResourcesNotUploaded(hashes.ToArray()).Length != 0; + if (noHashesExist) goto removePhoto; - List hashes = new() - { - photo.LargeHash, - photo.MediumHash, - photo.SmallHash, - photo.PlanHash, - }; - - noHashesExist = FileHelper.ResourcesNotUploaded(hashes.ToArray()).Length != 0; - if (noHashesExist) goto removePhoto; - - LbpFile? file = LbpFile.FromHash(photo.LargeHash); + LbpFile? file = LbpFile.FromHash(photo.LargeHash); // Console.WriteLine(file.FileType, ); - if (file == null || file.FileType != LbpFileType.Jpeg && file.FileType != LbpFileType.Png) - { - largeHashIsInvalidFile = true; - goto removePhoto; - } - - if (photo.Subjects.Count > 4) - { - tooManyPhotoSubjects = true; - goto removePhoto; - } - - List subjectUserIds = new(4); - foreach (PhotoSubject subject in photo.Subjects) - { - if (subjectUserIds.Contains(subject.UserId)) - { - duplicatePhotoSubjects = true; - goto removePhoto; - } - subjectUserIds.Add(subject.UserId); - } - - continue; - - removePhoto: - - Console.WriteLine - ( - $"Removing photo (id: {photo.PhotoId}): " + - $"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " + - $"{nameof(noHashesExist)}: {noHashesExist}, " + - $"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " + - $"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" + - $"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}" - ); - - this.database.Photos.Remove(photo); + if (file == null || file.FileType != LbpFileType.Jpeg && file.FileType != LbpFileType.Png) + { + largeHashIsInvalidFile = true; + goto removePhoto; } - await this.database.SaveChangesAsync(); + if (photo.Subjects.Count > 4) + { + tooManyPhotoSubjects = true; + goto removePhoto; + } + + List subjectUserIds = new(4); + foreach (PhotoSubject subject in photo.Subjects) + { + if (subjectUserIds.Contains(subject.UserId)) + { + duplicatePhotoSubjects = true; + goto removePhoto; + } + subjectUserIds.Add(subject.UserId); + } + + continue; + + removePhoto: + + Console.WriteLine + ( + $"Removing photo (id: {photo.PhotoId}): " + + $"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " + + $"{nameof(noHashesExist)}: {noHashesExist}, " + + $"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " + + $"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" + + $"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}" + ); + + this.database.Photos.Remove(photo); } + + await this.database.SaveChangesAsync(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs index 5fb8f6ff..a9d6da95 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs +++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs @@ -4,30 +4,29 @@ using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types.Profiles; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs +namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; + +public class CleanupUnusedLocationsMaintenanceJob : IMaintenanceJob { - public class CleanupUnusedLocationsMaintenanceJob : IMaintenanceJob + private readonly Database database = new(); + public string Name() => "Cleanup Unused Locations"; + public string Description() => "Cleanup unused locations in the database."; + + public async Task Run() { - private readonly Database database = new(); - public string Name() => "Cleanup Unused Locations"; - public string Description() => "Cleanup unused locations in the database."; + List usedLocationIds = new(); - public async Task Run() + usedLocationIds.AddRange(this.database.Slots.Select(slot => slot.LocationId)); + usedLocationIds.AddRange(this.database.Users.Select(user => user.LocationId)); + + IQueryable locationsToRemove = this.database.Locations.Where(l => !usedLocationIds.Contains(l.Id)); + + foreach (Location location in locationsToRemove) { - List usedLocationIds = new(); - - usedLocationIds.AddRange(this.database.Slots.Select(slot => slot.LocationId)); - usedLocationIds.AddRange(this.database.Users.Select(user => user.LocationId)); - - IQueryable locationsToRemove = this.database.Locations.Where(l => !usedLocationIds.Contains(l.Id)); - - foreach (Location location in locationsToRemove) - { - Console.WriteLine("Removing location " + location.Id); - this.database.Locations.Remove(location); - } - - await this.database.SaveChangesAsync(); + Console.WriteLine("Removing location " + location.Id); + this.database.Locations.Remove(location); } + + await this.database.SaveChangesAsync(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs index ca269fec..4c6767ef 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs +++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs @@ -1,22 +1,21 @@ using System; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs +namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; + +public class DeleteAllTokensMaintenanceJob : IMaintenanceJob { - public class DeleteAllTokensMaintenanceJob : IMaintenanceJob + private readonly Database database = new(); + + public string Name() => "Delete ALL Tokens"; + public string Description() => "Deletes ALL game tokens and web tokens."; + public async Task Run() { - private readonly Database database = new(); + this.database.GameTokens.RemoveRange(this.database.GameTokens); + this.database.WebTokens.RemoveRange(this.database.WebTokens); - public string Name() => "Delete ALL Tokens"; - public string Description() => "Deletes ALL game tokens and web tokens."; - public async Task Run() - { - this.database.GameTokens.RemoveRange(this.database.GameTokens); - this.database.WebTokens.RemoveRange(this.database.WebTokens); + await this.database.SaveChangesAsync(); - await this.database.SaveChangesAsync(); - - Console.WriteLine("Deleted ALL tokens."); - } + Console.WriteLine("Deleted ALL tokens."); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs index efd0460f..b2e61c78 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs +++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs @@ -2,30 +2,29 @@ using System; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs +namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; + +public class FixAllBrokenPlayerRequirementsMaintenanceJob : IMaintenanceJob { - public class FixAllBrokenPlayerRequirementsMaintenanceJob : IMaintenanceJob + private readonly Database database = new(); + + public string Name() => "Fix All Broken Player Requirements"; + public string Description() => "Some LBP1 levels may report that they are designed for 0 players. This job will fix that."; + public async Task Run() { - private readonly Database database = new(); + int count = 0; + await foreach (Slot slot in this.database.Slots) + if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) + { + slot.MinimumPlayers = 1; + slot.MaximumPlayers = 4; - public string Name() => "Fix All Broken Player Requirements"; - public string Description() => "Some LBP1 levels may report that they are designed for 0 players. This job will fix that."; - public async Task Run() - { - int count = 0; - await foreach (Slot slot in this.database.Slots) - if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) - { - slot.MinimumPlayers = 1; - slot.MaximumPlayers = 4; + Console.WriteLine($"Fixed slotId {slot.SlotId}"); + count++; + } - Console.WriteLine($"Fixed slotId {slot.SlotId}"); - count++; - } + await this.database.SaveChangesAsync(); - await this.database.SaveChangesAsync(); - - Console.WriteLine($"Fixed {count} broken player requirements."); - } + Console.WriteLine($"Fixed {count} broken player requirements."); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs index 34eff362..3fb07d38 100644 --- a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs +++ b/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs @@ -10,10 +10,10 @@ namespace LBPUnion.ProjectLighthouse.Pages.Admin; public class AdminBanUserPage : BaseLayout { - public AdminBanUserPage(Database database) : base(database) - {} public User? TargetedUser; + public AdminBanUserPage(Database database) : base(database) + {} public async Task OnGet([FromRoute] int id) { diff --git a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs b/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs index 14b643eb..baa515dc 100644 --- a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs +++ b/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs @@ -11,12 +11,12 @@ namespace LBPUnion.ProjectLighthouse.Pages.Admin; public class AdminPanelUsersPage : BaseLayout { - public AdminPanelUsersPage(Database database) : base(database) - {} public int UserCount; public List Users; + public AdminPanelUsersPage(Database database) : base(database) + {} public async Task OnGet() { diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs index 6ca2576a..bb1d745a 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs +++ b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs @@ -9,31 +9,30 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth +namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; + +public class AuthenticationPage : BaseLayout { - public class AuthenticationPage : BaseLayout + + public List AuthenticationAttempts; + + public IPAddress? IpAddress; + public AuthenticationPage(Database database) : base(database) + {} + + public async Task OnGet() { + if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound(); + if (this.User == null) return this.StatusCode(403, ""); - public List AuthenticationAttempts; + this.IpAddress = this.HttpContext.Connection.RemoteIpAddress; - public IPAddress? IpAddress; - public AuthenticationPage(Database database) : base(database) - {} + this.AuthenticationAttempts = this.Database.AuthenticationAttempts.Include + (a => a.GameToken) + .Where(a => a.GameToken.UserId == this.User.UserId) + .OrderByDescending(a => a.Timestamp) + .ToList(); - public async Task OnGet() - { - if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound(); - if (this.User == null) return this.StatusCode(403, ""); - - this.IpAddress = this.HttpContext.Connection.RemoteIpAddress; - - this.AuthenticationAttempts = this.Database.AuthenticationAttempts.Include - (a => a.GameToken) - .Where(a => a.GameToken.UserId == this.User.UserId) - .OrderByDescending(a => a.Timestamp) - .ToList(); - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs index 00dfa3be..bd70b6c3 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs +++ b/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs @@ -7,23 +7,22 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth +namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; + +public class ManageUserApprovedIpAddressesPage : BaseLayout { - public class ManageUserApprovedIpAddressesPage : BaseLayout + + public List ApprovedIpAddresses; + public ManageUserApprovedIpAddressesPage(Database database) : base(database) + {} + + public async Task OnGet() { - public ManageUserApprovedIpAddressesPage(Database database) : base(database) - {} + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("/login"); - public List ApprovedIpAddresses; + this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync(); - public async Task OnGet() - { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("/login"); - - this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync(); - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml.cs b/ProjectLighthouse/Pages/LandingPage.cshtml.cs index de398d10..0d23a58e 100644 --- a/ProjectLighthouse/Pages/LandingPage.cshtml.cs +++ b/ProjectLighthouse/Pages/LandingPage.cshtml.cs @@ -9,37 +9,34 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class LandingPage : BaseLayout { - public class LandingPage : BaseLayout + + public int AuthenticationAttemptsCount; + public List PlayersOnline; + + public int PlayersOnlineCount; + public LandingPage(Database database) : base(database) + {} + + [UsedImplicitly] + public async Task OnGet() { - public List PlayersOnline; + User? user = this.Database.UserFromWebRequest(this.Request); + if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); - public int PlayersOnlineCount; + this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); - public int AuthenticationAttemptsCount; - public LandingPage(Database database) : base(database) - {} + if (user != null) + this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include + (a => a.GameToken) + .CountAsync(a => a.GameToken.UserId == user.UserId); - [UsedImplicitly] - public async Task OnGet() - { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + List userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); - this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); - - if (user != null) - { - this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include - (a => a.GameToken) - .CountAsync(a => a.GameToken.UserId == user.UserId); - } - - List userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); - - this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); - return this.Page(); - } + this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs index f71c2bb3..5b7f4931 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs @@ -3,42 +3,41 @@ using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace LBPUnion.ProjectLighthouse.Pages.Layouts +namespace LBPUnion.ProjectLighthouse.Pages.Layouts; + +public class BaseLayout : PageModel { - public class BaseLayout : PageModel + + public readonly Database Database; + + public readonly List NavigationItems = new() { - public BaseLayout(Database database) - { - this.Database = database; - } - - public bool IsMobile; - - public readonly Database Database; - - public readonly List NavigationItems = new() - { - new PageNavigationItem("Home", "/", "home"), - new PageNavigationItem("Photos", "/photos/0", "camera"), - new PageNavigationItem("Levels", "/slots/0", "certificate"), - }; - - public readonly List NavigationItemsRight = new(); - - public bool ShowTitleInPage = true; - - public string Title = string.Empty; - public string Description = string.Empty; - - private User? user; - - public new User? User { - get { - if (this.user != null) return this.user; - - return this.user = this.Database.UserFromWebRequest(this.Request); - } - set => this.user = value; + new PageNavigationItem("Home", "/", "home"), + new PageNavigationItem("Photos", "/photos/0", "camera"), + new PageNavigationItem("Levels", "/slots/0", "certificate"), + }; + + public readonly List NavigationItemsRight = new(); + public string Description = string.Empty; + + public bool IsMobile; + + public bool ShowTitleInPage = true; + + public string Title = string.Empty; + + private User? user; + public BaseLayout(Database database) + { + this.Database = database; + } + + public new User? User { + get { + if (this.user != null) return this.user; + + return this.user = this.Database.UserFromWebRequest(this.Request); } + set => this.user = value; } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs index 45f6d9cb..5e19c5a4 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -9,75 +9,74 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class LoginForm : BaseLayout { - public class LoginForm : BaseLayout + public LoginForm(Database database) : base(database) + {} + + public string Error { get; private set; } + + public bool WasLoginRequest { get; private set; } + + [UsedImplicitly] + public async Task OnPost(string username, string password) { - public LoginForm(Database database) : base(database) - {} - - public string Error { get; private set; } - - public bool WasLoginRequest { get; private set; } - - [UsedImplicitly] - public async Task OnPost(string username, string password) + if (string.IsNullOrWhiteSpace(username)) { - if (string.IsNullOrWhiteSpace(username)) - { - this.Error = "The username field is required."; - return this.Page(); - } - - if (string.IsNullOrWhiteSpace(password)) - { - this.Error = "The password field is required."; - return this.Page(); - } - - User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (user == null) - { - Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance); - this.Error = "The username or password you entered is invalid."; - return this.Page(); - } - - if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) - { - Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance); - this.Error = "The username or password you entered is invalid."; - return this.Page(); - } - - if (user.Banned) - { - Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance); - this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason; - return this.Page(); - } - - WebToken webToken = new() - { - UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), - }; - - this.Database.WebTokens.Add(webToken); - await this.Database.SaveChangesAsync(); - - this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); - - if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); - - return this.RedirectToPage(nameof(LandingPage)); - } - - [UsedImplicitly] - public async Task OnGet() - { - this.Error = string.Empty; + this.Error = "The username field is required."; return this.Page(); } + + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "The password field is required."; + return this.Page(); + } + + User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (user == null) + { + Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance); + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) + { + Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance); + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + if (user.Banned) + { + Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance); + this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason; + return this.Page(); + } + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); + + if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + + return this.RedirectToPage(nameof(LandingPage)); + } + + [UsedImplicitly] + public async Task OnGet() + { + this.Error = string.Empty; + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml.cs b/ProjectLighthouse/Pages/LogoutPage.cshtml.cs index 792947be..ad8dd64c 100644 --- a/ProjectLighthouse/Pages/LogoutPage.cshtml.cs +++ b/ProjectLighthouse/Pages/LogoutPage.cshtml.cs @@ -4,23 +4,22 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class LogoutPage : BaseLayout { - public class LogoutPage : BaseLayout + public LogoutPage(Database database) : base(database) + {} + public async Task OnGet() { - public LogoutPage(Database database) : base(database) - {} - public async Task OnGet() - { - WebToken? token = this.Database.WebTokenFromRequest(this.Request); - if (token == null) return this.BadRequest(); + WebToken? token = this.Database.WebTokenFromRequest(this.Request); + if (token == null) return this.BadRequest(); - this.Database.WebTokens.Remove(token); - await this.Database.SaveChangesAsync(); + this.Database.WebTokens.Remove(token); + await this.Database.SaveChangesAsync(); - this.Response.Cookies.Delete("LighthouseToken"); + this.Response.Cookies.Delete("LighthouseToken"); - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs index 8f844f7f..51649f72 100644 --- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs +++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs @@ -6,48 +6,47 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class PasswordResetPage : BaseLayout { - public class PasswordResetPage : BaseLayout + public PasswordResetPage(Database database) : base(database) + {} + + public string Error { get; private set; } + + [UsedImplicitly] + public async Task OnPost(string password, string confirmPassword) { - public PasswordResetPage(Database database) : base(database) - {} + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); - public string Error { get; private set; } - - [UsedImplicitly] - public async Task OnPost(string password, string confirmPassword) + if (string.IsNullOrWhiteSpace(password)) { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); - - if (string.IsNullOrWhiteSpace(password)) - { - this.Error = "The password field is required."; - return this.Page(); - } - - if (password != confirmPassword) - { - this.Error = "Passwords do not match!"; - return this.Page(); - } - - user.Password = HashHelper.BCryptHash(password); - user.PasswordResetRequired = false; - - await this.Database.SaveChangesAsync(); - - return this.Redirect("~/"); - } - - [UsedImplicitly] - public IActionResult OnGet() - { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); - + this.Error = "The password field is required."; return this.Page(); } + + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + user.Password = HashHelper.BCryptHash(password); + user.PasswordResetRequired = false; + + await this.Database.SaveChangesAsync(); + + return this.Redirect("~/"); + } + + [UsedImplicitly] + public IActionResult OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs index 55c07595..9739b4d3 100644 --- a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs +++ b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs @@ -5,22 +5,21 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class PasswordResetRequiredPage : BaseLayout { - public class PasswordResetRequiredPage : BaseLayout + public PasswordResetRequiredPage([NotNull] Database database) : base(database) + {} + + public bool WasResetRequest { get; private set; } + + public async Task OnGet() { - public PasswordResetRequiredPage([NotNull] Database database) : base(database) - {} + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset"); - public bool WasResetRequest { get; private set; } - - public async Task OnGet() - { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); - if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset"); - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml.cs b/ProjectLighthouse/Pages/PhotosPage.cshtml.cs index f5e132b0..b483fc2c 100644 --- a/ProjectLighthouse/Pages/PhotosPage.cshtml.cs +++ b/ProjectLighthouse/Pages/PhotosPage.cshtml.cs @@ -10,41 +10,37 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class PhotosPage : BaseLayout { - public class PhotosPage : BaseLayout + + public int PageAmount; + + public int PageNumber; + + public int PhotoCount; + + public List Photos; + public PhotosPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int pageNumber) { + this.PhotoCount = await StatisticsHelper.PhotoCount(); - public int PageNumber; + this.PageNumber = pageNumber; + this.PageAmount = (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize); - public int PhotoCount; + if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - public int PageAmount; + this.Photos = await this.Database.Photos.Include + (p => p.Creator) + .OrderByDescending(p => p.Timestamp) + .Skip(pageNumber * ServerStatics.PageSize) + .Take(ServerStatics.PageSize) + .ToListAsync(); - public List Photos; - public PhotosPage([NotNull] Database database) : base(database) - {} - - public async Task OnGet([FromRoute] int pageNumber) - { - this.PhotoCount = await StatisticsHelper.PhotoCount(); - - this.PageNumber = pageNumber; - this.PageAmount = (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize); - - if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) - { - return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - } - - this.Photos = await this.Database.Photos.Include - (p => p.Creator) - .OrderByDescending(p => p.Timestamp) - .Skip(pageNumber * ServerStatics.PageSize) - .Take(ServerStatics.PageSize) - .ToListAsync(); - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs index e09aa17e..310884d5 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs @@ -8,71 +8,70 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class RegisterForm : BaseLayout { - public class RegisterForm : BaseLayout + public RegisterForm(Database database) : base(database) + {} + + public string Error { get; private set; } + public bool WasRegisterRequest { get; private set; } + + [UsedImplicitly] + [SuppressMessage("ReSharper", "SpecifyStringComparison")] + public async Task OnPost(string username, string password, string confirmPassword) { - public RegisterForm(Database database) : base(database) - {} + if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); - public string Error { get; private set; } - public bool WasRegisterRequest { get; private set; } - - [UsedImplicitly] - [SuppressMessage("ReSharper", "SpecifyStringComparison")] - public async Task OnPost(string username, string password, string confirmPassword) + if (string.IsNullOrWhiteSpace(username)) { - if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); - - if (string.IsNullOrWhiteSpace(username)) - { - this.Error = "The username field is blank."; - return this.Page(); - } - - if (string.IsNullOrWhiteSpace(password)) - { - this.Error = "Password field is required."; - return this.Page(); - } - - if (password != confirmPassword) - { - this.Error = "Passwords do not match!"; - return this.Page(); - } - - bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null; - if (userExists) - { - this.Error = "The username you've chosen is already taken."; - return this.Page(); - } - - User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password)); - - WebToken webToken = new() - { - UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), - }; - - this.Database.WebTokens.Add(webToken); - await this.Database.SaveChangesAsync(); - - this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); - - return this.RedirectToPage(nameof(LandingPage)); - } - - [UsedImplicitly] - [SuppressMessage("ReSharper", "SpecifyStringComparison")] - public IActionResult OnGet() - { - this.Error = string.Empty; - if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); - + this.Error = "The username field is blank."; return this.Page(); } + + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "Password field is required."; + return this.Page(); + } + + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null; + if (userExists) + { + this.Error = "The username you've chosen is already taken."; + return this.Page(); + } + + User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password)); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); + + return this.RedirectToPage(nameof(LandingPage)); + } + + [UsedImplicitly] + [SuppressMessage("ReSharper", "SpecifyStringComparison")] + public IActionResult OnGet() + { + this.Error = string.Empty; + if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml.cs b/ProjectLighthouse/Pages/SlotPage.cshtml.cs index a0c65f17..03c7d3ff 100644 --- a/ProjectLighthouse/Pages/SlotPage.cshtml.cs +++ b/ProjectLighthouse/Pages/SlotPage.cshtml.cs @@ -6,23 +6,22 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class SlotPage : BaseLayout { - public class SlotPage : BaseLayout + + public Slot Slot; + public SlotPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int id) { + Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); - public Slot Slot; - public SlotPage([NotNull] Database database) : base(database) - {} + this.Slot = slot; - public async Task OnGet([FromRoute] int id) - { - Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); - if (slot == null) return this.NotFound(); - - this.Slot = slot; - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml b/ProjectLighthouse/Pages/SlotsPage.cshtml index 89ba207a..ad5b8180 100644 --- a/ProjectLighthouse/Pages/SlotsPage.cshtml +++ b/ProjectLighthouse/Pages/SlotsPage.cshtml @@ -32,7 +32,7 @@ Previous Page } @(Model.PageNumber + 1) / @(Model.PageAmount) -@if (Model.PageNumber < Model.PageAmount - 1) +@if (Model.PageNumber < Model.PageAmount - 1) { Next Page -} +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml.cs b/ProjectLighthouse/Pages/SlotsPage.cshtml.cs index 12c7432e..e4d84fa8 100644 --- a/ProjectLighthouse/Pages/SlotsPage.cshtml.cs +++ b/ProjectLighthouse/Pages/SlotsPage.cshtml.cs @@ -10,41 +10,37 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class SlotsPage : BaseLayout { - public class SlotsPage : BaseLayout + + public int PageAmount; + + public int PageNumber; + + public int SlotCount; + + public List Slots; + public SlotsPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int pageNumber) { + this.SlotCount = await StatisticsHelper.SlotCount(); - public int PageNumber; + this.PageNumber = pageNumber; + this.PageAmount = (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize); - public int SlotCount; - - public int PageAmount; + if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - public List Slots; - public SlotsPage([NotNull] Database database) : base(database) - {} + this.Slots = await this.Database.Slots.Include + (p => p.Creator) + .OrderByDescending(p => p.FirstUploaded) + .Skip(pageNumber * ServerStatics.PageSize) + .Take(ServerStatics.PageSize) + .ToListAsync(); - public async Task OnGet([FromRoute] int pageNumber) - { - this.SlotCount = await StatisticsHelper.SlotCount(); - - this.PageNumber = pageNumber; - this.PageAmount = (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize); - - if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) - { - return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - } - - this.Slots = await this.Database.Slots.Include - (p => p.Creator) - .OrderByDescending(p => p.FirstUploaded) - .Skip(pageNumber * ServerStatics.PageSize) - .Take(ServerStatics.PageSize) - .ToListAsync(); - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse/Pages/UserPage.cshtml index 1437fa80..733f02ff 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml +++ b/ProjectLighthouse/Pages/UserPage.cshtml @@ -125,9 +125,9 @@ @foreach (Comment comment in Model.Comments!) { DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000); - StringWriter messageWriter = new StringWriter(); + StringWriter messageWriter = new(); HttpUtility.HtmlDecode(comment.Message, messageWriter); - String decodedMessage = messageWriter.ToString(); + string decodedMessage = messageWriter.ToString();
@comment.Poster.Username: @decodedMessage diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse/Pages/UserPage.cshtml.cs index 0240ab59..4a5148ed 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse/Pages/UserPage.cshtml.cs @@ -8,40 +8,39 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages +namespace LBPUnion.ProjectLighthouse.Pages; + +public class UserPage : BaseLayout { - public class UserPage : BaseLayout + public List? Comments; + + public bool IsProfileUserHearted; + + public List? Photos; + + public User? ProfileUser; + public UserPage(Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int userId) { - public List? Comments; + this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); + if (this.ProfileUser == null) return this.NotFound(); - public bool IsProfileUserHearted; + this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); + this.Comments = await this.Database.Comments.Include + (p => p.Poster) + .Include(p => p.Target) + .OrderByDescending(p => p.Timestamp) + .Where(p => p.TargetUserId == userId) + .Take(50) + .ToListAsync(); - public List? Photos; + if (this.User != null) + this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync + (u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) != + null; - public User? ProfileUser; - public UserPage(Database database) : base(database) - {} - - public async Task OnGet([FromRoute] int userId) - { - this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); - if (this.ProfileUser == null) return this.NotFound(); - - this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); - this.Comments = await this.Database.Comments.Include - (p => p.Poster) - .Include(p => p.Target) - .OrderByDescending(p => p.Timestamp) - .Where(p => p.TargetUserId == userId) - .Take(50) - .ToListAsync(); - - if (this.User != null) - this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync - (u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) != - null; - - return this.Page(); - } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index 934cb837..56d9764a 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -12,55 +12,55 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse +namespace LBPUnion.ProjectLighthouse; + +public static class Program { - public static class Program + public static void Main(string[] args) { - public static void Main(string[] args) + if (args.Length != 0 && args[0] == "--wait-for-debugger") { - if (args.Length != 0 && args[0] == "--wait-for-debugger") - { - Console.WriteLine("Waiting for a debugger to be attached..."); - while (!Debugger.IsAttached) Thread.Sleep(100); - Console.WriteLine("Debugger attached."); - } + Console.WriteLine("Waiting for a debugger to be attached..."); + while (!Debugger.IsAttached) Thread.Sleep(100); + Console.WriteLine("Debugger attached."); + } - // Log startup time - Stopwatch stopwatch = new(); - stopwatch.Start(); + // Log startup time + Stopwatch stopwatch = new(); + stopwatch.Start(); - // Setup logging + // Setup logging - Logger.StartLogging(); - Logger.UpdateRate /= 2; - LoggerLine.LogFormat = "[{0}] {1}"; - Logger.AddLogger(new ConsoleLogger()); - Logger.AddLogger(new LighthouseFileLogger()); + Logger.StartLogging(); + Logger.UpdateRate /= 2; + LoggerLine.LogFormat = "[{0}] {1}"; + Logger.AddLogger(new ConsoleLogger()); + Logger.AddLogger(new LighthouseFileLogger()); - Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance); - Logger.Log($"Running {VersionHelper.FullVersion}", LoggerLevelStartup.Instance); + Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance); + Logger.Log($"Running {VersionHelper.FullVersion}", LoggerLevelStartup.Instance); - // This loads the config, see ServerSettings.cs for more information - Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance); + // This loads the config, see ServerSettings.cs for more information + Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance); - Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance); - bool dbConnected = ServerStatics.DbConnected; - Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); + Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance); + bool dbConnected = ServerStatics.DbConnected; + Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); - if (!dbConnected) Environment.Exit(1); - using Database database = new(); + if (!dbConnected) Environment.Exit(1); + using Database database = new(); - Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); - MigrateDatabase(database); + Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); + MigrateDatabase(database); - if (ServerSettings.Instance.InfluxEnabled) - { - Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance); - InfluxHelper.StartLogging().Wait(); - if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger()); - } + if (ServerSettings.Instance.InfluxEnabled) + { + Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance); + InfluxHelper.StartLogging().Wait(); + if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger()); + } - #if DEBUG + #if DEBUG Logger.Log ( "This is a debug build, so performance may suffer! " + @@ -69,48 +69,47 @@ namespace LBPUnion.ProjectLighthouse LoggerLevelStartup.Instance ); Logger.Log("You can do so by running any dotnet command with the flag: \"-c Release\". ", LoggerLevelStartup.Instance); - #endif + #endif - if (args.Length != 0) - { - MaintenanceHelper.RunCommand(args).Wait(); - return; - } - - stopwatch.Stop(); - Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); - - CreateHostBuilder(args).Build().Run(); - } - - public static void MigrateDatabase(Database database) + if (args.Length != 0) { - Stopwatch stopwatch = new(); - stopwatch.Start(); - - database.Database.MigrateAsync().Wait(); - - stopwatch.Stop(); - Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); + MaintenanceHelper.RunCommand(args).Wait(); + return; } - public static IHostBuilder CreateHostBuilder(string[] args) - => Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults - ( - webBuilder => - { - webBuilder.UseStartup(); - webBuilder.UseWebRoot("StaticFiles"); - } - ) - .ConfigureLogging - ( - logging => - { - logging.ClearProviders(); - logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - } - ); + stopwatch.Stop(); + Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); + + CreateHostBuilder(args).Build().Run(); } + + public static void MigrateDatabase(Database database) + { + Stopwatch stopwatch = new(); + stopwatch.Start(); + + database.Database.MigrateAsync().Wait(); + + stopwatch.Stop(); + Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + => Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults + ( + webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseWebRoot("StaticFiles"); + } + ) + .ConfigureLogging + ( + logging => + { + logging.ClearProviders(); + logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + ); } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/JsonOutputFormatter.cs b/ProjectLighthouse/Serialization/JsonOutputFormatter.cs index 1e5ea291..607568d7 100644 --- a/ProjectLighthouse/Serialization/JsonOutputFormatter.cs +++ b/ProjectLighthouse/Serialization/JsonOutputFormatter.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.Formatters; -namespace LBPUnion.ProjectLighthouse.Serialization +namespace LBPUnion.ProjectLighthouse.Serialization; + +public class JsonOutputFormatter : StringOutputFormatter { - public class JsonOutputFormatter : StringOutputFormatter + public JsonOutputFormatter() { - public JsonOutputFormatter() - { - this.SupportedMediaTypes.Add("text/json"); - this.SupportedMediaTypes.Add("application/json"); - } + this.SupportedMediaTypes.Add("text/json"); + this.SupportedMediaTypes.Add("application/json"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index cfa64535..c6c72de0 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -2,35 +2,34 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace LBPUnion.ProjectLighthouse.Serialization +namespace LBPUnion.ProjectLighthouse.Serialization; + +/// +/// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root +/// elements), +/// so I wrote my own crappy one. +/// +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public static class LbpSerializer { - /// - /// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root - /// elements), - /// so I wrote my own crappy one. - /// - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static class LbpSerializer - { - public static string BlankElement(string key) => $"<{key}>"; + public static string BlankElement(string key) => $"<{key}>"; - public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value}"; + public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value}"; - public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}"; + public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}"; - public static string StringElement(string key, object value) => $"<{key}>{value}"; + public static string StringElement(string key, object value) => $"<{key}>{value}"; - public static string TaggedStringElement - (KeyValuePair pair, KeyValuePair tagPair) - => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; + public static string TaggedStringElement + (KeyValuePair pair, KeyValuePair tagPair) + => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; - public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; + public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; - public static string TaggedStringElement(string key, object value, Dictionary attrKeyValuePairs) - => $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}"; + public static string TaggedStringElement(string key, object value, Dictionary attrKeyValuePairs) + => $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}"; - public static string Elements - (params KeyValuePair[] pairs) - => pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); - } + public static string Elements + (params KeyValuePair[] pairs) + => pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/XmlOutputFormatter.cs b/ProjectLighthouse/Serialization/XmlOutputFormatter.cs index 6103f8cd..e275601c 100644 --- a/ProjectLighthouse/Serialization/XmlOutputFormatter.cs +++ b/ProjectLighthouse/Serialization/XmlOutputFormatter.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.Formatters; -namespace LBPUnion.ProjectLighthouse.Serialization +namespace LBPUnion.ProjectLighthouse.Serialization; + +public class XmlOutputFormatter : StringOutputFormatter { - public class XmlOutputFormatter : StringOutputFormatter + public XmlOutputFormatter() { - public XmlOutputFormatter() - { - this.SupportedMediaTypes.Add("text/xml"); - this.SupportedMediaTypes.Add("application/xml"); - } + this.SupportedMediaTypes.Add("text/xml"); + this.SupportedMediaTypes.Add("application/xml"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Startup.cs b/ProjectLighthouse/Startup.cs index 405883f5..dcbd1a4c 100644 --- a/ProjectLighthouse/Startup.cs +++ b/ProjectLighthouse/Startup.cs @@ -14,187 +14,186 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Primitives; -namespace LBPUnion.ProjectLighthouse +namespace LBPUnion.ProjectLighthouse; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } + this.Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); - #if DEBUG + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + #if DEBUG services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation(); - #else - services.AddRazorPages().WithRazorPagesAtContentRoot(); - #endif + #else + services.AddRazorPages().WithRazorPagesAtContentRoot(); + #endif - services.AddMvc + services.AddMvc + ( + options => + { + options.OutputFormatters.Add(new XmlOutputFormatter()); + options.OutputFormatters.Add(new JsonOutputFormatter()); + } + ); + + services.AddDbContext(); + + services.Configure + ( + options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + } + ); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + bool computeDigests = true; + string serverDigestKey = ServerSettings.Instance.ServerDigestKey; + if (string.IsNullOrEmpty(serverDigestKey)) + { + Logger.Log ( - options => - { - options.OutputFormatters.Add(new XmlOutputFormatter()); - options.OutputFormatters.Add(new JsonOutputFormatter()); - } - ); - - services.AddDbContext(); - - services.Configure - ( - options => - { - options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; - } + "The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " + + "To increase security, it is recommended that you find and set this variable.", + LoggerLevelStartup.Instance ); + computeDigests = false; } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - bool computeDigests = true; - string serverDigestKey = ServerSettings.Instance.ServerDigestKey; - if (string.IsNullOrEmpty(serverDigestKey)) - { - Logger.Log - ( - "The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " + - "To increase security, it is recommended that you find and set this variable.", - LoggerLevelStartup.Instance - ); - computeDigests = false; - } - - #if DEBUG + #if DEBUG app.UseDeveloperExceptionPage(); - #endif + #endif - app.UseForwardedHeaders(); + app.UseForwardedHeaders(); - // Logs every request and the response to it - // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" - // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" - app.Use - ( - async (context, next) => - { - Stopwatch requestStopwatch = new(); - requestStopwatch.Start(); + // Logs every request and the response to it + // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" + // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" + app.Use + ( + async (context, next) => + { + Stopwatch requestStopwatch = new(); + requestStopwatch.Start(); - context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging + context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging - // Log all headers. + // Log all headers. // foreach (KeyValuePair header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}"); - await next(context); // Handle the request so we can get the status code from it + await next(context); // Handle the request so we can get the status code from it - requestStopwatch.Stop(); + requestStopwatch.Stop(); - Logger.Log - ( - $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", - LoggerLevelHttp.Instance - ); + Logger.Log + ( + $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", + LoggerLevelHttp.Instance + ); - #if DEBUG + #if DEBUG // Log post body if (context.Request.Method == "POST") { context.Request.Body.Position = 0; Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); } - #endif - } - ); + #endif + } + ); - // Digest check - app.Use - ( - async (context, next) => + // Digest check + app.Use + ( + async (context, next) => + { + // Client digest check. + if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; + string digestPath = context.Request.Path; + Stream body = context.Request.Body; + + if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) { - // Client digest check. - if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; - string digestPath = context.Request.Path; - Stream body = context.Request.Body; + string clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, serverDigestKey); - if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) - { - string clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, serverDigestKey); + // Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match. + if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest)) + if (clientRequestDigest != sentDigest) + { + context.Response.StatusCode = 403; + context.Abort(); + return; + } - // Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match. - if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest)) - if (clientRequestDigest != sentDigest) - { - context.Response.StatusCode = 403; - context.Abort(); - return; - } + context.Response.Headers.Add("X-Digest-B", clientRequestDigest); + context.Request.Body.Position = 0; + } - context.Response.Headers.Add("X-Digest-B", clientRequestDigest); - context.Request.Body.Position = 0; - } + // This does the same as above, but for the response stream. + await using MemoryStream responseBuffer = new(); + Stream oldResponseStream = context.Response.Body; + context.Response.Body = responseBuffer; - // This does the same as above, but for the response stream. - await using MemoryStream responseBuffer = new(); - Stream oldResponseStream = context.Response.Body; - context.Response.Body = responseBuffer; + await next(context); // Handle the request so we can get the server digest hash - await next(context); // Handle the request so we can get the server digest hash - - // Compute the server digest hash. - if (computeDigests) - { - responseBuffer.Position = 0; - - // Compute the digest for the response. - string serverDigest = await HashHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, serverDigestKey); - context.Response.Headers.Add("X-Digest-A", serverDigest); - } - - // Set the X-Original-Content-Length header to the length of the response buffer. - context.Response.Headers.Add("X-Original-Content-Length", responseBuffer.Length.ToString()); - - // Copy the buffered response to the actual respose stream. + // Compute the server digest hash. + if (computeDigests) + { responseBuffer.Position = 0; - await responseBuffer.CopyToAsync(oldResponseStream); - context.Response.Body = oldResponseStream; - } - ); - app.Use - ( - async (context, next) => + // Compute the digest for the response. + string serverDigest = await HashHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, serverDigestKey); + context.Response.Headers.Add("X-Digest-A", serverDigest); + } + + // Set the X-Original-Content-Length header to the length of the response buffer. + context.Response.Headers.Add("X-Original-Content-Length", responseBuffer.Length.ToString()); + + // Copy the buffered response to the actual respose stream. + responseBuffer.Position = 0; + await responseBuffer.CopyToAsync(oldResponseStream); + context.Response.Body = oldResponseStream; + } + ); + + app.Use + ( + async (context, next) => + { + #nullable enable + // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. + if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) { - #nullable enable - // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. - if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) - { - // We begin by grabbing a token from the request, if this is a LBPPS3_XML request of course. - await using Database database = new(); // Gets nuked at the end of the scope - GameToken? gameToken = await database.GameTokenFromRequest(context.Request); + // We begin by grabbing a token from the request, if this is a LBPPS3_XML request of course. + await using Database database = new(); // Gets nuked at the end of the scope + GameToken? gameToken = await database.GameTokenFromRequest(context.Request); - if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1) - // Ignore UserFromGameToken null because user must exist for a token to exist - await LastContactHelper.SetLastContact((await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1); - } - #nullable disable - - await next(context); + if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1) + // Ignore UserFromGameToken null because user must exist for a token to exist + await LastContactHelper.SetLastContact((await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1); } - ); + #nullable disable - app.UseRouting(); + await next(context); + } + ); - app.UseStaticFiles(); + app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapControllers()); - app.UseEndpoints(endpoints => endpoints.MapRazorPages()); - } + app.UseStaticFiles(); + + app.UseEndpoints(endpoints => endpoints.MapControllers()); + app.UseEndpoints(endpoints => endpoints.MapRazorPages()); } } \ No newline at end of file diff --git a/ProjectLighthouse/TestStartup.cs b/ProjectLighthouse/TestStartup.cs index 2601c9ca..55f58b59 100644 --- a/ProjectLighthouse/TestStartup.cs +++ b/ProjectLighthouse/TestStartup.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; -namespace LBPUnion.ProjectLighthouse -{ - public class TestStartup : Startup - { - public TestStartup(IConfiguration configuration) : base(configuration) - {} +namespace LBPUnion.ProjectLighthouse; - public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseMiddleware(); - base.Configure(app, env); - } +public class TestStartup : Startup +{ + public TestStartup(IConfiguration configuration) : base(configuration) + {} + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMiddleware(); + base.Configure(app, env); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/AuthenticationAttempt.cs b/ProjectLighthouse/Types/AuthenticationAttempt.cs index 57a10cd8..76c45278 100644 --- a/ProjectLighthouse/Types/AuthenticationAttempt.cs +++ b/ProjectLighthouse/Types/AuthenticationAttempt.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class AuthenticationAttempt { - public class AuthenticationAttempt - { - [Key] - public int AuthenticationAttemptId { get; set; } + [Key] + public int AuthenticationAttemptId { get; set; } - public long Timestamp { get; set; } - public Platform Platform { get; set; } - public string IPAddress { get; set; } + public long Timestamp { get; set; } + public Platform Platform { get; set; } + public string IPAddress { get; set; } - public int GameTokenId { get; set; } + public int GameTokenId { get; set; } - [ForeignKey(nameof(GameTokenId))] - public GameToken GameToken { get; set; } - } + [ForeignKey(nameof(GameTokenId))] + public GameToken GameToken { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/Category.cs b/ProjectLighthouse/Types/Categories/Category.cs index b5bebf07..c2d2918a 100644 --- a/ProjectLighthouse/Types/Categories/Category.cs +++ b/ProjectLighthouse/Types/Categories/Category.cs @@ -4,68 +4,65 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +[XmlType("category")] +[XmlRoot("category")] +public abstract class Category { - [XmlType("category")] - [XmlRoot("category")] - public abstract class Category + [XmlElement("name")] + public abstract string Name { get; set; } + + [XmlElement("description")] + public abstract string Description { get; set; } + + [XmlElement("icon")] + public abstract string IconHash { get; set; } + + [XmlIgnore] + public abstract string Endpoint { get; set; } + + [XmlElement("url")] + public string IngameEndpoint { + get => $"/searches/{this.Endpoint}"; + set => this.Endpoint = value.Replace("/searches/", ""); + } + + public abstract Slot? GetPreviewSlot(Database database); + + public abstract IEnumerable GetSlots(Database database, int pageStart, int pageSize); + + public abstract int GetTotalSlots(Database database); + + public string Serialize(Database database) { - [XmlElement("name")] - public abstract string Name { get; set; } + Slot? previewSlot = this.GetPreviewSlot(database); - [XmlElement("description")] - public abstract string Description { get; set; } - - [XmlElement("icon")] - public abstract string IconHash { get; set; } - - [XmlIgnore] - public abstract string Endpoint { get; set; } - - [XmlElement("url")] - public string IngameEndpoint { - get => $"/searches/{this.Endpoint}"; - set => this.Endpoint = value.Replace("/searches/", ""); - } - - public abstract Slot? GetPreviewSlot(Database database); - - public abstract IEnumerable GetSlots(Database database, int pageStart, int pageSize); - - public abstract int GetTotalSlots(Database database); - - public string Serialize(Database database) - { - Slot? previewSlot = this.GetPreviewSlot(database); - - string previewResults = ""; - if (previewSlot != null) - { - previewResults = LbpSerializer.TaggedStringElement - ( - "results", - previewSlot.Serialize(), - new Dictionary - { - { - "total", this.GetTotalSlots(database) - }, - { - "hint_start", "2" - }, - } - ); - } - - return LbpSerializer.StringElement + string previewResults = ""; + if (previewSlot != null) + previewResults = LbpSerializer.TaggedStringElement ( - "category", - LbpSerializer.StringElement("name", this.Name) + - LbpSerializer.StringElement("description", this.Description) + - LbpSerializer.StringElement("url", this.IngameEndpoint) + - (previewSlot == null ? "" : previewResults) + - LbpSerializer.StringElement("icon", IconHash) + "results", + previewSlot.Serialize(), + new Dictionary + { + { + "total", this.GetTotalSlots(database) + }, + { + "hint_start", "2" + }, + } ); - } + + return LbpSerializer.StringElement + ( + "category", + LbpSerializer.StringElement("name", this.Name) + + LbpSerializer.StringElement("description", this.Description) + + LbpSerializer.StringElement("url", this.IngameEndpoint) + + (previewSlot == null ? "" : previewResults) + + LbpSerializer.StringElement("icon", this.IconHash) + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/CategoryWithUser.cs b/ProjectLighthouse/Types/Categories/CategoryWithUser.cs index fee2f4d0..ffbf478e 100644 --- a/ProjectLighthouse/Types/Categories/CategoryWithUser.cs +++ b/ProjectLighthouse/Types/Categories/CategoryWithUser.cs @@ -1,6 +1,5 @@ #nullable enable using System.Collections.Generic; -using System.Diagnostics; using Kettu; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Serialization; @@ -52,7 +51,6 @@ public abstract class CategoryWithUser : Category string previewResults = ""; if (previewSlot != null) - { previewResults = LbpSerializer.TaggedStringElement ( "results", @@ -67,7 +65,6 @@ public abstract class CategoryWithUser : Category }, } ); - } return LbpSerializer.StringElement ( @@ -76,7 +73,7 @@ public abstract class CategoryWithUser : Category LbpSerializer.StringElement("description", this.Description) + LbpSerializer.StringElement("url", this.IngameEndpoint) + (previewSlot == null ? "" : previewResults) + - LbpSerializer.StringElement("icon", IconHash) + LbpSerializer.StringElement("icon", this.IconHash) ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/CustomCategory.cs b/ProjectLighthouse/Types/Categories/CustomCategory.cs index cd655844..54b824f4 100644 --- a/ProjectLighthouse/Types/Categories/CustomCategory.cs +++ b/ProjectLighthouse/Types/Categories/CustomCategory.cs @@ -3,39 +3,37 @@ using System.Collections.Generic; using System.Linq; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class CustomCategory : Category { - public class CustomCategory : Category + + public List SlotIds; + public CustomCategory(string name, string description, string endpoint, string icon, IEnumerable slotIds) { - public CustomCategory(string name, string description, string endpoint, string icon, IEnumerable slotIds) - { - this.Name = name; - this.Description = description; - this.IconHash = icon; - this.Endpoint = endpoint; + this.Name = name; + this.Description = description; + this.IconHash = icon; + this.Endpoint = endpoint; - this.SlotIds = slotIds.ToList(); - } - - public CustomCategory(DatabaseCategory category) - { - this.Name = category.Name; - this.Description = category.Description; - this.IconHash = category.IconHash; - this.Endpoint = category.Endpoint; - - this.SlotIds = category.SlotIds.ToList(); - } - - public List SlotIds; - - public sealed override string Name { get; set; } - public sealed override string Description { get; set; } - public sealed override string IconHash { get; set; } - public sealed override string Endpoint { get; set; } - public override Slot? GetPreviewSlot(Database database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]); - public override IEnumerable GetSlots(Database database, int pageStart, int pageSize) - => database.Slots.Where(s => this.SlotIds.Contains(s.SlotId)); - public override int GetTotalSlots(Database database) => this.SlotIds.Count; + this.SlotIds = slotIds.ToList(); } + + public CustomCategory(DatabaseCategory category) + { + this.Name = category.Name; + this.Description = category.Description; + this.IconHash = category.IconHash; + this.Endpoint = category.Endpoint; + + this.SlotIds = category.SlotIds.ToList(); + } + + public sealed override string Name { get; set; } + public sealed override string Description { get; set; } + public sealed override string IconHash { get; set; } + public sealed override string Endpoint { get; set; } + public override Slot? GetPreviewSlot(Database database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]); + public override IEnumerable GetSlots(Database database, int pageStart, int pageSize) => database.Slots.Where(s => this.SlotIds.Contains(s.SlotId)); + public override int GetTotalSlots(Database database) => this.SlotIds.Count; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/DatabaseCategory.cs b/ProjectLighthouse/Types/Categories/DatabaseCategory.cs index 2f728d8f..648985c9 100644 --- a/ProjectLighthouse/Types/Categories/DatabaseCategory.cs +++ b/ProjectLighthouse/Types/Categories/DatabaseCategory.cs @@ -2,24 +2,23 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class DatabaseCategory { - public class DatabaseCategory - { - [Key] - public int CategoryId { get; set; } + [Key] + public int CategoryId { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public string IconHash { get; set; } - public string Endpoint { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string IconHash { get; set; } + public string Endpoint { get; set; } - public string SlotIdsCollection { get; set; } + public string SlotIdsCollection { get; set; } - [NotMapped] - public int[] SlotIds { - get => SlotIdsCollection.Split(",").Select(int.Parse).ToArray(); - set => SlotIdsCollection = string.Join(",", value); - } + [NotMapped] + public int[] SlotIds { + get => this.SlotIdsCollection.Split(",").Select(int.Parse).ToArray(); + set => this.SlotIdsCollection = string.Join(",", value); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/HeartedCategory.cs b/ProjectLighthouse/Types/Categories/HeartedCategory.cs index 7414fcfa..318aa5c3 100644 --- a/ProjectLighthouse/Types/Categories/HeartedCategory.cs +++ b/ProjectLighthouse/Types/Categories/HeartedCategory.cs @@ -5,17 +5,16 @@ using System.Linq; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class HeartedCategory : CategoryWithUser { - public class HeartedCategory : CategoryWithUser - { - public override string Name { get; set; } = "My Hearted Levels"; - public override string Description { get; set; } = "Levels you've hearted in the past"; - public override string IconHash { get; set; } = "g820607"; - public override string Endpoint { get; set; } = "hearted"; - public override Slot? GetPreviewSlot(Database database, User user) => database.HeartedLevels.FirstOrDefault(h => h.UserId == user.UserId)?.Slot; - public override int GetTotalSlots(Database database, User user) => database.HeartedLevels.Count(h => h.UserId == user.UserId); - public override IEnumerable GetSlots(Database database, User user, int pageStart, int pageSize) - => database.HeartedLevels.Where(h => h.UserId == user.UserId).Include(h => h.Slot).Select(h => h.Slot).Skip(pageStart).Take(Math.Min(pageSize, 20)); - } + public override string Name { get; set; } = "My Hearted Levels"; + public override string Description { get; set; } = "Levels you've hearted in the past"; + public override string IconHash { get; set; } = "g820607"; + public override string Endpoint { get; set; } = "hearted"; + public override Slot? GetPreviewSlot(Database database, User user) => database.HeartedLevels.FirstOrDefault(h => h.UserId == user.UserId)?.Slot; + public override int GetTotalSlots(Database database, User user) => database.HeartedLevels.Count(h => h.UserId == user.UserId); + public override IEnumerable GetSlots(Database database, User user, int pageStart, int pageSize) + => database.HeartedLevels.Where(h => h.UserId == user.UserId).Include(h => h.Slot).Select(h => h.Slot).Skip(pageStart).Take(Math.Min(pageSize, 20)); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs b/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs index e010b2d1..65f14ccb 100644 --- a/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs +++ b/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs @@ -4,18 +4,17 @@ using System.Collections.Generic; using System.Linq; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class NewestLevelsCategory : Category { - public class NewestLevelsCategory : Category - { - public override string Name { get; set; } = "Newest Levels"; - public override string Description { get; set; } = "Levels recently published"; - public override string IconHash { get; set; } = "g820623"; - public override string Endpoint { get; set; } = "newest"; - public override Slot? GetPreviewSlot(Database database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(); - public override IEnumerable GetSlots - (Database database, int pageStart, int pageSize) - => database.Slots.OrderByDescending(s => s.FirstUploaded).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); - public override int GetTotalSlots(Database database) => database.Slots.Count(); - } + public override string Name { get; set; } = "Newest Levels"; + public override string Description { get; set; } = "Levels recently published"; + public override string IconHash { get; set; } = "g820623"; + public override string Endpoint { get; set; } = "newest"; + public override Slot? GetPreviewSlot(Database database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(); + public override IEnumerable GetSlots + (Database database, int pageStart, int pageSize) + => database.Slots.OrderByDescending(s => s.FirstUploaded).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); + public override int GetTotalSlots(Database database) => database.Slots.Count(); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/QueueCategory.cs b/ProjectLighthouse/Types/Categories/QueueCategory.cs index 274f8023..40d2f6b0 100644 --- a/ProjectLighthouse/Types/Categories/QueueCategory.cs +++ b/ProjectLighthouse/Types/Categories/QueueCategory.cs @@ -5,21 +5,20 @@ using System.Linq; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class QueueCategory : CategoryWithUser { - public class QueueCategory : CategoryWithUser - { - public override string Name { get; set; } = "My Queue"; - public override string Description { get; set; } = "Your queued levels"; - public override string IconHash { get; set; } = "g820614"; + public override string Name { get; set; } = "My Queue"; + public override string Description { get; set; } = "Your queued levels"; + public override string IconHash { get; set; } = "g820614"; - public override string Endpoint { get; set; } = "queue"; + public override string Endpoint { get; set; } = "queue"; - public override Slot? GetPreviewSlot(Database database, User user) - => database.QueuedLevels.Include(q => q.Slot).FirstOrDefault(q => q.UserId == user.UserId)?.Slot; - public override IEnumerable GetSlots(Database database, User user, int pageStart, int pageSize) - => database.QueuedLevels.Include(q => q.Slot).Include(q => q.Slot.Location).Select(q => q.Slot).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); + public override Slot? GetPreviewSlot(Database database, User user) + => database.QueuedLevels.Include(q => q.Slot).FirstOrDefault(q => q.UserId == user.UserId)?.Slot; + public override IEnumerable GetSlots(Database database, User user, int pageStart, int pageSize) + => database.QueuedLevels.Include(q => q.Slot).Include(q => q.Slot.Location).Select(q => q.Slot).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); - public override int GetTotalSlots(Database database, User user) => database.QueuedLevels.Count(q => q.UserId == user.UserId); - } + public override int GetTotalSlots(Database database, User user) => database.QueuedLevels.Count(q => q.UserId == user.UserId); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs b/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs index 883d20da..116daee3 100644 --- a/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs +++ b/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs @@ -4,18 +4,17 @@ using System.Collections.Generic; using System.Linq; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories +namespace LBPUnion.ProjectLighthouse.Types.Categories; + +public class TeamPicksCategory : Category { - public class TeamPicksCategory : Category - { - public override string Name { get; set; } = "Team Picks"; - public override string Description { get; set; } = "Levels given awards by your instance admin"; - public override string IconHash { get; set; } = "g820626"; - public override string Endpoint { get; set; } = "team_picks"; - public override Slot? GetPreviewSlot(Database database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick); - public override IEnumerable GetSlots - (Database database, int pageStart, int pageSize) - => database.Slots.OrderByDescending(s => s.FirstUploaded).Where(s => s.TeamPick).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); - public override int GetTotalSlots(Database database) => database.Slots.Count(s => s.TeamPick); - } + public override string Name { get; set; } = "Team Picks"; + public override string Description { get; set; } = "Levels given awards by your instance admin"; + public override string IconHash { get; set; } = "g820626"; + public override string Endpoint { get; set; } = "team_picks"; + public override Slot? GetPreviewSlot(Database database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick); + public override IEnumerable GetSlots + (Database database, int pageStart, int pageSize) + => database.Slots.OrderByDescending(s => s.FirstUploaded).Where(s => s.TeamPick).Skip(pageStart - 1).Take(Math.Min(pageSize, 20)); + public override int GetTotalSlots(Database database) => database.Slots.Count(s => s.TeamPick); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/DeletedBy.cs b/ProjectLighthouse/Types/DeletedBy.cs index 964b9e53..c0e57733 100644 --- a/ProjectLighthouse/Types/DeletedBy.cs +++ b/ProjectLighthouse/Types/DeletedBy.cs @@ -1,18 +1,17 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +[XmlRoot("deleted_by")] +public enum DeletedBy { - [XmlRoot("deleted_by")] - public enum DeletedBy - { - [XmlEnum(Name = "none")] - None, + [XmlEnum(Name = "none")] + None, - [XmlEnum(Name = "moderator")] - Moderator, + [XmlEnum(Name = "moderator")] + Moderator, - [XmlEnum(Name = "level_author")] - LevelAuthor, - // TODO: deletion types for comments (profile etc) - } + [XmlEnum(Name = "level_author")] + LevelAuthor, + // TODO: deletion types for comments (profile etc) } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFile.cs b/ProjectLighthouse/Types/Files/LbpFile.cs index d0c98314..0cd4a4d1 100644 --- a/ProjectLighthouse/Types/Files/LbpFile.cs +++ b/ProjectLighthouse/Types/Files/LbpFile.cs @@ -2,35 +2,34 @@ using System.IO; using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Types.Files +namespace LBPUnion.ProjectLighthouse.Types.Files; + +public class LbpFile { - public class LbpFile + + /// + /// A buffer of the file's data. + /// + public readonly byte[] Data; + + /// + /// The type of file. + /// + public LbpFileType FileType; + + public LbpFile(byte[] data) { + this.Data = data; + this.FileType = FileHelper.DetermineFileType(this.Data); + } - /// - /// A buffer of the file's data. - /// - public readonly byte[] Data; + public static LbpFile? FromHash(string hash) + { + string path = FileHelper.GetResourcePath(hash); + if (!File.Exists(path)) return null; - /// - /// The type of file. - /// - public LbpFileType FileType; + byte[] data = File.ReadAllBytes(path); - public LbpFile(byte[] data) - { - this.Data = data; - this.FileType = FileHelper.DetermineFileType(this.Data); - } - - public static LbpFile? FromHash(string hash) - { - string path = FileHelper.GetResourcePath(hash); - if (!File.Exists(path)) return null; - - byte[] data = File.ReadAllBytes(path); - - return new LbpFile(data); - } + return new LbpFile(data); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFileType.cs b/ProjectLighthouse/Types/Files/LbpFileType.cs index 69ba34e7..a6084228 100644 --- a/ProjectLighthouse/Types/Files/LbpFileType.cs +++ b/ProjectLighthouse/Types/Files/LbpFileType.cs @@ -1,16 +1,15 @@ -namespace LBPUnion.ProjectLighthouse.Types.Files +namespace LBPUnion.ProjectLighthouse.Types.Files; + +public enum LbpFileType { - public enum LbpFileType - { - Script, // .ff, FSH - Texture, // TEX - Level, // LVL - FileArchive, // .farc, (ends with FARC) - Plan, // PLN, uploaded with levels - Voice, // VOP, voice data - Painting, // PTG, paintings - Jpeg, // JFIF / FIF, used in sticker switches, - Png, // used in LBP Vita - Unknown, - } + Script, // .ff, FSH + Texture, // TEX + Level, // LVL + FileArchive, // .farc, (ends with FARC) + Plan, // PLN, uploaded with levels + Voice, // VOP, voice data + Painting, // PTG, paintings + Jpeg, // JFIF / FIF, used in sticker switches, + Png, // used in LBP Vita + Unknown, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/GameToken.cs b/ProjectLighthouse/Types/GameToken.cs index 74ea937d..d084c034 100644 --- a/ProjectLighthouse/Types/GameToken.cs +++ b/ProjectLighthouse/Types/GameToken.cs @@ -1,29 +1,28 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class GameToken { - public class GameToken - { - // ReSharper disable once UnusedMember.Global - [Key] - public int TokenId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int TokenId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public string UserToken { get; set; } + public string UserToken { get; set; } - public string UserLocation { get; set; } + public string UserLocation { get; set; } - public GameVersion GameVersion { get; set; } + public GameVersion GameVersion { get; set; } - // Set by /authentication webpage - public bool Approved { get; set; } + // Set by /authentication webpage + public bool Approved { get; set; } - // Set to true on login - public bool Used { get; set; } - } + // Set to true on login + public bool Used { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/GameVersion.cs b/ProjectLighthouse/Types/GameVersion.cs index 2ad35019..8e5c8b6b 100644 --- a/ProjectLighthouse/Types/GameVersion.cs +++ b/ProjectLighthouse/Types/GameVersion.cs @@ -1,17 +1,16 @@ -namespace LBPUnion.ProjectLighthouse.Types -{ - public enum GameVersion - { - LittleBigPlanet1 = 0, - LittleBigPlanet2 = 1, - LittleBigPlanet3 = 2, - LittleBigPlanetVita = 3, - LittleBigPlanetPSP = 4, - Unknown = -1, - } +namespace LBPUnion.ProjectLighthouse.Types; - public static class GameVersionExtensions - { - public static string ToPrettyString(this GameVersion gameVersion) => gameVersion.ToString().Replace("LittleBigPlanet", "LittleBigPlanet "); - } +public enum GameVersion +{ + LittleBigPlanet1 = 0, + LittleBigPlanet2 = 1, + LittleBigPlanet3 = 2, + LittleBigPlanetVita = 3, + LittleBigPlanetPSP = 4, + Unknown = -1, +} + +public static class GameVersionExtensions +{ + public static string ToPrettyString(this GameVersion gameVersion) => gameVersion.ToString().Replace("LittleBigPlanet", "LittleBigPlanet "); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/HeartedProfile.cs b/ProjectLighthouse/Types/HeartedProfile.cs index bf4ebea5..fcb2d454 100644 --- a/ProjectLighthouse/Types/HeartedProfile.cs +++ b/ProjectLighthouse/Types/HeartedProfile.cs @@ -2,27 +2,26 @@ using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class HeartedProfile { - public class HeartedProfile - { - // ReSharper disable once UnusedMember.Global - #if NET6_0_OR_GREATER - [Obsolete($"Use {nameof(HeartedUserId)} instead, this is a key which you should never need to use.")] - #else + // ReSharper disable once UnusedMember.Global + #if NET6_0_OR_GREATER + [Obsolete($"Use {nameof(HeartedUserId)} instead, this is a key which you should never need to use.")] + #else [Obsolete("Use HeartedUserId instead, this is a key which you should never need to use.")] - #endif - [Key] - public int HeartedProfileId { get; set; } + #endif + [Key] + public int HeartedProfileId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int HeartedUserId { get; set; } + public int HeartedUserId { get; set; } - [ForeignKey(nameof(HeartedUserId))] - public User HeartedUser { get; set; } - } + [ForeignKey(nameof(HeartedUserId))] + public User HeartedUser { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/HeartedLevel.cs b/ProjectLighthouse/Types/Levels/HeartedLevel.cs index 72b9855b..6500f3e9 100644 --- a/ProjectLighthouse/Types/Levels/HeartedLevel.cs +++ b/ProjectLighthouse/Types/Levels/HeartedLevel.cs @@ -1,22 +1,21 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +public class HeartedLevel { - public class HeartedLevel - { - // ReSharper disable once UnusedMember.Global - [Key] - public int HeartedLevelId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int HeartedLevelId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int SlotId { get; set; } + public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } - } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/LevelTags.cs b/ProjectLighthouse/Types/Levels/LevelTags.cs index 784985e4..2df0d786 100644 --- a/ProjectLighthouse/Types/Levels/LevelTags.cs +++ b/ProjectLighthouse/Types/Levels/LevelTags.cs @@ -1,89 +1,88 @@ using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +/// +/// A series of tags that can be applied to a level +/// +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public enum LevelTags { - /// - /// A series of tags that can be applied to a level - /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public enum LevelTags - { - Brilliant, - Beautiful, - Funky, - Points_Fest, - Weird, - Tricky, - Short, - Vehicles, - Easy, - Cute, - Quick, - Fun, - Relaxing, - Great, - Speedy, - Race, - Multi_Path, - Machines, - Complex, - Pretty, - Rubbish, - Toys, - Repetitive, - Machinery, - Satisfying, - Braaains, - Fast, - Simple, - Long, - Slow, - Mad, - Hectic, - Creepy, - Perilous, - Empty, - Ingenious, - Lousy, - Frustrating, - Timing, - Boss, - Springy, - Funny, - Musical, - Good, - Hilarious, - Electric, - Puzzler, - Platformer, - Difficult, - Mechanical, - Horizontal, - Splendid, - Fiery, - Swingy, - Single_Path, - Annoying, - Co_op, - Boring, - Moody, - Bubbly, - Nerve_wracking, - Hoists, - Ugly, - Daft, - Ramps, - Secrets, - Floaty, - Artistic, - Competitive, - Gas, - Varied, - Stickers, - Spikes, - Collectables, - Vertical, - Balancing, - } + Brilliant, + Beautiful, + Funky, + Points_Fest, + Weird, + Tricky, + Short, + Vehicles, + Easy, + Cute, + Quick, + Fun, + Relaxing, + Great, + Speedy, + Race, + Multi_Path, + Machines, + Complex, + Pretty, + Rubbish, + Toys, + Repetitive, + Machinery, + Satisfying, + Braaains, + Fast, + Simple, + Long, + Slow, + Mad, + Hectic, + Creepy, + Perilous, + Empty, + Ingenious, + Lousy, + Frustrating, + Timing, + Boss, + Springy, + Funny, + Musical, + Good, + Hilarious, + Electric, + Puzzler, + Platformer, + Difficult, + Mechanical, + Horizontal, + Splendid, + Fiery, + Swingy, + Single_Path, + Annoying, + Co_op, + Boring, + Moody, + Bubbly, + Nerve_wracking, + Hoists, + Ugly, + Daft, + Ramps, + Secrets, + Floaty, + Artistic, + Competitive, + Gas, + Varied, + Stickers, + Spikes, + Collectables, + Vertical, + Balancing, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/QueuedLevel.cs b/ProjectLighthouse/Types/Levels/QueuedLevel.cs index e238627e..2957d9ce 100644 --- a/ProjectLighthouse/Types/Levels/QueuedLevel.cs +++ b/ProjectLighthouse/Types/Levels/QueuedLevel.cs @@ -1,22 +1,21 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +public class QueuedLevel { - public class QueuedLevel - { - // ReSharper disable once UnusedMember.Global - [Key] - public int QueuedLevelId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int QueuedLevelId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int SlotId { get; set; } + public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } - } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/RatedLevel.cs b/ProjectLighthouse/Types/Levels/RatedLevel.cs index 8b2228a0..d34afd08 100644 --- a/ProjectLighthouse/Types/Levels/RatedLevel.cs +++ b/ProjectLighthouse/Types/Levels/RatedLevel.cs @@ -1,26 +1,25 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +public class RatedLevel { - public class RatedLevel - { - // ReSharper disable once UnusedMember.Global - [Key] - public int RatedLevelId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int RatedLevelId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int SlotId { get; set; } + public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } - public int Rating { get; set; } + public int Rating { get; set; } - public double RatingLBP1 { get; set; } - } + public double RatingLBP1 { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index faab5eee..a7abbcff 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -8,265 +8,264 @@ using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Reviews; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +/// +/// A LittleBigPlanet level. +/// +[XmlRoot("slot")] +[XmlType("slot")] +public class Slot { + [XmlAttribute("type")] + [NotMapped] + public string Type { get; set; } = "user"; + + [Key] + [XmlElement("id")] + public int SlotId { get; set; } + + [XmlElement("name")] + public string Name { get; set; } = ""; + + [XmlElement("description")] + public string Description { get; set; } = ""; + + [XmlElement("icon")] + public string IconHash { get; set; } = ""; + + [XmlElement("rootLevel")] + public string RootLevel { get; set; } = ""; + + public string ResourceCollection { get; set; } = ""; + + [NotMapped] + [XmlElement("resource")] + public string[] Resources { + get => this.ResourceCollection.Split(","); + set => this.ResourceCollection = string.Join(',', value); + } + + [XmlIgnore] + public int LocationId { get; set; } + + [XmlIgnore] + public int CreatorId { get; set; } + + [ForeignKey(nameof(CreatorId))] + public User? Creator { get; set; } + /// - /// A LittleBigPlanet level. + /// The location of the level on the creator's earth /// - [XmlRoot("slot")] - [XmlType("slot")] - public class Slot - { - [XmlAttribute("type")] - [NotMapped] - public string Type { get; set; } = "user"; + [XmlElement("location")] + [ForeignKey(nameof(LocationId))] + public Location? Location { get; set; } - [Key] - [XmlElement("id")] - public int SlotId { get; set; } + [XmlElement("initiallyLocked")] + public bool InitiallyLocked { get; set; } - [XmlElement("name")] - public string Name { get; set; } = ""; + [XmlElement("isSubLevel")] + public bool SubLevel { get; set; } - [XmlElement("description")] - public string Description { get; set; } = ""; + [XmlElement("isLBP1Only")] + public bool Lbp1Only { get; set; } - [XmlElement("icon")] - public string IconHash { get; set; } = ""; + [XmlElement("shareable")] + public int Shareable { get; set; } - [XmlElement("rootLevel")] - public string RootLevel { get; set; } = ""; + [XmlElement("authorLabels")] + public string AuthorLabels { get; set; } = ""; - public string ResourceCollection { get; set; } = ""; + [XmlElement("background")] + public string BackgroundHash { get; set; } = ""; - [NotMapped] - [XmlElement("resource")] - public string[] Resources { - get => this.ResourceCollection.Split(","); - set => this.ResourceCollection = string.Join(',', value); - } + [XmlElement("minPlayers")] + public int MinimumPlayers { get; set; } - [XmlIgnore] - public int LocationId { get; set; } + [XmlElement("maxPlayers")] + public int MaximumPlayers { get; set; } - [XmlIgnore] - public int CreatorId { get; set; } + [XmlElement("moveRequired")] + public bool MoveRequired { get; set; } - [ForeignKey(nameof(CreatorId))] - public User? Creator { get; set; } + [XmlIgnore] + public long FirstUploaded { get; set; } - /// - /// The location of the level on the creator's earth - /// - [XmlElement("location")] - [ForeignKey(nameof(LocationId))] - public Location? Location { get; set; } + [XmlIgnore] + public long LastUpdated { get; set; } - [XmlElement("initiallyLocked")] - public bool InitiallyLocked { get; set; } + [XmlIgnore] + public bool TeamPick { get; set; } - [XmlElement("isSubLevel")] - public bool SubLevel { get; set; } + [XmlIgnore] + public GameVersion GameVersion { get; set; } - [XmlElement("isLBP1Only")] - public bool Lbp1Only { get; set; } + [XmlIgnore] + [NotMapped] + public int Hearts { + get { + using Database database = new(); - [XmlElement("shareable")] - public int Shareable { get; set; } - - [XmlElement("authorLabels")] - public string AuthorLabels { get; set; } = ""; - - [XmlElement("background")] - public string BackgroundHash { get; set; } = ""; - - [XmlElement("minPlayers")] - public int MinimumPlayers { get; set; } - - [XmlElement("maxPlayers")] - public int MaximumPlayers { get; set; } - - [XmlElement("moveRequired")] - public bool MoveRequired { get; set; } - - [XmlIgnore] - public long FirstUploaded { get; set; } - - [XmlIgnore] - public long LastUpdated { get; set; } - - [XmlIgnore] - public bool TeamPick { get; set; } - - [XmlIgnore] - public GameVersion GameVersion { get; set; } - - [XmlIgnore] - [NotMapped] - public int Hearts { - get { - using Database database = new(); - - return database.HeartedLevels.Count(s => s.SlotId == this.SlotId); - } - } - - [XmlIgnore] - [NotMapped] - public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita; - - [XmlIgnore] - [NotMapped] - public int PlaysUnique => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique + this.PlaysLBPVitaUnique; - - [XmlIgnore] - [NotMapped] - public int PlaysComplete => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete + this.PlaysLBPVitaComplete; - - [XmlIgnore] - public int PlaysLBP1 { get; set; } - - [XmlIgnore] - public int PlaysLBP1Complete { get; set; } - - [XmlIgnore] - public int PlaysLBP1Unique { get; set; } - - [XmlIgnore] - public int PlaysLBP2 { get; set; } - - [XmlIgnore] - public int PlaysLBP2Complete { get; set; } - - [XmlIgnore] - public int PlaysLBP2Unique { get; set; } - - [XmlIgnore] - public int PlaysLBP3 { get; set; } - - [XmlIgnore] - public int PlaysLBP3Complete { get; set; } - - [XmlIgnore] - public int PlaysLBP3Unique { get; set; } - - [XmlIgnore] - public int PlaysLBPVita { get; set; } - - [XmlIgnore] - public int PlaysLBPVitaComplete { get; set; } - - [XmlIgnore] - public int PlaysLBPVitaUnique { get; set; } - - [NotMapped] - [XmlElement("thumbsup")] - public int Thumbsup { - get { - using Database database = new(); - - return database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == 1); - } - } - - [NotMapped] - [XmlElement("thumbsdown")] - public int Thumbsdown { - get { - using Database database = new(); - - return database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == -1); - } - } - - [NotMapped] - [XmlElement("averageRating")] - public double RatingLBP1 { - get { - using Database database = new(); - - IQueryable ratedLevels = database.RatedLevels.Where(r => r.SlotId == this.SlotId && r.RatingLBP1 > 0); - if (!ratedLevels.Any()) return 3.0; - - return Enumerable.Average(ratedLevels, r => r.RatingLBP1); - } - } - - [NotMapped] - [XmlElement("reviewCount")] - public int ReviewCount { - get { - using Database database = new(); - - return database.Reviews.Count(r => r.SlotId == this.SlotId); - } - } - - [XmlElement("leveltype")] - public string LevelType { get; set; } = ""; - - public string SerializeResources() - { - return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + - LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)); - } - - public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, Review? yourReview = null) - { - - string slotData = LbpSerializer.StringElement("name", this.Name) + - LbpSerializer.StringElement("id", this.SlotId) + - LbpSerializer.StringElement("game", (int)this.GameVersion) + - LbpSerializer.StringElement("npHandle", this.Creator?.Username) + - LbpSerializer.StringElement("description", this.Description) + - LbpSerializer.StringElement("icon", this.IconHash) + - LbpSerializer.StringElement("rootLevel", this.RootLevel) + - this.SerializeResources() + - LbpSerializer.StringElement("location", this.Location?.Serialize()) + - LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) + - LbpSerializer.StringElement("isSubLevel", this.SubLevel) + - LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) + - LbpSerializer.StringElement("shareable", this.Shareable) + - LbpSerializer.StringElement("background", this.BackgroundHash) + - LbpSerializer.StringElement("minPlayers", this.MinimumPlayers) + - LbpSerializer.StringElement("maxPlayers", this.MaximumPlayers) + - LbpSerializer.StringElement("moveRequired", this.MoveRequired) + - LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + - LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + - LbpSerializer.StringElement("mmpick", this.TeamPick) + - LbpSerializer.StringElement("heartCount", this.Hearts) + - LbpSerializer.StringElement("playCount", this.Plays) + - LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol - LbpSerializer.StringElement("completionCount", this.PlaysComplete) + - LbpSerializer.StringElement("lbp1PlayCount", this.PlaysLBP1) + - LbpSerializer.StringElement("lbp1CompletionCount", this.PlaysLBP1Complete) + - LbpSerializer.StringElement("lbp1UniquePlayCount", this.PlaysLBP1Unique) + - LbpSerializer.StringElement("lbp2PlayCount", this.PlaysLBP2) + - LbpSerializer.StringElement("lbp2CompletionCount", this.PlaysLBP2Complete) + - LbpSerializer.StringElement("lbp2UniquePlayCount", this.PlaysLBP2Unique) + // not actually used ingame, as per above comment - LbpSerializer.StringElement("lbp3PlayCount", this.PlaysLBP3) + - LbpSerializer.StringElement("lbp3CompletionCount", this.PlaysLBP3Complete) + - LbpSerializer.StringElement("lbp3UniquePlayCount", this.PlaysLBP3Unique) + - LbpSerializer.StringElement("lbpvitaPlayCount", this.PlaysLBPVita) + - LbpSerializer.StringElement("lbpvitaCompletionCount", this.PlaysLBPVitaComplete) + - LbpSerializer.StringElement("lbpvitaUniquePlayCount", this.PlaysLBPVitaUnique) + - LbpSerializer.StringElement("thumbsup", this.Thumbsup) + - LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) + - LbpSerializer.StringElement("averageRating", this.RatingLBP1) + - LbpSerializer.StringElement("leveltype", this.LevelType) + - LbpSerializer.StringElement("yourRating", yourRatingStats?.RatingLBP1) + - LbpSerializer.StringElement("yourDPadRating", yourRatingStats?.Rating) + - LbpSerializer.StringElement("yourLBP1PlayCount", yourVisitedStats?.PlaysLBP1) + - LbpSerializer.StringElement("yourLBP2PlayCount", yourVisitedStats?.PlaysLBP2) + - LbpSerializer.StringElement("yourLBP3PlayCount", yourVisitedStats?.PlaysLBP3) + - LbpSerializer.StringElement - ("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it - yourReview?.Serialize("yourReview") + - LbpSerializer.StringElement("reviewsEnabled", true) + - LbpSerializer.StringElement("commentsEnabled", false) + - LbpSerializer.StringElement("reviewCount", this.ReviewCount); - - return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); + return database.HeartedLevels.Count(s => s.SlotId == this.SlotId); } } + + [XmlIgnore] + [NotMapped] + public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita; + + [XmlIgnore] + [NotMapped] + public int PlaysUnique => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique + this.PlaysLBPVitaUnique; + + [XmlIgnore] + [NotMapped] + public int PlaysComplete => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete + this.PlaysLBPVitaComplete; + + [XmlIgnore] + public int PlaysLBP1 { get; set; } + + [XmlIgnore] + public int PlaysLBP1Complete { get; set; } + + [XmlIgnore] + public int PlaysLBP1Unique { get; set; } + + [XmlIgnore] + public int PlaysLBP2 { get; set; } + + [XmlIgnore] + public int PlaysLBP2Complete { get; set; } + + [XmlIgnore] + public int PlaysLBP2Unique { get; set; } + + [XmlIgnore] + public int PlaysLBP3 { get; set; } + + [XmlIgnore] + public int PlaysLBP3Complete { get; set; } + + [XmlIgnore] + public int PlaysLBP3Unique { get; set; } + + [XmlIgnore] + public int PlaysLBPVita { get; set; } + + [XmlIgnore] + public int PlaysLBPVitaComplete { get; set; } + + [XmlIgnore] + public int PlaysLBPVitaUnique { get; set; } + + [NotMapped] + [XmlElement("thumbsup")] + public int Thumbsup { + get { + using Database database = new(); + + return database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == 1); + } + } + + [NotMapped] + [XmlElement("thumbsdown")] + public int Thumbsdown { + get { + using Database database = new(); + + return database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == -1); + } + } + + [NotMapped] + [XmlElement("averageRating")] + public double RatingLBP1 { + get { + using Database database = new(); + + IQueryable ratedLevels = database.RatedLevels.Where(r => r.SlotId == this.SlotId && r.RatingLBP1 > 0); + if (!ratedLevels.Any()) return 3.0; + + return Enumerable.Average(ratedLevels, r => r.RatingLBP1); + } + } + + [NotMapped] + [XmlElement("reviewCount")] + public int ReviewCount { + get { + using Database database = new(); + + return database.Reviews.Count(r => r.SlotId == this.SlotId); + } + } + + [XmlElement("leveltype")] + public string LevelType { get; set; } = ""; + + public string SerializeResources() + { + return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + + LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)); + } + + public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, Review? yourReview = null) + { + + string slotData = LbpSerializer.StringElement("name", this.Name) + + LbpSerializer.StringElement("id", this.SlotId) + + LbpSerializer.StringElement("game", (int)this.GameVersion) + + LbpSerializer.StringElement("npHandle", this.Creator?.Username) + + LbpSerializer.StringElement("description", this.Description) + + LbpSerializer.StringElement("icon", this.IconHash) + + LbpSerializer.StringElement("rootLevel", this.RootLevel) + + this.SerializeResources() + + LbpSerializer.StringElement("location", this.Location?.Serialize()) + + LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) + + LbpSerializer.StringElement("isSubLevel", this.SubLevel) + + LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) + + LbpSerializer.StringElement("shareable", this.Shareable) + + LbpSerializer.StringElement("background", this.BackgroundHash) + + LbpSerializer.StringElement("minPlayers", this.MinimumPlayers) + + LbpSerializer.StringElement("maxPlayers", this.MaximumPlayers) + + LbpSerializer.StringElement("moveRequired", this.MoveRequired) + + LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + + LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + + LbpSerializer.StringElement("mmpick", this.TeamPick) + + LbpSerializer.StringElement("heartCount", this.Hearts) + + LbpSerializer.StringElement("playCount", this.Plays) + + LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol + LbpSerializer.StringElement("completionCount", this.PlaysComplete) + + LbpSerializer.StringElement("lbp1PlayCount", this.PlaysLBP1) + + LbpSerializer.StringElement("lbp1CompletionCount", this.PlaysLBP1Complete) + + LbpSerializer.StringElement("lbp1UniquePlayCount", this.PlaysLBP1Unique) + + LbpSerializer.StringElement("lbp2PlayCount", this.PlaysLBP2) + + LbpSerializer.StringElement("lbp2CompletionCount", this.PlaysLBP2Complete) + + LbpSerializer.StringElement("lbp2UniquePlayCount", this.PlaysLBP2Unique) + // not actually used ingame, as per above comment + LbpSerializer.StringElement("lbp3PlayCount", this.PlaysLBP3) + + LbpSerializer.StringElement("lbp3CompletionCount", this.PlaysLBP3Complete) + + LbpSerializer.StringElement("lbp3UniquePlayCount", this.PlaysLBP3Unique) + + LbpSerializer.StringElement("lbpvitaPlayCount", this.PlaysLBPVita) + + LbpSerializer.StringElement("lbpvitaCompletionCount", this.PlaysLBPVitaComplete) + + LbpSerializer.StringElement("lbpvitaUniquePlayCount", this.PlaysLBPVitaUnique) + + LbpSerializer.StringElement("thumbsup", this.Thumbsup) + + LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) + + LbpSerializer.StringElement("averageRating", this.RatingLBP1) + + LbpSerializer.StringElement("leveltype", this.LevelType) + + LbpSerializer.StringElement("yourRating", yourRatingStats?.RatingLBP1) + + LbpSerializer.StringElement("yourDPadRating", yourRatingStats?.Rating) + + LbpSerializer.StringElement("yourLBP1PlayCount", yourVisitedStats?.PlaysLBP1) + + LbpSerializer.StringElement("yourLBP2PlayCount", yourVisitedStats?.PlaysLBP2) + + LbpSerializer.StringElement("yourLBP3PlayCount", yourVisitedStats?.PlaysLBP3) + + LbpSerializer.StringElement + ("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it + yourReview?.Serialize("yourReview") + + LbpSerializer.StringElement("reviewsEnabled", true) + + LbpSerializer.StringElement("commentsEnabled", false) + + LbpSerializer.StringElement("reviewCount", this.ReviewCount); + + return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); + } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/SlotType.cs b/ProjectLighthouse/Types/Levels/SlotType.cs index 83689c1a..4554addd 100644 --- a/ProjectLighthouse/Types/Levels/SlotType.cs +++ b/ProjectLighthouse/Types/Levels/SlotType.cs @@ -1,13 +1,12 @@ -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +public enum SlotType { - public enum SlotType - { - Developer = 0, - User = 1, - Moon = 2, - Unknown = 3, - Unknown2 = 4, - Pod = 5, - DLC = 8, - } + Developer = 0, + User = 1, + Moon = 2, + Unknown = 3, + Unknown2 = 4, + Pod = 5, + DLC = 8, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/VisitedLevel.cs b/ProjectLighthouse/Types/Levels/VisitedLevel.cs index 416f4749..167bd258 100644 --- a/ProjectLighthouse/Types/Levels/VisitedLevel.cs +++ b/ProjectLighthouse/Types/Levels/VisitedLevel.cs @@ -1,27 +1,26 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels +namespace LBPUnion.ProjectLighthouse.Types.Levels; + +public class VisitedLevel { - public class VisitedLevel - { - // ReSharper disable once UnusedMember.Global - [Key] - public int VisitedLevelId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int VisitedLevelId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int SlotId { get; set; } + public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } - public int PlaysLBP1 { get; set; } - public int PlaysLBP2 { get; set; } - public int PlaysLBP3 { get; set; } - public int PlaysLBPVita { get; set; } - } + public int PlaysLBP1 { get; set; } + public int PlaysLBP2 { get; set; } + public int PlaysLBP3 { get; set; } + public int PlaysLBPVita { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/LoginData.cs b/ProjectLighthouse/Types/LoginData.cs index 5fcde0f1..ac57ac78 100644 --- a/ProjectLighthouse/Types/LoginData.cs +++ b/ProjectLighthouse/Types/LoginData.cs @@ -4,43 +4,42 @@ using System.IO; using System.Text; using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +/// +/// The data sent from POST /LOGIN. +/// +public class LoginData { - /// - /// The data sent from POST /LOGIN. - /// - public class LoginData - { - public static readonly string UsernamePrefix = Encoding.ASCII.GetString - ( - new byte[] - { - 0x04, 0x00, 0x20, - } - ); - - public string Username { get; set; } = null!; - - /// - /// Converts a X-I-5 Ticket into `LoginData`. - /// https://www.psdevwiki.com/ps3/X-I-5-Ticket - /// - public static LoginData? CreateFromString(string str) + public static readonly string UsernamePrefix = Encoding.ASCII.GetString + ( + new byte[] { - str = str.Replace("\b", ""); // Remove backspace characters - - using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); - using BinaryReader reader = new(ms); - - if (!str.Contains(UsernamePrefix)) return null; - - LoginData loginData = new(); - - reader.BaseStream.Position = str.IndexOf(UsernamePrefix, StringComparison.Ordinal) + UsernamePrefix.Length; - loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); - - return loginData; + 0x04, 0x00, 0x20, } + ); + + public string Username { get; set; } = null!; + + /// + /// Converts a X-I-5 Ticket into `LoginData`. + /// https://www.psdevwiki.com/ps3/X-I-5-Ticket + /// + public static LoginData? CreateFromString(string str) + { + str = str.Replace("\b", ""); // Remove backspace characters + + using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); + using BinaryReader reader = new(ms); + + if (!str.Contains(UsernamePrefix)) return null; + + LoginData loginData = new(); + + reader.BaseStream.Position = str.IndexOf(UsernamePrefix, StringComparison.Ordinal) + UsernamePrefix.Length; + loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); + + return loginData; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/LoginResult.cs b/ProjectLighthouse/Types/LoginResult.cs index bafb2416..274559b7 100644 --- a/ProjectLighthouse/Types/LoginResult.cs +++ b/ProjectLighthouse/Types/LoginResult.cs @@ -2,23 +2,22 @@ using System.Collections.Generic; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +/// +/// Response to POST /login +/// +[XmlRoot("loginResult")] +[XmlType("loginResult")] +public class LoginResult { - /// - /// Response to POST /login - /// - [XmlRoot("loginResult")] - [XmlType("loginResult")] - public class LoginResult - { - [XmlElement("authTicket")] - public string AuthTicket { get; set; } + [XmlElement("authTicket")] + public string AuthTicket { get; set; } - [XmlElement("lbpEnvVer")] - public string LbpEnvVer { get; set; } + [XmlElement("lbpEnvVer")] + public string LbpEnvVer { get; set; } - public string Serialize() - => LbpSerializer.Elements - (new KeyValuePair("authTicket", this.AuthTicket), new KeyValuePair("lbpEnvVer", this.LbpEnvVer)); - } + public string Serialize() + => LbpSerializer.Elements + (new KeyValuePair("authTicket", this.AuthTicket), new KeyValuePair("lbpEnvVer", this.LbpEnvVer)); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/CreateRoom.cs b/ProjectLighthouse/Types/Match/CreateRoom.cs index 3edeb8a0..1b244315 100644 --- a/ProjectLighthouse/Types/Match/CreateRoom.cs +++ b/ProjectLighthouse/Types/Match/CreateRoom.cs @@ -3,41 +3,40 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] +public class CreateRoom : IMatchData { - [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] - public class CreateRoom : IMatchData - { - public int BuildVersion; - public int HostMood; - public int Language; - public List Location; + public int BuildVersion; + public int HostMood; + public int Language; + public List Location; - public List NAT; - public int PassedNoJoinPoint; - public RoomState RoomState; + public List NAT; + public int PassedNoJoinPoint; + public RoomState RoomState; - public string Search; + public string Search; - //[CreateRoom,["Players":["LumaLivy"],"Reservations":["0"],"NAT":[2],"Slots":[[1,3]],"RoomState":0,"HostMood":1,"PassedNoJoinPoint":0,"Location":[0x7f000001],"Language":1,"BuildVersion":289,"Search":""]] - public List Players { get; set; } + //[CreateRoom,["Players":["LumaLivy"],"Reservations":["0"],"NAT":[2],"Slots":[[1,3]],"RoomState":0,"HostMood":1,"PassedNoJoinPoint":0,"Location":[0x7f000001],"Language":1,"BuildVersion":289,"Search":""]] + public List Players { get; set; } - public List Reservations { get; set; } + public List Reservations { get; set; } - // v slot type, 1 = 2974 - // "Slots":[[5,0]] - // ^ slot id - // no idea why this is an array, but we'll work with it i suppose - public List> Slots { get; set; } + // v slot type, 1 = 2974 + // "Slots":[[5,0]] + // ^ slot id + // no idea why this is an array, but we'll work with it i suppose + public List> Slots { get; set; } - [JsonIgnore] - public IEnumerable FirstSlot => this.Slots[0]; + [JsonIgnore] + public IEnumerable FirstSlot => this.Slots[0]; - public RoomSlot RoomSlot - => new() - { - SlotType = (SlotType)this.Slots[0][0], - SlotId = this.Slots[0][1], - }; - } + public RoomSlot RoomSlot + => new() + { + SlotType = (SlotType)this.Slots[0][0], + SlotId = this.Slots[0][1], + }; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/FindBestRoom.cs b/ProjectLighthouse/Types/Match/FindBestRoom.cs index 9b73b657..324747e2 100644 --- a/ProjectLighthouse/Types/Match/FindBestRoom.cs +++ b/ProjectLighthouse/Types/Match/FindBestRoom.cs @@ -2,27 +2,26 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +// Schema is the EXACT SAME as CreateRoom (but cant be a subclass here), so see comments there for details +[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] +public class FindBestRoom : IMatchData { - // Schema is the EXACT SAME as CreateRoom (but cant be a subclass here), so see comments there for details - [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] - public class FindBestRoom : IMatchData - { - public int BuildVersion; - public int HostMood; - public int Language; - public List Location; + public int BuildVersion; + public int HostMood; + public int Language; + public List Location; - public List NAT; - public int PassedNoJoinPoint; - public RoomState RoomState; - public string Search; - public List Players { get; set; } + public List NAT; + public int PassedNoJoinPoint; + public RoomState RoomState; + public string Search; + public List Players { get; set; } - public List Reservations { get; set; } - public List> Slots { get; set; } + public List Reservations { get; set; } + public List> Slots { get; set; } - [JsonIgnore] - public IEnumerable FirstSlot => this.Slots[0]; - } + [JsonIgnore] + public IEnumerable FirstSlot => this.Slots[0]; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs b/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs index 17f9c2ca..42b23769 100644 --- a/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs +++ b/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs @@ -2,21 +2,20 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +public class FindBestRoomResponse { - public class FindBestRoomResponse - { - public int RoomId; + public int RoomId; - public List Players { get; set; } + public List Players { get; set; } - public List> Slots { get; set; } + public List> Slots { get; set; } - [JsonIgnore] - public IEnumerable FirstSlot => this.Slots[0]; + [JsonIgnore] + public IEnumerable FirstSlot => this.Slots[0]; - [JsonPropertyName("Location")] - [SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] - public List Locations { get; set; } - } + [JsonPropertyName("Location")] + [SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] + public List Locations { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/IMatchData.cs b/ProjectLighthouse/Types/Match/IMatchData.cs index b785b433..28ecab3d 100644 --- a/ProjectLighthouse/Types/Match/IMatchData.cs +++ b/ProjectLighthouse/Types/Match/IMatchData.cs @@ -1,5 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match -{ - public interface IMatchData - {} -} \ No newline at end of file +namespace LBPUnion.ProjectLighthouse.Types.Match; + +public interface IMatchData +{} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/NatType.cs b/ProjectLighthouse/Types/Match/NatType.cs index fd6250ac..efd54b16 100644 --- a/ProjectLighthouse/Types/Match/NatType.cs +++ b/ProjectLighthouse/Types/Match/NatType.cs @@ -1,9 +1,8 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +public enum NatType { - public enum NatType - { - Open = 1, - Moderate = 2, - Strict = 3, - } + Open = 1, + Moderate = 2, + Strict = 3, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/Player.cs b/ProjectLighthouse/Types/Match/Player.cs index a0d57ccc..263ed4fe 100644 --- a/ProjectLighthouse/Types/Match/Player.cs +++ b/ProjectLighthouse/Types/Match/Player.cs @@ -2,18 +2,17 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +[Serializable] +public class Player { - [Serializable] - public class Player - { - [JsonIgnore] - public User User { get; set; } + [JsonIgnore] + public User User { get; set; } - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public string PlayerId => this.User.Username; + [SuppressMessage("ReSharper", "UnusedMember.Global")] + public string PlayerId => this.User.Username; - [JsonPropertyName("matching_res")] - public int MatchingRes { get; set; } - } + [JsonPropertyName("matching_res")] + public int MatchingRes { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/Room.cs b/ProjectLighthouse/Types/Match/Room.cs index 2af8302a..aa659041 100644 --- a/ProjectLighthouse/Types/Match/Room.cs +++ b/ProjectLighthouse/Types/Match/Room.cs @@ -2,41 +2,40 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +public class Room { - public class Room + public List Players; + public int RoomId; + + public GameVersion RoomVersion; + public RoomSlot Slot; + public RoomState State; + + public bool IsInPod => this.Slot.SlotType == SlotType.Pod; + public bool IsLookingForPlayers => this.State == RoomState.PlayingLevel || this.State == RoomState.DivingInWaiting; + + public User Host => this.Players[0]; + + #nullable enable + public override bool Equals(object? obj) { - public List Players; - public int RoomId; - public RoomSlot Slot; - public RoomState State; + if (obj is Room room) return room.RoomId == this.RoomId; - public bool IsInPod => this.Slot.SlotType == SlotType.Pod; - public bool IsLookingForPlayers => this.State == RoomState.PlayingLevel || this.State == RoomState.DivingInWaiting; - - public User Host => this.Players[0]; - - public GameVersion RoomVersion; - - #nullable enable - public override bool Equals(object? obj) - { - if (obj is Room room) return room.RoomId == this.RoomId; - - return false; - } - - public static bool operator ==(Room? room1, Room? room2) - { - if (ReferenceEquals(room1, room2)) return true; - if ((object?)room1 == null || (object?)room2 == null) return false; - - return room1.RoomId == room2.RoomId; - } - public static bool operator !=(Room? room1, Room? room2) => !(room1 == room2); - - [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override int GetHashCode() => this.RoomId; - #nullable disable + return false; } + + public static bool operator ==(Room? room1, Room? room2) + { + if (ReferenceEquals(room1, room2)) return true; + if ((object?)room1 == null || (object?)room2 == null) return false; + + return room1.RoomId == room2.RoomId; + } + public static bool operator !=(Room? room1, Room? room2) => !(room1 == room2); + + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override int GetHashCode() => this.RoomId; + #nullable disable } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomSlot.cs b/ProjectLighthouse/Types/Match/RoomSlot.cs index e4609b93..063715d4 100644 --- a/ProjectLighthouse/Types/Match/RoomSlot.cs +++ b/ProjectLighthouse/Types/Match/RoomSlot.cs @@ -1,10 +1,9 @@ using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +public class RoomSlot { - public class RoomSlot - { - public int SlotId; - public SlotType SlotType; - } + public int SlotId; + public SlotType SlotType; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomState.cs b/ProjectLighthouse/Types/Match/RoomState.cs index 0f542223..88145125 100644 --- a/ProjectLighthouse/Types/Match/RoomState.cs +++ b/ProjectLighthouse/Types/Match/RoomState.cs @@ -1,33 +1,32 @@ using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public enum RoomState { - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public enum RoomState - { - /// - /// The room isn't doing anything in particular. - /// - Idle = 0, + /// + /// The room isn't doing anything in particular. + /// + Idle = 0, - /// - /// The room is hosting a room on a slot for others to join. - /// - PlayingLevel = 1, + /// + /// The room is hosting a room on a slot for others to join. + /// + PlayingLevel = 1, - /// - /// ??? - /// - Unknown = 2, + /// + /// ??? + /// + Unknown = 2, - /// - /// The room is looking for other rooms to join. - /// - DivingIn = 3, + /// + /// The room is looking for other rooms to join. + /// + DivingIn = 3, - /// - /// The room is waiting for players to join their room. - /// - DivingInWaiting = 4, - } + /// + /// The room is waiting for players to join their room. + /// + DivingInWaiting = 4, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs b/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs index 50d774cf..2e11508e 100644 --- a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs +++ b/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs @@ -1,10 +1,9 @@ #nullable enable -namespace LBPUnion.ProjectLighthouse.Types.Match -{ - public class UpdateMyPlayerData : IMatchData - { - public string Player { get; set; } = null!; +namespace LBPUnion.ProjectLighthouse.Types.Match; - public RoomState? RoomState { get; set; } - } +public class UpdateMyPlayerData : IMatchData +{ + public string Player { get; set; } = null!; + + public RoomState? RoomState { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs b/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs index 3d54cd21..58288b5c 100644 --- a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs +++ b/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Match +namespace LBPUnion.ProjectLighthouse.Types.Match; + +[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] +public class UpdatePlayersInRoom : IMatchData { - [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] - public class UpdatePlayersInRoom : IMatchData - { - public List Players { get; set; } - public List Reservations { get; set; } - } + public List Players { get; set; } + public List Reservations { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/News/NewsEntry.cs b/ProjectLighthouse/Types/News/NewsEntry.cs index 2ef65ecb..4e291677 100644 --- a/ProjectLighthouse/Types/News/NewsEntry.cs +++ b/ProjectLighthouse/Types/News/NewsEntry.cs @@ -1,27 +1,26 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.News -{ - /// - /// Used on the info moon on LBP1. Broken for unknown reasons - /// - public class NewsEntry - { - public int Id { get; set; } - public string Title { get; set; } - public string Summary { get; set; } - public string Text { get; set; } - public NewsImage Image { get; set; } - public string Category { get; set; } - public long Date { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.News; - public string Serialize() - => LbpSerializer.StringElement("id", this.Id) + - LbpSerializer.StringElement("title", this.Title) + - LbpSerializer.StringElement("summary", this.Summary) + - LbpSerializer.StringElement("text", this.Text) + - LbpSerializer.StringElement("date", this.Date) + - this.Image.Serialize() + - LbpSerializer.StringElement("category", this.Category); - } +/// +/// Used on the info moon on LBP1. Broken for unknown reasons +/// +public class NewsEntry +{ + public int Id { get; set; } + public string Title { get; set; } + public string Summary { get; set; } + public string Text { get; set; } + public NewsImage Image { get; set; } + public string Category { get; set; } + public long Date { get; set; } + + public string Serialize() + => LbpSerializer.StringElement("id", this.Id) + + LbpSerializer.StringElement("title", this.Title) + + LbpSerializer.StringElement("summary", this.Summary) + + LbpSerializer.StringElement("text", this.Text) + + LbpSerializer.StringElement("date", this.Date) + + this.Image.Serialize() + + LbpSerializer.StringElement("category", this.Category); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/News/NewsImage.cs b/ProjectLighthouse/Types/News/NewsImage.cs index f174afff..07e69c12 100644 --- a/ProjectLighthouse/Types/News/NewsImage.cs +++ b/ProjectLighthouse/Types/News/NewsImage.cs @@ -1,13 +1,12 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.News -{ - public class NewsImage - { - public string Hash { get; set; } - public string Alignment { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.News; - public string Serialize() - => LbpSerializer.StringElement("image", LbpSerializer.StringElement("hash", this.Hash) + LbpSerializer.StringElement("alignment", this.Alignment)); - } +public class NewsImage +{ + public string Hash { get; set; } + public string Alignment { get; set; } + + public string Serialize() + => LbpSerializer.StringElement("image", LbpSerializer.StringElement("hash", this.Hash) + LbpSerializer.StringElement("alignment", this.Alignment)); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/PageNavigationItem.cs b/ProjectLighthouse/Types/PageNavigationItem.cs index b832fa70..11068c0a 100644 --- a/ProjectLighthouse/Types/PageNavigationItem.cs +++ b/ProjectLighthouse/Types/PageNavigationItem.cs @@ -1,17 +1,16 @@ #nullable enable -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class PageNavigationItem { - public class PageNavigationItem + public PageNavigationItem(string name, string url, string? icon = null) { - public PageNavigationItem(string name, string url, string? icon = null) - { - this.Name = name; - this.Url = url; - this.Icon = icon; - } - public string Name { get; set; } - public string Url { get; set; } - public string? Icon { get; set; } + this.Name = name; + this.Url = url; + this.Icon = icon; } + public string Name { get; set; } + public string Url { get; set; } + public string? Icon { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Photo.cs b/ProjectLighthouse/Types/Photo.cs index d5c9b4f3..5d84ca5d 100644 --- a/ProjectLighthouse/Types/Photo.cs +++ b/ProjectLighthouse/Types/Photo.cs @@ -8,94 +8,93 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +[XmlRoot("photo")] +[XmlType("photo")] +public class Photo { - [XmlRoot("photo")] - [XmlType("photo")] - public class Photo - { - [NotMapped] - private List? _subjects; + [NotMapped] + private List? _subjects; - [NotMapped] - [XmlArray("subjects")] - [XmlArrayItem("subject")] - public List? SubjectsXmlDontUseLiterallyEver; + [NotMapped] + [XmlArray("subjects")] + [XmlArrayItem("subject")] + public List? SubjectsXmlDontUseLiterallyEver; - [Key] - public int PhotoId { get; set; } + [Key] + public int PhotoId { get; set; } - // Uses seconds instead of milliseconds for some reason - [XmlAttribute("timestamp")] - public long Timestamp { get; set; } + // Uses seconds instead of milliseconds for some reason + [XmlAttribute("timestamp")] + public long Timestamp { get; set; } - [XmlElement("small")] - public string SmallHash { get; set; } = ""; + [XmlElement("small")] + public string SmallHash { get; set; } = ""; - [XmlElement("medium")] - public string MediumHash { get; set; } = ""; + [XmlElement("medium")] + public string MediumHash { get; set; } = ""; - [XmlElement("large")] - public string LargeHash { get; set; } = ""; + [XmlElement("large")] + public string LargeHash { get; set; } = ""; - [XmlElement("plan")] - public string PlanHash { get; set; } = ""; + [XmlElement("plan")] + public string PlanHash { get; set; } = ""; - [NotMapped] - public List Subjects { - get { - if (this.SubjectsXmlDontUseLiterallyEver != null) return this.SubjectsXmlDontUseLiterallyEver; - if (this._subjects != null) return this._subjects; + [NotMapped] + public List Subjects { + get { + if (this.SubjectsXmlDontUseLiterallyEver != null) return this.SubjectsXmlDontUseLiterallyEver; + if (this._subjects != null) return this._subjects; - List response = new(); - using Database database = new(); + List response = new(); + using Database database = new(); - foreach (string idStr in this.PhotoSubjectIds.Where(idStr => !string.IsNullOrEmpty(idStr))) - { - if (!int.TryParse(idStr, out int id)) throw new InvalidCastException(idStr + " is not a valid number."); + foreach (string idStr in this.PhotoSubjectIds.Where(idStr => !string.IsNullOrEmpty(idStr))) + { + if (!int.TryParse(idStr, out int id)) throw new InvalidCastException(idStr + " is not a valid number."); - PhotoSubject? photoSubject = database.PhotoSubjects.Include(p => p.User).FirstOrDefault(p => p.PhotoSubjectId == id); - if (photoSubject == null) continue; + PhotoSubject? photoSubject = database.PhotoSubjects.Include(p => p.User).FirstOrDefault(p => p.PhotoSubjectId == id); + if (photoSubject == null) continue; - response.Add(photoSubject); - } - - return response; + response.Add(photoSubject); } - set => this._subjects = value; + + return response; } + set => this._subjects = value; + } - [NotMapped] - [XmlIgnore] - public string[] PhotoSubjectIds { - get => this.PhotoSubjectCollection.Split(","); - set => this.PhotoSubjectCollection = string.Join(',', value); - } + [NotMapped] + [XmlIgnore] + public string[] PhotoSubjectIds { + get => this.PhotoSubjectCollection.Split(","); + set => this.PhotoSubjectCollection = string.Join(',', value); + } - public string PhotoSubjectCollection { get; set; } = ""; + public string PhotoSubjectCollection { get; set; } = ""; - public int CreatorId { get; set; } + public int CreatorId { get; set; } - [ForeignKey(nameof(CreatorId))] - public User? Creator { get; set; } + [ForeignKey(nameof(CreatorId))] + public User? Creator { get; set; } - public string Serialize(int slotId) - { - string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", "user"); + public string Serialize(int slotId) + { + string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", "user"); - string subjectsAggregate = this.Subjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize()); + string subjectsAggregate = this.Subjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize()); - string photo = LbpSerializer.StringElement("id", this.PhotoId) + - LbpSerializer.StringElement("small", this.SmallHash) + - LbpSerializer.StringElement("medium", this.MediumHash) + - LbpSerializer.StringElement("large", this.LargeHash) + - LbpSerializer.StringElement("plan", this.PlanHash) + - LbpSerializer.StringElement("author", this.Creator?.Username) + - LbpSerializer.StringElement("subjects", subjectsAggregate) + - slot; + string photo = LbpSerializer.StringElement("id", this.PhotoId) + + LbpSerializer.StringElement("small", this.SmallHash) + + LbpSerializer.StringElement("medium", this.MediumHash) + + LbpSerializer.StringElement("large", this.LargeHash) + + LbpSerializer.StringElement("plan", this.PlanHash) + + LbpSerializer.StringElement("author", this.Creator?.Username) + + LbpSerializer.StringElement("subjects", subjectsAggregate) + + slot; - return LbpSerializer.TaggedStringElement("photo", photo, "timestamp", this.Timestamp * 1000); - } + return LbpSerializer.TaggedStringElement("photo", photo, "timestamp", this.Timestamp * 1000); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/PhotoSubject.cs b/ProjectLighthouse/Types/PhotoSubject.cs index 05144f33..bf8d71e6 100644 --- a/ProjectLighthouse/Types/PhotoSubject.cs +++ b/ProjectLighthouse/Types/PhotoSubject.cs @@ -5,39 +5,38 @@ using System.Text.Json.Serialization; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types -{ +namespace LBPUnion.ProjectLighthouse.Types; + // [XmlRoot("subject")] - [XmlType("subject")] - [Serializable] - public class PhotoSubject +[XmlType("subject")] +[Serializable] +public class PhotoSubject +{ + [Key] + [XmlIgnore] + public int PhotoSubjectId { get; set; } + + [XmlIgnore] + public int UserId { get; set; } + + [XmlIgnore] + [ForeignKey(nameof(UserId))] + [JsonIgnore] + public User User { get; set; } + + [NotMapped] + [XmlElement("npHandle")] + public string Username { get; set; } + + [XmlElement("bounds")] + public string Bounds { get; set; } + + public string Serialize() { - [Key] - [XmlIgnore] - public int PhotoSubjectId { get; set; } + string response = LbpSerializer.StringElement("npHandle", this.User.Username) + + LbpSerializer.StringElement("displayName", this.User.Username) + + LbpSerializer.StringElement("bounds", this.Bounds); - [XmlIgnore] - public int UserId { get; set; } - - [XmlIgnore] - [ForeignKey(nameof(UserId))] - [JsonIgnore] - public User User { get; set; } - - [NotMapped] - [XmlElement("npHandle")] - public string Username { get; set; } - - [XmlElement("bounds")] - public string Bounds { get; set; } - - public string Serialize() - { - string response = LbpSerializer.StringElement("npHandle", this.User.Username) + - LbpSerializer.StringElement("displayName", this.User.Username) + - LbpSerializer.StringElement("bounds", this.Bounds); - - return LbpSerializer.StringElement("subject", response); - } + return LbpSerializer.StringElement("subject", response); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Platform.cs b/ProjectLighthouse/Types/Platform.cs index 3711848a..74fb1608 100644 --- a/ProjectLighthouse/Types/Platform.cs +++ b/ProjectLighthouse/Types/Platform.cs @@ -1,9 +1,8 @@ -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public enum Platform { - public enum Platform - { - PS3 = 0, - RPCS3 = 1, - Vita = 2, - } + PS3 = 0, + RPCS3 = 1, + Vita = 2, } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs b/ProjectLighthouse/Types/Profiles/ClientsConnected.cs index be6c77fe..d54590d0 100644 --- a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs +++ b/ProjectLighthouse/Types/Profiles/ClientsConnected.cs @@ -1,26 +1,25 @@ using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Profiles -{ - [Keyless] - public class ClientsConnected - { - public bool Lbp1 { get; set; } - public bool Lbp2 { get; set; } - public bool LbpMe { get; set; } - public bool Lbp3Ps3 { get; set; } - public bool Lbp3Ps4 { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.Profiles; - public string Serialize() - => LbpSerializer.StringElement - ( - "clientsConnected", - LbpSerializer.StringElement("lbp1", this.Lbp1) + - LbpSerializer.StringElement("lbp2", this.Lbp2) + - LbpSerializer.StringElement("lbpme", this.LbpMe) + - LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) + - LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4) - ); - } +[Keyless] +public class ClientsConnected +{ + public bool Lbp1 { get; set; } + public bool Lbp2 { get; set; } + public bool LbpMe { get; set; } + public bool Lbp3Ps3 { get; set; } + public bool Lbp3Ps4 { get; set; } + + public string Serialize() + => LbpSerializer.StringElement + ( + "clientsConnected", + LbpSerializer.StringElement("lbp1", this.Lbp1) + + LbpSerializer.StringElement("lbp2", this.Lbp2) + + LbpSerializer.StringElement("lbpme", this.LbpMe) + + LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) + + LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4) + ); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/Comment.cs b/ProjectLighthouse/Types/Profiles/Comment.cs index f2260fe2..ff3b0c91 100644 --- a/ProjectLighthouse/Types/Profiles/Comment.cs +++ b/ProjectLighthouse/Types/Profiles/Comment.cs @@ -3,46 +3,43 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles +namespace LBPUnion.ProjectLighthouse.Types.Profiles; + +[XmlRoot("comment")] +[XmlType("comment")] +public class Comment { - [XmlRoot("comment")] - [XmlType("comment")] - public class Comment - { - [Key] - [XmlAttribute("id")] - public int CommentId { get; set; } + [Key] + [XmlAttribute("id")] + public int CommentId { get; set; } - public int PosterUserId { get; set; } + public int PosterUserId { get; set; } - public int TargetUserId { get; set; } + public int TargetUserId { get; set; } - [ForeignKey(nameof(PosterUserId))] - public User Poster { get; set; } + [ForeignKey(nameof(PosterUserId))] + public User Poster { get; set; } - [ForeignKey(nameof(TargetUserId))] - public User Target { get; set; } + [ForeignKey(nameof(TargetUserId))] + public User Target { get; set; } - public long Timestamp { get; set; } + public long Timestamp { get; set; } - [XmlElement("message")] - public string Message { get; set; } + [XmlElement("message")] + public string Message { get; set; } - public int ThumbsUp { get; set; } - public int ThumbsDown { get; set; } + public int ThumbsUp { get; set; } + public int ThumbsDown { get; set; } - private string serialize() - => LbpSerializer.StringElement("id", this.CommentId) + - LbpSerializer.StringElement("npHandle", this.Poster.Username) + - LbpSerializer.StringElement("timestamp", this.Timestamp) + - LbpSerializer.StringElement("message", this.Message) + - LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + - LbpSerializer.StringElement("thumbsdown", this.ThumbsDown); + private string serialize() + => LbpSerializer.StringElement("id", this.CommentId) + + LbpSerializer.StringElement("npHandle", this.Poster.Username) + + LbpSerializer.StringElement("timestamp", this.Timestamp) + + LbpSerializer.StringElement("message", this.Message) + + LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + + LbpSerializer.StringElement("thumbsdown", this.ThumbsDown); - public string Serialize - (int yourThumb) - => LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb)); + public string Serialize(int yourThumb) => LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb)); - public string Serialize() => LbpSerializer.StringElement("comment", this.serialize()); - } + public string Serialize() => LbpSerializer.StringElement("comment", this.serialize()); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/LastContact.cs b/ProjectLighthouse/Types/Profiles/LastContact.cs index 295250b8..7fb0593a 100644 --- a/ProjectLighthouse/Types/Profiles/LastContact.cs +++ b/ProjectLighthouse/Types/Profiles/LastContact.cs @@ -1,14 +1,13 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types.Profiles +namespace LBPUnion.ProjectLighthouse.Types.Profiles; + +public class LastContact { - public class LastContact - { - [Key] - public int UserId { get; set; } + [Key] + public int UserId { get; set; } - public long Timestamp { get; set; } + public long Timestamp { get; set; } - public GameVersion GameVersion { get; set; } = GameVersion.Unknown; - } + public GameVersion GameVersion { get; set; } = GameVersion.Unknown; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/Location.cs b/ProjectLighthouse/Types/Profiles/Location.cs index 139565e6..6d07d62f 100644 --- a/ProjectLighthouse/Types/Profiles/Location.cs +++ b/ProjectLighthouse/Types/Profiles/Location.cs @@ -1,24 +1,23 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles +namespace LBPUnion.ProjectLighthouse.Types.Profiles; + +/// +/// The location of a slot on a planet. +/// +[XmlRoot("location")] +[XmlType("location")] +public class Location { - /// - /// The location of a slot on a planet. - /// - [XmlRoot("location")] - [XmlType("location")] - public class Location - { - [XmlIgnore] - public int Id { get; set; } + [XmlIgnore] + public int Id { get; set; } - [XmlElement("x")] - public int X { get; set; } + [XmlElement("x")] + public int X { get; set; } - [XmlElement("y")] - public int Y { get; set; } + [XmlElement("y")] + public int Y { get; set; } - public string Serialize() => LbpSerializer.StringElement("x", this.X) + LbpSerializer.StringElement("y", this.Y); - } + public string Serialize() => LbpSerializer.StringElement("x", this.X) + LbpSerializer.StringElement("y", this.Y); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/NPData.cs b/ProjectLighthouse/Types/Profiles/NPData.cs index a117d6bf..ed52fe1e 100644 --- a/ProjectLighthouse/Types/Profiles/NPData.cs +++ b/ProjectLighthouse/Types/Profiles/NPData.cs @@ -1,18 +1,17 @@ using System.Collections.Generic; using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles -{ - [XmlRoot("npdata")] - [XmlType("npdata")] - public class NPData - { - [XmlArray("friends")] - [XmlArrayItem("npHandle")] - public List Friends { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.Profiles; - [XmlArray("blocked")] - [XmlArrayItem("npHandle")] - public List BlockedUsers { get; set; } - } +[XmlRoot("npdata")] +[XmlType("npdata")] +public class NPData +{ + [XmlArray("friends")] + [XmlArrayItem("npHandle")] + public List Friends { get; set; } + + [XmlArray("blocked")] + [XmlArrayItem("npHandle")] + public List BlockedUsers { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/Pins.cs b/ProjectLighthouse/Types/Profiles/Pins.cs index 567e739e..f1ed3ef6 100644 --- a/ProjectLighthouse/Types/Profiles/Pins.cs +++ b/ProjectLighthouse/Types/Profiles/Pins.cs @@ -1,16 +1,15 @@ using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles +namespace LBPUnion.ProjectLighthouse.Types.Profiles; + +public class Pins { - public class Pins - { - [JsonPropertyName("progress")] - public long[] Progress { get; set; } + [JsonPropertyName("progress")] + public long[] Progress { get; set; } - [JsonPropertyName("awards")] - public long[] Awards { get; set; } + [JsonPropertyName("awards")] + public long[] Awards { get; set; } - [JsonPropertyName("profile_pins")] - public long[] ProfilePins { get; set; } - } + [JsonPropertyName("profile_pins")] + public long[] ProfilePins { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/ResourceList.cs b/ProjectLighthouse/Types/ResourceList.cs index 5af4425a..83f0dc9c 100644 --- a/ProjectLighthouse/Types/ResourceList.cs +++ b/ProjectLighthouse/Types/ResourceList.cs @@ -1,12 +1,11 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +[XmlRoot("resources")] +[XmlType("resources")] +public class ResourceList { - [XmlRoot("resources")] - [XmlType("resources")] - public class ResourceList - { - [XmlElement("resource")] - public string[] Resources; - } + [XmlElement("resource")] + public string[] Resources; } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Reviews/RatedReview.cs b/ProjectLighthouse/Types/Reviews/RatedReview.cs index 1ba9720f..e77530fb 100644 --- a/ProjectLighthouse/Types/Reviews/RatedReview.cs +++ b/ProjectLighthouse/Types/Reviews/RatedReview.cs @@ -1,24 +1,23 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Reviews +namespace LBPUnion.ProjectLighthouse.Types.Reviews; + +public class RatedReview { - public class RatedReview - { - // ReSharper disable once UnusedMember.Global - [Key] - public int RatedReviewId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int RatedReviewId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public int ReviewId { get; set; } + public int ReviewId { get; set; } - [ForeignKey(nameof(ReviewId))] - public Review Review { get; set; } + [ForeignKey(nameof(ReviewId))] + public Review Review { get; set; } - public int Thumb { get; set; } - } + public int Thumb { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Reviews/Review.cs b/ProjectLighthouse/Types/Reviews/Review.cs index 362d2e17..385233d7 100644 --- a/ProjectLighthouse/Types/Reviews/Review.cs +++ b/ProjectLighthouse/Types/Reviews/Review.cs @@ -7,88 +7,86 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Reviews +namespace LBPUnion.ProjectLighthouse.Types.Reviews; + +[XmlRoot("review")] +[XmlType("review")] +public class Review { - [XmlRoot("review")] - [XmlType("review")] - public class Review - { - // ReSharper disable once UnusedMember.Global - [Key] - public int ReviewId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int ReviewId { get; set; } - [XmlIgnore] - public int ReviewerId { get; set; } + [XmlIgnore] + public int ReviewerId { get; set; } - [ForeignKey(nameof(ReviewerId))] - public User Reviewer { get; set; } + [ForeignKey(nameof(ReviewerId))] + public User Reviewer { get; set; } - [XmlElement("slot_id")] - public int SlotId { get; set; } + [XmlElement("slot_id")] + public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } - [XmlElement("timestamp")] - public long Timestamp { get; set; } + [XmlElement("timestamp")] + public long Timestamp { get; set; } - [XmlElement("labels")] - public string LabelCollection { get; set; } + [XmlElement("labels")] + public string LabelCollection { get; set; } - [NotMapped] - [XmlIgnore] - public string[] Labels { - get => this.LabelCollection.Split(","); - set => this.LabelCollection = string.Join(',', value); - } - - [XmlElement("deleted")] - public bool Deleted { get; set; } - - [XmlElement("deleted_by")] - public DeletedBy DeletedBy { get; set; } - - [XmlElement("text")] - public string Text { get; set; } - - [XmlElement("thumb")] - public int Thumb { get; set; } - - [XmlElement("thumbsup")] - public int ThumbsUp { get; set; } - - [XmlElement("thumbsdown")] - public int ThumbsDown { get; set; } - - public string Serialize - (RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) - => this.Serialize("review", yourLevelRating, yourRatingStats); - - public string Serialize(string elementOverride, RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) - { - - XmlWriterSettings settings = new(); - settings.OmitXmlDeclaration = true; - - XmlSerializer serializer = new(typeof(DeletedBy)); - StringWriter stringWriter = new(); - using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings)) serializer.Serialize(xmlWriter, this.DeletedBy); - string deletedBy = stringWriter.ToString(); - - string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot.Type) + - LbpSerializer.StringElement("reviewer", this.Reviewer.Username) + - LbpSerializer.StringElement("thumb", this.Thumb) + - LbpSerializer.StringElement("timestamp", this.Timestamp) + - LbpSerializer.StringElement("labels", this.LabelCollection) + - LbpSerializer.StringElement("deleted", this.Deleted) + - deletedBy + - LbpSerializer.StringElement("text", this.Text) + - LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + - LbpSerializer.StringElement("thumbsdown", this.ThumbsDown) + - LbpSerializer.StringElement("yourthumb", yourRatingStats?.Thumb == null ? 0 : yourRatingStats?.Thumb); - - return LbpSerializer.TaggedStringElement(elementOverride, reviewData, "id", this.SlotId + "." + this.Reviewer.Username); - } + [NotMapped] + [XmlIgnore] + public string[] Labels { + get => this.LabelCollection.Split(","); + set => this.LabelCollection = string.Join(',', value); } + [XmlElement("deleted")] + public bool Deleted { get; set; } + + [XmlElement("deleted_by")] + public DeletedBy DeletedBy { get; set; } + + [XmlElement("text")] + public string Text { get; set; } + + [XmlElement("thumb")] + public int Thumb { get; set; } + + [XmlElement("thumbsup")] + public int ThumbsUp { get; set; } + + [XmlElement("thumbsdown")] + public int ThumbsDown { get; set; } + + public string Serialize + (RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) + => this.Serialize("review", yourLevelRating, yourRatingStats); + + public string Serialize(string elementOverride, RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) + { + + XmlWriterSettings settings = new(); + settings.OmitXmlDeclaration = true; + + XmlSerializer serializer = new(typeof(DeletedBy)); + StringWriter stringWriter = new(); + using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings)) serializer.Serialize(xmlWriter, this.DeletedBy); + string deletedBy = stringWriter.ToString(); + + string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot.Type) + + LbpSerializer.StringElement("reviewer", this.Reviewer.Username) + + LbpSerializer.StringElement("thumb", this.Thumb) + + LbpSerializer.StringElement("timestamp", this.Timestamp) + + LbpSerializer.StringElement("labels", this.LabelCollection) + + LbpSerializer.StringElement("deleted", this.Deleted) + + deletedBy + + LbpSerializer.StringElement("text", this.Text) + + LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + + LbpSerializer.StringElement("thumbsdown", this.ThumbsDown) + + LbpSerializer.StringElement("yourthumb", yourRatingStats?.Thumb == null ? 0 : yourRatingStats?.Thumb); + + return LbpSerializer.TaggedStringElement(elementOverride, reviewData, "id", this.SlotId + "." + this.Reviewer.Username); + } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Score.cs b/ProjectLighthouse/Types/Score.cs index 6b507e6f..85e569cf 100644 --- a/ProjectLighthouse/Types/Score.cs +++ b/ProjectLighthouse/Types/Score.cs @@ -4,59 +4,58 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +[XmlRoot("playRecord")] +[XmlType("playRecord")] +public class Score { - [XmlRoot("playRecord")] - [XmlType("playRecord")] - public class Score + [XmlIgnore] + [Key] + public int ScoreId { get; set; } + + [XmlIgnore] + public int SlotId { get; set; } + + [XmlIgnore] + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } + + [XmlElement("type")] + public int Type { get; set; } + + [XmlIgnore] + public string PlayerIdCollection { get; set; } + + [NotMapped] + [XmlElement("playerIds")] + public string[] PlayerIds { + get => this.PlayerIdCollection.Split(","); + set => this.PlayerIdCollection = string.Join(',', value); + } + + [NotMapped] + [XmlElement("mainPlayer")] + public string MainPlayer { + get => this.PlayerIds[0]; + set => this.PlayerIds[0] = value; + } + + [NotMapped] + [XmlElement("rank")] + public int Rank { get; set; } + + [XmlElement("score")] + public int Points { get; set; } + + public string Serialize() { - [XmlIgnore] - [Key] - public int ScoreId { get; set; } + string score = LbpSerializer.StringElement("type", this.Type) + + LbpSerializer.StringElement("playerIds", this.PlayerIdCollection) + + LbpSerializer.StringElement("mainPlayer", this.MainPlayer) + + LbpSerializer.StringElement("rank", this.Rank) + + LbpSerializer.StringElement("score", this.Points); - [XmlIgnore] - public int SlotId { get; set; } - - [XmlIgnore] - [ForeignKey(nameof(SlotId))] - public Slot Slot { get; set; } - - [XmlElement("type")] - public int Type { get; set; } - - [XmlIgnore] - public string PlayerIdCollection { get; set; } - - [NotMapped] - [XmlElement("playerIds")] - public string[] PlayerIds { - get => this.PlayerIdCollection.Split(","); - set => this.PlayerIdCollection = string.Join(',', value); - } - - [NotMapped] - [XmlElement("mainPlayer")] - public string MainPlayer { - get => this.PlayerIds[0]; - set => this.PlayerIds[0] = value; - } - - [NotMapped] - [XmlElement("rank")] - public int Rank { get; set; } - - [XmlElement("score")] - public int Points { get; set; } - - public string Serialize() - { - string score = LbpSerializer.StringElement("type", this.Type) + - LbpSerializer.StringElement("playerIds", this.PlayerIdCollection) + - LbpSerializer.StringElement("mainPlayer", this.MainPlayer) + - LbpSerializer.StringElement("rank", this.Rank) + - LbpSerializer.StringElement("score", this.Points); - - return LbpSerializer.StringElement("playRecord", score); - } + return LbpSerializer.StringElement("playRecord", score); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/PrivacySettings.cs b/ProjectLighthouse/Types/Settings/PrivacySettings.cs index ac636c5d..b8c2e637 100644 --- a/ProjectLighthouse/Types/Settings/PrivacySettings.cs +++ b/ProjectLighthouse/Types/Settings/PrivacySettings.cs @@ -1,17 +1,16 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Settings -{ - public class PrivacySettings - { - public string LevelVisibility { get; set; } - public string ProfileVisibility { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.Settings; - public string Serialize() - => LbpSerializer.StringElement - ( - "privacySettings", - LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility) - ); - } +public class PrivacySettings +{ + public string LevelVisibility { get; set; } + public string ProfileVisibility { get; set; } + + public string Serialize() + => LbpSerializer.StringElement + ( + "privacySettings", + LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility) + ); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index c3b7af60..ba6cd179 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -6,120 +6,119 @@ using JetBrains.Annotations; using Kettu; using LBPUnion.ProjectLighthouse.Logging; -namespace LBPUnion.ProjectLighthouse.Types.Settings +namespace LBPUnion.ProjectLighthouse.Types.Settings; + +[Serializable] +public class ServerSettings { - [Serializable] - public class ServerSettings + + public const int CurrentConfigVersion = 16; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! + static ServerSettings() { + if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own - public const int CurrentConfigVersion = 16; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! - static ServerSettings() + if (File.Exists(ConfigFileName)) { - if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own + string configFile = File.ReadAllText(ConfigFileName); - if (File.Exists(ConfigFileName)) - { - string configFile = File.ReadAllText(ConfigFileName); + Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); - Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); + if (Instance.ConfigVersion >= CurrentConfigVersion) return; - if (Instance.ConfigVersion >= CurrentConfigVersion) return; + Logger.Log($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LoggerLevelConfig.Instance); + Instance.ConfigVersion = CurrentConfigVersion; + configFile = JsonSerializer.Serialize + ( + Instance, + typeof(ServerSettings), + new JsonSerializerOptions + { + WriteIndented = true, + } + ); - Logger.Log($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LoggerLevelConfig.Instance); - Instance.ConfigVersion = CurrentConfigVersion; - configFile = JsonSerializer.Serialize - ( - Instance, - typeof(ServerSettings), - new JsonSerializerOptions - { - WriteIndented = true, - } - ); - - File.WriteAllText(ConfigFileName, configFile); - } - else - { - string configFile = JsonSerializer.Serialize - ( - new ServerSettings(), - typeof(ServerSettings), - new JsonSerializerOptions - { - WriteIndented = true, - } - ); - - File.WriteAllText(ConfigFileName, configFile); - - Logger.Log - ( - "The configuration file was not found. " + - "A blank configuration file has been created for you at " + - $"{Path.Combine(Environment.CurrentDirectory, ConfigFileName)}", - LoggerLevelConfig.Instance - ); - - Environment.Exit(1); - } + File.WriteAllText(ConfigFileName, configFile); } + else + { + string configFile = JsonSerializer.Serialize + ( + new ServerSettings(), + typeof(ServerSettings), + new JsonSerializerOptions + { + WriteIndented = true, + } + ); - public bool InfluxEnabled { get; set; } - public bool InfluxLoggingEnabled { get; set; } - public string InfluxOrg { get; set; } = "lighthouse"; - public string InfluxBucket { get; set; } = "lighthouse"; - public string InfluxToken { get; set; } = ""; - public string InfluxUrl { get; set; } = "http://localhost:8086"; + File.WriteAllText(ConfigFileName, configFile); - public string EulaText { get; set; } = ""; - - #if !DEBUG - public string AnnounceText { get; set; } = "You are now logged in as %user."; - #else - public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; - #endif - - public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; - - public string ExternalUrl { get; set; } = "http://localhost:10060"; - public string ServerDigestKey { get; set; } - public bool UseExternalAuth { get; set; } - - public bool CheckForUnsafeFiles { get; set; } = true; - - public bool RegistrationEnabled { get; set; } = true; - - /// - /// The maximum amount of slots allowed on users' earth - /// - public int EntitledSlots { get; set; } = 50; - - public int ListsQuota { get; set; } = 50; - - public int PhotosQuota { get; set; } = 500; - - public bool GoogleAnalyticsEnabled { get; set; } - - public string GoogleAnalyticsId { get; set; } = ""; - - public bool BlockDeniedUsers { get; set; } = true; - - public bool BooingEnabled { get; set; } = true; - - public bool VitaCreateMode { get; set; } = false; - - #region Meta - - [NotNull] - public static ServerSettings Instance; - - [JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")] - public int ConfigVersion { get; set; } = CurrentConfigVersion; - - public const string ConfigFileName = "lighthouse.config.json"; - - #endregion Meta + Logger.Log + ( + "The configuration file was not found. " + + "A blank configuration file has been created for you at " + + $"{Path.Combine(Environment.CurrentDirectory, ConfigFileName)}", + LoggerLevelConfig.Instance + ); + Environment.Exit(1); + } } -} + + public bool InfluxEnabled { get; set; } + public bool InfluxLoggingEnabled { get; set; } + public string InfluxOrg { get; set; } = "lighthouse"; + public string InfluxBucket { get; set; } = "lighthouse"; + public string InfluxToken { get; set; } = ""; + public string InfluxUrl { get; set; } = "http://localhost:8086"; + + public string EulaText { get; set; } = ""; + + #if !DEBUG + public string AnnounceText { get; set; } = "You are now logged in as %user."; + #else + public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; + #endif + + public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + + public string ExternalUrl { get; set; } = "http://localhost:10060"; + public string ServerDigestKey { get; set; } + public bool UseExternalAuth { get; set; } + + public bool CheckForUnsafeFiles { get; set; } = true; + + public bool RegistrationEnabled { get; set; } = true; + + /// + /// The maximum amount of slots allowed on users' earth + /// + public int EntitledSlots { get; set; } = 50; + + public int ListsQuota { get; set; } = 50; + + public int PhotosQuota { get; set; } = 500; + + public bool GoogleAnalyticsEnabled { get; set; } + + public string GoogleAnalyticsId { get; set; } = ""; + + public bool BlockDeniedUsers { get; set; } = true; + + public bool BooingEnabled { get; set; } = true; + + public bool VitaCreateMode { get; set; } + + #region Meta + + [NotNull] + public static ServerSettings Instance; + + [JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")] + public int ConfigVersion { get; set; } = CurrentConfigVersion; + + public const string ConfigFileName = "lighthouse.config.json"; + + #endregion Meta + +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerStatics.cs b/ProjectLighthouse/Types/Settings/ServerStatics.cs index 12af421e..62c2a7b5 100644 --- a/ProjectLighthouse/Types/Settings/ServerStatics.cs +++ b/ProjectLighthouse/Types/Settings/ServerStatics.cs @@ -4,34 +4,33 @@ using System.Linq; using Kettu; using LBPUnion.ProjectLighthouse.Logging; -namespace LBPUnion.ProjectLighthouse.Types.Settings +namespace LBPUnion.ProjectLighthouse.Types.Settings; + +public static class ServerStatics { - public static class ServerStatics - { - public const string ServerName = "ProjectLighthouse"; + public const string ServerName = "ProjectLighthouse"; - public const int PageSize = 20; + public const int PageSize = 20; - public static bool DbConnected { - get { - try - { - return new Database().Database.CanConnect(); - } - catch(Exception e) - { - Logger.Log(e.ToString(), LoggerLevelDatabase.Instance); - return false; - } + public static bool DbConnected { + get { + try + { + return new Database().Database.CanConnect(); + } + catch(Exception e) + { + Logger.Log(e.ToString(), LoggerLevelDatabase.Instance); + return false; } } - - public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit")); - - #if DEBUG - public static readonly bool IsDebug = true; - #else - public static readonly bool IsDebug = false; - #endif } + + public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit")); + + #if DEBUG + public static readonly bool IsDebug = true; + #else + public static readonly bool IsDebug = false; + #endif } \ No newline at end of file diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index b941d656..3e2384e4 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -6,247 +6,245 @@ using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class User { - public class User - { - public readonly ClientsConnected ClientsConnected = new(); - public int UserId { get; set; } - public string Username { get; set; } - public string Password { get; set; } - public string IconHash { get; set; } - public int Game { get; set; } + public readonly ClientsConnected ClientsConnected = new(); + public int UserId { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string IconHash { get; set; } + public int Game { get; set; } - [NotMapped] - public int Lists => 0; + [NotMapped] + public int Lists => 0; - /// - /// A user-customizable biography shown on the profile card - /// - public string Biography { get; set; } + /// + /// A user-customizable biography shown on the profile card + /// + public string Biography { get; set; } - [NotMapped] - public int Reviews { - get { - using Database database = new(); - return database.Reviews.Count(r => r.ReviewerId == this.UserId); - } - } - - [NotMapped] - public int Comments { - get { - using Database database = new(); - return database.Comments.Count(c => c.TargetUserId == this.UserId); - } - } - - [NotMapped] - public int PhotosByMe { - get { - using Database database = new(); - return database.Photos.Count(p => p.CreatorId == this.UserId); - } - } - - [NotMapped] - public int PhotosWithMe { - get { - using Database database = new(); - return Enumerable.Sum(database.Photos, photo => photo.Subjects.Count(subject => subject.User.UserId == this.UserId)); - } - } - - public int LocationId { get; set; } - - /// - /// The location of the profile card on the user's earth - /// - [ForeignKey("LocationId")] - public Location Location { get; set; } - - [NotMapped] - public int HeartedLevels { - get { - using Database database = new(); - return database.HeartedLevels.Count(p => p.UserId == this.UserId); - } - } - - [NotMapped] - public int HeartedUsers { - get { - using Database database = new(); - return database.HeartedProfiles.Count(p => p.UserId == this.UserId); - } - } - - [NotMapped] - public int QueuedLevels { - get { - using Database database = new(); - return database.QueuedLevels.Count(p => p.UserId == this.UserId); - } - } - - public string Pins { get; set; } = ""; - - public string PlanetHash { get; set; } = ""; - - public int Hearts { - get { - using Database database = new(); - - return database.HeartedProfiles.Count(s => s.HeartedUserId == this.UserId); - } - } - - public bool IsAdmin { get; set; } = false; - - public bool PasswordResetRequired { get; set; } - - public string YayHash { get; set; } = ""; - public string BooHash { get; set; } = ""; - public string MehHash { get; set; } = ""; - - #nullable enable - [NotMapped] - public string Status { - get { - using Database database = new(); - LastContact? lastMatch = database.LastContacts.Where - (l => l.UserId == this.UserId) - .FirstOrDefault(l => TimestampHelper.Timestamp - l.Timestamp < 300); - - if (lastMatch == null) return "Offline"; - - return "Currently online on " + lastMatch.GameVersion.ToPrettyString(); - } - } - #nullable disable - - public bool Banned { get; set; } - - public string BannedReason { get; set; } - - public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) - { - string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + - LbpSerializer.StringElement("game", this.Game) + - this.SerializeSlots(gameVersion == GameVersion.LittleBigPlanetVita) + - LbpSerializer.StringElement("lists", this.Lists) + - LbpSerializer.StringElement - ("lists_quota", ServerSettings.Instance.ListsQuota) + // technically not a part of the user but LBP expects it - LbpSerializer.StringElement("biography", this.Biography) + - LbpSerializer.StringElement("reviewCount", this.Reviews) + - LbpSerializer.StringElement("commentCount", this.Comments) + - LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) + - LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) + - LbpSerializer.StringElement("commentsEnabled", "true") + - LbpSerializer.StringElement("location", this.Location.Serialize()) + - LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) + - LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) + - LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels) + - LbpSerializer.StringElement("pins", this.Pins) + - LbpSerializer.StringElement("planets", this.PlanetHash) + - LbpSerializer.BlankElement("photos") + - LbpSerializer.StringElement("heartCount", this.Hearts) + - LbpSerializer.StringElement("yay2", this.YayHash) + - LbpSerializer.StringElement("boo2", this.YayHash) + - LbpSerializer.StringElement("meh2", this.YayHash); - this.ClientsConnected.Serialize(); - - return LbpSerializer.TaggedStringElement("user", user, "type", "user"); - } - - #region Slots - - /// - /// The number of used slots on the earth - /// - [NotMapped] - public int UsedSlots { - get { - using Database database = new(); - return database.Slots.Count(s => s.CreatorId == this.UserId); - } - } - - public int GetUsedSlotsForGame(GameVersion version) - { + [NotMapped] + public int Reviews { + get { using Database database = new(); - return database.Slots.Count(s => s.CreatorId == this.UserId && s.GameVersion == version); + return database.Reviews.Count(r => r.ReviewerId == this.UserId); } - - /// - /// The number of slots remaining on the earth - /// - public int FreeSlots => ServerSettings.Instance.EntitledSlots - this.UsedSlots; - - private static readonly string[] slotTypes = - { -// "lbp1", - "lbp2", "lbp3", "crossControl", - }; - - private string SerializeSlots(bool isVita = false) - { - string slots = string.Empty; - - string[] slotTypesLocal; - - if (isVita) - { - slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanetVita)); - slotTypesLocal = new[] - { - "lbp2", - }; - } - else - { - slots += LbpSerializer.StringElement("lbp1UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet1)); - slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet2)); - slots += LbpSerializer.StringElement("lbp3UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet3)); - slotTypesLocal = slotTypes; - } - - slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.Instance.EntitledSlots); - slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); - - foreach (string slotType in slotTypesLocal) - { - slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.Instance.EntitledSlots); - // ReSharper disable once StringLiteralTypo - slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); - slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); - } - return slots; - - } - - #endregion Slots - - #nullable enable - public override bool Equals(object? obj) - { - if (obj is User user) return user.UserId == this.UserId; - - return false; - } - - [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")] - public static bool operator ==(User? user1, User? user2) - { - if (ReferenceEquals(user1, user2)) return true; - if ((object?)user1 == null || (object?)user2 == null) return false; - - return user1.UserId == user2.UserId; - } - public static bool operator !=(User? user1, User? user2) => !(user1 == user2); - - [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override int GetHashCode() => this.UserId; - #nullable disable } + + [NotMapped] + public int Comments { + get { + using Database database = new(); + return database.Comments.Count(c => c.TargetUserId == this.UserId); + } + } + + [NotMapped] + public int PhotosByMe { + get { + using Database database = new(); + return database.Photos.Count(p => p.CreatorId == this.UserId); + } + } + + [NotMapped] + public int PhotosWithMe { + get { + using Database database = new(); + return Enumerable.Sum(database.Photos, photo => photo.Subjects.Count(subject => subject.User.UserId == this.UserId)); + } + } + + public int LocationId { get; set; } + + /// + /// The location of the profile card on the user's earth + /// + [ForeignKey("LocationId")] + public Location Location { get; set; } + + [NotMapped] + public int HeartedLevels { + get { + using Database database = new(); + return database.HeartedLevels.Count(p => p.UserId == this.UserId); + } + } + + [NotMapped] + public int HeartedUsers { + get { + using Database database = new(); + return database.HeartedProfiles.Count(p => p.UserId == this.UserId); + } + } + + [NotMapped] + public int QueuedLevels { + get { + using Database database = new(); + return database.QueuedLevels.Count(p => p.UserId == this.UserId); + } + } + + public string Pins { get; set; } = ""; + + public string PlanetHash { get; set; } = ""; + + public int Hearts { + get { + using Database database = new(); + + return database.HeartedProfiles.Count(s => s.HeartedUserId == this.UserId); + } + } + + public bool IsAdmin { get; set; } = false; + + public bool PasswordResetRequired { get; set; } + + public string YayHash { get; set; } = ""; + public string BooHash { get; set; } = ""; + public string MehHash { get; set; } = ""; + + #nullable enable + [NotMapped] + public string Status { + get { + using Database database = new(); + LastContact? lastMatch = database.LastContacts.Where + (l => l.UserId == this.UserId) + .FirstOrDefault(l => TimestampHelper.Timestamp - l.Timestamp < 300); + + if (lastMatch == null) return "Offline"; + + return "Currently online on " + lastMatch.GameVersion.ToPrettyString(); + } + } + #nullable disable + + public bool Banned { get; set; } + + public string BannedReason { get; set; } + + public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) + { + string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + + LbpSerializer.StringElement("game", this.Game) + + this.SerializeSlots(gameVersion == GameVersion.LittleBigPlanetVita) + + LbpSerializer.StringElement("lists", this.Lists) + + LbpSerializer.StringElement("lists_quota", ServerSettings.Instance.ListsQuota) + // technically not a part of the user but LBP expects it + LbpSerializer.StringElement("biography", this.Biography) + + LbpSerializer.StringElement("reviewCount", this.Reviews) + + LbpSerializer.StringElement("commentCount", this.Comments) + + LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) + + LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) + + LbpSerializer.StringElement("commentsEnabled", "true") + + LbpSerializer.StringElement("location", this.Location.Serialize()) + + LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) + + LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) + + LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels) + + LbpSerializer.StringElement("pins", this.Pins) + + LbpSerializer.StringElement("planets", this.PlanetHash) + + LbpSerializer.BlankElement("photos") + + LbpSerializer.StringElement("heartCount", this.Hearts) + + LbpSerializer.StringElement("yay2", this.YayHash) + + LbpSerializer.StringElement("boo2", this.YayHash) + + LbpSerializer.StringElement("meh2", this.YayHash); + this.ClientsConnected.Serialize(); + + return LbpSerializer.TaggedStringElement("user", user, "type", "user"); + } + + #region Slots + + /// + /// The number of used slots on the earth + /// + [NotMapped] + public int UsedSlots { + get { + using Database database = new(); + return database.Slots.Count(s => s.CreatorId == this.UserId); + } + } + + public int GetUsedSlotsForGame(GameVersion version) + { + using Database database = new(); + return database.Slots.Count(s => s.CreatorId == this.UserId && s.GameVersion == version); + } + + /// + /// The number of slots remaining on the earth + /// + public int FreeSlots => ServerSettings.Instance.EntitledSlots - this.UsedSlots; + + private static readonly string[] slotTypes = + { +// "lbp1", + "lbp2", "lbp3", "crossControl", + }; + + private string SerializeSlots(bool isVita = false) + { + string slots = string.Empty; + + string[] slotTypesLocal; + + if (isVita) + { + slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanetVita)); + slotTypesLocal = new[] + { + "lbp2", + }; + } + else + { + slots += LbpSerializer.StringElement("lbp1UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet1)); + slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet2)); + slots += LbpSerializer.StringElement("lbp3UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet3)); + slotTypesLocal = slotTypes; + } + + slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.Instance.EntitledSlots); + slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); + + foreach (string slotType in slotTypesLocal) + { + slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.Instance.EntitledSlots); + // ReSharper disable once StringLiteralTypo + slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); + slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); + } + return slots; + + } + + #endregion Slots + + #nullable enable + public override bool Equals(object? obj) + { + if (obj is User user) return user.UserId == this.UserId; + + return false; + } + + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")] + public static bool operator ==(User? user1, User? user2) + { + if (ReferenceEquals(user1, user2)) return true; + if ((object?)user1 == null || (object?)user2 == null) return false; + + return user1.UserId == user2.UserId; + } + public static bool operator !=(User? user1, User? user2) => !(user1 == user2); + + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override int GetHashCode() => this.UserId; + #nullable disable } \ No newline at end of file diff --git a/ProjectLighthouse/Types/UserApprovedIpAddress.cs b/ProjectLighthouse/Types/UserApprovedIpAddress.cs index 06c84183..577e159e 100644 --- a/ProjectLighthouse/Types/UserApprovedIpAddress.cs +++ b/ProjectLighthouse/Types/UserApprovedIpAddress.cs @@ -1,18 +1,17 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class UserApprovedIpAddress { - public class UserApprovedIpAddress - { - [Key] - public int UserApprovedIpAddressId { get; set; } + [Key] + public int UserApprovedIpAddressId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - [ForeignKey(nameof(UserId))] - public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } - public string IpAddress { get; set; } - } + public string IpAddress { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/WebToken.cs b/ProjectLighthouse/Types/WebToken.cs index 2bf8fe9c..fdcd6d5b 100644 --- a/ProjectLighthouse/Types/WebToken.cs +++ b/ProjectLighthouse/Types/WebToken.cs @@ -1,15 +1,14 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types +namespace LBPUnion.ProjectLighthouse.Types; + +public class WebToken { - public class WebToken - { - // ReSharper disable once UnusedMember.Global - [Key] - public int TokenId { get; set; } + // ReSharper disable once UnusedMember.Global + [Key] + public int TokenId { get; set; } - public int UserId { get; set; } + public int UserId { get; set; } - public string UserToken { get; set; } - } + public string UserToken { get; set; } } \ No newline at end of file diff --git a/README.md b/README.md index e1836621..d430aa82 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # Project Lighthouse -Project Lighthouse is a clean-room, open-source custom server for LittleBigPlanet. This is a project conducted by the [LBP Union Ministry of Technology Research and Development team.](https://www.lbpunion.com/technology) For concerns and inquiries about the project, please [contact us here.](https://www.lbpunion.com/contact) For general questions and discussion about Project Lighthouse, please see the [megathread](https://www.lbpunion.com/forum/union-hall/project-lighthouse-littlebigplanet-private-servers-megathread) on our forum. +Project Lighthouse is a clean-room, open-source custom server for LittleBigPlanet. This is a project conducted by +the [LBP Union Ministry of Technology Research and Development team.](https://www.lbpunion.com/technology) For concerns +and inquiries about the project, please [contact us here.](https://www.lbpunion.com/contact) For general questions and +discussion about Project Lighthouse, please see +the [megathread](https://www.lbpunion.com/forum/union-hall/project-lighthouse-littlebigplanet-private-servers-megathread) +on our forum. ## WARNING! @@ -39,8 +44,9 @@ you can follow at your own discretion. *Note: This requires a modified copy of RPCS3. You can find a working version [on our GitHub](https://github.com/LBPUnion/rpcs3).* -Start by getting a copy of LittleBigPlanet 1/2 installed. (Check the LittleBigPlanet 1 section, since you'll need to do extra steps for your game to not crash upon entering pod computer). It can be digital (NPUA80472/NPUA80662) or disc (BCUS98148/BCUS98245). For those -that don't, the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it. +Start by getting a copy of LittleBigPlanet 1/2 installed. (Check the LittleBigPlanet 1 section, since you'll need to do +extra steps for your game to not crash upon entering pod computer). It can be digital (NPUA80472/NPUA80662) or disc ( +BCUS98148/BCUS98245). For those that don't, the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it. Next, download [UnionPatcher](https://github.com/LBPUnion/UnionPatcher/). Binaries can be found by reading the README.md file. @@ -69,11 +75,15 @@ For LittleBigPlanet 1 to work with RPCS3, follow the steps for LittleBigPlanet 2 First, open your favourite hex editor. We recommend [HxD](https://mh-nexus.de/downloads/HxDSetup.zip). -Once you have a hex editor open, open your `EBOOTlocalhost.elf` file and search for the hex values `73 63 65 4E 70 43 6F 6D 6D 65 72 63 65 32`. In HxD, this would be done by clicking on Search -> Replace, clicking on the `Hex-values` tab, and entering the hex there. +Once you have a hex editor open, open your `EBOOTlocalhost.elf` file and search for the hex +values `73 63 65 4E 70 43 6F 6D 6D 65 72 63 65 32`. In HxD, this would be done by clicking on Search -> Replace, +clicking on the `Hex-values` tab, and entering the hex there. Then, you can zero it out by replacing it with `00 00 00 00 00 00 00 00 00 00 00 00 00 00`. -What this does is remove all the references to the sceNpCommerce2 function. The function is used for purchasing DLC, which is impossible on Lighthouse. The reason why it must be patched out is because RPCS3 doesn't support the function at this moment. +What this does is remove all the references to the sceNpCommerce2 function. The function is used for purchasing DLC, +which is impossible on Lighthouse. The reason why it must be patched out is because RPCS3 doesn't support the function +at this moment. Then save the file, and your LBP1 EBOOT can now be used with RPCS3.