From 329ab660430820e87879f60f310840b9682eac4f Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 27 Mar 2023 19:39:54 -0500 Subject: [PATCH] Refactor serialization system (#702) * Initial work for serialization refactor * Experiment with new naming conventions * Mostly implement user and slot serialization. Still needs to be fine tuned to match original implementation Many things are left in a broken state like website features/api endpoints/lbp3 categories * Fix release building * Migrate scores, reviews, and more to new serialization system. Many things are still broken but progress is steadily being made * Fix Api responses and migrate serialization for most types * Make serialization better and fix bugs Fix recursive PrepareSerialization when recursive item is set during root item's PrepareSerialization, items, should be properly indexed in order but it's only tested to 1 level of recursion * Fix review serialization * Fix user serialization producing malformed SQL query * Remove DefaultIfEmpty query * MariaDB doesn't like double nested queries * Fix LBP1 tag counter * Implement lbp3 categories and add better deserialization handling * Implement expression tree caching to speed up reflection and write new serializer tests * Remove Game column from UserEntity and rename DatabaseContextModelSnapshot.cs back to DatabaseModelSnapshot.cs * Make UserEntity username not required * Fix recursive serialization of lists and add relevant unit tests * Actually commit the migration * Fix LocationTests to use new deserialization class * Fix comments not serializing the right author username * Replace all occurrences of StatusCode with their respective ASP.NET named result instead of StatusCode(403) everything is now in the form of Forbid() * Fix SlotBase.ConvertToEntity and LocationTests * Fix compilation error * Give Location a default value in GameUserSlot and GameUser * Reimplement stubbed website functions * Convert grief reports to new serialization system * Update DatabaseModelSnapshot and bump dotnet tool version * Remove unused directives * Fix broken type reference * Fix rated comments on website * Don't include banned users in website comments * Optimize score submission * Fix slot id calculating in in-game comment posting * Move serialization interfaces to types folder and add more documentation * Allow uploading of versus scores --- .config/dotnet-tools.json | 2 +- .../Controllers/SlotEndpoints.cs | 15 +- .../Controllers/StatusController.cs | 5 +- .../Controllers/UserEndpoints.cs | 26 +- .../Responses/ApiSlot.cs | 40 ++ .../Responses/ApiUser.cs | 42 ++ .../Responses/MinimalSlot.cs | 31 -- .../ClientConfigurationController.cs | 16 +- .../Controllers/CommentController.cs | 34 +- .../Controllers/DeveloperController.cs | 5 +- .../Controllers/FriendsController.cs | 33 +- .../Controllers/LoginController.cs | 28 +- .../Controllers/LogoutController.cs | 6 +- .../Matching/EnterLevelController.cs | 22 +- .../Controllers/Matching/MatchController.cs | 12 +- .../Controllers/MessageController.cs | 6 +- .../Controllers/ReportController.cs | 19 +- .../Controllers/Resources/PhotosController.cs | 93 +++-- .../Resources/ResourcesController.cs | 9 +- .../Controllers/Slots/CollectionController.cs | 140 +++---- .../Controllers/Slots/LevelTagsController.cs | 6 +- .../Controllers/Slots/ListController.cs | 145 +++---- .../Controllers/Slots/PublishController.cs | 119 +++--- .../Controllers/Slots/ReviewController.cs | 141 ++----- .../Controllers/Slots/ScoreController.cs | 183 +++++---- .../Controllers/Slots/SearchController.cs | 15 +- .../Controllers/Slots/SlotsController.cs | 267 ++++++------- .../Controllers/StatisticsController.cs | 8 +- .../Controllers/UserController.cs | 79 ++-- .../Middlewares/SetLastContactMiddleware.cs | 2 +- .../Startup/GameServerStartup.cs | 2 +- .../Startup/TokenAuthHandler.cs | 2 +- .../Types/Categories/CategoryHelper.cs | 2 +- .../Types/Categories/CategoryWithUser.cs | 51 +-- .../Types/Categories/CustomCategory.cs | 6 +- .../Types/Categories/HeartedCategory.cs | 6 +- .../Types/Categories/HighestRatedCategory.cs | 4 +- .../Types/Categories/LuckyDipCategory.cs | 4 +- .../Types/Categories/MostHeartedCategory.cs | 4 +- .../Types/Categories/MostPlayedCategory.cs | 4 +- .../Types/Categories/NewestLevelsCategory.cs | 4 +- .../Types/Categories/QueueCategory.cs | 6 +- .../Types/Categories/TeamPicksCategory.cs | 4 +- .../Types/Misc/ResourceList.cs | 3 +- .../Types/Users/ClientsConnected.cs | 25 -- .../Types/Users/PrivacySettings.cs | 12 +- .../Admin/AdminReportController.cs | 12 +- .../Controllers/Admin/AdminUserController.cs | 12 +- .../ExternalAuth/AuthenticationController.cs | 10 +- .../Moderator/ModerationCaseController.cs | 6 +- .../Moderator/ModerationRemovalController.cs | 21 +- .../Moderator/ModerationSlotController.cs | 18 +- .../Controllers/SlotPageController.cs | 24 +- .../Controllers/UserPageController.cs | 20 +- .../Extensions/PartialExtensions.cs | 8 +- .../UserRequiredRedirectMiddleware.cs | 4 +- .../Pages/Admin/AdminApiKeyPage.cshtml | 2 +- .../Pages/Admin/AdminApiKeyPage.cshtml.cs | 8 +- .../Pages/Admin/AdminPanelPage.cshtml.cs | 2 +- .../Pages/Admin/AdminPanelUsersPage.cshtml | 2 +- .../Pages/Admin/AdminPanelUsersPage.cshtml.cs | 4 +- .../Admin/AdminSetGrantedSlotsPage.cshtml.cs | 6 +- .../Pages/Debug/RoomVisualizerPage.cshtml | 2 +- .../Pages/Debug/RoomVisualizerPage.cshtml.cs | 2 +- .../CompleteEmailVerificationPage.cshtml.cs | 6 +- .../Email/SendVerificationEmailPage.cshtml.cs | 2 +- .../Pages/Email/SetEmailForm.cshtml.cs | 6 +- .../ExternalAuth/AuthenticationPage.cshtml | 2 +- .../ExternalAuth/AuthenticationPage.cshtml.cs | 4 +- .../Pages/LandingPage.cshtml | 6 +- .../Pages/LandingPage.cshtml.cs | 8 +- .../Pages/Layouts/BaseLayout.cshtml.cs | 4 +- .../Pages/Login/LoginForm.cshtml.cs | 6 +- .../Pages/Login/LogoutPage.cshtml.cs | 2 +- .../Pages/Login/PasswordResetPage.cshtml.cs | 4 +- .../Login/PasswordResetRequestForm.cshtml.cs | 4 +- .../Login/PasswordResetRequiredPage.cshtml.cs | 2 +- .../Pages/Login/PirateSignupPage.cshtml.cs | 4 +- .../Pages/Login/RegisterForm.cshtml.cs | 6 +- .../Pages/Moderation/BannedUsersPage.cshtml | 2 +- .../Moderation/BannedUsersPage.cshtml.cs | 4 +- .../Pages/Moderation/CasePage.cshtml | 2 +- .../Pages/Moderation/CasePage.cshtml.cs | 4 +- .../Pages/Moderation/HiddenLevelsPage.cshtml | 2 +- .../Moderation/HiddenLevelsPage.cshtml.cs | 4 +- .../Pages/Moderation/ModPanelPage.cshtml.cs | 2 +- .../Pages/Moderation/NewCasePage.cshtml.cs | 6 +- .../Pages/Moderation/ReportPage.cshtml.cs | 6 +- .../Pages/Moderation/ReportsPage.cshtml | 2 +- .../Pages/Moderation/ReportsPage.cshtml.cs | 6 +- .../AdminSetGrantedSlotsFormPartial.cshtml | 2 +- .../Pages/Partials/CommentsPartial.cshtml | 110 +++--- .../Pages/Partials/LeaderboardPartial.cshtml | 4 +- .../Partials/Links/UserLinkPartial.cshtml | 6 +- .../Partials/ModerationCasePartial.cshtml | 6 +- .../Pages/Partials/PhotoPartial.cshtml | 13 +- .../Pages/Partials/ReportPartial.cshtml | 2 +- .../Pages/Partials/ReviewPartial.cshtml | 3 +- .../Pages/Partials/SlotCardPartial.cshtml | 4 +- .../Pages/Partials/UserCardPartial.cshtml | 21 +- .../Pages/PhotosPage.cshtml | 2 +- .../Pages/PhotosPage.cshtml.cs | 5 +- .../Pages/SlotPage.cshtml | 5 +- .../Pages/SlotPage.cshtml.cs | 26 +- .../Pages/SlotSettingsPage.cshtml.cs | 2 +- .../Pages/SlotsPage.cshtml | 2 +- .../Pages/SlotsPage.cshtml.cs | 4 +- .../TwoFactor/DisableTwoFactorPage.cshtml.cs | 4 +- .../TwoFactor/SetupTwoFactorPage.cshtml.cs | 8 +- .../TwoFactor/TwoFactorLoginPage.cshtml.cs | 8 +- .../Pages/UserPage.cshtml | 8 +- .../Pages/UserPage.cshtml.cs | 22 +- .../Pages/UserSettingsPage.cshtml.cs | 2 +- .../Pages/UsersPage.cshtml | 2 +- .../Pages/UsersPage.cshtml.cs | 2 +- .../Tests/DatabaseTests.cs | 4 +- .../Tests/LoginTests.cs | 2 +- .../Tests/SlotTests.cs | 9 +- .../Tests/AdminTests.cs | 8 +- .../Tests/AuthenticationTests.cs | 16 +- .../Tests/RegisterTests.cs | 6 +- .../LighthouseServerTest.cs | 5 +- .../Tests/LocationTests.cs | 19 +- .../SerializationDependencyTests.cs | 159 ++++++++ .../Tests/Serialization/SerializationTests.cs | 218 ++++++++++ .../Tests/SerializerTests.cs | 42 -- .../Commands/CreateAPIKeyCommand.cs | 2 +- .../Maintenance/Commands/CreateUserCommand.cs | 2 +- .../Maintenance/Commands/DeleteUserCommand.cs | 2 +- .../Maintenance/Commands/RenameUserCommand.cs | 2 +- .../Commands/ResetPasswordCommand.cs | 2 +- .../Commands/WipeTokensForUserCommand.cs | 6 +- .../Maintenance/MaintenanceHelper.cs | 2 +- .../CleanupBrokenPhotosMaintenanceJob.cs | 4 +- .../CleanupBrokenVersusScoresMigration.cs | 2 +- .../CleanupDuplicateScoresMigration.cs | 4 +- .../CleanupSlotVersionMismatchMigration.cs | 2 +- .../RepeatingTasks/PerformCaseActionsTask.cs | 6 +- ...omments.cs => DatabaseContext.Comments.cs} | 16 +- ...okens.cs => DatabaseContext.GameTokens.cs} | 8 +- .../Database/DatabaseContext.Slots.cs | 90 +++++ ...eUserUtils.cs => DatabaseContext.Utils.cs} | 39 +- ...Tokens.cs => DatabaseContext.WebTokens.cs} | 24 +- ProjectLighthouse/Database/DatabaseContext.cs | 63 ++- .../Database/DatabaseSlotUtils.cs | 90 ----- .../Extensions/ControllerExtensions.cs | 17 +- .../Extensions/DatabaseExtensions.cs | 17 +- .../Extensions/RoomExtensions.cs | 6 +- ProjectLighthouse/Helpers/EmailHelper.cs | 4 +- .../Helpers/LastContactHelper.cs | 6 +- ProjectLighthouse/Helpers/ReflectionHelper.cs | 34 ++ ProjectLighthouse/Helpers/RoomHelper.cs | 13 +- ProjectLighthouse/Helpers/SlotHelper.cs | 4 +- .../20230310075648_RemoveGameFromUser.cs | 30 ++ ...9024330_AddCommentForeignKeyToReactions.cs | 74 ++++ ...424_RenameReactionsTableToRatedComments.cs | 113 ++++++ .../Migrations/DatabaseModelSnapshot.cs | 240 +++++------ .../Serialization/CustomXmlSerializer.cs | 162 ++++++++ .../ExcludeNamespaceXmlWriter.cs | 57 +++ .../Serialization/LbpOutputFormatter.cs | 39 ++ .../Serialization/LbpSerializer.cs | 38 -- .../Serialization/LighthouseSerializer.cs | 89 +++++ .../WriteFullClosingTagXmlWriter.cs | 14 + .../Serialization/XmlOutputFormatter.cs | 12 - .../Serialization/XmlWriterWrapper.cs | 127 ++++++ ProjectLighthouse/StartupTasks.cs | 4 +- ...{HeartedLevel.cs => HeartedLevelEntity.cs} | 6 +- ...edPlaylist.cs => HeartedPlaylistEntity.cs} | 6 +- ...rtedProfile.cs => HeartedProfileEntity.cs} | 6 +- .../{QueuedLevel.cs => QueuedLevelEntity.cs} | 6 +- .../Interaction/RatedCommentEntity.cs | 26 ++ .../{RatedLevel.cs => RatedLevelEntity.cs} | 6 +- .../{RatedReview.cs => RatedReviewEntity.cs} | 6 +- .../Types/Entities/Interaction/Reaction.cs | 17 - ...{VisitedLevel.cs => VisitedLevelEntity.cs} | 6 +- ...eCategory.cs => DatabaseCategoryEntity.cs} | 2 +- .../Types/Entities/Level/Playlist.cs | 71 ---- .../Types/Entities/Level/PlaylistEntity.cs | 32 ++ .../Types/Entities/Level/Review.cs | 101 ----- .../Types/Entities/Level/ReviewEntity.cs | 46 +++ .../Types/Entities/Level/Score.cs | 63 --- .../Types/Entities/Level/ScoreEntity.cs | 32 ++ .../Types/Entities/Level/Slot.cs | 373 ------------------ .../Types/Entities/Level/SlotEntity.cs | 171 ++++++++ ...gration.cs => CompletedMigrationEntity.cs} | 2 +- .../{GriefReport.cs => GriefReportEntity.cs} | 27 +- ...erationCase.cs => ModerationCaseEntity.cs} | 10 +- ...ckedProfile.cs => BlockedProfileEntity.cs} | 6 +- .../Profile/{Comment.cs => CommentEntity.cs} | 31 +- .../{LastContact.cs => LastContactEntity.cs} | 4 +- .../Types/Entities/Profile/Photo.cs | 113 ------ .../Types/Entities/Profile/PhotoEntity.cs | 36 ++ .../Types/Entities/Profile/PhotoSubject.cs | 49 --- .../Entities/Profile/PhotoSubjectEntity.cs | 22 ++ ...ttempt.cs => PlatformLinkAttemptEntity.cs} | 5 +- .../Types/Entities/Profile/User.cs | 355 ----------------- .../Types/Entities/Profile/UserEntity.cs | 160 ++++++++ .../Token/{ApiKey.cs => ApiKeyEntity.cs} | 2 +- ...mailSetToken.cs => EmailSetTokenEntity.cs} | 4 +- ...ken.cs => EmailVerificationTokenEntity.cs} | 4 +- .../{GameToken.cs => GameTokenEntity.cs} | 4 +- ...etToken.cs => PasswordResetTokenEntity.cs} | 2 +- ...ionToken.cs => RegistrationTokenEntity.cs} | 2 +- .../Token/{WebToken.cs => WebTokenEntity.cs} | 2 +- ProjectLighthouse/Types/Levels/Category.cs | 43 +- ProjectLighthouse/Types/Logging/LogArea.cs | 1 + ProjectLighthouse/Types/Matchmaking/Player.cs | 2 +- ProjectLighthouse/Types/Misc/Location.cs | 3 - .../Types/Serialization/Author.cs | 18 + .../Serialization/CategoryListResponse.cs | 34 ++ .../Serialization/CommentListResponse.cs | 17 + .../Types/Serialization/FriendResponse.cs | 19 + .../Types/Serialization/GameCategory.cs | 37 ++ .../Types/Serialization/GameComment.cs | 87 ++++ .../Types/Serialization/GameDeveloperSlot.cs | 46 +++ .../Serialization/GameDeveloperVideos.cs | 7 + .../Types/Serialization/GameGriefReport.cs | 49 +++ .../Types/Serialization/GamePhoto.cs | 107 +++++ .../Types/Serialization/GamePhotoSubject.cs | 30 ++ .../Types/Serialization/GamePlaylist.cs | 86 ++++ .../Types/Serialization/GameReview.cs | 118 ++++++ .../Types/Serialization/GameScore.cs | 40 ++ .../Types/Serialization/GameUser.cs | 242 ++++++++++++ .../Types/Serialization/GameUserSlot.cs | 298 ++++++++++++++ .../Serialization/GenericPlaylistResponse.cs | 42 ++ .../Serialization/GenericSlotResponse.cs | 41 ++ .../Serialization/GenericUserResponse.cs | 41 ++ .../Types/Serialization/IHasCustomRoot.cs | 10 + .../Types/Serialization/ILbpSerializable.cs | 6 + .../INeedsPreparationForSerialization.cs | 8 + .../Types/Serialization/IconList.cs | 18 + .../Serialization/MinimalUserListResponse.cs | 26 ++ .../Types/Serialization/NpHandle.cs | 23 ++ .../Types/Serialization/PhotoListResponse.cs | 18 + .../Types/Serialization/PhotoSlot.cs | 25 ++ .../Serialization/PlanetStatsResponse.cs | 22 ++ .../Types/Serialization/PlaylistResponse.cs | 18 + .../Types/Serialization/ReviewResponse.cs | 28 ++ .../Types/Serialization/ReviewSlot.cs | 15 + .../Types/Serialization/ScoreboardResponse.cs | 44 +++ .../Types/Serialization/SlotBase.cs | 113 ++++++ .../Serialization/SlotResourceResponse.cs | 22 ++ .../Serialization/TelemetryConfigResponse.cs | 13 + .../Types/Serialization/TextSearch.cs | 10 + .../Types/Serialization/UserListResponse.cs | 41 ++ ProjectLighthouse/Types/Users/GameVersion.cs | 6 + ProjectLighthouse/Types/Users/LoginResult.cs | 13 +- ProjectLighthouse/Types/Users/UserStatus.cs | 2 +- 248 files changed, 4993 insertions(+), 2896 deletions(-) create mode 100644 ProjectLighthouse.Servers.API/Responses/ApiSlot.cs create mode 100644 ProjectLighthouse.Servers.API/Responses/ApiUser.cs delete mode 100644 ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs delete mode 100644 ProjectLighthouse.Servers.GameServer/Types/Users/ClientsConnected.cs create mode 100644 ProjectLighthouse.Tests/Tests/Serialization/SerializationDependencyTests.cs create mode 100644 ProjectLighthouse.Tests/Tests/Serialization/SerializationTests.cs delete mode 100644 ProjectLighthouse.Tests/Tests/SerializerTests.cs rename ProjectLighthouse/Database/{DatabaseComments.cs => DatabaseContext.Comments.cs} (82%) rename ProjectLighthouse/Database/{DatabaseGameTokens.cs => DatabaseContext.GameTokens.cs} (72%) create mode 100644 ProjectLighthouse/Database/DatabaseContext.Slots.cs rename ProjectLighthouse/Database/{DatabaseUserUtils.cs => DatabaseContext.Utils.cs} (78%) rename ProjectLighthouse/Database/{DatabaseWebTokens.cs => DatabaseContext.WebTokens.cs} (73%) delete mode 100644 ProjectLighthouse/Database/DatabaseSlotUtils.cs create mode 100644 ProjectLighthouse/Helpers/ReflectionHelper.cs create mode 100644 ProjectLighthouse/Migrations/20230310075648_RemoveGameFromUser.cs create mode 100644 ProjectLighthouse/Migrations/20230319024330_AddCommentForeignKeyToReactions.cs create mode 100644 ProjectLighthouse/Migrations/20230319024424_RenameReactionsTableToRatedComments.cs create mode 100644 ProjectLighthouse/Serialization/CustomXmlSerializer.cs create mode 100644 ProjectLighthouse/Serialization/ExcludeNamespaceXmlWriter.cs create mode 100644 ProjectLighthouse/Serialization/LbpOutputFormatter.cs delete mode 100644 ProjectLighthouse/Serialization/LbpSerializer.cs create mode 100644 ProjectLighthouse/Serialization/LighthouseSerializer.cs create mode 100644 ProjectLighthouse/Serialization/WriteFullClosingTagXmlWriter.cs delete mode 100644 ProjectLighthouse/Serialization/XmlOutputFormatter.cs create mode 100644 ProjectLighthouse/Serialization/XmlWriterWrapper.cs rename ProjectLighthouse/Types/Entities/Interaction/{HeartedLevel.cs => HeartedLevelEntity.cs} (82%) rename ProjectLighthouse/Types/Entities/Interaction/{HeartedPlaylist.cs => HeartedPlaylistEntity.cs} (79%) rename ProjectLighthouse/Types/Entities/Interaction/{HeartedProfile.cs => HeartedProfileEntity.cs} (77%) rename ProjectLighthouse/Types/Entities/Interaction/{QueuedLevel.cs => QueuedLevelEntity.cs} (82%) create mode 100644 ProjectLighthouse/Types/Entities/Interaction/RatedCommentEntity.cs rename ProjectLighthouse/Types/Entities/Interaction/{RatedLevel.cs => RatedLevelEntity.cs} (85%) rename ProjectLighthouse/Types/Entities/Interaction/{RatedReview.cs => RatedReviewEntity.cs} (82%) delete mode 100644 ProjectLighthouse/Types/Entities/Interaction/Reaction.cs rename ProjectLighthouse/Types/Entities/Interaction/{VisitedLevel.cs => VisitedLevelEntity.cs} (84%) rename ProjectLighthouse/Types/Entities/Level/{DatabaseCategory.cs => DatabaseCategoryEntity.cs} (94%) delete mode 100644 ProjectLighthouse/Types/Entities/Level/Playlist.cs create mode 100644 ProjectLighthouse/Types/Entities/Level/PlaylistEntity.cs delete mode 100644 ProjectLighthouse/Types/Entities/Level/Review.cs create mode 100644 ProjectLighthouse/Types/Entities/Level/ReviewEntity.cs delete mode 100644 ProjectLighthouse/Types/Entities/Level/Score.cs create mode 100644 ProjectLighthouse/Types/Entities/Level/ScoreEntity.cs delete mode 100644 ProjectLighthouse/Types/Entities/Level/Slot.cs create mode 100644 ProjectLighthouse/Types/Entities/Level/SlotEntity.cs rename ProjectLighthouse/Types/Entities/Maintenance/{CompletedMigration.cs => CompletedMigrationEntity.cs} (94%) rename ProjectLighthouse/Types/Entities/Moderation/{GriefReport.cs => GriefReportEntity.cs} (70%) rename ProjectLighthouse/Types/Entities/Moderation/{ModerationCase.cs => ModerationCaseEntity.cs} (85%) rename ProjectLighthouse/Types/Entities/Profile/{BlockedProfile.cs => BlockedProfileEntity.cs} (77%) rename ProjectLighthouse/Types/Entities/Profile/{Comment.cs => CommentEntity.cs} (50%) rename ProjectLighthouse/Types/Entities/Profile/{LastContact.cs => LastContactEntity.cs} (86%) delete mode 100644 ProjectLighthouse/Types/Entities/Profile/Photo.cs create mode 100644 ProjectLighthouse/Types/Entities/Profile/PhotoEntity.cs delete mode 100644 ProjectLighthouse/Types/Entities/Profile/PhotoSubject.cs create mode 100644 ProjectLighthouse/Types/Entities/Profile/PhotoSubjectEntity.cs rename ProjectLighthouse/Types/Entities/Profile/{PlatformLinkAttempt.cs => PlatformLinkAttemptEntity.cs} (86%) delete mode 100644 ProjectLighthouse/Types/Entities/Profile/User.cs create mode 100644 ProjectLighthouse/Types/Entities/Profile/UserEntity.cs rename ProjectLighthouse/Types/Entities/Token/{ApiKey.cs => ApiKeyEntity.cs} (91%) rename ProjectLighthouse/Types/Entities/Token/{EmailSetToken.cs => EmailSetTokenEntity.cs} (87%) rename ProjectLighthouse/Types/Entities/Token/{EmailVerificationToken.cs => EmailVerificationTokenEntity.cs} (85%) rename ProjectLighthouse/Types/Entities/Token/{GameToken.cs => GameTokenEntity.cs} (91%) rename ProjectLighthouse/Types/Entities/Token/{PasswordResetToken.cs => PasswordResetTokenEntity.cs} (90%) rename ProjectLighthouse/Types/Entities/Token/{RegistrationToken.cs => RegistrationTokenEntity.cs} (90%) rename ProjectLighthouse/Types/Entities/Token/{WebToken.cs => WebTokenEntity.cs} (93%) create mode 100644 ProjectLighthouse/Types/Serialization/Author.cs create mode 100644 ProjectLighthouse/Types/Serialization/CategoryListResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/CommentListResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/FriendResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameCategory.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameComment.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameDeveloperSlot.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameDeveloperVideos.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameGriefReport.cs create mode 100644 ProjectLighthouse/Types/Serialization/GamePhoto.cs create mode 100644 ProjectLighthouse/Types/Serialization/GamePhotoSubject.cs create mode 100644 ProjectLighthouse/Types/Serialization/GamePlaylist.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameReview.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameScore.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameUser.cs create mode 100644 ProjectLighthouse/Types/Serialization/GameUserSlot.cs create mode 100644 ProjectLighthouse/Types/Serialization/GenericPlaylistResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/GenericSlotResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/GenericUserResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/IHasCustomRoot.cs create mode 100644 ProjectLighthouse/Types/Serialization/ILbpSerializable.cs create mode 100644 ProjectLighthouse/Types/Serialization/INeedsPreparationForSerialization.cs create mode 100644 ProjectLighthouse/Types/Serialization/IconList.cs create mode 100644 ProjectLighthouse/Types/Serialization/MinimalUserListResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/NpHandle.cs create mode 100644 ProjectLighthouse/Types/Serialization/PhotoListResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/PhotoSlot.cs create mode 100644 ProjectLighthouse/Types/Serialization/PlanetStatsResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/PlaylistResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/ReviewResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/ReviewSlot.cs create mode 100644 ProjectLighthouse/Types/Serialization/ScoreboardResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/SlotBase.cs create mode 100644 ProjectLighthouse/Types/Serialization/SlotResourceResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/TelemetryConfigResponse.cs create mode 100644 ProjectLighthouse/Types/Serialization/TextSearch.cs create mode 100644 ProjectLighthouse/Types/Serialization/UserListResponse.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index f6baf5a4..a836656a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "7.0.3", + "version": "7.0.4", "commands": [ "dotnet-ef" ] diff --git a/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs index 89265f7b..caa1f3c0 100644 --- a/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs @@ -28,15 +28,18 @@ public class SlotEndpoints : ApiEndpointController /// The slot /// The slot list, if successful. [HttpGet("slots")] - [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public async Task GetSlots([FromQuery] int limit = 20, [FromQuery] int skip = 0) { if (skip < 0) skip = 0; if (limit < 0) limit = 0; limit = Math.Min(ServerStatics.PageSize, limit); - IEnumerable minimalSlots = (await this.database.Slots.OrderByDescending(s => s.FirstUploaded).Skip(skip).Take(limit).ToListAsync()).Select - (MinimalSlot.FromSlot); + IEnumerable minimalSlots = await this.database.Slots.OrderByDescending(s => s.FirstUploaded) + .Skip(skip) + .Take(limit) + .Select(s => ApiSlot.CreateFromEntity(s)) + .ToListAsync(); return this.Ok(minimalSlots); } @@ -49,13 +52,13 @@ public class SlotEndpoints : ApiEndpointController /// The slot, if successful. /// The slot could not be found. [HttpGet("slot/{id:int}")] - [ProducesResponseType(typeof(Slot), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiSlot), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetSlot(int id) { - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(u => u.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(u => u.SlotId == id); if (slot == null) return this.NotFound(); - return this.Ok(slot); + return this.Ok(ApiSlot.CreateFromEntity(slot)); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.API/Controllers/StatusController.cs b/ProjectLighthouse.Servers.API/Controllers/StatusController.cs index 2d790856..ce883dba 100644 --- a/ProjectLighthouse.Servers.API/Controllers/StatusController.cs +++ b/ProjectLighthouse.Servers.API/Controllers/StatusController.cs @@ -2,10 +2,7 @@ using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers; -[ApiController] -[Route("/api/v1")] -[Produces("application/json")] -public class StatusController : ControllerBase +public class StatusController : ApiEndpointController { [AcceptVerbs("GET", "HEAD", Route = "status")] public IActionResult GetStatus() => this.Ok(); diff --git a/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs index e1505241..0f195631 100644 --- a/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs @@ -1,6 +1,7 @@ #nullable enable using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Servers.API.Responses; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Users; @@ -29,25 +30,25 @@ public class UserEndpoints : ApiEndpointController /// The user, if successful. /// The user could not be found. [HttpGet("user/{id:int}")] - [ProducesResponseType(typeof(User), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiUser), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetUser(int id) { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (user == null) return this.NotFound(); - return this.Ok(user); + return this.Ok(ApiUser.CreateFromEntity(user)); } [HttpGet("username/{username}")] - [ProducesResponseType(typeof(User), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiUser), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetUser(string username) { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + UserEntity? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) return this.NotFound(); - return this.Ok(user); + return this.Ok(ApiUser.CreateFromEntity(user)); } /// @@ -58,15 +59,16 @@ public class UserEndpoints : ApiEndpointController /// The list of users, if any were found /// No users matched the query [HttpGet("search/user")] - [ProducesResponseType(typeof(User), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiUser), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task SearchUsers(string query) { - List users = await this.database.Users + List users = await this.database.Users .Where(u => u.PermissionLevel != PermissionLevel.Banned && u.Username.Contains(query)) .Where(u => u.ProfileVisibility == PrivacyType.All) // TODO: change check for when user is logged in .OrderByDescending(b => b.UserId) .Take(20) + .Select(u => ApiUser.CreateFromEntity(u)) .ToListAsync(); if (!users.Any()) return this.NotFound(); @@ -81,7 +83,7 @@ public class UserEndpoints : ApiEndpointController /// The user's status, if successful. /// The user could not be found. [HttpGet("user/{id:int}/status")] - [ProducesResponseType(typeof(UserStatus), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiUser), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public IActionResult GetUserStatus(int id) { @@ -102,8 +104,8 @@ public class UserEndpoints : ApiEndpointController string authToken = authHeader[(authHeader.IndexOf(' ') + 1)..]; - ApiKey? apiKey = await this.database.APIKeys.FirstOrDefaultAsync(k => k.Key == authToken); - if (apiKey == null) return this.StatusCode(403, null); + ApiKeyEntity? apiKey = await this.database.APIKeys.FirstOrDefaultAsync(k => k.Key == authToken); + if (apiKey == null) return this.Forbid(); if (!string.IsNullOrWhiteSpace(username)) { @@ -111,7 +113,7 @@ public class UserEndpoints : ApiEndpointController if (userExists) return this.BadRequest(); } - RegistrationToken token = new() + RegistrationTokenEntity token = new() { Created = DateTime.Now, Token = CryptoHelper.GenerateAuthToken(), diff --git a/ProjectLighthouse.Servers.API/Responses/ApiSlot.cs b/ProjectLighthouse.Servers.API/Responses/ApiSlot.cs new file mode 100644 index 00000000..bf5f354f --- /dev/null +++ b/ProjectLighthouse.Servers.API/Responses/ApiSlot.cs @@ -0,0 +1,40 @@ +using LBPUnion.ProjectLighthouse.Types.Entities.Level; +using LBPUnion.ProjectLighthouse.Types.Misc; +using LBPUnion.ProjectLighthouse.Types.Users; + +namespace LBPUnion.ProjectLighthouse.Servers.API.Responses; + +public struct ApiSlot +{ + public int SlotId { get; set; } + public string Name { get; set; } + public string IconHash { get; set; } + public bool TeamPick { get; set; } + public bool IsAdventure { get; set; } + public Location Location { get; set; } + public GameVersion GameVersion { get; set; } + public long FirstUploaded { get; set; } + public long LastUpdated { get; set; } + public int Plays { get; set; } + public int PlaysUnique { get; set; } + public int PlaysComplete { get; set; } + public bool CommentsEnabled { get; set; } + + public static ApiSlot CreateFromEntity(SlotEntity slot) => + new() + { + SlotId = slot.SlotId, + Name = slot.Name, + IconHash = slot.IconHash, + TeamPick = slot.TeamPick, + IsAdventure = slot.IsAdventurePlanet, + Location = slot.Location, + GameVersion = slot.GameVersion, + FirstUploaded = slot.FirstUploaded, + LastUpdated = slot.LastUpdated, + Plays = slot.Plays, + PlaysUnique = slot.PlaysUnique, + PlaysComplete = slot.PlaysComplete, + CommentsEnabled = slot.CommentsEnabled, + }; +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.API/Responses/ApiUser.cs b/ProjectLighthouse.Servers.API/Responses/ApiUser.cs new file mode 100644 index 00000000..2fd6fc07 --- /dev/null +++ b/ProjectLighthouse.Servers.API/Responses/ApiUser.cs @@ -0,0 +1,42 @@ +using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using LBPUnion.ProjectLighthouse.Types.Misc; +using LBPUnion.ProjectLighthouse.Types.Users; + +namespace LBPUnion.ProjectLighthouse.Servers.API.Responses; + +public struct ApiUser +{ + public int UserId { get; set; } + public string Username { get; set; } + public bool EmailAddressVerified { get; set; } + public string IconHash { get; set; } + public string Biography { get; set; } + public Location Location { get; set; } + public string YayHash { get; set; } + public string MehHash { get; set; } + public string BooHash { get; set; } + public long LastLogin { get; set; } + public long LastLogout { get; set; } + public PrivacyType LevelVisibility { get; set; } + public PrivacyType ProfileVisibility { get; set; } + public bool CommentsEnabled { get; set; } + + public static ApiUser CreateFromEntity(UserEntity entity) => + new() + { + UserId = entity.UserId, + Username = entity.Username, + EmailAddressVerified = entity.EmailAddressVerified, + IconHash = entity.IconHash, + Biography = entity.Biography, + Location = entity.Location, + YayHash = entity.YayHash, + MehHash = entity.MehHash, + BooHash = entity.BooHash, + LastLogin = entity.LastLogin, + LastLogout = entity.LastLogin, + LevelVisibility = entity.LevelVisibility, + ProfileVisibility = entity.ProfileVisibility, + CommentsEnabled = entity.CommentsEnabled, + }; +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs b/ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs deleted file mode 100644 index 33016135..00000000 --- a/ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs +++ /dev/null @@ -1,31 +0,0 @@ -using LBPUnion.ProjectLighthouse.Types.Entities.Level; -using LBPUnion.ProjectLighthouse.Types.Users; - -namespace LBPUnion.ProjectLighthouse.Servers.API.Responses; - -public struct MinimalSlot -{ - public int SlotId { get; set; } - public string Name { get; set; } - public string IconHash { get; set; } - public bool TeamPick { get; set; } - public bool IsAdventure { get; set; } - public GameVersion GameVersion { get; set; } - #if DEBUG - public long FirstUploaded { get; set; } - #endif - - public static MinimalSlot FromSlot(Slot slot) - => new() - { - SlotId = slot.SlotId, - Name = slot.Name, - IconHash = slot.IconHash, - TeamPick = slot.TeamPick, - IsAdventure = slot.IsAdventurePlanet, - GameVersion = slot.GameVersion, - #if DEBUG - FirstUploaded = slot.FirstUploaded, - #endif - }; -} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs index 00fb8505..d0806072 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs @@ -5,7 +5,7 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; -using LBPUnion.ProjectLighthouse.Types.Entities.Token; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -41,7 +41,7 @@ public class ClientConfigurationController : ControllerBase [HttpGet("t_conf")] [Produces("text/xml")] - public IActionResult Conf() => this.Ok("false"); + public IActionResult Conf() => this.Ok(new TelemetryConfigResponse()); [HttpGet("ChallengeConfig.xml")] [Produces("text/xml")] @@ -54,8 +54,8 @@ public class ClientConfigurationController : ControllerBase [Produces("text/xml")] public async Task GetPrivacySettings() { - User? user = await this.database.UserFromGameToken(this.GetToken()); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(this.GetToken()); + if (user == null) return this.Forbid(); PrivacySettings ps = new() { @@ -63,15 +63,15 @@ public class ClientConfigurationController : ControllerBase ProfileVisibility = user.ProfileVisibility.ToSerializedString(), }; - return this.Ok(ps.Serialize()); + return this.Ok(ps); } [HttpPost("privacySettings")] [Produces("text/xml")] public async Task SetPrivacySetting() { - User? user = await this.database.UserFromGameToken(this.GetToken()); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(this.GetToken()); + if (user == null) return this.Forbid(); PrivacySettings? settings = await this.DeserializeBody(); if (settings == null) return this.BadRequest(); @@ -100,6 +100,6 @@ public class ClientConfigurationController : ControllerBase ProfileVisibility = user.ProfileVisibility.ToSerializedString(), }; - return this.Ok(ps.Serialize()); + return this.Ok(ps); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 1cb9ee79..af17842f 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -2,10 +2,10 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -29,7 +29,7 @@ public class CommentController : ControllerBase [HttpPost("rateComment/{slotType}/{slotId:int}")] public async Task RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); // Return bad request if both are true or both are false if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); @@ -44,7 +44,7 @@ public class CommentController : ControllerBase [HttpGet("userComments/{username}")] public async Task GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0 || pageStart < 0) return this.BadRequest(); @@ -77,45 +77,37 @@ public class CommentController : ControllerBase where blockedProfile.UserId == token.UserId select blockedProfile.BlockedUserId).ToListAsync(); - List comments = await this.database.Comments.Where(p => p.TargetId == targetId && p.Type == type) + List comments = await this.database.Comments.Where(p => p.TargetId == targetId && p.Type == type) .OrderByDescending(p => p.Timestamp) .Where(p => !blockedUsers.Contains(p.PosterUserId)) .Include(c => c.Poster) .Where(p => p.Poster.PermissionLevel != PermissionLevel.Banned) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) + .Select(c => GameComment.CreateFromEntity(c, token.UserId)) .ToListAsync(); - string outputXml = comments.Aggregate - (string.Empty, (current, comment) => current + comment.Serialize(this.getReaction(token.UserId, comment.CommentId).Result)); - return this.Ok(LbpSerializer.StringElement("comments", outputXml)); - } - - private async Task getReaction(int userId, int commentId) - { - return await this.database.Reactions.Where(r => r.UserId == userId) - .Where(r => r.TargetId == commentId) - .Select(r => r.Rating) - .FirstOrDefaultAsync(); + return this.Ok(new CommentListResponse(comments)); } [HttpPost("postUserComment/{username}")] [HttpPost("postComment/{slotType}/{slotId:int}")] public async Task PostComment(string? username, string? slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Comment? comment = await this.DeserializeBody(); + GameComment? comment = await this.DeserializeBody(); if (comment == null) return this.BadRequest(); if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); + if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); + CommentType type = username == null ? CommentType.Level : CommentType.Profile; int targetId; if (type == CommentType.Level) { - slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); targetId = await this.database.Slots.Where(s => s.SlotId == slotId) .Where(s => s.CommentsEnabled && !s.Hidden) .Select(s => s.SlotId) @@ -138,11 +130,11 @@ public class CommentController : ControllerBase [HttpPost("deleteComment/{slotType}/{slotId:int}")] public async Task DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest(); - Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); + CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); if (comment == null) return this.NotFound(); if (comment.Deleted) return this.Ok(); @@ -169,7 +161,7 @@ public class CommentController : ControllerBase canDelete = comment.PosterUserId == token.UserId || slotCreator == token.UserId; } - if (!canDelete) return this.StatusCode(403, ""); + if (!canDelete) return this.Forbid(); comment.Deleted = true; comment.DeletedBy = await this.database.UsernameFromGameToken(token); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs index e515b843..503356d6 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using LBPUnion.ProjectLighthouse.Types.Serialization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; @@ -10,5 +11,5 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; public class DeveloperController : Controller { [HttpGet("developer_videos")] - public IActionResult DeveloperVideos() => this.Ok(""); + public IActionResult DeveloperVideos() => this.Ok(new GameDeveloperVideos()); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs index 7a71e916..3229e940 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs @@ -2,11 +2,11 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users; using LBPUnion.ProjectLighthouse.StorableLists.Stores; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -29,17 +29,17 @@ public class FriendsController : ControllerBase [HttpPost("npdata")] public async Task NPData() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); NPData? npData = await this.DeserializeBody(); if (npData == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(npData); - List friends = new(); + List friends = new(); foreach (string friendName in npData.Friends ?? new List()) { - User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName); + UserEntity? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName); if (friend == null) continue; friends.Add(friend); @@ -48,7 +48,7 @@ public class FriendsController : ControllerBase List blockedUsers = new(); foreach (string blockedUserName in npData.BlockedUsers ?? new List()) { - User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName); + UserEntity? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName); if (blockedUser == null) continue; blockedUsers.Add(blockedUser.UserId); @@ -61,30 +61,35 @@ public class FriendsController : ControllerBase UserFriendStore.UpdateFriendData(friendStore); - string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); + List minimalFriends = + friends.Select(u => new MinimalUserProfile + { + UserHandle = new NpHandle(u.Username, ""), + }).ToList(); - return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized))); + return this.Ok(new FriendResponse(minimalFriends)); } [HttpGet("myFriends")] public async Task MyFriends() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); UserFriendData? friendStore = UserFriendStore.GetUserFriendData(token.UserId); - if (friendStore == null) - return this.Ok(LbpSerializer.BlankElement("myFriends")); + GenericUserResponse response = new("myFriends", new List()); + + if (friendStore == null) + return this.Ok(response); - string friends = ""; foreach (int friendId in friendStore.FriendIds) { - User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == friendId); + UserEntity? friend = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == friendId); if (friend == null) continue; - friends += friend.Serialize(token.GameVersion); + response.Users.Add(GameUser.CreateFromEntity(friend, token.GameVersion)); } - return this.Ok(LbpSerializer.StringElement("myFriends", friends)); + return this.Ok(response); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs index e5d0fba3..f405e7ec 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs @@ -64,12 +64,12 @@ public class LoginController : ControllerBase if (username == null) { Logger.Warn("Unable to determine username, rejecting login", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } await this.database.RemoveExpiredTokens(); - User? user; + UserEntity? user; switch (npTicket.Platform) { @@ -91,7 +91,7 @@ public class LoginController : ControllerBase if (user == null) { // Check if there is an account with that username already - User? targetUsername = await this.database.Users.FirstOrDefaultAsync(u => u.Username == npTicket.Username); + UserEntity? targetUsername = await this.database.Users.FirstOrDefaultAsync(u => u.Username == npTicket.Username); if (targetUsername != null) { ulong targetPlatform = npTicket.Platform == Platform.RPCS3 @@ -102,7 +102,7 @@ public class LoginController : ControllerBase if (targetPlatform != 0) { Logger.Warn($"New user tried to login but their name is already taken, username={username}", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } // if there is already a pending link request don't create another @@ -111,9 +111,9 @@ public class LoginController : ControllerBase p.PlatformId == npTicket.UserId && p.UserId == targetUsername.UserId); - if (linkAttemptExists) return this.StatusCode(403, ""); + if (linkAttemptExists) return this.Forbid(); - PlatformLinkAttempt linkAttempt = new() + PlatformLinkAttemptEntity linkAttempt = new() { Platform = npTicket.Platform, UserId = targetUsername.UserId, @@ -124,13 +124,13 @@ public class LoginController : ControllerBase this.database.PlatformLinkAttempts.Add(linkAttempt); await this.database.SaveChangesAsync(); Logger.Success($"User '{npTicket.Username}' tried to login but platform isn't linked, platform={npTicket.Platform}", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } if (!ServerConfiguration.Instance.Authentication.AutomaticAccountCreation) { Logger.Warn($"Unknown user tried to connect username={username}", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } // create account for user if they don't exist user = await this.database.CreateUser(username, "$"); @@ -162,7 +162,7 @@ public class LoginController : ControllerBase { Logger.Warn($"{npTicket.Platform} user changed their name to a name that is already taken," + $" oldName='{user.Username}', newName='{npTicket.Username}'", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } Logger.Info($"User's username has changed, old='{user.Username}', new='{npTicket.Username}', platform={npTicket.Platform}", LogArea.Login); user.Username = username; @@ -176,26 +176,26 @@ public class LoginController : ControllerBase await this.database.SaveChangesAsync(); } - GameToken? token = await this.database.GameTokens.Include(t => t.User) + GameTokenEntity? token = await this.database.GameTokens.Include(t => t.User) .FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == npTicket.Username && t.TicketHash == npTicket.TicketHash); if (token != null) { Logger.Warn($"Rejecting duplicate ticket from {username}", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } token = await this.database.AuthenticateUser(user, npTicket, ipAddress); if (token == null) { Logger.Warn($"Unable to find/generate a token for username {npTicket.Username}", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } if (user.IsBanned) { Logger.Error($"User {npTicket.Username} tried to login but is banned", LogArea.Login); - return this.StatusCode(403, ""); + return this.Forbid(); } Logger.Success($"Successfully logged in user {user.Username} as {token.GameVersion} client", LogArea.Login); @@ -214,7 +214,7 @@ public class LoginController : ControllerBase AuthTicket = "MM_AUTH=" + token.UserToken, ServerBrand = VersionHelper.EnvVer, TitleStorageUrl = ServerConfiguration.Instance.GameApiExternalUrl, - }.Serialize() + } ); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs index 349d57e3..342cb1c5 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/LogoutController.cs @@ -25,10 +25,10 @@ public class LogoutController : ControllerBase [HttpPost] public async Task OnPost() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); user.LastLogout = TimeHelper.TimestampMillis; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs index d2923cf9..e8b2aa37 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs @@ -28,18 +28,18 @@ public class EnterLevelController : ControllerBase [HttpPost("play/{slotType}/{slotId:int}")] public async Task PlayLevel(string slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); // don't count plays for developer slots if (slotType == "developer") return this.Ok(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.BadRequest(); - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); - VisitedLevel? v; + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); + VisitedLevelEntity? v; if (!visited.Any()) { switch (token.GameVersion) @@ -57,7 +57,7 @@ public class EnterLevelController : ControllerBase default: return this.BadRequest(); } - v = new VisitedLevel + v = new VisitedLevelEntity { SlotId = slotId, UserId = token.UserId, @@ -98,22 +98,22 @@ public class EnterLevelController : ControllerBase [HttpPost("enterLevel/{slotType}/{slotId:int}")] public async Task EnterLevel(string slotType, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); if (slotType == "developer") return this.Ok(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.NotFound(); - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); - VisitedLevel? v; + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); + VisitedLevelEntity? v; if (!visited.Any()) { slot.PlaysLBP1Unique++; - v = new VisitedLevel + v = new VisitedLevelEntity { SlotId = slotId, UserId = token.UserId, diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs index 7c5733a5..e022bddb 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs @@ -37,10 +37,10 @@ public class MatchController : ControllerBase [Produces("text/plain")] public async Task Match() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); #region Parse match data @@ -113,7 +113,7 @@ public class MatchController : ControllerBase List users = new(); foreach (string playerUsername in createRoom.Players) { - User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); + UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (player != null) users.Add(player.UserId); else return this.BadRequest(); @@ -129,10 +129,10 @@ public class MatchController : ControllerBase if (room != null) { - List users = new(); + List users = new(); foreach (string playerUsername in updatePlayersInRoom.Players) { - User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); + UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (player != null) users.Add(player); else return this.BadRequest(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index 36add3f4..9043c5a7 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -46,7 +46,7 @@ along with this program. If not, see ."; [HttpGet("announce")] public async Task Announce() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); string username = await this.database.UsernameFromGameToken(token); @@ -81,7 +81,7 @@ along with this program. If not, see ."; [HttpPost("filter")] public async Task Filter() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); string message = await this.ReadBodyAsync(); @@ -92,7 +92,7 @@ along with this program. If not, see ."; if (await this.database.Users.AnyAsync(u => u.EmailAddress == email)) return this.Ok(); - User? user = await this.database.UserFromGameToken(token); + UserEntity? user = await this.database.UserFromGameToken(token); if (user == null || user.EmailAddressVerified) return this.Ok(); user.EmailAddress = email; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs index d5904f57..eb26129f 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs @@ -8,6 +8,7 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types.Entities.Moderation; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Moderation.Reports; +using LBPUnion.ProjectLighthouse.Types.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -29,11 +30,11 @@ public class ReportController : ControllerBase [HttpPost("grief")] public async Task Report() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); string username = await this.database.UsernameFromGameToken(token); - GriefReport? report = await this.DeserializeBody(); + GameGriefReport? report = await this.DeserializeBody(); if (report == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(report); @@ -46,18 +47,20 @@ public class ReportController : ControllerBase if (report.XmlPlayers.Any(p => !this.database.IsUsernameValid(p.Name))) return this.BadRequest(); - report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle)); - report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[])); - report.Timestamp = TimeHelper.TimestampMillis; - report.ReportingPlayerId = token.UserId; + GriefReportEntity reportEntity = GameGriefReport.ConvertToEntity(report); - this.database.Reports.Add(report); + reportEntity.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle)); + reportEntity.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[])); + reportEntity.Timestamp = TimeHelper.TimestampMillis; + reportEntity.ReportingPlayerId = token.UserId; + + this.database.Reports.Add(reportEntity); await this.database.SaveChangesAsync(); await WebhookHelper.SendWebhook( title: "New grief report", description: $"Submitted by {username}\n" + - $"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{report.ReportId}).", + $"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{reportEntity.ReportId}).", dest: WebhookHelper.WebhookDestination.Moderation ); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs index 738d9794..e9bad05c 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs @@ -6,12 +6,12 @@ using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Logging; +using LBPUnion.ProjectLighthouse.Types.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -34,17 +34,17 @@ public class PhotosController : ControllerBase [HttpPost("uploadPhoto")] public async Task UploadPhoto() { - User? user = await this.database.UserFromGameToken(this.GetToken()); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(this.GetToken()); + if (user == null) return this.Forbid(); - if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest(); + if (user.GetUploadedPhotoCount(this.database) >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest(); - Photo? photo = await this.DeserializeBody(); + GamePhoto? photo = await this.DeserializeBody(); if (photo == null) return this.BadRequest(); SanitizationHelper.SanitizeStringsInClass(photo); - foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId)) + foreach (PhotoEntity 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(); @@ -52,20 +52,28 @@ public class PhotosController : ControllerBase if (p.PlanHash == photo.PlanHash) return this.Ok(); } - photo.CreatorId = user.UserId; - photo.Creator = user; + PhotoEntity photoEntity = new() + { + CreatorId = user.UserId, + Creator = user, + SmallHash = photo.SmallHash, + MediumHash = photo.MediumHash, + LargeHash = photo.LargeHash, + PlanHash = photo.PlanHash, + Timestamp = photo.Timestamp, + }; - if (photo.XmlLevelInfo?.RootLevel != null) + if (photo.LevelInfo?.RootLevel != null) { bool validLevel = false; - PhotoSlot photoSlot = photo.XmlLevelInfo; + PhotoSlot photoSlot = photo.LevelInfo; if (photoSlot.SlotType is SlotType.Pod or SlotType.Local) photoSlot.SlotId = 0; switch (photoSlot.SlotType) { case SlotType.User: { // We'll grab the slot by the RootLevel and see what happens from here. - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel)); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel)); if (slot == null) break; if (!string.IsNullOrEmpty(slot.RootLevel)) validLevel = true; @@ -76,7 +84,7 @@ public class PhotosController : ControllerBase case SlotType.Local: case SlotType.Developer: { - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId); if (slot != null) photoSlot.SlotId = slot.SlotId; else @@ -92,23 +100,23 @@ public class PhotosController : ControllerBase break; } - if (validLevel) photo.SlotId = photo.XmlLevelInfo.SlotId; + if (validLevel) photoEntity.SlotId = photoSlot.SlotId; } - if (photo.XmlSubjects?.Count > 4) return this.BadRequest(); + if (photo.Subjects?.Count > 4) return this.BadRequest(); if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp; - this.database.Photos.Add(photo); + this.database.Photos.Add(photoEntity); // Save to get photo ID for the PhotoSubject foreign keys await this.database.SaveChangesAsync(); - if (photo.XmlSubjects != null) + if (photo.Subjects != null) { // Check for duplicate photo subjects List subjectUserIds = new(4); - foreach (PhotoSubject subject in photo.PhotoSubjects) + foreach (GamePhotoSubject subject in photo.Subjects) { if (subjectUserIds.Contains(subject.Username) && !string.IsNullOrEmpty(subject.Username)) return this.BadRequest(); @@ -116,17 +124,23 @@ public class PhotosController : ControllerBase subjectUserIds.Add(subject.Username); } - foreach (PhotoSubject subject in photo.XmlSubjects.Where(subject => !string.IsNullOrEmpty(subject.Username))) + foreach (GamePhotoSubject subject in photo.Subjects.Where(subject => !string.IsNullOrEmpty(subject.Username))) { - subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); + subject.UserId = await this.database.Users.Where(u => u.Username == subject.Username) + .Select(u => u.UserId) + .FirstOrDefaultAsync(); - if (subject.User == null) continue; + if (subject.UserId == 0) continue; + + PhotoSubjectEntity subjectEntity = new() + { + PhotoId = photoEntity.PhotoId, + UserId = subject.UserId, + }; - subject.UserId = subject.User.UserId; - subject.PhotoId = photo.PhotoId; Logger.Debug($"Adding PhotoSubject (userid {subject.UserId}) to db", LogArea.Photos); - this.database.PhotoSubjects.Add(subject); + this.database.PhotoSubjects.Add(subjectEntity); } } @@ -155,16 +169,15 @@ public class PhotosController : ControllerBase if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); - List photos = await this.database.Photos.Include(p => p.Creator) - .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) + List photos = await this.database.Photos.Include(p => p.PhotoSubjects) .Where(p => p.SlotId == id) .OrderByDescending(s => s.Timestamp) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) + .Select(p => GamePhoto.CreateFromEntity(p)) .ToListAsync(); - string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id, SlotHelper.ParseType(slotType))); - return this.Ok(LbpSerializer.StringElement("photos", response)); + + return this.Ok(new PhotoListResponse(photos)); } [HttpGet("photos/by")] @@ -175,16 +188,14 @@ public class PhotosController : ControllerBase int targetUserId = await this.database.UserIdFromUsername(user); if (targetUserId == 0) return this.NotFound(); - List photos = await this.database.Photos.Include(p => p.Creator) - .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) + List photos = await this.database.Photos.Include(p => p.PhotoSubjects) .Where(p => p.CreatorId == targetUserId) .OrderByDescending(s => s.Timestamp) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) + .Select(p => GamePhoto.CreateFromEntity(p)) .ToListAsync(); - string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize()); - return this.Ok(LbpSerializer.StringElement("photos", response)); + return this.Ok(new PhotoListResponse(photos)); } [HttpGet("photos/with")] @@ -195,32 +206,30 @@ public class PhotosController : ControllerBase int targetUserId = await this.database.UserIdFromUsername(user); if (targetUserId == 0) return this.NotFound(); - List photos = await this.database.Photos.Include(p => p.Creator) - .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) + List photos = await this.database.Photos.Include(p => p.PhotoSubjects) .Where(p => p.PhotoSubjects.Any(ps => ps.UserId == targetUserId)) .OrderByDescending(s => s.Timestamp) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) + .Select(p => GamePhoto.CreateFromEntity(p)) .ToListAsync(); - string response = photos.Aggregate(string.Empty, (current, photo) => current + photo.Serialize()); - return this.Ok(LbpSerializer.StringElement("photos", response)); + return this.Ok(new PhotoListResponse(photos)); } [HttpPost("deletePhoto/{id:int}")] public async Task DeletePhoto(int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); + PhotoEntity? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); if (photo == null) return this.NotFound(); // If user isn't photo creator then check if they own the level if (photo.CreatorId != token.UserId) { - Slot? photoSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId && s.Type == SlotType.User); - if (photoSlot == null || photoSlot.CreatorId != token.UserId) return this.StatusCode(401, ""); + SlotEntity? photoSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId && s.Type == SlotType.User); + if (photoSlot == null || photoSlot.CreatorId != token.UserId) return this.Unauthorized(); } HashSet photoResources = new(){photo.LargeHash, photo.SmallHash, photo.MediumHash, photo.PlanHash,}; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs index 446206c5..0ba95849 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs @@ -4,7 +4,6 @@ using System.IO.Pipelines; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc; using LBPUnion.ProjectLighthouse.Types.Logging; using LBPUnion.ProjectLighthouse.Types.Resources; @@ -22,7 +21,7 @@ public class ResourcesController : ControllerBase { [HttpPost("showModerated")] - public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources")); + public IActionResult ShowModerated() => this.Ok(new ResourceList()); [HttpPost("filterResources")] [HttpPost("showNotUploaded")] @@ -31,11 +30,9 @@ public class ResourcesController : ControllerBase ResourceList? resourceList = await this.DeserializeBody(); if (resourceList?.Resources == null) return this.BadRequest(); - string resources = resourceList.Resources.Where - (s => !FileHelper.ResourceExists(s)) - .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); + resourceList.Resources = resourceList.Resources.Where(r => !FileHelper.ResourceExists(r)).ToArray(); - return this.Ok(LbpSerializer.StringElement("resources", resources)); + return this.Ok(resourceList); } [HttpGet("r/{hash}")] diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs index 33c1fbdb..90b5cb24 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs @@ -3,13 +3,13 @@ using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Logging; +using LBPUnion.ProjectLighthouse.Types.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -32,16 +32,18 @@ public class CollectionController : ControllerBase [HttpGet("playlists/{playlistId:int}/slots")] public async Task GetPlaylistSlots(int playlistId) { - Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); + PlaylistEntity? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); if (targetPlaylist == null) return this.BadRequest(); - IQueryable slots = this.database.Slots.Include(s => s.Creator) - .Where(s => targetPlaylist.SlotIds.Contains(s.SlotId)); + GameTokenEntity token = this.GetToken(); + + List slots = await this.database.Slots.Where(s => targetPlaylist.SlotIds.Contains(s.SlotId)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); int total = targetPlaylist.SlotIds.Length; - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", total)); + return this.Ok(new GenericSlotResponse(slots, total, 0)); } [HttpPost("playlists/{playlistId:int}")] @@ -50,9 +52,9 @@ public class CollectionController : ControllerBase [HttpPost("playlists/{playlistId:int}/order_slots")] public async Task UpdatePlaylist(int playlistId, int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); + PlaylistEntity? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId); if (targetPlaylist == null) return this.BadRequest(); if (token.UserId != targetPlaylist.CreatorId) return this.BadRequest(); @@ -65,7 +67,7 @@ public class CollectionController : ControllerBase return this.Ok(this.GetUserPlaylists(token.UserId)); } - Playlist? newPlaylist = await this.DeserializeBody("playlist", "levels"); + GamePlaylist? newPlaylist = await this.DeserializeBody("playlist", "levels"); if (newPlaylist == null) return this.BadRequest(); @@ -97,41 +99,48 @@ public class CollectionController : ControllerBase return this.Ok(this.GetUserPlaylists(token.UserId)); } - private string GetUserPlaylists(int userId) + private async Task GetUserPlaylists(int userId) { - string response = Enumerable.Aggregate( - this.database.Playlists.Include(p => p.Creator).Where(p => p.CreatorId == userId), - string.Empty, - (current, slot) => current + slot.Serialize()); + List playlists = await this.database.Playlists.Include(p => p.Creator) + .Where(p => p.CreatorId == userId) + .Select(p => GamePlaylist.CreateFromEntity(p)) + .ToListAsync(); int total = this.database.Playlists.Count(p => p.CreatorId == userId); - return LbpSerializer.TaggedStringElement("playlists", response, new Dictionary + return new PlaylistResponse { - {"total", total}, - {"hint_start", total+1}, - }); + Playlists = playlists, + Total = total, + HintStart = total+1, + }; } [HttpPost("playlists")] public async Task CreatePlaylist() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); int playlistCount = await this.database.Playlists.CountAsync(p => p.CreatorId == token.UserId); if (playlistCount > ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota) return this.BadRequest(); - Playlist? playlist = await this.DeserializeBody("playlist"); + GamePlaylist? playlist = await this.DeserializeBody("playlist"); if (playlist == null) return this.BadRequest(); - playlist.CreatorId = token.UserId; + PlaylistEntity playlistEntity = new() + { + CreatorId = token.UserId, + Description = playlist.Description, + Name = playlist.Name, + SlotIds = playlist.SlotIds, + }; - this.database.Playlists.Add(playlist); + this.database.Playlists.Add(playlistEntity); await this.database.SaveChangesAsync(); - return this.Ok(this.GetUserPlaylists(token.UserId)); + return this.Ok(await this.GetUserPlaylists(token.UserId)); } [HttpGet("user/{username}/playlists")] @@ -140,101 +149,60 @@ public class CollectionController : ControllerBase int targetUserId = await this.database.UserIdFromUsername(username); if (targetUserId == 0) return this.BadRequest(); - return this.Ok(this.GetUserPlaylists(targetUserId)); + return this.Ok(await this.GetUserPlaylists(targetUserId)); } [HttpGet("searches")] [HttpGet("genres")] public async Task GenresAndSearches() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); - string categoriesSerialized = CategoryHelper.Categories.Aggregate - ( - string.Empty, - (current, category) => - { - string serialized; + List categories = new(); - if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user); - else serialized = category.Serialize(this.database); + foreach (Category category in CategoryHelper.Categories.ToList()) + { + if(category is CategoryWithUser categoryWithUser) categories.Add(categoryWithUser.Serialize(this.database, user)); + else categories.Add(category.Serialize(this.database)); + } - return current + serialized; - } - ); - - categoriesSerialized += LbpSerializer.StringElement("text_search", LbpSerializer.StringElement("url", "/slots/searchLBP3")); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ( - "categories", - categoriesSerialized, - new Dictionary - { - { - "hint", "" - }, - { - "hint_start", 1 - }, - { - "total", CategoryHelper.Categories.Count - }, - } - ) - ); + return this.Ok(new CategoryListResponse(categories, CategoryHelper.Categories.Count, 0, 1)); } [HttpGet("searches/{endpointName}")] public async Task GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); if (category == null) return this.NotFound(); Logger.Debug("Found category " + category, LogArea.Category); - List slots; + List slots; int totalSlots; if (category is CategoryWithUser categoryWithUser) { - slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList(); + slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToList(); totalSlots = categoryWithUser.GetTotalSlots(this.database, user); } else { - slots = category.GetSlots(this.database, pageStart, pageSize).ToList(); + slots = category.GetSlots(this.database, pageStart, pageSize) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToList(); totalSlots = category.GetTotalSlots(this.database); } - string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); - - return this.Ok - ( - LbpSerializer.TaggedStringElement - ( - "results", - slotsSerialized, - new Dictionary - { - { - "total", totalSlots - }, - { - "hint_start", pageStart + pageSize - }, - } - ) - ); + return this.Ok(new GenericSlotResponse("results", slots, totalSlots, pageStart + pageSize)); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs index 2e7b9b47..2b069099 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs @@ -43,9 +43,9 @@ public class LevelTagsController : ControllerBase [HttpPost("tag/{slotType}/{id:int}")] public async Task PostTag([FromForm(Name = "t")] string tagName, [FromRoute] string slotType, [FromRoute] int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync(); + SlotEntity? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync(); if (slot == null) return this.BadRequest(); if (!LabelHelper.IsValidTag(tagName)) return this.BadRequest(); @@ -56,7 +56,7 @@ public class LevelTagsController : ControllerBase if (slotType != "user") return this.BadRequest(); - RatedLevel? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId); + RatedLevelEntity? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId); if (rating == null) return this.BadRequest(); rating.TagLBP1 = tagName; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs index 559e2a60..e390149a 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs @@ -2,12 +2,12 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -43,35 +43,27 @@ public class ListController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - GameVersion gameVersion = token.GameVersion; - - IEnumerable queuedLevels = this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.Queue) + List queuedLevels = await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.Queue) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); + .Select(s => SlotBase.CreateFromEntity(s, token)).ToListAsync(); - string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Serialize(gameVersion)); + int total = await this.database.QueuedLevels.CountAsync(q => q.UserId == token.UserId); + int start = pageStart + Math.Min(pageSize, 30); - return this.Ok - ( - LbpSerializer.TaggedStringElement("slots", response, new Dictionary - { - { "total", await this.database.QueuedLevels.CountAsync(q => q.UserId == token.UserId) }, - { "hint_start", pageStart + Math.Min(pageSize, 30) }, - }) - ); + return this.Ok(new GenericSlotResponse(queuedLevels, total, start)); } [HttpPost("lolcatftw/add/user/{id:int}")] public async Task AddQueuedLevel(int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); await this.database.QueueLevel(token.UserId, slot); @@ -82,9 +74,9 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/remove/user/{id:int}")] public async Task RemoveQueuedLevel(int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); await this.database.UnqueueLevel(token.UserId, slot); @@ -95,7 +87,7 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/clear")] public async Task ClearQueuedLevels() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == token.UserId)); @@ -120,30 +112,23 @@ public class ListController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - GameVersion gameVersion = token.GameVersion; + UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (targetUser == null) return this.Forbid(); - User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (targetUser == null) return this.StatusCode(403, ""); - - IEnumerable heartedLevels = this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.FavouriteSlots) + List heartedLevels = await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.FavouriteSlots) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Serialize(gameVersion)); + int total = await this.database.HeartedLevels.CountAsync(q => q.UserId == targetUser.UserId); + int start = pageStart + Math.Min(pageSize, 30); - return this.Ok - ( - LbpSerializer.TaggedStringElement("favouriteSlots", response, new Dictionary - { - { "total", await this.database.HeartedLevels.CountAsync(q => q.UserId == targetUser.UserId) }, - { "hint_start", pageStart + Math.Min(pageSize, 30) }, - }) - ); + return this.Ok(new GenericSlotResponse("favouriteSlots", heartedLevels, total, start)); } private const int FirstLbp2DeveloperSlotId = 124806; // This is the first known level slot GUID in LBP2. Feel free to change it if a lower one is found. @@ -151,13 +136,13 @@ public class ListController : ControllerBase [HttpPost("favourite/slot/{slotType}/{id:int}")] public async Task AddFavouriteSlot(string slotType, int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); if (slotType == "developer") @@ -174,13 +159,13 @@ public class ListController : ControllerBase [HttpPost("unfavourite/slot/{slotType}/{id:int}")] public async Task RemoveFavouriteSlot(string slotType, int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); if (slotType == "developer") @@ -204,29 +189,31 @@ public class ListController : ControllerBase if (pageSize <= 0) return this.BadRequest(); int targetUserId = await this.database.UserIdFromUsername(username); - if (targetUserId == 0) return this.StatusCode(403, ""); + if (targetUserId == 0) return this.Forbid(); - IEnumerable heartedPlaylists = this.database.HeartedPlaylists.Where(p => p.UserId == targetUserId) - .Include(p => p.Playlist).Include(p => p.Playlist.Creator).OrderByDescending(p => p.HeartedPlaylistId).Select(p => p.Playlist); + List heartedPlaylists = await this.database.HeartedPlaylists.Where(p => p.UserId == targetUserId) + .Include(p => p.Playlist) + .Include(p => p.Playlist.Creator) + .OrderByDescending(p => p.HeartedPlaylistId) + .Select(p => p.Playlist) + .Select(p => GamePlaylist.CreateFromEntity(p)) + .ToListAsync(); - string response = heartedPlaylists.Aggregate(string.Empty, (current, p) => current + p.Serialize()); + int total = await this.database.HeartedPlaylists.CountAsync(p => p.UserId == targetUserId); - return this.Ok - ( - LbpSerializer.TaggedStringElement("favouritePlaylists", response, new Dictionary - { - { "total", this.database.HeartedPlaylists.Count(p => p.UserId == targetUserId) }, - { "hint_start", pageStart + Math.Min(pageSize, 30) }, - }) - ); + return this.Ok(new GenericPlaylistResponse("favouritePlaylists", heartedPlaylists) + { + Total = total, + HintStart = pageStart + Math.Min(pageSize, 30), + }); } [HttpPost("favourite/playlist/{playlistId:int}")] public async Task AddFavouritePlaylist(int playlistId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); + PlaylistEntity? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); if (playlist == null) return this.NotFound(); await this.database.HeartPlaylist(token.UserId, playlist); @@ -237,9 +224,9 @@ public class ListController : ControllerBase [HttpPost("unfavourite/playlist/{playlistId:int}")] public async Task RemoveFavouritePlaylist(int playlistId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); + PlaylistEntity? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); if (playlist == null) return this.NotFound(); await this.database.UnheartPlaylist(token.UserId, playlist); @@ -256,40 +243,34 @@ public class ListController : ControllerBase [HttpGet("favouriteUsers/{username}")] public async Task GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (targetUser == null) return this.StatusCode(403, ""); + UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (targetUser == null) return this.Forbid(); if (pageSize <= 0) return this.BadRequest(); - IEnumerable heartedProfiles = this.database.HeartedProfiles.Include - (q => q.HeartedUser) - .OrderBy(q => q.HeartedProfileId) - .Where(q => q.UserId == targetUser.UserId) - .Select(q => q.HeartedUser) + List heartedProfiles = await this.database.HeartedProfiles.Include + (h => h.HeartedUser) + .OrderBy(h => h.HeartedProfileId) + .Where(h => h.UserId == targetUser.UserId) + .Select(h => h.HeartedUser) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) - .AsEnumerable(); + .Select(h => GameUser.CreateFromEntity(h, token.GameVersion)) + .ToListAsync(); - string response = heartedProfiles.Aggregate(string.Empty, (current, u) => current + u.Serialize(token.GameVersion)); + int total = await this.database.HeartedProfiles.CountAsync(h => h.UserId == targetUser.UserId); - return this.Ok - ( - LbpSerializer.TaggedStringElement("favouriteUsers", response, new Dictionary - { - { "total", await this.database.HeartedProfiles.CountAsync(q => q.UserId == targetUser.UserId) }, - { "hint_start", pageStart + Math.Min(pageSize, 30) }, - }) - ); + return this.Ok(new GenericUserResponse("favouriteUsers", heartedProfiles, total, pageStart + Math.Min(pageSize, 30))); } [HttpPost("favourite/user/{username}")] public async Task AddFavouriteUser(string username) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); await this.database.HeartUser(token.UserId, heartedUser); @@ -300,9 +281,9 @@ public class ListController : ControllerBase [HttpPost("unfavourite/user/{username}")] public async Task RemoveFavouriteUser(string username) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); await this.database.UnheartUser(token.UserId, heartedUser); @@ -313,7 +294,7 @@ public class ListController : ControllerBase #endregion #region Filtering - enum ListFilterType // used to collapse code that would otherwise be two separate functions + internal enum ListFilterType // used to collapse code that would otherwise be two separate functions { Queue, FavouriteSlots, @@ -337,7 +318,7 @@ public class ListController : ControllerBase }; } - private IQueryable filterListByRequest(string? gameFilterType, string? dateFilterType, GameVersion version, string username, ListFilterType filterType) + private IQueryable filterListByRequest(string? gameFilterType, string? dateFilterType, GameVersion version, string username, ListFilterType filterType) { if (version is GameVersion.LittleBigPlanetPSP or GameVersion.Unknown) { @@ -358,7 +339,7 @@ public class ListController : ControllerBase if (filterType == ListFilterType.Queue) { - IQueryable whereQueuedLevels; + IQueryable whereQueuedLevels; // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (gameFilterType == "both") @@ -374,7 +355,7 @@ public class ListController : ControllerBase return whereQueuedLevels.OrderByDescending(q => q.QueuedLevelId).Include(q => q.Slot.Creator).Select(q => q.Slot).ByGameVersion(gameVersion, false, false, true); } - IQueryable whereHeartedLevels; + IQueryable whereHeartedLevels; // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (gameFilterType == "both") diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index c81ead07..aad6d46a 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -5,12 +5,12 @@ using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Logging; using LBPUnion.ProjectLighthouse.Types.Resources; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -37,12 +37,12 @@ public class PublishController : ControllerBase [HttpPost("startPublish")] public async Task StartPublish() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); - Slot? slot = await this.DeserializeBody(); + GameUserSlot? slot = await this.DeserializeBody(); if (slot == null) { Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish); @@ -55,7 +55,7 @@ public class PublishController : ControllerBase return this.BadRequest(); } - if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel; + if (slot.Resources?.Length == 0) slot.Resources = new[]{slot.RootLevel,}; if (slot.Resources == null) { @@ -63,10 +63,12 @@ public class PublishController : ControllerBase return this.BadRequest(); } + int usedSlots = await this.database.Slots.CountAsync(s => s.CreatorId == token.UserId && s.GameVersion == token.GameVersion); + // Republish logic if (slot.SlotId != 0) { - Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + SlotEntity? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); if (oldSlot == null) { Logger.Warn("Rejecting level republish, could not find old slot", LogArea.Publish); @@ -78,18 +80,18 @@ public class PublishController : ControllerBase return this.BadRequest(); } } - else if (user.GetUsedSlotsForGame(token.GameVersion) > user.EntitledSlots) + else if (usedSlots > user.EntitledSlots) { - return this.StatusCode(403, ""); + return this.Forbid(); } - slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here + HashSet resources = new(slot.Resources) + { + slot.IconHash, + }; + resources = resources.Where(hash => !FileHelper.ResourceExists(hash)).ToHashSet(); - 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")); + return this.Ok(new SlotResourceResponse(resources.ToList())); } /// @@ -98,12 +100,12 @@ public class PublishController : ControllerBase [HttpPost("publish")] public async Task Publish([FromQuery] string? game) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); - Slot? slot = await this.DeserializeBody(); + GameUserSlot? slot = await this.DeserializeBody(); if (slot == null) { @@ -111,6 +113,12 @@ public class PublishController : ControllerBase return this.BadRequest(); } + if (slot.Resources == null) + { + Logger.Warn("Rejecting level upload, resource list is null", LogArea.Publish); + return this.BadRequest(); + } + slot.Description = CensorHelper.FilterMessage(slot.Description); if (slot.Description.Length > 512) @@ -168,7 +176,7 @@ public class PublishController : ControllerBase // Republish logic if (slot.SlotId != 0) { - Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + SlotEntity? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); if (oldSlot == null) { Logger.Warn("Rejecting level republish, wasn't able to find old slot", LogArea.Publish); @@ -190,36 +198,32 @@ public class PublishController : ControllerBase // Delete the useless rootLevel that lbp3 just uploaded if (slotVersion == GameVersion.LittleBigPlanet3) FileHelper.DeleteResource(slot.RootLevel); - - slot.GameVersion = oldSlot.GameVersion; - slot.RootLevel = oldSlot.RootLevel; - slot.ResourceCollection = oldSlot.ResourceCollection; + else + { + oldSlot.GameVersion = slot.GameVersion; + oldSlot.RootLevel = slot.RootLevel; + oldSlot.Resources = slot.Resources; + } } } - slot.CreatorId = oldSlot.CreatorId; - slot.SlotId = oldSlot.SlotId; + oldSlot.Name = slot.Name; + oldSlot.Description = slot.Description; + oldSlot.Location = slot.Location; + oldSlot.IconHash = slot.IconHash; + oldSlot.BackgroundHash = slot.BackgroundHash; + oldSlot.AuthorLabels = slot.AuthorLabels; + oldSlot.Shareable = slot.IsShareable; + oldSlot.Resources = slot.Resources; + oldSlot.InitiallyLocked = slot.InitiallyLocked; + oldSlot.Lbp1Only = slot.IsLbp1Only; + oldSlot.IsAdventurePlanet = slot.IsAdventurePlanet; + oldSlot.LevelType = slot.LevelType; + oldSlot.SubLevel = slot.IsSubLevel; + oldSlot.MoveRequired = slot.IsMoveRequired; + oldSlot.CrossControllerRequired = slot.IsCrossControlRequired; - #region Set plays - - 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.PlaysLBP3 = oldSlot.PlaysLBP3; - slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete; - slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique; - - #endregion - - slot.FirstUploaded = oldSlot.FirstUploaded; - slot.LastUpdated = TimeHelper.TimestampMillis; - - slot.TeamPick = oldSlot.TeamPick; + oldSlot.LastUpdated = TimeHelper.TimestampMillis; if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) { @@ -227,20 +231,23 @@ public class PublishController : ControllerBase slot.MaximumPlayers = 4; } - slot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4); - slot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4); + oldSlot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4); + oldSlot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4); - this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); - return this.Ok(oldSlot.Serialize(token.GameVersion)); + return this.Ok(SlotBase.CreateFromEntity(oldSlot, this.GetToken())); } - if (user.GetUsedSlotsForGame(slotVersion) > user.EntitledSlots) + int usedSlots = await this.database.Slots.CountAsync(s => s.CreatorId == token.UserId && s.GameVersion == slotVersion); + + if (usedSlots > user.EntitledSlots) { Logger.Warn("Rejecting level upload, too many published slots", LogArea.Publish); return this.BadRequest(); } + SlotEntity slotEntity = SlotBase.ConvertToEntity(slot); + slot.CreatorId = user.UserId; slot.FirstUploaded = TimeHelper.TimestampMillis; slot.LastUpdated = TimeHelper.TimestampMillis; @@ -254,7 +261,7 @@ public class PublishController : ControllerBase slot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4); slot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4); - this.database.Slots.Add(slot); + this.database.Slots.Add(slotEntity); await this.database.SaveChangesAsync(); if (user.LevelVisibility == PrivacyType.All) @@ -265,18 +272,18 @@ public class PublishController : ControllerBase Logger.Success($"Successfully published level {slot.Name} (id: {slot.SlotId}) by {user.Username} (id: {user.UserId})", LogArea.Publish); - return this.Ok(slot.Serialize(token.GameVersion)); + return this.Ok(SlotBase.CreateFromEntity(slotEntity, this.GetToken())); } [HttpPost("unpublish/{id:int}")] public async Task Unpublish(int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - if (slot.CreatorId != token.UserId) return this.StatusCode(403, ""); + if (slot.CreatorId != token.UserId) return this.Forbid(); this.database.Slots.Remove(slot); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs index 746252eb..8bdee6b6 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs @@ -2,10 +2,10 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Token; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -30,15 +30,15 @@ public class ReviewController : ControllerBase [HttpPost("rate/user/{slotId:int}")] public async Task Rate(int slotId, [FromQuery] int rating) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); + SlotEntity? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.Forbid(); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); + RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { - ratedLevel = new RatedLevel + ratedLevel = new RatedLevelEntity { SlotId = slotId, UserId = token.UserId, @@ -59,15 +59,15 @@ public class ReviewController : ControllerBase [HttpPost("dpadrate/user/{slotId:int}")] public async Task DPadRate(int slotId, [FromQuery] int rating) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); - if (slot == null) return this.StatusCode(403, ""); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.Forbid(); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); + RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { - ratedLevel = new RatedLevel + ratedLevel = new RatedLevelEntity { SlotId = slotId, UserId = token.UserId, @@ -79,7 +79,7 @@ public class ReviewController : ControllerBase ratedLevel.Rating = Math.Clamp(rating, -1, 1); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId); + ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId); if (review != null) review.Thumb = ratedLevel.Rating; await this.database.SaveChangesAsync(); @@ -90,20 +90,20 @@ public class ReviewController : ControllerBase [HttpPost("postReview/user/{slotId:int}")] public async Task PostReview(int slotId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - Review? newReview = await this.DeserializeBody(); + GameReview? newReview = await this.DeserializeBody(); if (newReview == null) return this.BadRequest(); newReview.Text = CensorHelper.FilterMessage(newReview.Text); if (newReview.Text.Length > 512) return this.BadRequest(); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId); + ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId); if (review == null) { - review = new Review + review = new ReviewEntity { SlotId = slotId, ReviewerId = token.UserId, @@ -121,10 +121,10 @@ public class ReviewController : ControllerBase review.Timestamp = TimeHelper.TimestampMillis; // 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 == token.UserId); + RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { - ratedLevel = new RatedLevel + ratedLevel = new RatedLevelEntity { SlotId = slotId, UserId = token.UserId, @@ -144,58 +144,32 @@ public class ReviewController : ControllerBase [HttpGet("reviewsFor/user/{slotId:int}")] public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.BadRequest(); - IQueryable reviews = this.database.Reviews.ByGameVersion(gameVersion, true) + List reviews = await this.database.Reviews.ByGameVersion(gameVersion, true) .Where(r => r.SlotId == slotId) - .Include(r => r.Reviewer) - .Include(r => r.Slot) .OrderByDescending(r => r.ThumbsUp - r.ThumbsDown) .ThenByDescending(r => r.Timestamp) .Skip(Math.Max(0, pageStart - 1)) - .Take(pageSize); + .Take(Math.Min(pageSize, 30)) + .Select(r => GameReview.CreateFromEntity(r, token)) + .ToListAsync(); - List reviewList = reviews.ToList(); - string inner = reviewList.Aggregate - ( - string.Empty, - (current, review) => - { - if (review == null) return current; - - RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); - return current + review.Serialize(yourThumb); - } - ); - string response = LbpSerializer.TaggedStringElement - ( - "reviews", - inner, - new Dictionary - { - { - "hint_start", pageStart + pageSize - }, - { - "hint", reviewList.LastOrDefault()?.Timestamp ?? 0 - }, - } - ); - return this.Ok(response); + return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart + Math.Min(pageSize, 30))); } [HttpGet("reviewsBy/{username}")] public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -205,61 +179,32 @@ public class ReviewController : ControllerBase if (targetUserId == 0) return this.BadRequest(); - IEnumerable reviews = this.database.Reviews.ByGameVersion(gameVersion, true) - .Include(r => r.Reviewer) - .Include(r => r.Slot) + List reviews = await this.database.Reviews.ByGameVersion(gameVersion, true) .Where(r => r.ReviewerId == targetUserId) .OrderByDescending(r => r.Timestamp) .Skip(Math.Max(0, pageStart - 1)) - .Take(pageSize); + .Take(Math.Min(pageSize, 30)) + .Select(r => GameReview.CreateFromEntity(r, token)) + .ToListAsync(); - List reviewList = reviews.ToList(); - - string inner = reviewList.Aggregate - ( - string.Empty, - (current, review) => - { - if (review == null) return current; - - RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); - return current + review.Serialize(ratedReview); - } - ); - - string response = LbpSerializer.TaggedStringElement - ( - "reviews", - inner, - new Dictionary - { - { - "hint_start", pageStart - }, - { - "hint", reviewList.LastOrDefault()?.Timestamp ?? 0 // Seems to be the timestamp of oldest - }, - } - ); - - return this.Ok(response); + return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart)); } [HttpPost("rateReview/user/{slotId:int}/{username}")] public async Task RateReview(int slotId, string username, [FromQuery] int rating = 0) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); int reviewerId = await this.database.UserIdFromUsername(username); - if (reviewerId == 0) return this.StatusCode(400, ""); + if (reviewerId == 0) return this.BadRequest(); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); - if (review == null) return this.StatusCode(400, ""); + ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); + if (review == null) return this.BadRequest(); - RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); + RatedReviewEntity? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); if (ratedReview == null) { - ratedReview = new RatedReview + ratedReview = new RatedReviewEntity { ReviewId = review.ReviewId, UserId = token.UserId, @@ -301,18 +246,18 @@ public class ReviewController : ControllerBase [HttpPost("deleteReview/user/{slotId:int}/{username}")] public async Task DeleteReview(int slotId, string username) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync(); - if (creatorId == 0) return this.StatusCode(400, ""); + if (creatorId == 0) return this.BadRequest(); - if (token.UserId != creatorId) return this.StatusCode(403, ""); + if (token.UserId != creatorId) return this.Unauthorized(); int reviewerId = await this.database.UserIdFromUsername(username); - if (reviewerId == 0) return this.StatusCode(400, ""); + if (reviewerId == 0) return this.BadRequest(); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); - if (review == null) return this.StatusCode(400, ""); + ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); + if (review == null) return this.BadRequest(); review.Deleted = true; review.DeletedBy = DeletedBy.LevelAuthor; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs index cd9faed1..15f7f02d 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs @@ -4,12 +4,12 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.StorableLists.Stores; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Logging; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -34,7 +34,7 @@ public class ScoreController : ControllerBase [HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")] public async Task SubmitScore(string slotType, int id, int childId) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); string username = await this.database.UsernameFromGameToken(token); @@ -44,16 +44,15 @@ public class ScoreController : ControllerBase return this.BadRequest(); } - Score? score = await this.DeserializeBody(); + GameScore? score = await this.DeserializeBody(); if (score == null) { Logger.Warn($"Rejecting score upload, score is null (slotType={slotType}, slotId={id}, user={username})", LogArea.Score); return this.BadRequest(); } - // This only seems to happens on lbp2 versus levels, not sure why - if (score.PlayerIdCollection.Contains(':')) - score.PlayerIdCollection = score.PlayerIdCollection.Replace(':', ','); + // Workaround for parsing player ids of versus levels + if (score.PlayerIds.Length == 1 && score.PlayerIds[0].Contains(':')) score.PlayerIds = score.PlayerIds[0].Split(":"); if (score.PlayerIds.Length == 0) { @@ -89,15 +88,14 @@ public class ScoreController : ControllerBase SanitizationHelper.SanitizeStringsInClass(score); - if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); + int slotId = id; - score.SlotId = id; - score.ChildSlotId = childId; + if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); - Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) { - Logger.Warn($"Rejecting score upload, slot is null (slotId={score.SlotId}, slotType={slotType}, reqId={id}, user={username})", LogArea.Score); + Logger.Warn($"Rejecting score upload, slot is null (slotId={slotId}, slotType={slotType}, reqId={id}, user={username})", LogArea.Score); return this.BadRequest(); } @@ -115,46 +113,56 @@ public class ScoreController : ControllerBase break; case GameVersion.LittleBigPlanetPSP: case GameVersion.Unknown: - default: throw new ArgumentOutOfRangeException(); + default: + return this.BadRequest(); } - Score playerScore = new() - { - PlayerIdCollection = string.Join(',', score.PlayerIds), - Type = score.Type, - Points = score.Points, - SlotId = score.SlotId, - ChildSlotId = score.ChildSlotId, - }; + await this.database.SaveChangesAsync(); - IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == playerScore.SlotId) + string playerIdCollection = string.Join(',', score.PlayerIds); + + ScoreEntity? existingScore = await this.database.Scores.Where(s => s.SlotId == slot.SlotId) .Where(s => s.ChildSlotId == 0 || s.ChildSlotId == childId) - .Where(s => s.PlayerIdCollection == playerScore.PlayerIdCollection) - .Where(s => s.Type == playerScore.Type); - if (existingScore.Any()) + .Where(s => s.PlayerIdCollection == playerIdCollection) + .Where(s => s.Type == score.Type) + .FirstOrDefaultAsync(); + if (existingScore != null) { - Score first = existingScore.First(s => s.SlotId == playerScore.SlotId); - playerScore.ScoreId = first.ScoreId; - playerScore.Points = Math.Max(first.Points, playerScore.Points); - this.database.Entry(first).CurrentValues.SetValues(playerScore); + existingScore.Points = Math.Max(existingScore.Points, score.Points); } else { + ScoreEntity playerScore = new() + { + PlayerIdCollection = playerIdCollection, + Type = score.Type, + Points = score.Points, + SlotId = slotId, + ChildSlotId = childId, + }; this.database.Scores.Add(playerScore); } await this.database.SaveChangesAsync(); - string myRanking = this.getScores(score.SlotId, score.Type, username, -1, 5, "scoreboardSegment", childId: score.ChildSlotId); - - return this.Ok(myRanking); + return this.Ok(this.getScores(new LeaderboardOptions + { + RootName = "scoreboardSegment", + PageSize = 5, + PageStart = -1, + SlotId = slotId, + ChildSlotId = childId, + ScoreType = score.Type, + TargetUsername = username, + TargetPlayerIds = null, + })); } [HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")] [HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")] public async Task FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -180,7 +188,17 @@ public class ScoreController : ControllerBase if (friendUsername != null) friendNames.Add(friendUsername); } - return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, "scores", friendNames.ToArray(), childId)); + return this.Ok(this.getScores(new LeaderboardOptions + { + RootName = "scores", + PageSize = pageSize, + PageStart = pageStart, + SlotId = slotId, + ChildSlotId = childId, + ScoreType = type, + TargetUsername = username, + TargetPlayerIds = friendNames.ToArray(), + })); } [HttpGet("topscores/{slotType}/{slotId:int}/{type:int}")] @@ -188,7 +206,7 @@ public class ScoreController : ControllerBase [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public async Task TopScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -198,79 +216,60 @@ public class ScoreController : ControllerBase if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); - return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, childId: childId)); + return this.Ok(this.getScores(new LeaderboardOptions + { + RootName = "scores", + PageSize = pageSize, + PageStart = pageStart, + SlotId = slotId, + ChildSlotId = childId, + ScoreType = type, + TargetUsername = username, + TargetPlayerIds = null, + })); } - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private string getScores - ( - int slotId, - int type, - string username, - int pageStart = -1, - int pageSize = 5, - string rootName = "scores", - string[]? playerIds = null, - int? childId = 0 - ) + private class LeaderboardOptions + { + public int SlotId { get; set; } + public int ScoreType { get; set; } + public string TargetUsername { get; set; } = ""; + public int PageStart { get; set; } = -1; + public int PageSize { get; set; } = 5; + public string RootName { get; set; } = "scores"; + public string[]? TargetPlayerIds; + public int? ChildSlotId; + } + + private ScoreboardResponse getScores(LeaderboardOptions options) { // 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) - .Where(s => s.ChildSlotId == 0 || s.ChildSlotId == childId) + var rankedScores = this.database.Scores.Where(s => s.SlotId == options.SlotId && s.Type == options.ScoreType) + .Where(s => s.ChildSlotId == 0 || s.ChildSlotId == options.ChildSlotId) .AsEnumerable() - .Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Split(",").Contains(id))) + .Where(s => options.TargetPlayerIds == null || + options.TargetPlayerIds.Any(id => s.PlayerIdCollection.Split(",").Contains(id))) .OrderByDescending(s => s.Points) .ThenBy(s => s.ScoreId) .ToList() - .Select - ( - (s, rank) => new - { - Score = s, - Rank = rank + 1, - } - ); + .Select((s, rank) => new + { + Score = s, + Rank = rank + 1, + }) + .ToList(); + // 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.Split(",").Contains(username)).MaxBy(rs => rs.Score.Points); + var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Split(",").Contains(options.TargetUsername)).MaxBy(rs => rs.Score.Points); // 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)); + var pagedScores = rankedScores.Skip(options.PageStart != -1 || myScore == null ? options.PageStart - 1 : myScore.Rank - 3).Take(Math.Min(options.PageSize, 30)); - string serializedScores = pagedScores.Aggregate - ( - string.Empty, - (current, rs) => - { - rs.Score.Rank = rs.Rank; - return current + rs.Score.Serialize(); - } - ); + List gameScores = pagedScores.Select(ps => GameScore.CreateFromEntity(ps.Score, ps.Rank)).ToList(); - string res; - if (myScore == null) res = LbpSerializer.StringElement(rootName, serializedScores); - else - res = LbpSerializer.TaggedStringElement - ( - rootName, - 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; + return new ScoreboardResponse(options.RootName, gameScores, myScore?.Score.Points ?? 0, myScore?.Rank ?? 0, rankedScores.Count); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs index 3773f21d..a31aa0c1 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs @@ -1,10 +1,10 @@ #nullable enable using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -35,7 +35,7 @@ public class SearchController : ControllerBase string? keyName = "slots" ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -45,7 +45,7 @@ public class SearchController : ControllerBase string[] keywords = query.Split(" "); - IQueryable dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true) + IQueryable dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true) .Where(s => s.Type == SlotType.User) .OrderBy(s => !s.TeamPick) .ThenByDescending(s => s.FirstUploaded) @@ -61,11 +61,12 @@ public class SearchController : ControllerBase s.SlotId.ToString().Equals(keyword) ); - List slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)).Take(Math.Min(pageSize, 30)).ToListAsync(); + List slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)) + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, this.GetToken())) + .ToListAsync(); - string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(token.GameVersion)); - - return this.Ok(LbpSerializer.TaggedStringElement(keyName, response, "total", dbQuery.Count())); + return this.Ok(new GenericSlotResponse(keyName, slots, await dbQuery.CountAsync(), 0)); } // /LITTLEBIGPLANETPS3_XML?pageStart=1&pageSize=10&resultTypes[]=slot&resultTypes[]=playlist&resultTypes[]=user&adventure=dontCare&textFilter=qwer diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs index 3a8f9047..d9e5731b 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs @@ -1,14 +1,14 @@ #nullable enable +using System.Security.Cryptography; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -28,113 +28,102 @@ public class SlotsController : ControllerBase this.database = database; } - private static string generateSlotsResponse(string slotAggregate, int start, int total) => - LbpSerializer.TaggedStringElement("slots", - slotAggregate, - new Dictionary - { - { - "hint_start", start - }, - { - "total", total - }, - }); - [HttpGet("slots/by")] - public async Task SlotsBy([FromQuery(Name="u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize) + public async Task SlotsBy([FromQuery(Name = "u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - GameVersion gameVersion = token.GameVersion; - int targetUserId = await this.database.UserIdFromUsername(username); if (targetUserId == 0) return this.NotFound(); int usedSlots = this.database.Slots.Count(s => s.CreatorId == targetUserId); - string response = Enumerable.Aggregate - ( - this.database.Slots.Where(s => s.CreatorId == targetUserId) - .ByGameVersion(gameVersion, token.UserId == targetUserId, true) - .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, usedSlots)), - string.Empty, - (current, slot) => current + slot.Serialize(token.GameVersion) - ); + List slots = await this.database.Slots.Where(s => s.CreatorId == targetUserId) + .ByGameVersion(token.GameVersion, token.UserId == targetUserId) + .Skip(Math.Max(0, pageStart - 1)) + .Take(Math.Min(pageSize, usedSlots)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); + int start = pageStart + Math.Min(pageSize, usedSlots); int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId); - return this.Ok(generateSlotsResponse(response, start, total)); + + return this.Ok(new GenericSlotResponse("slots", slots, total, start)); } [HttpGet("slotList")] - public async Task GetSlotListAlt([FromQuery] int[] s) + public async Task GetSlotListAlt([FromQuery(Name = "s")] int[] slotIds) { - List serializedSlots = new(); - foreach (int slotId in s) + GameTokenEntity token = this.GetToken(); + + List slots = new(); + foreach (int slotId in slotIds) { - Slot? slot = await this.database.Slots.Include(t => t.Creator).Where(t => t.SlotId == slotId && t.Type == SlotType.User).FirstOrDefaultAsync(); + SlotEntity? slot = await this.database.Slots.Include(t => t.Creator).Where(t => t.SlotId == slotId && t.Type == SlotType.User).FirstOrDefaultAsync(); if (slot == null) { slot = await this.database.Slots.Where(t => t.InternalSlotId == slotId && t.Type == SlotType.Developer).FirstOrDefaultAsync(); if (slot == null) { - serializedSlots.Add($"{slotId}"); + slots.Add(new GameDeveloperSlot + { + SlotId = slotId, + }); continue; } } - serializedSlots.Add(slot.Serialize()); + + slots.Add(SlotBase.CreateFromEntity(slot, token)); } - string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => slot == null ? current : current + slot); - return this.Ok(LbpSerializer.TaggedStringElement("slots", serialized, "total", serializedSlots.Count)); + return this.Ok(new GenericSlotResponse(slots, slots.Count, 0)); } [HttpGet("slots/developer")] public async Task StoryPlayers() { + GameTokenEntity token = this.GetToken(); + List activeSlotIds = RoomHelper.Rooms.Where(r => r.Slot.SlotType == SlotType.Developer).Select(r => r.Slot.SlotId).ToList(); - List serializedSlots = new(); + List slots = new(); foreach (int id in activeSlotIds) { int placeholderSlotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); - Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == placeholderSlotId); - serializedSlots.Add(slot.SerializeDevSlot()); + SlotEntity slot = await this.database.Slots.FirstAsync(s => s.SlotId == placeholderSlotId); + + slots.Add(SlotBase.CreateFromEntity(slot, token)); } - string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => current + slot); - - return this.Ok(LbpSerializer.StringElement("slots", serialized)); + return this.Ok(new GenericSlotResponse(slots)); } [HttpGet("s/developer/{id:int}")] public async Task SDev(int id) { - int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); - Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId); + GameTokenEntity token = this.GetToken(); - return this.Ok(slot.SerializeDevSlot()); + int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer); + SlotEntity slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId); + + return this.Ok(SlotBase.CreateFromEntity(slot, token)); } [HttpGet("s/user/{id:int}")] public async Task SUser(int id) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); GameVersion gameVersion = token.GameVersion; - Slot? slot = await this.database.Slots.ByGameVersion(gameVersion, true, true).FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.ByGameVersion(gameVersion, true, true).FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == token.UserId); - VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == token.UserId); - Review? review = await this.database.Reviews.Include(r => r.Slot).FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == token.UserId); - return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review, true)); + return this.Ok(SlotBase.CreateFromEntity(slot, token, SerializationMode.Full)); } [HttpGet("slots/cool")] @@ -163,27 +152,28 @@ public class SlotsController : ControllerBase [HttpGet("slots")] public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) + List slots = await this.database.Slots.ByGameVersion(gameVersion, false, true) .OrderByDescending(s => s.FirstUploaded) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/like/{slotType}/{slotId:int}")] public async Task SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -191,10 +181,10 @@ public class SlotsController : ControllerBase GameVersion gameVersion = token.GameVersion; - Slot? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + SlotEntity? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (targetSlot == null) return this.BadRequest(); - string[] tags = targetSlot.LevelTags; + string[] tags = targetSlot.LevelTags(this.database); List slotIdsWithTag = this.database.RatedLevels .Where(r => r.TagLBP1.Length > 0) @@ -202,105 +192,108 @@ public class SlotsController : ControllerBase .Select(r => r.SlotId) .ToList(); - IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) + List slots = await this.database.Slots.ByGameVersion(gameVersion, false, true) .Where(s => slotIdsWithTag.Contains(s.SlotId)) .OrderByDescending(s => s.PlaysLBP1) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = slotIdsWithTag.Count; - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/highestRated")] public async Task HighestRatedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - IEnumerable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) - .AsEnumerable() + List slots = await this.database.Slots.ByGameVersion(gameVersion, false, true) + .ToAsyncEnumerable() .OrderByDescending(s => s.RatingLBP1) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCount(this.database); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/tag")] public async Task SimilarSlots([FromQuery] string tag, [FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - GameVersion gameVersion = token.GameVersion; - List slotIdsWithTag = await this.database.RatedLevels.Where(r => r.TagLBP1.Length > 0) .Where(r => r.TagLBP1 == tag) .Select(s => s.SlotId) .ToListAsync(); - IQueryable slots = this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId)) - .ByGameVersion(gameVersion, false, true) + List slots = await this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId)) + .ByGameVersion(token.GameVersion, false, true) .OrderByDescending(s => s.PlaysLBP1) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = slotIdsWithTag.Count; - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/mmpicks")] public async Task TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - GameVersion gameVersion = token.GameVersion; - - IQueryable slots = this.database.Slots.Where(s => s.TeamPick) - .ByGameVersion(gameVersion, false, true) + List slots = await this.database.Slots.Where(s => s.TeamPick) + .ByGameVersion(token.GameVersion, false, true) .OrderByDescending(s => s.LastUpdated) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); - string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.TeamPickCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/lbp2luckydip")] public async Task LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); GameVersion gameVersion = token.GameVersion; - IEnumerable slots = this.database.Slots.ByGameVersion(gameVersion, false, true).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30)); + List slots = await this.database.Slots.ByGameVersion(gameVersion, false, true) + .OrderBy(_ => EF.Functions.Random()) + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/thumbs")] @@ -314,24 +307,25 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); Random rand = new(); - IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) - .AsEnumerable() + List slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) + .AsAsyncEnumerable() .OrderByDescending(s => s.Thumbsup) .ThenBy(_ => rand.Next()) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/mostUniquePlays")] @@ -345,20 +339,19 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); Random rand = new(); - IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) - .AsEnumerable() - .OrderByDescending - ( + List slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) + .AsAsyncEnumerable() + .OrderByDescending( // probably not the best way to do this? s => { - return this.getGameFilter(gameFilterType, token.GameVersion) switch + return getGameFilter(gameFilterType, token.GameVersion) switch { GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, @@ -366,17 +359,17 @@ public class SlotsController : ControllerBase GameVersion.LittleBigPlanetVita => s.PlaysLBP2Unique, _ => s.PlaysUnique, }; - } - ) + }) .ThenBy(_ => rand.Next()) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } [HttpGet("slots/mostHearted")] @@ -390,24 +383,23 @@ public class SlotsController : ControllerBase [FromQuery] string? dateFilterType = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); - Random rand = new(); - - IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) - .AsEnumerable() + List slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) + .AsAsyncEnumerable() .OrderByDescending(s => s.Hearts) - .ThenBy(_ => rand.Next()) + .ThenBy(_ => RandomNumberGenerator.GetInt32(int.MaxValue)) .Skip(Math.Max(0, pageStart - 1)) - .Take(Math.Min(pageSize, 30)); + .Take(Math.Min(pageSize, 30)) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .ToListAsync(); - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } // /slots/busiest?pageStart=1&pageSize=30&gameFilterType=both&players=1&move=true @@ -421,7 +413,7 @@ public class SlotsController : ControllerBase [FromQuery] bool? move = null ) { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); if (pageSize <= 0) return this.BadRequest(); @@ -447,42 +439,45 @@ public class SlotsController : ControllerBase .OrderByDescending(kvp => kvp.Value) .Select(kvp => kvp.Key); - List slots = new(); + List slots = new(); foreach (int slotId in orderedPlayersBySlotId) { - Slot? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true) - .FirstOrDefaultAsync(s => s.SlotId == slotId); + SlotBase? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true) + .Where(s => s.SlotId == slotId) + .Select(s => SlotBase.CreateFromEntity(s, token)) + .FirstOrDefaultAsync(); if (slot == null) continue; // shouldn't happen ever unless the room is borked slots.Add(slot); } - string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int total = playersBySlotId.Count; - return this.Ok(generateSlotsResponse(response, start, total)); + return this.Ok(new GenericSlotResponse(slots, total, start)); } - private GameVersion getGameFilter(string? gameFilterType, GameVersion version) + private static GameVersion getGameFilter(string? gameFilterType, GameVersion version) { - if (version == GameVersion.LittleBigPlanetVita) return GameVersion.LittleBigPlanetVita; - if (version == GameVersion.LittleBigPlanetPSP) return GameVersion.LittleBigPlanetPSP; - - return gameFilterType switch + return version switch { - "lbp1" => GameVersion.LittleBigPlanet1, - "lbp2" => GameVersion.LittleBigPlanet2, - "lbp3" => GameVersion.LittleBigPlanet3, - "both" => GameVersion.LittleBigPlanet2, // LBP2 default option - null => GameVersion.LittleBigPlanet1, - _ => GameVersion.Unknown, + GameVersion.LittleBigPlanetVita => GameVersion.LittleBigPlanetVita, + GameVersion.LittleBigPlanetPSP => GameVersion.LittleBigPlanetPSP, + _ => gameFilterType switch + { + "lbp1" => GameVersion.LittleBigPlanet1, + "lbp2" => GameVersion.LittleBigPlanet2, + "lbp3" => GameVersion.LittleBigPlanet3, + "both" => GameVersion.LittleBigPlanet2, // LBP2 default option + null => GameVersion.LittleBigPlanet1, + _ => GameVersion.Unknown, + }, }; } - private IQueryable filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version) + private IQueryable filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version) { if (version == GameVersion.LittleBigPlanetVita || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) { @@ -498,9 +493,9 @@ public class SlotsController : ControllerBase _ => 0, }; - GameVersion gameVersion = this.getGameFilter(gameFilterType, version); + GameVersion gameVersion = getGameFilter(gameFilterType, version); - IQueryable whereSlots; + IQueryable whereSlots; // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (gameFilterType == "both") diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs index b8af87ff..da325e48 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs @@ -1,9 +1,9 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Types.Serialization; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; @@ -33,11 +33,7 @@ public class StatisticsController : ControllerBase int totalSlotCount = await StatisticsHelper.SlotCountForGame(this.database, this.GetToken().GameVersion); int mmPicksCount = await StatisticsHelper.TeamPickCountForGame(this.database, this.GetToken().GameVersion); - return this.Ok - ( - LbpSerializer.StringElement - ("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount)) - ); + return this.Ok(new PlanetStatsResponse(totalSlotCount, mmPicksCount)); } [HttpGet("planetStats/totalLevelCount")] diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs index 4e483aff..53d495e0 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs @@ -4,12 +4,14 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Logging; +using LBPUnion.ProjectLighthouse.Types.Serialization; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -30,57 +32,41 @@ public class UserController : ControllerBase this.database = database; } - private async Task getSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1) - { - User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - return user?.Serialize(gameVersion); - } - - private async Task getSerializedUserPicture(string username) - { - // use an anonymous type to only fetch certain columns - var partialUser = await this.database.Users.Where(u => u.Username == username) - .Select(u => new - { - u.Username, - u.IconHash, - }) - .FirstOrDefaultAsync(); - if (partialUser == null) return null; - - string user = LbpSerializer.TaggedStringElement("npHandle", partialUser.Username, "icon", partialUser.IconHash); - return LbpSerializer.TaggedStringElement("user", user, "type", "user"); - } - [HttpGet("user/{username}")] public async Task GetUser(string username) { - GameToken token = this.GetToken(); - - string? user = await this.getSerializedUser(username, token.GameVersion); + UserEntity? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) return this.NotFound(); - return this.Ok(user); + return this.Ok(GameUser.CreateFromEntity(user, this.GetToken().GameVersion)); } [HttpGet("users")] - public async Task GetUserAlt([FromQuery] string[] u) + public async Task GetUserAlt([FromQuery(Name = "u")] string[] userList) { - List serializedUsers = new(); - foreach (string userId in u) serializedUsers.Add(await this.getSerializedUserPicture(userId)); + List minimalUserList = new(); + foreach (string username in userList) + { + MinimalUserProfile? profile = await this.database.Users.Where(u => u.Username == username) + .Select(u => new MinimalUserProfile + { + UserHandle = new NpHandle(u.Username, u.IconHash), + }) + .FirstOrDefaultAsync(); + if (profile == null) continue; + minimalUserList.Add(profile); + } - string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user); - - return this.Ok(LbpSerializer.StringElement("users", serialized)); + return this.Ok(new MinimalUserListResponse(minimalUserList)); } [HttpPost("updateUser")] public async Task UpdateUser() { - GameToken token = this.GetToken(); + GameTokenEntity token = this.GetToken(); - User? user = await this.database.UserFromGameToken(token); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(token); + if (user == null) return this.Forbid(); UserUpdate? update = await this.DeserializeBody("updateUser", "user"); @@ -118,17 +104,17 @@ public class UserController : ControllerBase if (update.Slots != null) { + update.Slots = update.Slots.Where(s => s.Type == SlotType.User) + .Where(s => s.Location != null) + .Where(s => s.SlotId != 0).ToList(); foreach (UserUpdateSlot? updateSlot in update.Slots) { - // ReSharper disable once MergeIntoNegatedPattern - if (updateSlot.Type != SlotType.User || updateSlot.Location == null || updateSlot.SlotId == 0) continue; - - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId); if (slot == null) continue; if (slot.CreatorId != token.UserId) continue; - slot.Location = updateSlot.Location; + slot.Location = updateSlot.Location!; } } @@ -159,7 +145,11 @@ public class UserController : ControllerBase case GameVersion.Unknown: default: // The rest do not support custom earths. { - throw new ArgumentException($"invalid gameVersion {token.GameVersion} for setting earth"); + string bodyString = await this.ReadBodyAsync(); + Logger.Warn($"User with invalid gameVersion '{token.GameVersion}' tried to set earth hash: \n" + + $"body: '{bodyString}'", + LogArea.Resources); + break; } } } @@ -170,10 +160,11 @@ public class UserController : ControllerBase } [HttpPost("update_my_pins")] + [Produces("text/json")] public async Task UpdateMyPins() { - User? user = await this.database.UserFromGameToken(this.GetToken()); - if (user == null) return this.StatusCode(403, ""); + UserEntity? user = await this.database.UserFromGameToken(this.GetToken()); + if (user == null) return this.Forbid(); string bodyString = await this.ReadBodyAsync(); diff --git a/ProjectLighthouse.Servers.GameServer/Middlewares/SetLastContactMiddleware.cs b/ProjectLighthouse.Servers.GameServer/Middlewares/SetLastContactMiddleware.cs index a941b9d9..52a8b067 100644 --- a/ProjectLighthouse.Servers.GameServer/Middlewares/SetLastContactMiddleware.cs +++ b/ProjectLighthouse.Servers.GameServer/Middlewares/SetLastContactMiddleware.cs @@ -18,7 +18,7 @@ public class SetLastContactMiddleware : MiddlewareDBContext 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. - GameToken? gameToken = await database.GameTokenFromRequest(context.Request); + GameTokenEntity? gameToken = await database.GameTokenFromRequest(context.Request); if (gameToken?.GameVersion == GameVersion.LittleBigPlanet1) // Ignore UserFromGameToken null because user must exist for a token to exist diff --git a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs index 04b64283..42db0d8b 100644 --- a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs +++ b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs @@ -42,7 +42,7 @@ public class GameServerStartup ( options => { - options.OutputFormatters.Add(new XmlOutputFormatter()); + options.OutputFormatters.Add(new LbpOutputFormatter()); options.OutputFormatters.Add(new JsonOutputFormatter()); } ); diff --git a/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs b/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs index 08be82c1..3d625f70 100644 --- a/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs +++ b/ProjectLighthouse.Servers.GameServer/Startup/TokenAuthHandler.cs @@ -35,7 +35,7 @@ public class TokenAuthHandler : AuthenticationHandler GetSlots(DatabaseContext database, User user, int pageStart, int pageSize); - public override IList GetSlots(DatabaseContext database, int pageStart, int pageSize) + public abstract IQueryable GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize); + public override IList GetSlots(DatabaseContext database, int pageStart, int pageSize) { #if DEBUG Logger.Error("tried to get slots without user on CategoryWithUser", LogArea.Category); if (Debugger.IsAttached) Debugger.Break(); #endif - return new List(); + return new List(); } public new string Serialize(DatabaseContext database) @@ -48,35 +49,13 @@ public abstract class CategoryWithUser : Category return string.Empty; } - public string Serialize(DatabaseContext database, User user) + public GameCategory Serialize(DatabaseContext database, UserEntity user) { - Slot? previewSlot = this.GetPreviewSlot(database, user); - - string previewResults = ""; - if (previewSlot != null) - previewResults = LbpSerializer.TaggedStringElement - ( - "results", - previewSlot.Serialize(), - new Dictionary - { - { - "total", this.GetTotalSlots(database, user) - }, - { - "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) - ); + List slots = new() + { + SlotBase.CreateFromEntity(this.GetPreviewSlot(database, user), GameVersion.LittleBigPlanet3, user.UserId), + }; + int totalSlots = this.GetTotalSlots(database, user); + return GameCategory.CreateFromEntity(this, new GenericSlotResponse(slots, totalSlots, 2)); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/CustomCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/CustomCategory.cs index 8fb2033a..6757e9ff 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/CustomCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/CustomCategory.cs @@ -21,7 +21,7 @@ public class CustomCategory : Category this.SlotIds = slotIds.ToList(); } - public CustomCategory(DatabaseCategory category) + public CustomCategory(DatabaseCategoryEntity category) { this.Name = category.Name; this.Description = category.Description; @@ -35,8 +35,8 @@ public class CustomCategory : Category 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(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]); - public override IQueryable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]); + public override IQueryable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3).Where(s => this.SlotIds.Contains(s.SlotId)); public override int GetTotalSlots(DatabaseContext database) => this.SlotIds.Count; diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/HeartedCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/HeartedCategory.cs index 80aaccba..015226fa 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/HeartedCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/HeartedCategory.cs @@ -15,7 +15,7 @@ public class HeartedCategory : CategoryWithUser public override string Description { get; set; } = "Content you've hearted"; public override string IconHash { get; set; } = "g820611"; public override string Endpoint { get; set; } = "hearted"; - public override Slot? GetPreviewSlot(DatabaseContext database, User user) // note: developer slots act up in LBP3 when listed here, so I omitted it + public override SlotEntity? GetPreviewSlot(DatabaseContext database, UserEntity user) // note: developer slots act up in LBP3 when listed here, so I omitted it => database.HeartedLevels.Where(h => h.UserId == user.UserId) .Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .OrderByDescending(h => h.HeartedLevelId) @@ -24,7 +24,7 @@ public class HeartedCategory : CategoryWithUser .ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true) .FirstOrDefault(); - public override IQueryable GetSlots(DatabaseContext database, User user, int pageStart, int pageSize) + public override IQueryable GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize) => database.HeartedLevels.Where(h => h.UserId == user.UserId) .Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .OrderByDescending(h => h.HeartedLevelId) @@ -34,5 +34,5 @@ public class HeartedCategory : CategoryWithUser .Skip(Math.Max(0, pageStart)) .Take(Math.Min(pageSize, 20)); - public override int GetTotalSlots(DatabaseContext database, User user) => database.HeartedLevels.Count(h => h.UserId == user.UserId); + public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.HeartedLevels.Count(h => h.UserId == user.UserId); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/HighestRatedCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/HighestRatedCategory.cs index e7a48be7..13811f9e 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/HighestRatedCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/HighestRatedCategory.cs @@ -14,8 +14,8 @@ public class HighestRatedCategory : Category public override string Description { get; set; } = "Community Highest Rated content"; public override string IconHash { get; set; } = "g820603"; public override string Endpoint { get; set; } = "thumbs"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Thumbsup); - public override IEnumerable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Thumbsup); + public override IEnumerable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .AsEnumerable() diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/LuckyDipCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/LuckyDipCategory.cs index 7d078a42..651af049 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/LuckyDipCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/LuckyDipCategory.cs @@ -14,8 +14,8 @@ public class LuckyDipCategory : Category public override string Description { get; set; } = "Randomized uploaded content"; public override string IconHash { get; set; } = "g820605"; public override string Endpoint { get; set; } = "lbp2luckydip"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault(); - public override IQueryable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault(); + public override IQueryable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(_ => EF.Functions.Random()) diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/MostHeartedCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/MostHeartedCategory.cs index fbd55805..8a80c3fd 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/MostHeartedCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/MostHeartedCategory.cs @@ -14,8 +14,8 @@ public class MostHeartedCategory : Category public override string Description { get; set; } = "The Most Hearted Content"; public override string IconHash { get; set; } = "g820607"; public override string Endpoint { get; set; } = "mostHearted"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Hearts); - public override IEnumerable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Hearts); + public override IEnumerable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .AsEnumerable() diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/MostPlayedCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/MostPlayedCategory.cs index eb9772b4..86f68741 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/MostPlayedCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/MostPlayedCategory.cs @@ -13,12 +13,12 @@ public class MostPlayedCategory : Category public override string Description { get; set; } = "The most played content"; public override string IconHash { get; set; } = "g820608"; public override string Endpoint { get; set; } = "mostUniquePlays"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots .Where(s => s.Type == SlotType.User) .OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique) .ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3) .FirstOrDefault(); - public override IQueryable GetSlots + public override IQueryable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique) diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/NewestLevelsCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/NewestLevelsCategory.cs index 8331076a..985d4866 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/NewestLevelsCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/NewestLevelsCategory.cs @@ -13,8 +13,8 @@ public class NewestLevelsCategory : Category public override string Description { get; set; } = "The most recently published content"; public override string IconHash { get; set; } = "g820623"; public override string Endpoint { get; set; } = "newest"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault(); - public override IQueryable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault(); + public override IQueryable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(s => s.FirstUploaded) diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/QueueCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/QueueCategory.cs index ffbc4bf8..265bc876 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/QueueCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/QueueCategory.cs @@ -15,7 +15,7 @@ public class QueueCategory : CategoryWithUser public override string Description { get; set; } = "Your queued content"; public override string IconHash { get; set; } = "g820614"; public override string Endpoint { get; set; } = "queue"; - public override Slot? GetPreviewSlot(DatabaseContext database, User user) + public override SlotEntity? GetPreviewSlot(DatabaseContext database, UserEntity user) => database.QueuedLevels.Where(q => q.UserId == user.UserId) .Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .OrderByDescending(q => q.QueuedLevelId) @@ -24,7 +24,7 @@ public class QueueCategory : CategoryWithUser .ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true) .FirstOrDefault(); - public override IQueryable GetSlots(DatabaseContext database, User user, int pageStart, int pageSize) + public override IQueryable GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize) => database.QueuedLevels.Where(q => q.UserId == user.UserId) .Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .OrderByDescending(q => q.QueuedLevelId) @@ -34,5 +34,5 @@ public class QueueCategory : CategoryWithUser .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 20)); - public override int GetTotalSlots(DatabaseContext database, User user) => database.QueuedLevels.Count(q => q.UserId == user.UserId); + public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.QueuedLevels.Count(q => q.UserId == user.UserId); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Types/Categories/TeamPicksCategory.cs b/ProjectLighthouse.Servers.GameServer/Types/Categories/TeamPicksCategory.cs index 62a18419..a8fef3bc 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Categories/TeamPicksCategory.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Categories/TeamPicksCategory.cs @@ -13,8 +13,8 @@ public class TeamPicksCategory : Category public override string Description { get; set; } = "Community Team Picks"; public override string IconHash { get; set; } = "g820626"; public override string Endpoint { get; set; } = "team_picks"; - public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick); - public override IQueryable GetSlots + public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick); + public override IQueryable GetSlots (DatabaseContext database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(s => s.FirstUploaded) diff --git a/ProjectLighthouse.Servers.GameServer/Types/Misc/ResourceList.cs b/ProjectLighthouse.Servers.GameServer/Types/Misc/ResourceList.cs index 140e4394..07a3b2d2 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Misc/ResourceList.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Misc/ResourceList.cs @@ -1,4 +1,5 @@ using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Types.Serialization; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc; @@ -9,7 +10,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc; /// [XmlRoot("resources")] [XmlType("resources")] -public class ResourceList +public class ResourceList : ILbpSerializable { [XmlElement("resource")] public string[]? Resources; diff --git a/ProjectLighthouse.Servers.GameServer/Types/Users/ClientsConnected.cs b/ProjectLighthouse.Servers.GameServer/Types/Users/ClientsConnected.cs deleted file mode 100644 index a9880709..00000000 --- a/ProjectLighthouse.Servers.GameServer/Types/Users/ClientsConnected.cs +++ /dev/null @@ -1,25 +0,0 @@ -using LBPUnion.ProjectLighthouse.Serialization; -using Microsoft.EntityFrameworkCore; - -namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users; - -[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.Servers.GameServer/Types/Users/PrivacySettings.cs b/ProjectLighthouse.Servers.GameServer/Types/Users/PrivacySettings.cs index 00ce3201..b1b2fdbd 100644 --- a/ProjectLighthouse.Servers.GameServer/Types/Users/PrivacySettings.cs +++ b/ProjectLighthouse.Servers.GameServer/Types/Users/PrivacySettings.cs @@ -1,24 +1,16 @@ #nullable enable using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Types.Serialization; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users; [XmlRoot("privacySettings")] [XmlType("privacySettings")] -public class PrivacySettings +public class PrivacySettings : ILbpSerializable { [XmlElement("levelVisiblity")] public string? LevelVisibility { get; set; } [XmlElement("profileVisiblity")] 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.Servers.Website/Controllers/Admin/AdminReportController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs index deddcfc5..f84bcef6 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs @@ -22,10 +22,10 @@ public class AdminReportController : ControllerBase [HttpGet("remove")] public async Task DeleteReport([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.Forbid(); - GriefReport? report = await this.database.Reports.FirstOrDefaultAsync(r => r.ReportId == id); + GriefReportEntity? report = await this.database.Reports.FirstOrDefaultAsync(r => r.ReportId == id); if (report == null) return this.NotFound(); List hashes = new() @@ -49,10 +49,10 @@ public class AdminReportController : ControllerBase [HttpGet("dismiss")] public async Task DismissReport([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsModerator) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsModerator) return this.Forbid(); - GriefReport? report = await this.database.Reports.FirstOrDefaultAsync(r => r.ReportId == id); + GriefReportEntity? report = await this.database.Reports.FirstOrDefaultAsync(r => r.ReportId == id); if (report == null) return this.NotFound(); FileHelper.DeleteResource(report.JpegHash); diff --git a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs index a8b95a24..00babada 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs @@ -28,10 +28,10 @@ public class AdminUserController : ControllerBase /// [HttpGet("wipePlanets")] public async Task WipePlanets([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null || !user.IsModerator) return this.NotFound(); - User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (targetedUser == null) return this.NotFound(); string[] hashes = { @@ -47,7 +47,7 @@ public class AdminUserController : ControllerBase if (string.IsNullOrWhiteSpace(hash)) continue; // Find users with a matching hash - List users = await this.database.Users + List users = await this.database.Users .Where(u => u.PlanetHashLBP2 == hash || u.PlanetHashLBP3 == hash || u.PlanetHashLBPVita == hash) @@ -57,7 +57,7 @@ public class AdminUserController : ControllerBase System.Diagnostics.Debug.Assert(users.Count != 0); // Reset each users' hash. - foreach (User userWithPlanet in users) + foreach (UserEntity userWithPlanet in users) { userWithPlanet.PlanetHashLBP2 = ""; userWithPlanet.PlanetHashLBP3 = ""; @@ -92,10 +92,10 @@ public class AdminUserController : ControllerBase [HttpPost("/admin/user/{id:int}/setPermissionLevel")] public async Task SetUserPermissionLevel([FromRoute] int id, [FromForm] PermissionLevel role) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? 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); + UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (targetedUser == null) return this.NotFound(); if (role != PermissionLevel.Banned) diff --git a/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs index a8a75caa..4de4709e 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs @@ -22,7 +22,7 @@ public class AuthenticationController : ControllerBase [HttpGet("unlink/{platform}")] public async Task UnlinkPlatform(string platform) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); Platform[] invalidTokens; @@ -48,10 +48,10 @@ public class AuthenticationController : ControllerBase [HttpGet("approve/{id:int}")] public async Task Approve(int id) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); - PlatformLinkAttempt? linkAttempt = await this.database.PlatformLinkAttempts + PlatformLinkAttemptEntity? linkAttempt = await this.database.PlatformLinkAttempts .FirstOrDefaultAsync(l => l.PlatformLinkAttemptId == id); if (linkAttempt == null) return this.NotFound(); @@ -76,10 +76,10 @@ public class AuthenticationController : ControllerBase [HttpGet("deny/{id:int}")] public async Task Deny(int id) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); - PlatformLinkAttempt? linkAttempt = await this.database.PlatformLinkAttempts + PlatformLinkAttemptEntity? linkAttempt = await this.database.PlatformLinkAttempts .FirstOrDefaultAsync(l => l.PlatformLinkAttemptId == id); if (linkAttempt == null) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationCaseController.cs b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationCaseController.cs index 7fe7e3e3..da8bffcd 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationCaseController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationCaseController.cs @@ -20,10 +20,10 @@ public class ModerationCaseController : ControllerBase [HttpGet("dismiss")] public async Task DismissCase([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsModerator) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsModerator) return this.Forbid(); - ModerationCase? @case = await this.database.Cases.FirstOrDefaultAsync(c => c.CaseId == id); + ModerationCaseEntity? @case = await this.database.Cases.FirstOrDefaultAsync(c => c.CaseId == id); if (@case == null) return this.NotFound(); @case.DismissedAt = DateTime.Now; diff --git a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationRemovalController.cs b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationRemovalController.cs index bcb7e485..9395f01b 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationRemovalController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationRemovalController.cs @@ -1,6 +1,7 @@ using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using LBPUnion.ProjectLighthouse.Types.Serialization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -17,9 +18,9 @@ public class ModerationRemovalController : ControllerBase this.database = database; } - private async Task Delete(DbSet dbSet, int id, string? callbackUrl, Func> getHandler) where T: class + private async Task Delete(DbSet dbSet, int id, string? callbackUrl, Func> getHandler) where T: class { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); T? item = await getHandler(user, id); @@ -34,9 +35,9 @@ public class ModerationRemovalController : ControllerBase [HttpGet("deleteScore/{scoreId:int}")] public async Task DeleteScore(int scoreId, [FromQuery] string? callbackUrl) { - return await this.Delete(this.database.Scores, scoreId, callbackUrl, async (user, id) => + return await this.Delete(this.database.Scores, scoreId, callbackUrl, async (user, id) => { - Score? score = await this.database.Scores.Include(s => s.Slot).FirstOrDefaultAsync(s => s.ScoreId == id); + ScoreEntity? score = await this.database.Scores.Include(s => s.Slot).FirstOrDefaultAsync(s => s.ScoreId == id); if (score == null) return null; return user.IsModerator ? score : null; @@ -46,10 +47,10 @@ public class ModerationRemovalController : ControllerBase [HttpGet("deleteComment/{commentId:int}")] public async Task DeleteComment(int commentId, [FromQuery] string? callbackUrl) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); - Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); + CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); if (comment == null) return this.Redirect("~/404"); if (comment.Deleted) return this.Redirect(callbackUrl ?? "~/"); @@ -82,10 +83,10 @@ public class ModerationRemovalController : ControllerBase [HttpGet("deleteReview/{reviewId:int}")] public async Task DeleteReview(int reviewId, [FromQuery] string? callbackUrl) { - User? user = this.database.UserFromWebRequest(this.Request); + UserEntity? user = this.database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); - Review? review = await this.database.Reviews.Include(r => r.Slot).FirstOrDefaultAsync(c => c.ReviewId == reviewId); + ReviewEntity? review = await this.database.Reviews.Include(r => r.Slot).FirstOrDefaultAsync(c => c.ReviewId == reviewId); if (review == null) return this.Redirect("~/404"); if (review.Deleted) return this.Redirect(callbackUrl ?? "~/"); @@ -103,9 +104,9 @@ public class ModerationRemovalController : ControllerBase [HttpGet("deletePhoto/{photoId:int}")] public async Task DeletePhoto(int photoId, [FromQuery] string? callbackUrl) { - return await this.Delete(this.database.Photos, photoId, callbackUrl, async (user, id) => + return await this.Delete(this.database.Photos, photoId, callbackUrl, async (user, id) => { - Photo? photo = await this.database.Photos.Include(p => p.Slot).FirstOrDefaultAsync(p => p.PhotoId == id); + PhotoEntity? photo = await this.database.Photos.Include(p => p.Slot).FirstOrDefaultAsync(p => p.PhotoId == id); if (photo == null) return null; if (!user.IsModerator && photo.CreatorId != user.UserId) return null; diff --git a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationSlotController.cs b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationSlotController.cs index bf255822..de55e6ee 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationSlotController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Moderator/ModerationSlotController.cs @@ -23,10 +23,10 @@ public class ModerationSlotController : ControllerBase [HttpGet("teamPick")] public async Task TeamPick([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsModerator) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsModerator) return this.Forbid(); - Slot? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); slot.TeamPick = true; @@ -42,10 +42,10 @@ public class ModerationSlotController : ControllerBase [HttpGet("removeTeamPick")] public async Task RemoveTeamPick([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsModerator) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsModerator) return this.Forbid(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); slot.TeamPick = false; @@ -58,10 +58,10 @@ public class ModerationSlotController : ControllerBase [HttpGet("delete")] public async Task DeleteLevel([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsModerator) return this.StatusCode(403, ""); + UserEntity? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsModerator) return this.Forbid(); - Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.Ok(); await this.database.RemoveSlot(slot); diff --git a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index 0837f21c..c92ad074 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -30,10 +30,10 @@ public class SlotPageController : ControllerBase [HttpGet("unpublish")] public async Task UnpublishSlot([FromRoute] int id) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - Slot? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (targetSlot == null) return this.Redirect("~/slots/0"); if (targetSlot.CreatorId != token.UserId) return this.Redirect("~/slot/" + id); @@ -48,7 +48,7 @@ public class SlotPageController : ControllerBase [HttpGet("rateComment")] public async Task RateComment([FromRoute] int id, [FromQuery] int commentId, [FromQuery] int rating) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); await this.database.RateComment(token.UserId, commentId, rating); @@ -59,7 +59,7 @@ public class SlotPageController : ControllerBase [HttpPost("postComment")] public async Task PostComment([FromRoute] int id, [FromForm] string? msg) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); if (msg == null) @@ -90,10 +90,10 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (heartedSlot == null) return this.NotFound(); await this.database.HeartLevel(token.UserId, heartedSlot); @@ -106,10 +106,10 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (heartedSlot == null) return this.NotFound(); await this.database.UnheartLevel(token.UserId, heartedSlot); @@ -122,10 +122,10 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (queuedSlot == null) return this.NotFound(); await this.database.QueueLevel(token.UserId, queuedSlot); @@ -138,10 +138,10 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + SlotEntity? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (queuedSlot == null) return this.NotFound(); await this.database.UnqueueLevel(token.UserId, queuedSlot); diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index e2862f9a..ad73199b 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -24,7 +24,7 @@ public class UserPageController : ControllerBase [HttpGet("rateComment")] public async Task RateComment([FromRoute] int id, [FromQuery] int? commentId, [FromQuery] int? rating) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); await this.database.RateComment(token.UserId, commentId.GetValueOrDefault(), rating.GetValueOrDefault()); @@ -35,7 +35,7 @@ public class UserPageController : ControllerBase [HttpPost("postComment")] public async Task PostComment([FromRoute] int id, [FromForm] string? msg) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); if (msg == null) @@ -64,10 +64,10 @@ public class UserPageController : ControllerBase [HttpGet("heart")] public async Task HeartUser([FromRoute] int id) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (heartedUser == null) return this.NotFound(); await this.database.HeartUser(token.UserId, heartedUser); @@ -78,10 +78,10 @@ public class UserPageController : ControllerBase [HttpGet("unheart")] public async Task UnheartUser([FromRoute] int id) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (heartedUser == null) return this.NotFound(); await this.database.UnheartUser(token.UserId, heartedUser); @@ -92,10 +92,10 @@ public class UserPageController : ControllerBase [HttpGet("block")] public async Task BlockUser([FromRoute] int id) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (blockedUser == null) return this.NotFound(); await this.database.BlockUser(token.UserId, blockedUser); @@ -106,10 +106,10 @@ public class UserPageController : ControllerBase [HttpGet("unblock")] public async Task UnblockUser([FromRoute] int id) { - WebToken? token = this.database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + UserEntity? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (blockedUser == null) return this.NotFound(); await this.database.UnblockUser(token.UserId, blockedUser); diff --git a/ProjectLighthouse.Servers.Website/Extensions/PartialExtensions.cs b/ProjectLighthouse.Servers.Website/Extensions/PartialExtensions.cs index 71be2f75..6248411e 100644 --- a/ProjectLighthouse.Servers.Website/Extensions/PartialExtensions.cs +++ b/ProjectLighthouse.Servers.Website/Extensions/PartialExtensions.cs @@ -32,15 +32,15 @@ public static class PartialExtensions } } - public static Task ToLink(this User user, IHtmlHelper helper, ViewDataDictionary viewData, string language, string timeZone = "", bool includeStatus = false) + public static Task ToLink(this UserEntity user, IHtmlHelper helper, ViewDataDictionary viewData, string language, string timeZone = "", bool includeStatus = false) => helper.PartialAsync("Partials/Links/UserLinkPartial", user, viewData.WithLang(language).WithTime(timeZone).WithKeyValue("IncludeStatus", includeStatus)); public static Task ToHtml ( - this Slot slot, + this SlotEntity slot, IHtmlHelper helper, ViewDataDictionary viewData, - User? user, + UserEntity? user, string callbackUrl, string language = "", string timeZone = "", @@ -55,6 +55,6 @@ public static class PartialExtensions .WithKeyValue("IsMini", isMini) .WithKeyValue("IsMobile", isMobile)); - public static Task ToHtml(this Photo photo, IHtmlHelper helper, ViewDataDictionary viewData, string language, string timeZone, bool canDelete = false) + public static Task ToHtml(this PhotoEntity photo, IHtmlHelper helper, ViewDataDictionary viewData, string language, string timeZone, bool canDelete = false) => helper.PartialAsync("Partials/PhotoPartial", photo, viewData.WithLang(language).WithTime(timeZone).CanDelete(canDelete)); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs b/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs index 6b5ea5fa..a6e8a68a 100644 --- a/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs +++ b/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs @@ -14,14 +14,14 @@ public class UserRequiredRedirectMiddleware : MiddlewareDBContext public override async Task InvokeAsync(HttpContext ctx, DatabaseContext database) { - WebToken? token = database.WebTokenFromRequest(ctx.Request); + WebTokenEntity? token = database.WebTokenFromRequest(ctx.Request); if (token == null || pathContains(ctx, "/logout")) { await this.next(ctx); return; } - User? user = await database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); + UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); if (user == null) { await this.next(ctx); diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml index 7a32a1fe..3eceb36d 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml @@ -38,7 +38,7 @@ }
- @foreach (ApiKey key in Model.ApiKeys) + @foreach (ApiKeyEntity key in Model.ApiKeys) {
diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml.cs index c8c7498d..2513c730 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminApiKeyPage.cshtml.cs @@ -9,7 +9,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin; public class AdminApiKeyPageModel : BaseLayout { - public List ApiKeys = new(); + public List ApiKeys = new(); public int KeyCount; public AdminApiKeyPageModel(DatabaseContext database) : base(database) @@ -17,7 +17,7 @@ public class AdminApiKeyPageModel : BaseLayout public async Task OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsAdmin) return this.NotFound(); @@ -29,10 +29,10 @@ public class AdminApiKeyPageModel : BaseLayout public async Task OnPost(string keyId) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsAdmin) return this.NotFound(); - ApiKey? apiKey = await this.Database.APIKeys.FirstOrDefaultAsync(k => k.Id == int.Parse(keyId)); + ApiKeyEntity? apiKey = await this.Database.APIKeys.FirstOrDefaultAsync(k => k.Id == int.Parse(keyId)); if (apiKey == null) return this.NotFound(); this.Database.APIKeys.Remove(apiKey); await this.Database.SaveChangesAsync(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs index 024556a3..ed2456d1 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs @@ -24,7 +24,7 @@ public class AdminPanelPage : BaseLayout public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob, [FromQuery] string? log) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsAdmin) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml index 99cbbc3b..16b2f571 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml @@ -12,7 +12,7 @@

Note: Users are ordered by their permissions, then by most-recent-first.

- @foreach (User user in Model.Users) + @foreach (UserEntity user in Model.Users) { string color; string subtitle; diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs index da391679..b6ed868a 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs @@ -11,13 +11,13 @@ public class AdminPanelUsersPage : BaseLayout { public int UserCount; - public List Users = new(); + public List Users = new(); public AdminPanelUsersPage(DatabaseContext database) : base(database) {} public async Task OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsAdmin) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs index 4a5f3f04..97ab1f61 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs @@ -12,11 +12,11 @@ public class AdminSetGrantedSlotsPage : BaseLayout public AdminSetGrantedSlotsPage(DatabaseContext database) : base(database) {} - public User? TargetedUser; + public UserEntity? TargetedUser; public async Task OnGet([FromRoute] int id) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsAdmin) return this.NotFound(); this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id); @@ -27,7 +27,7 @@ public class AdminSetGrantedSlotsPage : BaseLayout public async Task OnPost([FromRoute] int id, int grantedSlotCount) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsAdmin) return this.NotFound(); this.TargetedUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == id); diff --git a/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml index f05b5a06..c04f20e5 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml @@ -81,7 +81,7 @@ }

@room.PlayerIds.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString() on platform @room.RoomPlatform

Slot type: @room.Slot.SlotType, slot id: @room.Slot.SlotId

- @foreach (User player in room.GetPlayers(Model.Database)) + @foreach (UserEntity player in room.GetPlayers(Model.Database)) {
@player.Username
} diff --git a/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs index 68d6eb92..b26d7690 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs @@ -17,7 +17,7 @@ public class RoomVisualizerPage : BaseLayout public IActionResult OnGet() { #if !DEBUG - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsAdmin) return this.NotFound(); #endif diff --git a/ProjectLighthouse.Servers.Website/Pages/Email/CompleteEmailVerificationPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Email/CompleteEmailVerificationPage.cshtml.cs index 71ba3cbd..d3b7d383 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Email/CompleteEmailVerificationPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Email/CompleteEmailVerificationPage.cshtml.cs @@ -21,14 +21,14 @@ public class CompleteEmailVerificationPage : BaseLayout { if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); - EmailVerificationToken? emailVerifyToken = await this.Database.EmailVerificationTokens.FirstOrDefaultAsync(e => e.EmailToken == token); + EmailVerificationTokenEntity? emailVerifyToken = await this.Database.EmailVerificationTokens.FirstOrDefaultAsync(e => e.EmailToken == token); if (emailVerifyToken == null) { this.Error = "Invalid verification token"; return this.Page(); } - User user = await this.Database.Users.FirstAsync(u => u.UserId == emailVerifyToken.UserId); + UserEntity user = await this.Database.Users.FirstAsync(u => u.UserId == emailVerifyToken.UserId); if (DateTime.Now > emailVerifyToken.ExpiresAt) { @@ -50,7 +50,7 @@ public class CompleteEmailVerificationPage : BaseLayout if (user.Password != null) return this.Page(); // if user's account was created automatically - WebToken webToken = new() + WebTokenEntity webToken = new() { ExpiresAt = DateTime.Now.AddDays(7), Verified = true, diff --git a/ProjectLighthouse.Servers.Website/Pages/Email/SendVerificationEmailPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Email/SendVerificationEmailPage.cshtml.cs index a7f9cad9..96066cb2 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Email/SendVerificationEmailPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Email/SendVerificationEmailPage.cshtml.cs @@ -19,7 +19,7 @@ public class SendVerificationEmailPage : BaseLayout { if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); if (user.EmailAddressVerified) return this.Redirect("/"); diff --git a/ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs index 6e549a33..b760f03e 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Email/SetEmailForm.cshtml.cs @@ -22,7 +22,7 @@ public class SetEmailForm : BaseLayout public IActionResult OnGet() { if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); - WebToken? token = this.Database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.Database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("/login"); return this.Page(); @@ -33,10 +33,10 @@ public class SetEmailForm : BaseLayout { if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); - WebToken? token = this.Database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.Database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/login"); - User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); + UserEntity? user = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); if (user == null) return this.Redirect("~/login"); if (!SanitizationHelper.IsValidEmail(emailAddress)) diff --git a/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml index a1beb900..ca590a31 100644 --- a/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml @@ -22,7 +22,7 @@ else } } -@foreach (PlatformLinkAttempt authAttempt in Model.LinkAttempts) +@foreach (PlatformLinkAttemptEntity authAttempt in Model.LinkAttempts) { DateTimeOffset timestamp = TimeZoneInfo.ConvertTime(DateTimeOffset.FromUnixTimeMilliseconds(authAttempt.Timestamp), timeZoneInfo);
diff --git a/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs index 26ba8b2d..f49823cd 100644 --- a/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs @@ -10,7 +10,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth; public class AuthenticationPage : BaseLayout { - public List LinkAttempts = new(); + public List LinkAttempts = new(); public IPAddress? IpAddress; public AuthenticationPage(DatabaseContext database) : base(database) @@ -18,7 +18,7 @@ public class AuthenticationPage : BaseLayout public IActionResult OnGet() { - if (this.User == null) return this.StatusCode(403, ""); + if (this.User == null) return this.Forbid(); this.IpAddress = this.HttpContext.Connection.RemoteIpAddress; diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml index fe0eec5b..5749f0ce 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -46,7 +46,7 @@ @{ int i = 0; - foreach (User user in Model.PlayersOnline) + foreach (UserEntity user in Model.PlayersOnline) { i++; @await user.ToLink(Html, ViewData, language, timeZone, true) @@ -66,7 +66,7 @@

@Model.Translate(LandingPageStrings.LatestTeamPicks)

- @foreach (Slot slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@ + @foreach (SlotEntity slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@ { @await slot.ToHtml(Html, ViewData, Model.User, $"~/slot/{slot.SlotId}", language, timeZone, isMobile, true, true)
@@ -83,7 +83,7 @@

@Model.Translate(LandingPageStrings.NewestLevels)

- @foreach (Slot slot in Model.NewestLevels!) @* Can't reach a point where this is null *@ + @foreach (SlotEntity slot in Model.NewestLevels!) @* Can't reach a point where this is null *@ { @await slot.ToHtml(Html, ViewData, Model.User, $"~/slot/{slot.SlotId}", language, timeZone, isMobile, true, true)
diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs index 08117494..d88ac9d8 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs @@ -17,15 +17,15 @@ public class LandingPage : BaseLayout {} public int PendingAuthAttempts; - public List PlayersOnline = new(); + public List PlayersOnline = new(); - public List? LatestTeamPicks; - public List? NewestLevels; + public List? LatestTeamPicks; + public List? NewestLevels; [UsedImplicitly] public async Task OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); if (user != null) diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs index e803bc1a..fbc5ad11 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs @@ -25,7 +25,7 @@ public class BaseLayout : PageModel public string Title = string.Empty; - private User? user; + private UserEntity? user; public BaseLayout(DatabaseContext database) { this.Database = database; @@ -35,7 +35,7 @@ public class BaseLayout : PageModel this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderSlots, "/slots/0", "globe americas")); } - public new User? User { + public new UserEntity? User { get { if (this.user != null) return this.user; diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs index fec4b543..ebcc4a56 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs @@ -44,7 +44,7 @@ public class LoginForm : BaseLayout return this.Page(); } - User? user; + UserEntity? user; if (!ServerConfiguration.Instance.Mail.MailEnabled) { @@ -55,7 +55,7 @@ public class LoginForm : BaseLayout user = await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress == username); if (user == null) { - User? noEmailUser = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); + UserEntity? noEmailUser = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); if (noEmailUser != null && noEmailUser.EmailAddress == null) user = noEmailUser; } @@ -86,7 +86,7 @@ public class LoginForm : BaseLayout return this.Page(); } - WebToken webToken = new() + WebTokenEntity webToken = new() { UserId = user.UserId, UserToken = CryptoHelper.GenerateAuthToken(), diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/LogoutPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/LogoutPage.cshtml.cs index 3972735b..df6b06a9 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/LogoutPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/LogoutPage.cshtml.cs @@ -12,7 +12,7 @@ public class LogoutPage : BaseLayout {} public async Task OnGet() { - WebToken? token = this.Database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.Database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("~/"); this.Database.WebTokens.Remove(token); diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetPage.cshtml.cs index 393c3d6b..0d9ece62 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetPage.cshtml.cs @@ -19,7 +19,7 @@ public class PasswordResetPage : BaseLayout [UsedImplicitly] public async Task OnPost(string password, string confirmPassword) { - User? user; + UserEntity? user; if (this.Request.Query.ContainsKey("token")) { user = await this.Database.UserFromPasswordResetToken(this.Request.Query["token"][0]); @@ -63,7 +63,7 @@ public class PasswordResetPage : BaseLayout { if (this.Request.Query.ContainsKey("token")) return this.Page(); - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); return this.Page(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequestForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequestForm.cshtml.cs index 5814b871..bb7cf9ea 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequestForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequestForm.cshtml.cs @@ -42,7 +42,7 @@ public class PasswordResetRequestForm : BaseLayout return this.Page(); } - User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress == email && u.EmailAddressVerified); + UserEntity? user = await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress == email && u.EmailAddressVerified); if (user == null) { @@ -51,7 +51,7 @@ public class PasswordResetRequestForm : BaseLayout return this.Page(); } - PasswordResetToken token = new() + PasswordResetTokenEntity token = new() { Created = DateTime.Now, UserId = user.UserId, diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequiredPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequiredPage.cshtml.cs index 045095e6..81abc271 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequiredPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/PasswordResetRequiredPage.cshtml.cs @@ -15,7 +15,7 @@ public class PasswordResetRequiredPage : BaseLayout public IActionResult OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset"); diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/PirateSignupPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/PirateSignupPage.cshtml.cs index bdc175fd..fdab2ee4 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/PirateSignupPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/PirateSignupPage.cshtml.cs @@ -12,7 +12,7 @@ public class PirateSignupPage : BaseLayout public IActionResult OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); return this.Page(); @@ -20,7 +20,7 @@ public class PirateSignupPage : BaseLayout public async Task OnPost() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); user.Language = user.Language == "en-PT" ? "en" : "en-PT"; diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/RegisterForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/RegisterForm.cshtml.cs index 0977371b..90207eb7 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Login/RegisterForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Login/RegisterForm.cshtml.cs @@ -54,7 +54,7 @@ public class RegisterForm : BaseLayout return this.Page(); } - User? existingUser = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()); + UserEntity? existingUser = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()); if (existingUser != null) { this.Error = this.Translate(ErrorStrings.UsernameTaken); @@ -74,9 +74,9 @@ public class RegisterForm : BaseLayout return this.Page(); } - User user = await this.Database.CreateUser(username, CryptoHelper.BCryptHash(password), emailAddress); + UserEntity user = await this.Database.CreateUser(username, CryptoHelper.BCryptHash(password), emailAddress); - WebToken webToken = new() + WebTokenEntity webToken = new() { UserId = user.UserId, UserToken = CryptoHelper.GenerateAuthToken(), diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml index 1fc37816..2a5f608d 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml @@ -12,7 +12,7 @@

There are @Model.UserCount banned users.

-@foreach (User user in Model.Users) +@foreach (UserEntity user in Model.Users) {
@await Html.PartialAsync("Partials/UserCardPartial", user, new ViewDataDictionary(ViewData) diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml.cs index eaec543c..7376adbc 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/BannedUsersPage.cshtml.cs @@ -13,7 +13,7 @@ public class BannedUsersPage : BaseLayout public BannedUsersPage(DatabaseContext database) : base(database) {} - public List Users = new(); + public List Users = new(); public int PageAmount; @@ -23,7 +23,7 @@ public class BannedUsersPage : BaseLayout public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) { - WebToken? token = this.Database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.Database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("/login"); this.Users = await this.Database.Users diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml index 93acda29..eef4d801 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml @@ -21,7 +21,7 @@
-@foreach (ModerationCase @case in Model.Cases) +@foreach (ModerationCaseEntity @case in Model.Cases) { @(await Html.PartialAsync("Partials/ModerationCasePartial", @case, ViewData.WithTime(timeZone))) } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml.cs index 7433dd8c..8be500c4 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/CasePage.cshtml.cs @@ -13,7 +13,7 @@ public class CasePage : BaseLayout public CasePage(DatabaseContext database) : base(database) {} - public List Cases = new(); + public List Cases = new(); public int CaseCount; public int DismissedCaseCount; @@ -23,7 +23,7 @@ public class CasePage : BaseLayout public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.NotFound(); if (!user.IsModerator) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml index 5a6ce48d..0d4b2ef8 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml @@ -15,7 +15,7 @@

There are @Model.SlotCount hidden levels.

-@foreach (Slot slot in Model.Slots) +@foreach (SlotEntity slot in Model.Slots) {
@await slot.ToHtml(Html, ViewData, Model.User, $"~/moderation/hiddenLevels/{Model.PageNumber}", language, timeZone, isMobile, true) diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml.cs index ed0f772b..3ccf1fc1 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/HiddenLevelsPage.cshtml.cs @@ -19,11 +19,11 @@ public class HiddenLevelsPage : BaseLayout public int SlotCount; - public List Slots = new(); + public List Slots = new(); public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) { - WebToken? token = this.Database.WebTokenFromRequest(this.Request); + WebTokenEntity? token = this.Database.WebTokenFromRequest(this.Request); if (token == null) return this.Redirect("/login"); this.Slots = await this.Database.Slots diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/ModPanelPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/ModPanelPage.cshtml.cs index 0ed69c38..379ea204 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/ModPanelPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/ModPanelPage.cshtml.cs @@ -16,7 +16,7 @@ public class ModPanelPage : BaseLayout public async Task OnGet() { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsModerator) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/NewCasePage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/NewCasePage.cshtml.cs index fe365547..2c9daf57 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/NewCasePage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/NewCasePage.cshtml.cs @@ -17,7 +17,7 @@ public class NewCasePage : BaseLayout public IActionResult OnGet([FromQuery] CaseType? type, [FromQuery] int? affectedId) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsModerator) return this.Redirect("/login"); if (type == null) return this.BadRequest(); @@ -31,7 +31,7 @@ public class NewCasePage : BaseLayout public async Task OnPost(CaseType? type, string? reason, string? modNotes, DateTime expires, int? affectedId) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsModerator) return this.Redirect("/login"); if (type == null) return this.BadRequest(); @@ -43,7 +43,7 @@ public class NewCasePage : BaseLayout // if id is invalid then return bad request if (!await type.Value.IsIdValid((int)affectedId, this.Database)) return this.BadRequest(); - ModerationCase @case = new() + ModerationCaseEntity @case = new() { Type = type.Value, Reason = reason, diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportPage.cshtml.cs index 8fc751ca..66246e99 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportPage.cshtml.cs @@ -14,15 +14,15 @@ public class ReportPage : BaseLayout public ReportPage(DatabaseContext database) : base(database) {} - public GriefReport Report = null!; // Report is not used if it's null in OnGet + public GriefReportEntity Report = null!; // Report is not used if it's null in OnGet public async Task OnGet([FromRoute] int reportId) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsAdmin) return this.NotFound(); - GriefReport? report = await this.Database.Reports + GriefReportEntity? report = await this.Database.Reports .Include(r => r.ReportingPlayer) .FirstOrDefaultAsync(r => r.ReportId == reportId); if (report == null) return this.NotFound(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml index d37a6b08..a48bed1b 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml @@ -28,7 +28,7 @@ let images = []; -@foreach (GriefReport report in Model.Reports) +@foreach (GriefReportEntity report in Model.Reports) { @await Html.PartialAsync("Partials/ReportPartial", report, ViewData.WithTime(timeZone)) } diff --git a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml.cs index 5be1a733..8d98cce3 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Moderation/ReportsPage.cshtml.cs @@ -19,7 +19,7 @@ public class ReportsPage : BaseLayout public int ReportCount; - public List Reports = new(); + public List Reports = new(); public string SearchValue = ""; @@ -28,7 +28,7 @@ public class ReportsPage : BaseLayout public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) { - User? user = this.Database.UserFromWebRequest(this.Request); + UserEntity? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); if (!user.IsModerator) return this.NotFound(); @@ -51,7 +51,7 @@ public class ReportsPage : BaseLayout .Take(ServerStatics.PageSize) .ToListAsync(); - foreach (GriefReport r in this.Reports) + foreach (GriefReportEntity r in this.Reports) { r.XmlPlayers = (ReportPlayer[]?)JsonSerializer.Deserialize(r.Players, typeof(ReportPlayer[])) ?? Array.Empty(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml index 25174fe1..fc594c50 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml @@ -1,4 +1,4 @@ -@model LBPUnion.ProjectLighthouse.Types.Entities.Profile.User +@model LBPUnion.ProjectLighthouse.Types.Entities.Profile.UserEntity
@Html.AntiForgeryToken() diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml index 6543c6c4..6c607a20 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml @@ -2,6 +2,7 @@ @using System.IO @using LBPUnion.ProjectLighthouse.Localization @using LBPUnion.ProjectLighthouse.Servers.Website.Extensions +@using LBPUnion.ProjectLighthouse.Types.Entities.Interaction @using LBPUnion.ProjectLighthouse.Types.Entities.Profile @{ @@ -42,65 +43,70 @@
} } + + @{ + int i = 0; + foreach (KeyValuePair commentAndReaction in Model.Comments) + { + CommentEntity comment = commentAndReaction.Key; + int yourThumb = commentAndReaction.Value?.Rating ?? 0; + DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000).ToLocalTime(); + StringWriter messageWriter = new(); + HttpUtility.HtmlDecode(comment.getComment(), messageWriter); - @for(int i = 0; i < Model.Comments.Count; i++) - { - Comment comment = Model.Comments[i]; - DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000).ToLocalTime(); - StringWriter messageWriter = new(); - HttpUtility.HtmlDecode(comment.getComment(), messageWriter); + string decodedMessage = messageWriter.ToString(); + string? url = Url.RouteUrl(ViewContext.RouteData.Values); + if (url == null) continue; - string decodedMessage = messageWriter.ToString(); - string? url = Url.RouteUrl(ViewContext.RouteData.Values); - if (url == null) continue; + int rating = comment.ThumbsUp - comment.ThumbsDown; - int rating = comment.ThumbsUp - comment.ThumbsDown; - -
- @{ - string style = ""; - if (Model.User?.UserId == comment.PosterUserId) - { - style = "pointer-events: none"; +
+ @{ + string style = ""; + if (Model.User?.UserId == comment.PosterUserId) + { + style = "pointer-events: none"; + } } - } -
- - - - @(rating) - - - -
+
+ + + + @(rating) + + + +
-
- @await comment.Poster.ToLink(Html, ViewData, language): - @if (comment.Deleted) - { - +
+ @await comment.Poster.ToLink(Html, ViewData, language): + @if (comment.Deleted) + { + + @decodedMessage + + } + else + { @decodedMessage - - } - else - { - @decodedMessage - } - @if (((Model.User?.IsModerator ?? false) || Model.User?.UserId == comment.PosterUserId || Model.User?.UserId == pageOwnerId) && !comment.Deleted) - { - - } -

- @TimeZoneInfo.ConvertTime(timestamp, timeZoneInfo).ToString("M/d/yyyy @ h:mm:ss tt") -

- @if (i != Model.Comments.Count - 1) - { -
- } + } + @if (((Model.User?.IsModerator ?? false) || Model.User?.UserId == comment.PosterUserId || Model.User?.UserId == pageOwnerId) && !comment.Deleted) + { + + } +

+ @TimeZoneInfo.ConvertTime(timestamp, timeZoneInfo).ToString("M/d/yyyy @ h:mm:ss tt") +

+ @if (i != Model.Comments.Count - 1) + { +
+ } +
-
+ i++; + } }