From 511fda6fb6c86a11261d64281368a12cb6858852 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 23 Apr 2024 19:02:43 -0400 Subject: [PATCH 01/15] Remove grief reports from database --- Refresh.GameServer/Authentication/Token.cs | 1 + .../Database/GameDatabaseContext.Reports.cs | 16 - .../Database/GameDatabaseProvider.cs | 4 +- .../Endpoints/Game/ReportingEndpoints.cs | 2 - Refresh.GameServer/Types/Report/GameReport.cs | 9 +- .../GameDatabaseProvider.cs | 406 ++++++++++++++++++ .../Refresh.LegacyDatabase.csproj | 13 + Refresh.sln | 8 + .../Reporting/ReportingEndpointsTests.cs | 15 - 9 files changed, 431 insertions(+), 43 deletions(-) delete mode 100644 Refresh.GameServer/Database/GameDatabaseContext.Reports.cs create mode 100644 Refresh.LegacyDatabase/GameDatabaseProvider.cs create mode 100644 Refresh.LegacyDatabase/Refresh.LegacyDatabase.csproj diff --git a/Refresh.GameServer/Authentication/Token.cs b/Refresh.GameServer/Authentication/Token.cs index 5e1ef306..882dbc25 100644 --- a/Refresh.GameServer/Authentication/Token.cs +++ b/Refresh.GameServer/Authentication/Token.cs @@ -44,5 +44,6 @@ public TokenGame TokenGame public DateTimeOffset ExpiresAt { get; set; } public DateTimeOffset LoginDate { get; set; } + public ObjectId UserId { get; set; } public GameUser User { get; set; } } \ No newline at end of file diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Reports.cs b/Refresh.GameServer/Database/GameDatabaseContext.Reports.cs deleted file mode 100644 index e901b98a..00000000 --- a/Refresh.GameServer/Database/GameDatabaseContext.Reports.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Refresh.GameServer.Types.Report; - -namespace Refresh.GameServer.Database; - -public partial class GameDatabaseContext -{ - public void AddGriefReport(GameReport report) - { - this.AddSequentialObject(report, () => {}); - } - - public DatabaseList GetGriefReports(int count, int skip) - { - return new DatabaseList(this._realm.All(), skip, count); - } -} \ No newline at end of file diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index bf55fa12..8b14d3d9 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -34,9 +34,9 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 121; + protected override ulong SchemaVersion => 1; - protected override string Filename => "refreshGameServer.realm"; + protected override string Filename => "refreshGameServer-new.realm"; protected override List SchemaTypes { get; } = new() { diff --git a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs index 35433884..99bbbc56 100644 --- a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs @@ -94,8 +94,6 @@ public Response UploadReport(RequestContext context, GameDatabaseContext databas if (body.Players is { Length: > 4 } || body.ScreenElements is { Player.Length: > 4 }) //Return OK on PSP, since if we dont, it will error when trying to access the community moon and soft-lock the save file return context.IsPSP() ? OK : BadRequest; - - database.AddGriefReport(body); return OK; } diff --git a/Refresh.GameServer/Types/Report/GameReport.cs b/Refresh.GameServer/Types/Report/GameReport.cs index ab80aceb..5d901c37 100644 --- a/Refresh.GameServer/Types/Report/GameReport.cs +++ b/Refresh.GameServer/Types/Report/GameReport.cs @@ -7,7 +7,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("griefReport")] -public partial class GameReport : IRealmObject, ISequentialId +public partial class GameReport { [XmlIgnore] private IList InternalInfoBubble { get; } @@ -85,11 +85,4 @@ public Player[] Players [XmlElement("screenElements")] public ScreenElements ScreenElements { get; set; } - - [PrimaryKey] - public int SequentialId - { - get; - set; - } } \ No newline at end of file diff --git a/Refresh.LegacyDatabase/GameDatabaseProvider.cs b/Refresh.LegacyDatabase/GameDatabaseProvider.cs new file mode 100644 index 00000000..91908321 --- /dev/null +++ b/Refresh.LegacyDatabase/GameDatabaseProvider.cs @@ -0,0 +1,406 @@ +using Bunkum.RealmDatabase; +using Realms; +using Refresh.GameServer.Authentication; +using Refresh.GameServer.Database; +using Refresh.GameServer.Time; +using Refresh.GameServer.Types; +using Refresh.GameServer.Types.Activity; +using Refresh.GameServer.Types.Assets; +using Refresh.GameServer.Types.Comments; +using Refresh.GameServer.Types.Contests; +using Refresh.GameServer.Types.Levels; +using Refresh.GameServer.Types.Levels.SkillRewards; +using Refresh.GameServer.Types.Notifications; +using Refresh.GameServer.Types.Relations; +using Refresh.GameServer.Types.Report; +using Refresh.GameServer.Types.Reviews; +using Refresh.GameServer.Types.UserData; +using Refresh.GameServer.Types.UserData.Leaderboard; +using GamePhoto = Refresh.GameServer.Types.Photos.GamePhoto; +using GamePhotoSubject = Refresh.GameServer.Types.Photos.GamePhotoSubject; + +namespace Refresh.LegacyDatabase; + +public class GameDatabaseProvider : RealmDatabaseProvider +{ + private readonly IDateTimeProvider _time; + + public GameDatabaseProvider() + { + this._time = new SystemDateTimeProvider(); + } + + protected GameDatabaseProvider(IDateTimeProvider time) + { + this._time = time; + } + + protected override ulong SchemaVersion => 121; + + protected override string Filename => "refreshGameServer.realm"; + + protected override List SchemaTypes { get; } = new() + { + typeof(GameUser), + typeof(GameLocation), + typeof(UserPins), + typeof(Token), + typeof(GameLevel), + typeof(GameSkillReward), + typeof(GameComment), + typeof(CommentRelation), + typeof(FavouriteLevelRelation), + typeof(QueueLevelRelation), + typeof(FavouriteUserRelation), + typeof(PlayLevelRelation), + typeof(UniquePlayLevelRelation), + typeof(RateLevelRelation), + typeof(Event), + typeof(GameSubmittedScore), + typeof(GameAsset), + typeof(GameNotification), + typeof(GamePhoto), + typeof(GamePhotoSubject), + typeof(GameIpVerificationRequest), + typeof(GameAnnouncement), + typeof(QueuedRegistration), + typeof(EmailVerificationCode), + typeof(RequestStatistics), + typeof(SequentialIdStorage), + typeof(GameContest), + //grief report items + typeof(GameReport), + typeof(InfoBubble), + typeof(Marqee), + typeof(Player), + typeof(Rect), + typeof(ScreenElements), + typeof(ScreenRect), + typeof(Slot), + typeof(GameReview), + typeof(DisallowedUser), + }; + + public override void Warmup() + { + using GameDatabaseContext context = this.GetContext(); + _ = context.GetTotalLevelCount(); + } + + protected override GameDatabaseContext CreateContext() + { + return new GameDatabaseContext(this._time); + } + + protected override void Migrate(Migration migration, ulong oldVersion) + { + // Get the current unix timestamp for when we add timestamps to objects + long timestampMilliseconds = this._time.TimestampMilliseconds; + + // DO NOT USE FOR NEW MIGRATIONS! LBP almost never actually uses seconds for timestamps. + // This is from a mistake made early in development where this was not understood by me. + // Unless you are certain second timestamps are used, use the millisecond timestamps set above. + long timestampSeconds = this._time.TimestampSeconds; + + IQueryable? oldUsers = migration.OldRealm.DynamicApi.All("GameUser"); + IQueryable? newUsers = migration.NewRealm.All(); + + for (int i = 0; i < newUsers.Count(); i++) + { + dynamic oldUser = oldUsers.ElementAt(i); + GameUser newUser = newUsers.ElementAt(i); + + if (oldVersion < 3) + { + newUser.Description = ""; + newUser.Location = new GameLocation { X = 0, Y = 0, }; + } + + //In version 4, GameLocation went from TopLevel -> Embedded, and UserPins was added + if (oldVersion < 4) newUser.Pins = new UserPins(); + + // In version 12, users were given IconHashes + if (oldVersion < 12) newUser.IconHash = "0"; + + // In version 13, users were given PlanetsHashes + if (oldVersion < 13) newUser.Lbp2PlanetsHash = "0"; + + // In version 23, users were given bcrypt passwords + if (oldVersion < 23) newUser.PasswordBcrypt = null; + + // In version 26, users were given join dates + if (oldVersion < 26) newUser.JoinDate = DateTimeOffset.MinValue; + + // In version 40, we switched to Realm source generators which requires some values to be reset + if (oldVersion < 40) + { + newUser.IconHash = oldUser.IconHash; + newUser.Description = oldUser.Description; + newUser.Lbp2PlanetsHash = oldUser.PlanetsHash; + } + + // In version 67, users switched to dates to store their join date + if (oldVersion < 67) newUser.JoinDate = DateTimeOffset.FromUnixTimeMilliseconds(oldUser.JoinDate); + + // In version 69 (nice), users were given last login dates. For now, we'll set that to now. + if(oldVersion < 69 /*nice*/) newUser.LastLoginDate = DateTimeOffset.Now; + + // In version 72, users got settings for permissions regarding certain platforms. + // To avoid breakage, we set them to true for existing users. + if (oldVersion < 72) + { + newUser.PsnAuthenticationAllowed = true; + newUser.RpcnAuthenticationAllowed = true; + } + + // In version 81, we split out planet hashes for LBP2, LBP Vita, and LBP3 + // Since we've supported LBP2 as the primary game so far, set LBP2's hash to the old hash + if (oldVersion < 81) + { + newUser.Lbp2PlanetsHash = oldUser.PlanetsHash; + newUser.Lbp3PlanetsHash = oldUser.PlanetsHash; // Planets are forwards compatible + } + + // Fix vita + if (oldVersion < 82) + { + newUser.VitaPlanetsHash = "0"; + } + + // In version 94, we added an option to redirect grief reports to photos + if (oldVersion < 94) newUser.RedirectGriefReportsToPhotos = false; + + // In version 100, we started enforcing lowercase email addresses + if (oldVersion < 100) newUser.EmailAddress = oldUser.EmailAddress?.ToLowerInvariant(); + + // Version was bumped here to delete invalid favourite level relations + if (oldVersion < 101) migration.NewRealm.RemoveRange(newUser.FavouriteLevelRelations.Where(r => r.Level == null)); + + // In version 102 we split the Vita icon hash from the PS3 icon hash + if (oldVersion < 102) newUser.VitaIconHash = "0"; + + //In version 117, we started tracking the amount of data the user has uploaded to the server + if (oldVersion < 117) + { + newUser.FilesizeQuotaUsage = migration.NewRealm.All() + .AsEnumerable() + .Where(a => a.OriginalUploader?.UserId == newUser.UserId) + .Sum(a => a.SizeInBytes); + } + } + + IQueryable? oldLevels = migration.OldRealm.DynamicApi.All("GameLevel"); + IQueryable? newLevels = migration.NewRealm.All(); + + for (int i = 0; i < newLevels.Count(); i++) + { + dynamic oldLevel = oldLevels.ElementAt(i); + GameLevel newLevel = newLevels.ElementAt(i); + + // In version 10, GameLevels switched to int-based ids. + if (oldVersion < 10) + { + newLevel.LevelId = i + 1; + } + + // In version 11, timestamps were added to levels. + if (oldVersion < 11) + { + // Since we dont have a reference point for when the level was actually uploaded, default to now + newLevel.PublishDate = timestampSeconds; + newLevel.UpdateDate = timestampSeconds; + } + + // In version 14, level timestamps were fixed + if (oldVersion < 14) + { + newLevel.PublishDate = oldLevel.PublishDate * 1000; + newLevel.UpdateDate = oldLevel.UpdateDate * 1000; + } + + // In version 40, we switched to Realm source generators which requires some values to be reset + if (oldVersion < 40) + { + newLevel.Title = oldLevel.Title; + newLevel.IconHash = oldLevel.IconHash; + newLevel.Description = oldLevel.Description; + newLevel.RootResource = oldLevel.RootResource; + } + + // In version 57, we implemented minimum and maximum players which are 1 and 4 by default + if (oldVersion < 57) + { + newLevel.MinPlayers = 1; + newLevel.MaxPlayers = 4; + } + + // In version 79, we started tracking the version in which the level was uploaded from + // Set all levels to LBP2 by default, since that's the version we've supported up until now. + if (oldVersion < 79) + { + newLevel._GameVersion = (int)TokenGame.LittleBigPlanet2; + } + + // In version 92, we started storing both user and story levels. + // Set all existing levels to user levels, since that's what has existed up until now. + if (oldVersion < 92) + { + newLevel._Source = (int)GameLevelSource.User; + } + } + + // In version 22, tokens added expiry and types so just wipe them all + if (oldVersion < 22) migration.NewRealm.RemoveAll(); + + // In version 35, tokens added platforms and games + if (oldVersion < 35) migration.NewRealm.RemoveAll(); + + // IQueryable? oldEvents = migration.OldRealm.DynamicApi.All("Event"); + IQueryable? newEvents = migration.NewRealm.All(); + + List eventsToNuke = new(); + for (int i = 0; i < newEvents.Count(); i++) + { + // dynamic oldEvent = oldEvents.ElementAt(i); + Event newEvent = newEvents.ElementAt(i); + + // In version 30, events were given timestamps + if (oldVersion < 30) newEvent.Timestamp = timestampSeconds; + + // Fixes events with broken timestamps + if (oldVersion < 32 && newEvent.Timestamp == 0) newEvent.Timestamp = timestampSeconds; + + // Converts events to use millisecond timestamps + if (oldVersion < 33 && newEvent.Timestamp < 1000000000000) newEvent.Timestamp *= 1000; + + // fixup for dumb bad code not clearing score events when levels are deleted + if (oldVersion < 96 && newEvent.StoredDataType == EventDataType.Score) + { + GameSubmittedScore? score = migration.NewRealm.All().FirstOrDefault(s => s.ScoreId == newEvent.StoredObjectId); + if(score == null) eventsToNuke.Add(newEvent); + } + } + + // realm won't let you use an IEnumerable in RemoveRange. too bad! + foreach (Event eventToNuke in eventsToNuke) + { + migration.NewRealm.Remove(eventToNuke); + } + + // IQueryable? oldTokens = migration.OldRealm.DynamicApi.All("Token"); + IQueryable? newTokens = migration.NewRealm.All(); + + for (int i = 0; i < newTokens.Count(); i++) + { + // dynamic oldToken = oldTokens.ElementAt(i); + Token newToken = newTokens.ElementAt(i); + + if (oldVersion < 36) newToken.LoginDate = DateTimeOffset.FromUnixTimeMilliseconds(timestampMilliseconds); + } + + IQueryable? oldComments = migration.OldRealm.DynamicApi.All("GameComment"); + IQueryable? newComments = migration.NewRealm.All(); + + for (int i = 0; i < newComments.Count(); i++) + { + dynamic oldComment = oldComments.ElementAt(i); + GameComment newComment = newComments.ElementAt(i); + + // In version 40, we switched to Realm source generators which requires some values to be reset + if (oldVersion < 40) + { + newComment.Content = oldComment.Content; + } + } + + IQueryable? oldPhotos = migration.OldRealm.DynamicApi.All("GamePhoto"); + IQueryable? newPhotos = migration.NewRealm.All(); + + for (int i = 0; i < oldPhotos.Count(); i++) + { + dynamic oldPhoto = oldPhotos.ElementAt(i); + GamePhoto newPhoto = newPhotos.ElementAt(i); + + // In version 52, the timestamp on photos were corrected + if (oldVersion < 52) + { + newPhoto.TakenAt = DateTimeOffset.FromUnixTimeSeconds(oldPhoto.TakenAt.ToUnixTimeMilliseconds()); + } + + if (oldVersion < 110) + { + newPhoto.LargeAsset = migration.NewRealm.Find(oldPhoto.LargeHash.StartsWith("psp/") ? oldPhoto.LargeHash.Substring(4) : oldPhoto.LargeHash); + newPhoto.MediumAsset = migration.NewRealm.Find(oldPhoto.MediumHash.StartsWith("psp/") ? oldPhoto.MediumHash.Substring(4) : oldPhoto.MediumHash); + newPhoto.SmallAsset = migration.NewRealm.Find(oldPhoto.SmallHash.StartsWith("psp/") ? oldPhoto.SmallHash.Substring(4) : oldPhoto.SmallHash); + } + } + + IQueryable? oldAssets = migration.OldRealm.DynamicApi.All("GameAsset"); + IQueryable? newAssets = migration.NewRealm.All(); + + for (int i = 0; i < newAssets.Count(); i++) + { + dynamic oldAsset = oldAssets.ElementAt(i); + GameAsset newAsset = newAssets.ElementAt(i); + + if (oldVersion < 88) + { + // We don't have any more advanced heuristics here, + // but TGA files are the only asset currently affected by the tracking of `IsPSP`, + // and PSP is the only game to upload TGA files. + newAsset.IsPSP = newAsset.AssetType == GameAssetType.Tga; + } + } + + // Remove all scores with a null level, as in version 92 we started tracking story leaderboards differently + if (oldVersion < 92) migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Level == null)); + + IQueryable? oldScores = migration.OldRealm.DynamicApi.All("GameSubmittedScore"); + IQueryable? newScores = migration.NewRealm.All(); + + for (int i = 0; i < newScores.Count(); i++) + { + dynamic oldScore = oldScores.ElementAt(i); + GameSubmittedScore newScore = newScores.ElementAt(i); + + if (oldVersion < 92) + { + newScore.Game = newScore.Level.GameVersion; + } + + // In version 104 we started tracking the platform + if (oldVersion < 104) + { + // Determine the most reasonable platform for the score's game + TokenPlatform platform = newScore.Game switch + { + TokenGame.LittleBigPlanet1 => TokenPlatform.PS3, + TokenGame.LittleBigPlanet2 => TokenPlatform.PS3, + TokenGame.LittleBigPlanet3 => TokenPlatform.PS3, + TokenGame.LittleBigPlanetVita => TokenPlatform.Vita, + TokenGame.LittleBigPlanetPSP => TokenPlatform.PSP, + TokenGame.BetaBuild => TokenPlatform.RPCS3, + TokenGame.Website => throw new InvalidOperationException($"what? score id {newScore.ScoreId} by {newScore.Players[0].Username} is fucked"), + _ => throw new ArgumentOutOfRangeException(), + }; + + newScore.Platform = platform; + } + } + + IQueryable? oldPlayLevelRelations = migration.OldRealm.DynamicApi.All("PlayLevelRelation"); + IQueryable? newPlayLevelRelations = migration.NewRealm.All(); + + for (int i = 0; i < newPlayLevelRelations.Count(); i++) + { + dynamic oldPlayLevelRelation = oldPlayLevelRelations.ElementAt(i); + PlayLevelRelation newPlayLevelRelation = newPlayLevelRelations.ElementAt(i); + + //In version 93, we added a count to PlayLevelRelation + if (oldVersion < 93) + { + newPlayLevelRelation.Count = 1; + } + } + } +} \ No newline at end of file diff --git a/Refresh.LegacyDatabase/Refresh.LegacyDatabase.csproj b/Refresh.LegacyDatabase/Refresh.LegacyDatabase.csproj new file mode 100644 index 00000000..de56a517 --- /dev/null +++ b/Refresh.LegacyDatabase/Refresh.LegacyDatabase.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/Refresh.sln b/Refresh.sln index a834dfa7..971f2740 100644 --- a/Refresh.sln +++ b/Refresh.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefreshTests.GameServer", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Refresh.Common", "Refresh.Common\Refresh.Common.csproj", "{A3D76B31-8732-4134-907B-F36773DF70D3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Refresh.LegacyDatabase", "Refresh.LegacyDatabase\Refresh.LegacyDatabase.csproj", "{EB712F6C-8CE2-4CAC-9327-7E8517BFF750}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,12 @@ Global {A3D76B31-8732-4134-907B-F36773DF70D3}.Release|Any CPU.Build.0 = Release|Any CPU {A3D76B31-8732-4134-907B-F36773DF70D3}.DebugLocalBunkum|Any CPU.ActiveCfg = Debug|Any CPU {A3D76B31-8732-4134-907B-F36773DF70D3}.DebugLocalBunkum|Any CPU.Build.0 = Debug|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.Release|Any CPU.Build.0 = Release|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.DebugLocalBunkum|Any CPU.ActiveCfg = Debug|Any CPU + {EB712F6C-8CE2-4CAC-9327-7E8517BFF750}.DebugLocalBunkum|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution EndGlobalSection diff --git a/RefreshTests.GameServer/Tests/Reporting/ReportingEndpointsTests.cs b/RefreshTests.GameServer/Tests/Reporting/ReportingEndpointsTests.cs index 2e08e5b9..31d3b8da 100644 --- a/RefreshTests.GameServer/Tests/Reporting/ReportingEndpointsTests.cs +++ b/RefreshTests.GameServer/Tests/Reporting/ReportingEndpointsTests.cs @@ -76,14 +76,6 @@ public void UploadReport() HttpResponseMessage response = client.PostAsync("/lbp/grief", new StringContent(report.AsXML())).Result; Assert.That(response.StatusCode, Is.EqualTo(OK)); - - context.Database.Refresh(); - - DatabaseList griefReports = context.Database.GetGriefReports(10, 0); - Assert.That(griefReports.TotalItems, Is.EqualTo(1)); - List reports = griefReports.Items.ToList(); - Assert.That(reports[0].Description, Is.EqualTo(report.Description)); - Assert.That(reports[0].LevelId, Is.EqualTo(report.LevelId)); } [Test] @@ -101,13 +93,6 @@ public void CanUploadReportWithBadLevel() HttpResponseMessage response = client.PostAsync("/lbp/grief", new StringContent(report.AsXML())).Result; Assert.That(response.StatusCode, Is.EqualTo(OK)); - - context.Database.Refresh(); - - DatabaseList griefReports = context.Database.GetGriefReports(10, 0); - Assert.That(griefReports.TotalItems, Is.EqualTo(1)); - List reports = griefReports.Items.ToList(); - Assert.That(reports[0].Description, Is.EqualTo(report.Description)); } [TestCase(true)] From 6cee3921d274cb7263cf1efec86a7540048d67c9 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 23 Apr 2024 19:48:26 -0400 Subject: [PATCH 02/15] Remove relation backlinks from GameLevel --- .../Database/GameDatabaseContext.Relations.cs | 24 +++++++++++++- .../Database/GameDatabaseProvider.cs | 9 ------ .../Response/ApiGameLevelResponse.cs | 21 +++++++----- .../DataTypes/Response/GameLevelResponse.cs | 32 ++++++++----------- Refresh.GameServer/Types/Levels/GameLevel.cs | 24 +++----------- Refresh.GameServer/Types/Report/GameReport.cs | 8 ++--- Refresh.GameServer/Types/Report/InfoBubble.cs | 2 +- Refresh.GameServer/Types/Report/Marqee.cs | 2 +- Refresh.GameServer/Types/Report/Player.cs | 2 +- Refresh.GameServer/Types/Report/Rect.cs | 2 +- .../Types/Report/ScreenElements.cs | 12 +++---- Refresh.GameServer/Types/Report/ScreenRect.cs | 2 +- Refresh.GameServer/Types/Report/Slot.cs | 2 +- .../Types/Reviews/SerializedGameReview.cs | 13 ++++++-- .../Workers/CoolLevelsWorker.cs | 18 +++++------ .../Tests/Levels/ScoreLeaderboardTests.cs | 11 ++++--- 16 files changed, 95 insertions(+), 89 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs index 05a125dc..e84701e5 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs @@ -53,6 +53,10 @@ public bool UnfavouriteLevel(GameLevel level, GameUser user) return true; } + + public int GetFavouriteCountForLevel(GameLevel level) => this._realm.All() + .Count(r => r.Level == level); + #endregion #region Favouriting Users @@ -219,6 +223,9 @@ public bool RateLevel(GameLevel level, GameUser user, RatingType type) return true; } + public int GetTotalRatingsForLevel(GameLevel level, RatingType type) => + this._realm.All().Count(r => r.Level == level && r._RatingType == (int)type); + /// /// Adds a review to the database, deleting any old ones by the user on that level. /// @@ -304,7 +311,22 @@ public void PlayLevel(GameLevel level, GameUser user, int count) public bool HasUserPlayedLevel(GameLevel level, GameUser user) => this._realm.All() .FirstOrDefault(r => r.Level == level && r.User == user) != null; - + + public IEnumerable GetAllPlaysForLevel(GameLevel level) => + this._realm.All().Where(r => r.Level == level); + + public IEnumerable GetAllPlaysForLevelByUser(GameLevel level, GameUser user) => + this._realm.All().Where(r => r.Level == level && r.User == user); + + public int GetTotalPlaysForLevel(GameLevel level) => + this.GetAllPlaysForLevel(level).Sum(playLevelRelation => playLevelRelation.Count); + + public int GetTotalPlaysForLevelByUser(GameLevel level, GameUser user) => + this.GetAllPlaysForLevelByUser(level, user).Sum(playLevelRelation => playLevelRelation.Count); + + public int GetUniquePlaysForLevel(GameLevel level) => + this._realm.All().Count(r => r.Level == level); + #endregion #region Comments diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index 8b14d3d9..d17936fc 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -67,15 +67,6 @@ protected GameDatabaseProvider(IDateTimeProvider time) typeof(RequestStatistics), typeof(SequentialIdStorage), typeof(GameContest), - //grief report items - typeof(GameReport), - typeof(InfoBubble), - typeof(Marqee), - typeof(Player), - typeof(Rect), - typeof(ScreenElements), - typeof(ScreenRect), - typeof(Slot), typeof(GameReview), typeof(DisallowedUser), }; diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs index 03137d5f..434bfb8a 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs @@ -34,10 +34,10 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom? SkillRewards { get; set; } - public required int YayRatings { get; set; } - public required int BooRatings { get; set; } - public required int Hearts { get; set; } - public required int UniquePlays { get; set; } + public int YayRatings { get; set; } + public int BooRatings { get; set; } + public int Hearts { get; set; } + public int UniquePlays { get; set; } public required bool TeamPicked { get; set; } public required GameLevelType LevelType { get; set; } public required bool IsLocked { get; set; } @@ -69,10 +69,6 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom r._RatingType == (int)RatingType.Yay), - BooRatings = level.Ratings.Count(r => r._RatingType == (int)RatingType.Boo), - Hearts = level.FavouriteRelations.Count(), - UniquePlays = level.UniquePlays.Count(), TeamPicked = level.TeamPicked, RootLevelHash = level.RootResource, GameVersion = level.GameVersion, @@ -90,6 +86,15 @@ public void FillInExtraData(GameDatabaseContext database, IDataStore dataStore) //Get the icon form of the icon asset this.IconHash = database.GetAssetFromHash(this._originalIconHash ?? "0")?.GetAsIcon(TokenGame.Website, database, dataStore) ?? this.IconHash; + + // TODO: no + GameLevel? level = database.GetLevelById(this.LevelId); + if (level == null) return; + + this.YayRatings = database.GetTotalRatingsForLevel(level, RatingType.Yay); + this.BooRatings = database.GetTotalRatingsForLevel(level, RatingType.Boo); + this.Hearts = database.GetFavouriteCountForLevel(level); + this.UniquePlays = database.GetUniquePlaysForLevel(level); } public static ApiGameLevelResponse? FromOldWithExtraData(GameLevel? old, GameDatabaseContext database, IDataStore dataStore) diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index e49da67f..4df4a3a0 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -43,14 +43,14 @@ public class GameLevelResponse : IDataConvertableFrom r._RatingType == (int)RatingType.Yay), - BooCount = old.Ratings.Count(r => r._RatingType == (int)RatingType.Boo), SkillRewards = old.SkillRewards.ToList(), TeamPicked = old.TeamPicked, LevelType = old.LevelType.ToGameString(), @@ -126,7 +115,6 @@ public class GameLevelResponse : IDataConvertableFrom p.User == user); + this.YourLbp2PlayCount = database.GetTotalPlaysForLevelByUser(level, user); this.PlayerCount = matchService.GetPlayerCountForLevel(RoomSlotType.Online, this.LevelId); GameAsset? rootResourceAsset = database.GetAssetFromHash(this.RootResource); @@ -175,5 +163,13 @@ private void FillInExtraData(GameDatabaseContext database, MatchService matchSer this.IconHash = database.GetAssetFromHash(this.IconHash)?.GetAsIcon(game, database, dataStore) ?? this.IconHash; this.CommentCount = level.LevelComments.Count; + + this.HeartCount = database.GetFavouriteCountForLevel(level); + this.TotalPlayCount = database.GetTotalPlaysForLevel(level); + this.UniquePlayCount = database.GetUniquePlaysForLevel(level); + this.YayCount = database.GetTotalRatingsForLevel(level, RatingType.Yay); + this.BooCount = database.GetTotalRatingsForLevel(level, RatingType.Boo); + + this.AverageStarRating = level.CalculateAverageStarRating(database); } } \ No newline at end of file diff --git a/Refresh.GameServer/Types/Levels/GameLevel.cs b/Refresh.GameServer/Types/Levels/GameLevel.cs index ff280dd3..0415726c 100644 --- a/Refresh.GameServer/Types/Levels/GameLevel.cs +++ b/Refresh.GameServer/Types/Levels/GameLevel.cs @@ -92,20 +92,6 @@ [Ignored] public GameLevelSource Source #nullable disable public IList LevelComments { get; } - [Backlink(nameof(FavouriteLevelRelation.Level))] - public IQueryable FavouriteRelations { get; } - - [Backlink(nameof(UniquePlayLevelRelation.Level))] - public IQueryable UniquePlays { get; } - - [Backlink(nameof(PlayLevelRelation.Level))] - public IQueryable AllPlays { get; } - [Backlink(nameof(GameSubmittedScore.Level))] - public IQueryable Scores { get; } - - [Backlink(nameof(RateLevelRelation.Level))] - public IQueryable Ratings { get; } - // ILists can't be serialized to XML, and Lists/Arrays cannot be stored in realm, // hence _SkillRewards and SkillRewards both existing // ReSharper disable once InconsistentNaming @@ -153,13 +139,13 @@ public int SequentialId /// Calculates the average rating of a level based on the ratings it has. /// /// A double between 1 and 5, indicating the level's average ratings. - public double CalculateAverageStarRating() + public double CalculateAverageStarRating(GameDatabaseContext database) { - int yayCount = this.Ratings.Count(x => x._RatingType == (int)RatingType.Yay); - int booCount = this.Ratings.Count(x => x._RatingType == (int)RatingType.Boo); - int neutralCount = this.Ratings.Count(x => x._RatingType == (int)RatingType.Neutral); + int yayCount = database.GetTotalRatingsForLevel(this, RatingType.Yay); + int booCount = database.GetTotalRatingsForLevel(this, RatingType.Boo); + int neutralCount = database.GetTotalRatingsForLevel(this, RatingType.Neutral); - // Return 0 if all the counts are 0, we dont want a div by 0 error! + // Return 0 if all the counts are 0, we don't want a div by 0 error! if (yayCount + booCount + neutralCount == 0) return 0; return (double)((5 * yayCount) + (1 * booCount) + (3 * neutralCount)) / (yayCount + booCount + neutralCount); diff --git a/Refresh.GameServer/Types/Report/GameReport.cs b/Refresh.GameServer/Types/Report/GameReport.cs index 5d901c37..788b2d4b 100644 --- a/Refresh.GameServer/Types/Report/GameReport.cs +++ b/Refresh.GameServer/Types/Report/GameReport.cs @@ -9,8 +9,7 @@ namespace Refresh.GameServer.Types.Report; [XmlRoot("griefReport")] public partial class GameReport { - [XmlIgnore] - private IList InternalInfoBubble { get; } + [XmlIgnore] private List InternalInfoBubble { get; } = []; [Ignored] [XmlElement("infoBubble")] @@ -60,9 +59,8 @@ public InfoBubble[] InfoBubble [XmlElement("jpegHash")] public string JpegHash { get; set; } - - [XmlIgnore] - private IList InternalPlayers { get; } + + [XmlIgnore] private List InternalPlayers { get; } = []; [Ignored] [XmlElement("player")] diff --git a/Refresh.GameServer/Types/Report/InfoBubble.cs b/Refresh.GameServer/Types/Report/InfoBubble.cs index 65d191c3..e2e94d08 100644 --- a/Refresh.GameServer/Types/Report/InfoBubble.cs +++ b/Refresh.GameServer/Types/Report/InfoBubble.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("infoBubble")] -public partial class InfoBubble : IEmbeddedObject +public partial class InfoBubble { [XmlElement("slot")] public Slot Slot { get; set; } diff --git a/Refresh.GameServer/Types/Report/Marqee.cs b/Refresh.GameServer/Types/Report/Marqee.cs index d7c28709..e91d675c 100644 --- a/Refresh.GameServer/Types/Report/Marqee.cs +++ b/Refresh.GameServer/Types/Report/Marqee.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("marqee")] -public partial class Marqee : IEmbeddedObject +public partial class Marqee { [XmlElement("rect")] public Rect Rect { get; set; } diff --git a/Refresh.GameServer/Types/Report/Player.cs b/Refresh.GameServer/Types/Report/Player.cs index c88d5730..c7b4031b 100644 --- a/Refresh.GameServer/Types/Report/Player.cs +++ b/Refresh.GameServer/Types/Report/Player.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("player")] -public partial class Player : IEmbeddedObject +public partial class Player { [XmlElement("id")] public string Username { get; set; } diff --git a/Refresh.GameServer/Types/Report/Rect.cs b/Refresh.GameServer/Types/Report/Rect.cs index 3f578edb..0f92f452 100644 --- a/Refresh.GameServer/Types/Report/Rect.cs +++ b/Refresh.GameServer/Types/Report/Rect.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Types.Report; [XmlRoot("rect")] -public partial class Rect : IEmbeddedObject +public partial class Rect { [XmlAttribute(AttributeName="t")] public long Top { get; set; } diff --git a/Refresh.GameServer/Types/Report/ScreenElements.cs b/Refresh.GameServer/Types/Report/ScreenElements.cs index cc18283d..a9159b1b 100644 --- a/Refresh.GameServer/Types/Report/ScreenElements.cs +++ b/Refresh.GameServer/Types/Report/ScreenElements.cs @@ -6,10 +6,9 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("screenElements")] -public partial class ScreenElements : IEmbeddedObject -{ - [XmlIgnore] - private IList InternalSlot { get; } +public partial class ScreenElements +{ + [XmlIgnore] private List InternalSlot { get; } = []; [XmlElement("slot")] public Slot[] Slot @@ -28,9 +27,8 @@ public Slot[] Slot this.InternalSlot.Add(slot); } } - - [XmlIgnore] - private IList InternalPlayer { get; } + + [XmlIgnore] private List InternalPlayer = []; [XmlElement("player")] public Player[] Player diff --git a/Refresh.GameServer/Types/Report/ScreenRect.cs b/Refresh.GameServer/Types/Report/ScreenRect.cs index 5ea6eeff..d7e49418 100644 --- a/Refresh.GameServer/Types/Report/ScreenRect.cs +++ b/Refresh.GameServer/Types/Report/ScreenRect.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("screenRect")] -public partial class ScreenRect : IEmbeddedObject +public partial class ScreenRect { [XmlElement("rect")] public Rect Rect { get; set; } diff --git a/Refresh.GameServer/Types/Report/Slot.cs b/Refresh.GameServer/Types/Report/Slot.cs index 09559859..a9c97e82 100644 --- a/Refresh.GameServer/Types/Report/Slot.cs +++ b/Refresh.GameServer/Types/Report/Slot.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Types.Report; #nullable disable [XmlRoot("slot")] -public partial class Slot : IEmbeddedObject +public partial class Slot { [XmlElement("id")] public int Id { get; set; } diff --git a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs index 4d5c49f5..44a14e46 100644 --- a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs +++ b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs @@ -68,7 +68,7 @@ public class SerializedGameReview : IDataConvertableFrom r.User == review.Publisher)?.RatingType.ToDPad() ?? 0, + Thumb = 0, ThumbsUp = 0, ThumbsDown = 0, YourThumb = 0, @@ -77,7 +77,16 @@ public class SerializedGameReview : IDataConvertableFrom FromOldList(IEnumerable oldList) => oldList.Select(FromOld).ToList()!; diff --git a/Refresh.GameServer/Workers/CoolLevelsWorker.cs b/Refresh.GameServer/Workers/CoolLevelsWorker.cs index 88f7f2a3..bfe4cb2a 100644 --- a/Refresh.GameServer/Workers/CoolLevelsWorker.cs +++ b/Refresh.GameServer/Workers/CoolLevelsWorker.cs @@ -32,10 +32,10 @@ public bool DoWork(Logger logger, IDataStore dataStore, GameDatabaseContext data logger.LogTrace(RefreshContext.CoolLevels, "Calculating score for '{0}' ({1})", level.Title, level.LevelId); float decayMultiplier = CalculateLevelDecayMultiplier(logger, now, level); - // Calculate positive & negative score separately so we don't run into issues with + // Calculate positive & negative score separately, so we don't run into issues with // the multiplier having an opposite effect with the negative score as time passes - int positiveScore = CalculatePositiveScore(logger, level); - int negativeScore = CalculateNegativeScore(logger, level); + int positiveScore = CalculatePositiveScore(logger, level, database); + int negativeScore = CalculateNegativeScore(logger, level, database); // Increase to tweak how little negative score gets affected by decay const int negativeScoreMultiplier = 2; @@ -74,7 +74,7 @@ private static float CalculateLevelDecayMultiplier(Logger logger, long now, Game return multiplier; } - private static int CalculatePositiveScore(Logger logger, GameLevel level) + private static int CalculatePositiveScore(Logger logger, GameLevel level, GameDatabaseContext database) { int score = 15; // Start levels off with a few points to prevent one dislike from bombing the level const int positiveRatingPoints = 5; @@ -85,9 +85,9 @@ private static int CalculatePositiveScore(Logger logger, GameLevel level) if (level.TeamPicked) score += 10; - score += level.Ratings.Count(r => r._RatingType == (int)RatingType.Yay) * positiveRatingPoints; - score += level.UniquePlays.Count() * uniquePlayPoints; - score += level.FavouriteRelations.Count() * heartPoints; + score += database.GetTotalRatingsForLevel(level, RatingType.Yay) * positiveRatingPoints; + score += database.GetUniquePlaysForLevel(level) * uniquePlayPoints; + score += database.GetFavouriteCountForLevel(level) * heartPoints; if (level.Publisher?.Role == GameUserRole.Trusted) score += trustedAuthorPoints; @@ -96,7 +96,7 @@ private static int CalculatePositiveScore(Logger logger, GameLevel level) return score; } - private static int CalculateNegativeScore(Logger logger, GameLevel level) + private static int CalculateNegativeScore(Logger logger, GameLevel level, GameDatabaseContext database) { int penalty = 0; const int negativeRatingPenalty = 5; @@ -104,7 +104,7 @@ private static int CalculateNegativeScore(Logger logger, GameLevel level) const int restrictedAuthorPenalty = 50; const int bannedAuthorPenalty = 100; - penalty += level.Ratings.Count(r => r._RatingType == (int)RatingType.Boo) * negativeRatingPenalty; + penalty += database.GetTotalRatingsForLevel(level, RatingType.Boo) * negativeRatingPenalty; if (level.Publisher == null) penalty += noAuthorPenalty; diff --git a/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs b/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs index cbeca77d..0cb61487 100644 --- a/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs +++ b/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs @@ -333,7 +333,7 @@ public void PlayLevel() context.Database.Refresh(); - Assert.That(level.AllPlays.Count(), Is.EqualTo(1)); + Assert.That(context.Database.GetTotalPlaysForLevel(level), Is.EqualTo(1)); } [Test] @@ -363,7 +363,7 @@ public void PlayLevelWithCount() context.Database.Refresh(); - Assert.That(level.AllPlays.AsEnumerable().Sum(p => p.Count), Is.EqualTo(2)); + Assert.That(context.Database.GetTotalPlaysForLevel(level), Is.EqualTo(2)); } [Test] @@ -415,8 +415,9 @@ public void OvertakeNotificationsWorkProperly() for (int i = 0; i < users.Count; i++) { - GameSubmittedScore? lastBestScore = level - .Scores.OrderByDescending(s => s.Score).FirstOrDefault(); + // GameSubmittedScore? lastBestScore = level + // .Scores.OrderByDescending(s => s.Score).FirstOrDefault(); + GameSubmittedScore? lastBestScore = context.Database.GetTopScoresForLevel(level, 1, 0, 1, true).Items.FirstOrDefault(); GameUser user = users[i]; context.SubmitScore(i, 1, level, user, TokenGame.LittleBigPlanet2, TokenPlatform.PS3); @@ -440,7 +441,7 @@ public void OvertakeNotificationsWorkProperly() } // Check that notification was not sent to people who weren't previously #1 - if (level.Scores.Count() <= 2) continue; + if (context.Database.GetTotalPlaysForLevel(level) <= 2) continue; notificationRecipient = users[i - 2]; Assert.That(notificationRecipient.Notifications.Any(), Is.False); From d40d01b09fdb0d5c35b955cd1b5c3a76ce569719 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 23 Apr 2024 20:08:51 -0400 Subject: [PATCH 03/15] Remove IDataConvertableFrom --- Refresh.GameServer/Database/DatabaseList.cs | 8 +++----- .../Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs | 2 +- .../Endpoints/ApiV3/AuthenticationApiEndpoints.cs | 4 ++-- .../Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs | 8 -------- .../Response/Admin/ApiAdminQueuedRegistrationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiActivityPageResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiContestResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiErrorResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiEventResponse.cs | 2 +- .../DataTypes/Response/ApiExtendedGameUserResponse.cs | 2 +- .../DataTypes/Response/ApiGameAnnouncementResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameAssetResponse.cs | 2 +- .../Response/ApiGameIpVerificationRequestResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameLevelResponse.cs | 2 +- .../DataTypes/Response/ApiGameNotificationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs | 2 +- .../DataTypes/Response/ApiGamePhotoSubjectResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameReviewResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameRoomResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameScoreResponse.cs | 2 +- .../DataTypes/Response/ApiGameSkillRewardResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameUserResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiParameterResponse.cs | 2 +- .../DataTypes/Response/ApiRequestStatisticsResponse.cs | 2 +- .../Response/ApiRichPresenceConfigurationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiRouteResponse.cs | 2 +- .../Endpoints/ApiV3/LeaderboardApiEndpoints.cs | 2 +- Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs | 2 +- .../Endpoints/ApiV3/NotificationApiEndpoints.cs | 2 +- Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs | 8 ++++---- Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs | 2 +- .../Game/DataTypes/Response/GameLevelResponse.cs | 3 +-- .../Endpoints/Game/DataTypes/Response/GameUserResponse.cs | 3 +-- .../Types/Levels/GameMinimalLevelResponse.cs | 3 +-- Refresh.GameServer/Types/Reviews/SerializedGameReview.cs | 3 +-- 37 files changed, 42 insertions(+), 56 deletions(-) delete mode 100644 Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs diff --git a/Refresh.GameServer/Database/DatabaseList.cs b/Refresh.GameServer/Database/DatabaseList.cs index b423748b..7de3b24b 100644 --- a/Refresh.GameServer/Database/DatabaseList.cs +++ b/Refresh.GameServer/Database/DatabaseList.cs @@ -1,5 +1,3 @@ -using Refresh.GameServer.Endpoints.ApiV3.DataTypes; - namespace Refresh.GameServer.Database; public class DatabaseList where TObject : class @@ -54,11 +52,11 @@ private DatabaseList(int oldTotalItems, int oldNextPageIndex, IEnumerable FromOldList(DatabaseList oldList) - where TNewObject : class, IDataConvertableFrom + public static DatabaseList FromOldList(DatabaseList oldList, Func mapFunc) + where TNewObject : class where TOldObject : class { - DatabaseList newList = new(oldList.TotalItems, oldList.NextPageIndex, TNewObject.FromOldList(oldList.Items)); + DatabaseList newList = new(oldList.TotalItems, oldList.NextPageIndex, oldList.Items.Select(mapFunc)!); return newList; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs index 7a846546..5d2c753b 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs @@ -48,7 +48,7 @@ public ApiResponse GetExtendedUserByUuid(RequestCon public ApiListResponse GetExtendedUsers(RequestContext context, GameDatabaseContext database, IDataStore dataStore) { (int skip, int count) = context.GetPageData(true); - DatabaseList list = DatabaseList.FromOldList(database.GetUsers(count, skip)); + DatabaseList list = DatabaseList.FromOldList(database.GetUsers(count, skip), ApiExtendedGameUserResponse.FromOld); //Fill in the extra data of all the users foreach (ApiExtendedGameUserResponse user in list.Items) user.FillInExtraData(database, dataStore); return list; diff --git a/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs index 714ebe71..2c3b8d03 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs @@ -185,8 +185,8 @@ public ApiListResponse GetVerificationRequ { (int skip, int count) = context.GetPageData(true); - return DatabaseList.FromOldList - (database.GetIpVerificationRequestsForUser(user, count, skip)); + return DatabaseList.FromOldList + (database.GetIpVerificationRequestsForUser(user, count, skip), ApiGameIpVerificationRequestResponse.FromOld); } [ApiV3Endpoint("verificationRequests/approve", HttpMethods.Put)] diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs deleted file mode 100644 index 787d576c..00000000 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes; - -public interface IDataConvertableFrom where TNew : IDataConvertableFrom -{ - public static abstract TNew? FromOld(TOld? old); - - public static abstract IEnumerable FromOldList(IEnumerable oldList); -} \ No newline at end of file diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs index 79a4c4a0..2618d36a 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response.Admin; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiAdminQueuedRegistrationResponse : IApiResponse, IDataConvertableFrom +public class ApiAdminQueuedRegistrationResponse : IApiResponse { public required string RegistrationId { get; set; } public required string Username { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs index 2a2c4cf8..cdc138ff 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiActivityPageResponse : IApiResponse, IDataConvertableFrom +public class ApiActivityPageResponse : IApiResponse { public required IEnumerable Events { get; set; } public required IEnumerable Users { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs index bb8bd758..5b9f7dfc 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiContestResponse : IApiResponse, IDataConvertableFrom +public class ApiContestResponse : IApiResponse { public required string ContestId { get; set; } public required ApiGameUserResponse Organizer { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs index 0c7c6353..14705e3b 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiErrorResponse : IApiResponse, IDataConvertableFrom +public class ApiErrorResponse : IApiResponse { public required string Name { get; set; } public required string OccursWhen { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs index 911074d9..a9d3993a 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiEventResponse : IApiResponse, IDataConvertableFrom +public class ApiEventResponse : IApiResponse { public required string EventId { get; set; } public required int EventType { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs index 54195b3b..a88ecb38 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs @@ -11,7 +11,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; /// A user with full information, like current role, ban status, etc. /// [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiExtendedGameUserResponse : IApiResponse, IDataConvertableFrom +public class ApiExtendedGameUserResponse : IApiResponse { public required string UserId { get; set; } public required string Username { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs index c81637ae..ad633306 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameAnnouncementResponse : IApiResponse, IDataConvertableFrom +public class ApiGameAnnouncementResponse : IApiResponse { public required string AnnouncementId { get; set; } public required string Title { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs index 3cb3f431..2846bcf1 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameAssetResponse : IApiResponse, IDataConvertableFrom +public class ApiGameAssetResponse : IApiResponse { public required string AssetHash { get; set; } public required ApiGameUserResponse? OriginalUploader { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs index 41101380..2b2bcda5 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameIpVerificationRequestResponse : IApiResponse, IDataConvertableFrom +public class ApiGameIpVerificationRequestResponse : IApiResponse { public required string IpAddress { get; set; } public required DateTimeOffset CreatedAt { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs index 434bfb8a..5a010bad 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs @@ -7,7 +7,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom +public class ApiGameLevelResponse : IApiResponse { public required int LevelId { get; set; } public required ApiGameUserResponse? Publisher { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs index d208eff2..105ba69e 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameNotificationResponse : IApiResponse, IDataConvertableFrom +public class ApiGameNotificationResponse : IApiResponse { public required string NotificationId { get; set; } public required string Title { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs index f312aff6..269fe3a6 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGamePhotoResponse : IApiResponse, IDataConvertableFrom +public class ApiGamePhotoResponse : IApiResponse { public required int PhotoId { get; set; } public required DateTimeOffset TakenAt { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs index c85c88af..8a3d9d25 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGamePhotoSubjectResponse : IApiResponse,IDataConvertableFrom +public class ApiGamePhotoSubjectResponse : IApiResponse { public required ApiGameUserResponse? User { get; set; } public required string DisplayName { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs index b0cf9140..cf624d70 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameReviewResponse : IApiResponse, IDataConvertableFrom +public class ApiGameReviewResponse : IApiResponse { public required int ReviewId { get; set; } public required ApiGameLevelResponse Level { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs index ca78c594..f93be5a2 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameRoomPlayerResponse : IApiResponse, IDataConvertableFrom +public class ApiGameRoomPlayerResponse : IApiResponse { public required string Username { get; set; } public required string? UserId { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs index f040c1ad..248cc57f 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameRoomResponse : IApiResponse, IDataConvertableFrom +public class ApiGameRoomResponse : IApiResponse { public required string RoomId { get; set; } public required IEnumerable PlayerIds { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs index 4dec52a3..b1042370 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameScoreResponse : IApiResponse, IDataConvertableFrom +public class ApiGameScoreResponse : IApiResponse { public required string ScoreId { get; set; } public required ApiGameLevelResponse Level { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs index fe80b8f7..47d9499c 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameSkillRewardResponse : IApiResponse, IDataConvertableFrom +public class ApiGameSkillRewardResponse : IApiResponse { public int Id { get; set; } public bool Enabled { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs index b03c79c4..9e5254ea 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs @@ -7,7 +7,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameUserResponse : IApiResponse, IDataConvertableFrom +public class ApiGameUserResponse : IApiResponse { // HEY! When adding fields here, remember to propagate them in ApiExtendedGameUser too! // Otherwise, they won't show up in the admin panel endpoints or /users/me. Thank you! diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs index c6ab7d7b..0fa34151 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs @@ -11,7 +11,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiLevelCategoryResponse : IApiResponse, IDataConvertableFrom +public class ApiLevelCategoryResponse : IApiResponse { public required string Name { get; set; } public required string Description { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs index 92b048a1..896c74b9 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiParameterResponse : IApiResponse, IDataConvertableFrom +public class ApiParameterResponse : IApiResponse { public required string Name { get; set; } [JsonConverter(typeof(StringEnumConverter))] diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs index 8a2492ff..a42e896b 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRequestStatisticsResponse : IApiResponse, IDataConvertableFrom +public class ApiRequestStatisticsResponse : IApiResponse { public required long TotalRequests { get; set; } public required long ApiRequests { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs index ff3419b1..597cda2d 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRichPresenceConfigurationResponse : IApiResponse, IDataConvertableFrom +public class ApiRichPresenceConfigurationResponse : IApiResponse { public required string ApplicationId { get; set; } public required string PartyIdPrefix { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs index fd57eacc..7ace7915 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRouteResponse : IApiResponse, IDataConvertableFrom +public class ApiRouteResponse : IApiResponse { public required string Method { get; set; } public required string RouteUri { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs index 25e1e693..b8c973f3 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs @@ -34,7 +34,7 @@ public ApiListResponse GetTopScoresForLevel(RequestContext if (!result) return ApiValidationError.BooleanParseError; DatabaseList scores = database.GetTopScoresForLevel(level, count, skip, (byte)mode, showAll); - DatabaseList ret = DatabaseList.FromOldList(scores); + DatabaseList ret = DatabaseList.FromOldList(scores, ApiGameScoreResponse.FromOld); foreach (ApiGameScoreResponse score in ret.Items) score.FillInExtraData(database, dataStore); return ret; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs index cf8a1259..c2a9cac5 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs @@ -57,7 +57,7 @@ public ApiListResponse GetLevels(RequestContext context, G if (list == null) return ApiNotFoundError.Instance; - DatabaseList levels = DatabaseList.FromOldList(list); + DatabaseList levels = DatabaseList.FromOldList(list, ApiGameLevelResponse.FromOld); foreach (ApiGameLevelResponse level in levels.Items) { level.FillInExtraData(database, dataStore); diff --git a/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs index 265ad2da..ad58e800 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs @@ -24,7 +24,7 @@ public ApiListResponse GetNotifications(RequestCont { (int skip, int count) = context.GetPageData(true); DatabaseList notifications = database.GetNotificationsByUser(user, count, skip); - return DatabaseList.FromOldList(notifications); + return DatabaseList.FromOldList(notifications, ApiGameNotificationResponse.FromOld); } [ApiV3Endpoint("notifications/{uuid}"), MinimumRole(GameUserRole.Restricted)] diff --git a/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs index 0353fe79..f504ea48 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs @@ -39,7 +39,7 @@ private static ApiListResponse PhotosByUser(RequestContext (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosByUser(user, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos); + DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } @@ -50,7 +50,7 @@ private static ApiListResponse PhotosWithUser(RequestConte (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosWithUser(user, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos); + DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } @@ -61,7 +61,7 @@ private static ApiListResponse PhotosInLevel(RequestContex (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosInLevel(level, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos); + DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); foreach (ApiGamePhotoResponse photo in photosResponse.Items) { photo.FillInExtraData(database, dataStore); @@ -111,7 +111,7 @@ public ApiListResponse RecentPhotos(RequestContext context (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetRecentPhotos(count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos); + DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs index 599c2343..8093aa22 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs @@ -28,7 +28,7 @@ public ApiListResponse GetTopScoresForLevel(RequestContex (int skip, int count) = context.GetPageData(true); DatabaseList reviews = database.GetReviewsForLevel(level, count, skip); - DatabaseList ret = DatabaseList.FromOldList(reviews); + DatabaseList ret = DatabaseList.FromOldList(reviews, ApiGameReviewResponse.FromOld); return ret; } diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index 4df4a3a0..29779a84 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -2,7 +2,6 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; -using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Extensions; using Refresh.GameServer.Services; using Refresh.GameServer.Types; @@ -18,7 +17,7 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Response; [XmlRoot("slot")] [XmlType("slot")] -public class GameLevelResponse : IDataConvertableFrom +public class GameLevelResponse { [XmlElement("id")] public required int LevelId { get; set; } diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index 86010fdc..f11437d1 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -2,7 +2,6 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; -using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Types; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Lists; @@ -11,7 +10,7 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Response; [XmlRoot("user")] -public class GameUserResponse : IDataConvertableFrom +public class GameUserResponse { public const int MaximumLevels = 9_999; diff --git a/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs b/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs index 457c657f..49ef6db0 100644 --- a/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs +++ b/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs @@ -2,7 +2,6 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; -using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Endpoints.Game.DataTypes.Response; using Refresh.GameServer.Services; using Refresh.GameServer.Types.Matching; @@ -10,7 +9,7 @@ namespace Refresh.GameServer.Types.Levels; -public class GameMinimalLevelResponse : IDataConvertableFrom, IDataConvertableFrom +public class GameMinimalLevelResponse { //NOTE: THIS MUST BE AT THE TOP OF THE XML RESPONSE OR ELSE LBP PSP WILL CRASH [XmlElement("id")] public required int LevelId { get; set; } diff --git a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs index 44a14e46..fa9d086c 100644 --- a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs +++ b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs @@ -1,6 +1,5 @@ using System.Xml.Serialization; using Refresh.GameServer.Database; -using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.UserData; @@ -8,7 +7,7 @@ namespace Refresh.GameServer.Types.Reviews; [XmlRoot("review")] [XmlType("review")] -public class SerializedGameReview : IDataConvertableFrom +public class SerializedGameReview { [XmlElement("id")] public int Id { get; set; } From cf238cb4c6fd71af4729bf585a92da4b1b5b2b2f Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 23 Apr 2024 20:37:59 -0400 Subject: [PATCH 04/15] Revert "Remove IDataConvertableFrom" This reverts commit d40d01b09fdb0d5c35b955cd1b5c3a76ce569719. --- Refresh.GameServer/Database/DatabaseList.cs | 8 +++++--- .../Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs | 2 +- .../Endpoints/ApiV3/AuthenticationApiEndpoints.cs | 4 ++-- .../Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs | 8 ++++++++ .../Response/Admin/ApiAdminQueuedRegistrationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiActivityPageResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiContestResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiErrorResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiEventResponse.cs | 2 +- .../DataTypes/Response/ApiExtendedGameUserResponse.cs | 2 +- .../DataTypes/Response/ApiGameAnnouncementResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameAssetResponse.cs | 2 +- .../Response/ApiGameIpVerificationRequestResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameLevelResponse.cs | 2 +- .../DataTypes/Response/ApiGameNotificationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs | 2 +- .../DataTypes/Response/ApiGamePhotoSubjectResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameReviewResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameRoomResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameScoreResponse.cs | 2 +- .../DataTypes/Response/ApiGameSkillRewardResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiGameUserResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiParameterResponse.cs | 2 +- .../DataTypes/Response/ApiRequestStatisticsResponse.cs | 2 +- .../Response/ApiRichPresenceConfigurationResponse.cs | 2 +- .../ApiV3/DataTypes/Response/ApiRouteResponse.cs | 2 +- .../Endpoints/ApiV3/LeaderboardApiEndpoints.cs | 2 +- Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs | 2 +- .../Endpoints/ApiV3/NotificationApiEndpoints.cs | 2 +- Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs | 8 ++++---- Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs | 2 +- .../Game/DataTypes/Response/GameLevelResponse.cs | 3 ++- .../Endpoints/Game/DataTypes/Response/GameUserResponse.cs | 3 ++- .../Types/Levels/GameMinimalLevelResponse.cs | 3 ++- Refresh.GameServer/Types/Reviews/SerializedGameReview.cs | 3 ++- 37 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs diff --git a/Refresh.GameServer/Database/DatabaseList.cs b/Refresh.GameServer/Database/DatabaseList.cs index 7de3b24b..b423748b 100644 --- a/Refresh.GameServer/Database/DatabaseList.cs +++ b/Refresh.GameServer/Database/DatabaseList.cs @@ -1,3 +1,5 @@ +using Refresh.GameServer.Endpoints.ApiV3.DataTypes; + namespace Refresh.GameServer.Database; public class DatabaseList where TObject : class @@ -52,11 +54,11 @@ private DatabaseList(int oldTotalItems, int oldNextPageIndex, IEnumerable FromOldList(DatabaseList oldList, Func mapFunc) - where TNewObject : class + public static DatabaseList FromOldList(DatabaseList oldList) + where TNewObject : class, IDataConvertableFrom where TOldObject : class { - DatabaseList newList = new(oldList.TotalItems, oldList.NextPageIndex, oldList.Items.Select(mapFunc)!); + DatabaseList newList = new(oldList.TotalItems, oldList.NextPageIndex, TNewObject.FromOldList(oldList.Items)); return newList; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs index 5d2c753b..7a846546 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/Admin/AdminUserApiEndpoints.cs @@ -48,7 +48,7 @@ public ApiResponse GetExtendedUserByUuid(RequestCon public ApiListResponse GetExtendedUsers(RequestContext context, GameDatabaseContext database, IDataStore dataStore) { (int skip, int count) = context.GetPageData(true); - DatabaseList list = DatabaseList.FromOldList(database.GetUsers(count, skip), ApiExtendedGameUserResponse.FromOld); + DatabaseList list = DatabaseList.FromOldList(database.GetUsers(count, skip)); //Fill in the extra data of all the users foreach (ApiExtendedGameUserResponse user in list.Items) user.FillInExtraData(database, dataStore); return list; diff --git a/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs index 2c3b8d03..714ebe71 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/AuthenticationApiEndpoints.cs @@ -185,8 +185,8 @@ public ApiListResponse GetVerificationRequ { (int skip, int count) = context.GetPageData(true); - return DatabaseList.FromOldList - (database.GetIpVerificationRequestsForUser(user, count, skip), ApiGameIpVerificationRequestResponse.FromOld); + return DatabaseList.FromOldList + (database.GetIpVerificationRequestsForUser(user, count, skip)); } [ApiV3Endpoint("verificationRequests/approve", HttpMethods.Put)] diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs new file mode 100644 index 00000000..787d576c --- /dev/null +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/IDataConvertableFrom.cs @@ -0,0 +1,8 @@ +namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes; + +public interface IDataConvertableFrom where TNew : IDataConvertableFrom +{ + public static abstract TNew? FromOld(TOld? old); + + public static abstract IEnumerable FromOldList(IEnumerable oldList); +} \ No newline at end of file diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs index 2618d36a..79a4c4a0 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Admin/ApiAdminQueuedRegistrationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response.Admin; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiAdminQueuedRegistrationResponse : IApiResponse +public class ApiAdminQueuedRegistrationResponse : IApiResponse, IDataConvertableFrom { public required string RegistrationId { get; set; } public required string Username { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs index cdc138ff..2a2c4cf8 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiActivityPageResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiActivityPageResponse : IApiResponse +public class ApiActivityPageResponse : IApiResponse, IDataConvertableFrom { public required IEnumerable Events { get; set; } public required IEnumerable Users { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs index 5b9f7dfc..bb8bd758 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiContestResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiContestResponse : IApiResponse +public class ApiContestResponse : IApiResponse, IDataConvertableFrom { public required string ContestId { get; set; } public required ApiGameUserResponse Organizer { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs index 14705e3b..0c7c6353 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiErrorResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiErrorResponse : IApiResponse +public class ApiErrorResponse : IApiResponse, IDataConvertableFrom { public required string Name { get; set; } public required string OccursWhen { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs index a9d3993a..911074d9 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiEventResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiEventResponse : IApiResponse +public class ApiEventResponse : IApiResponse, IDataConvertableFrom { public required string EventId { get; set; } public required int EventType { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs index a88ecb38..54195b3b 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs @@ -11,7 +11,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; /// A user with full information, like current role, ban status, etc. /// [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiExtendedGameUserResponse : IApiResponse +public class ApiExtendedGameUserResponse : IApiResponse, IDataConvertableFrom { public required string UserId { get; set; } public required string Username { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs index ad633306..c81637ae 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAnnouncementResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameAnnouncementResponse : IApiResponse +public class ApiGameAnnouncementResponse : IApiResponse, IDataConvertableFrom { public required string AnnouncementId { get; set; } public required string Title { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs index 2846bcf1..3cb3f431 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameAssetResponse : IApiResponse +public class ApiGameAssetResponse : IApiResponse, IDataConvertableFrom { public required string AssetHash { get; set; } public required ApiGameUserResponse? OriginalUploader { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs index 2b2bcda5..41101380 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameIpVerificationRequestResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameIpVerificationRequestResponse : IApiResponse +public class ApiGameIpVerificationRequestResponse : IApiResponse, IDataConvertableFrom { public required string IpAddress { get; set; } public required DateTimeOffset CreatedAt { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs index 5a010bad..434bfb8a 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameLevelResponse.cs @@ -7,7 +7,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameLevelResponse : IApiResponse +public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom { public required int LevelId { get; set; } public required ApiGameUserResponse? Publisher { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs index 105ba69e..d208eff2 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameNotificationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameNotificationResponse : IApiResponse +public class ApiGameNotificationResponse : IApiResponse, IDataConvertableFrom { public required string NotificationId { get; set; } public required string Title { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs index 269fe3a6..f312aff6 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGamePhotoResponse : IApiResponse +public class ApiGamePhotoResponse : IApiResponse, IDataConvertableFrom { public required int PhotoId { get; set; } public required DateTimeOffset TakenAt { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs index 8a3d9d25..c85c88af 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGamePhotoSubjectResponse.cs @@ -5,7 +5,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGamePhotoSubjectResponse : IApiResponse +public class ApiGamePhotoSubjectResponse : IApiResponse,IDataConvertableFrom { public required ApiGameUserResponse? User { get; set; } public required string DisplayName { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs index cf624d70..b0cf9140 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameReviewResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameReviewResponse : IApiResponse +public class ApiGameReviewResponse : IApiResponse, IDataConvertableFrom { public required int ReviewId { get; set; } public required ApiGameLevelResponse Level { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs index f93be5a2..ca78c594 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomPlayerResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameRoomPlayerResponse : IApiResponse +public class ApiGameRoomPlayerResponse : IApiResponse, IDataConvertableFrom { public required string Username { get; set; } public required string? UserId { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs index 248cc57f..f040c1ad 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameRoomResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameRoomResponse : IApiResponse +public class ApiGameRoomResponse : IApiResponse, IDataConvertableFrom { public required string RoomId { get; set; } public required IEnumerable PlayerIds { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs index b1042370..4dec52a3 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameScoreResponse.cs @@ -6,7 +6,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameScoreResponse : IApiResponse +public class ApiGameScoreResponse : IApiResponse, IDataConvertableFrom { public required string ScoreId { get; set; } public required ApiGameLevelResponse Level { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs index 47d9499c..fe80b8f7 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameSkillRewardResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameSkillRewardResponse : IApiResponse +public class ApiGameSkillRewardResponse : IApiResponse, IDataConvertableFrom { public int Id { get; set; } public bool Enabled { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs index 9e5254ea..b03c79c4 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameUserResponse.cs @@ -7,7 +7,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiGameUserResponse : IApiResponse +public class ApiGameUserResponse : IApiResponse, IDataConvertableFrom { // HEY! When adding fields here, remember to propagate them in ApiExtendedGameUser too! // Otherwise, they won't show up in the admin panel endpoints or /users/me. Thank you! diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs index 0fa34151..c6ab7d7b 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiLevelCategoryResponse.cs @@ -11,7 +11,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiLevelCategoryResponse : IApiResponse +public class ApiLevelCategoryResponse : IApiResponse, IDataConvertableFrom { public required string Name { get; set; } public required string Description { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs index 896c74b9..92b048a1 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiParameterResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiParameterResponse : IApiResponse +public class ApiParameterResponse : IApiResponse, IDataConvertableFrom { public required string Name { get; set; } [JsonConverter(typeof(StringEnumConverter))] diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs index a42e896b..8a2492ff 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRequestStatisticsResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRequestStatisticsResponse : IApiResponse +public class ApiRequestStatisticsResponse : IApiResponse, IDataConvertableFrom { public required long TotalRequests { get; set; } public required long ApiRequests { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs index 597cda2d..ff3419b1 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRichPresenceConfigurationResponse.cs @@ -3,7 +3,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRichPresenceConfigurationResponse : IApiResponse +public class ApiRichPresenceConfigurationResponse : IApiResponse, IDataConvertableFrom { public required string ApplicationId { get; set; } public required string PartyIdPrefix { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs index 7ace7915..fd57eacc 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiRouteResponse.cs @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response; [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] -public class ApiRouteResponse : IApiResponse +public class ApiRouteResponse : IApiResponse, IDataConvertableFrom { public required string Method { get; set; } public required string RouteUri { get; set; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs index b8c973f3..25e1e693 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/LeaderboardApiEndpoints.cs @@ -34,7 +34,7 @@ public ApiListResponse GetTopScoresForLevel(RequestContext if (!result) return ApiValidationError.BooleanParseError; DatabaseList scores = database.GetTopScoresForLevel(level, count, skip, (byte)mode, showAll); - DatabaseList ret = DatabaseList.FromOldList(scores, ApiGameScoreResponse.FromOld); + DatabaseList ret = DatabaseList.FromOldList(scores); foreach (ApiGameScoreResponse score in ret.Items) score.FillInExtraData(database, dataStore); return ret; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs index c2a9cac5..cf8a1259 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs @@ -57,7 +57,7 @@ public ApiListResponse GetLevels(RequestContext context, G if (list == null) return ApiNotFoundError.Instance; - DatabaseList levels = DatabaseList.FromOldList(list, ApiGameLevelResponse.FromOld); + DatabaseList levels = DatabaseList.FromOldList(list); foreach (ApiGameLevelResponse level in levels.Items) { level.FillInExtraData(database, dataStore); diff --git a/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs index ad58e800..265ad2da 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/NotificationApiEndpoints.cs @@ -24,7 +24,7 @@ public ApiListResponse GetNotifications(RequestCont { (int skip, int count) = context.GetPageData(true); DatabaseList notifications = database.GetNotificationsByUser(user, count, skip); - return DatabaseList.FromOldList(notifications, ApiGameNotificationResponse.FromOld); + return DatabaseList.FromOldList(notifications); } [ApiV3Endpoint("notifications/{uuid}"), MinimumRole(GameUserRole.Restricted)] diff --git a/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs index f504ea48..0353fe79 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/PhotoApiEndpoints.cs @@ -39,7 +39,7 @@ private static ApiListResponse PhotosByUser(RequestContext (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosByUser(user, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); + DatabaseList photosResponse = DatabaseList.FromOldList(photos); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } @@ -50,7 +50,7 @@ private static ApiListResponse PhotosWithUser(RequestConte (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosWithUser(user, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); + DatabaseList photosResponse = DatabaseList.FromOldList(photos); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } @@ -61,7 +61,7 @@ private static ApiListResponse PhotosInLevel(RequestContex (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetPhotosInLevel(level, count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); + DatabaseList photosResponse = DatabaseList.FromOldList(photos); foreach (ApiGamePhotoResponse photo in photosResponse.Items) { photo.FillInExtraData(database, dataStore); @@ -111,7 +111,7 @@ public ApiListResponse RecentPhotos(RequestContext context (int skip, int count) = context.GetPageData(true); DatabaseList photos = database.GetRecentPhotos(count, skip); - DatabaseList photosResponse = DatabaseList.FromOldList(photos, ApiGamePhotoResponse.FromOld); + DatabaseList photosResponse = DatabaseList.FromOldList(photos); foreach (ApiGamePhotoResponse photo in photosResponse.Items) photo.FillInExtraData(database, dataStore); return photosResponse; } diff --git a/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs b/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs index 8093aa22..599c2343 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/ReviewApiEndpoints.cs @@ -28,7 +28,7 @@ public ApiListResponse GetTopScoresForLevel(RequestContex (int skip, int count) = context.GetPageData(true); DatabaseList reviews = database.GetReviewsForLevel(level, count, skip); - DatabaseList ret = DatabaseList.FromOldList(reviews, ApiGameReviewResponse.FromOld); + DatabaseList ret = DatabaseList.FromOldList(reviews); return ret; } diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index 29779a84..4df4a3a0 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -2,6 +2,7 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; +using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Extensions; using Refresh.GameServer.Services; using Refresh.GameServer.Types; @@ -17,7 +18,7 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Response; [XmlRoot("slot")] [XmlType("slot")] -public class GameLevelResponse +public class GameLevelResponse : IDataConvertableFrom { [XmlElement("id")] public required int LevelId { get; set; } diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index f11437d1..86010fdc 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -2,6 +2,7 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; +using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Types; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Lists; @@ -10,7 +11,7 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Response; [XmlRoot("user")] -public class GameUserResponse +public class GameUserResponse : IDataConvertableFrom { public const int MaximumLevels = 9_999; diff --git a/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs b/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs index 49ef6db0..457c657f 100644 --- a/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs +++ b/Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs @@ -2,6 +2,7 @@ using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; +using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Endpoints.Game.DataTypes.Response; using Refresh.GameServer.Services; using Refresh.GameServer.Types.Matching; @@ -9,7 +10,7 @@ namespace Refresh.GameServer.Types.Levels; -public class GameMinimalLevelResponse +public class GameMinimalLevelResponse : IDataConvertableFrom, IDataConvertableFrom { //NOTE: THIS MUST BE AT THE TOP OF THE XML RESPONSE OR ELSE LBP PSP WILL CRASH [XmlElement("id")] public required int LevelId { get; set; } diff --git a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs index fa9d086c..44a14e46 100644 --- a/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs +++ b/Refresh.GameServer/Types/Reviews/SerializedGameReview.cs @@ -1,5 +1,6 @@ using System.Xml.Serialization; using Refresh.GameServer.Database; +using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.UserData; @@ -7,7 +8,7 @@ namespace Refresh.GameServer.Types.Reviews; [XmlRoot("review")] [XmlType("review")] -public class SerializedGameReview +public class SerializedGameReview : IDataConvertableFrom { [XmlElement("id")] public int Id { get; set; } From a51ee7fa69569cb883c06ae485be4f661f04298d Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 19:51:31 -0400 Subject: [PATCH 05/15] Merge fixes for GameLevelResponse --- .../DataTypes/Response/GameLevelResponse.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index f82a748b..16776ca8 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -142,12 +142,6 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext) public static GameLevelResponse? FromOld(GameLevel? old, DataContext dataContext) { if (old == null) return null; - - int totalPlayCount = 0; - foreach (PlayLevelRelation playLevelRelation in old.AllPlays) - { - totalPlayCount += playLevelRelation.Count; - } GameLevelResponse response = new() { @@ -164,11 +158,11 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext) MaxPlayers = old.MaxPlayers, EnforceMinMaxPlayers = old.EnforceMinMaxPlayers, SameScreenGame = old.SameScreenGame, - HeartCount = old.FavouriteRelations.Count(), - TotalPlayCount = totalPlayCount, - UniquePlayCount = old.UniquePlays.Count(), - YayCount = old.Ratings.Count(r => r._RatingType == (int)RatingType.Yay), - BooCount = old.Ratings.Count(r => r._RatingType == (int)RatingType.Boo), + HeartCount = dataContext.Database.GetFavouriteCountForLevel(old), + TotalPlayCount = dataContext.Database.GetTotalPlaysForLevel(old), + UniquePlayCount = dataContext.Database.GetUniquePlaysForLevel(old), + YayCount = dataContext.Database.GetTotalRatingsForLevel(old, RatingType.Yay), + BooCount = dataContext.Database.GetTotalRatingsForLevel(old, RatingType.Boo), SkillRewards = old.SkillRewards.ToList(), TeamPicked = old.TeamPicked, LevelType = old.LevelType.ToGameString(), @@ -177,7 +171,7 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext) IsSubLevel = old.IsSubLevel, BackgroundGuid = old.BackgroundGuid, Links = "", - AverageStarRating = old.CalculateAverageStarRating(), + AverageStarRating = old.CalculateAverageStarRating(dataContext.Database), ReviewCount = old.Reviews.Count, CommentCount = old.LevelComments.Count, }; @@ -210,7 +204,7 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext) response.YourRating = rating?.ToDPad() ?? (int)RatingType.Neutral; response.YourStarRating = rating?.ToLBP1() ?? 0; - response.YourLbp2PlayCount = old.AllPlays.Count(p => p.User == dataContext.User); + response.YourLbp2PlayCount = dataContext.Database.GetTotalPlaysForLevelByUser(old, dataContext.User); } response.PlayerCount = dataContext.Match.GetPlayerCountForLevel(RoomSlotType.Online, response.LevelId); From 88bc84e5fd78cdfee753c0299a3be8791168216f Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 20:20:24 -0400 Subject: [PATCH 06/15] Remove GameUser.FavouriteLevelRelations backlink --- .../Database/GameDatabaseContext.Relations.cs | 8 ++++++++ .../Database/GameDatabaseContext.Users.cs | 11 ++++++----- Refresh.GameServer/Database/GameDatabaseProvider.cs | 3 ++- .../Game/DataTypes/Response/GameUserResponse.cs | 12 +++++++----- Refresh.GameServer/Types/UserData/GameUser.cs | 3 --- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs index 7e171463..05826fc7 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs @@ -26,6 +26,10 @@ public DatabaseList GetLevelsFavouritedByUser(GameUser user, int coun .FilterByLevelFilterSettings(accessor, levelFilterSettings) .FilterByGameVersion(levelFilterSettings.GameVersion), skip, count); + public int GetTotalLevelsFavouritedByUser(GameUser user) + => this._realm.All() + .Count(r => r.User == user); + public bool FavouriteLevel(GameLevel level, GameUser user) { if (this.IsLevelFavouritedByUser(level, user)) return false; @@ -88,6 +92,10 @@ public IEnumerable GetUsersFavouritedByUser(GameUser user, int count, .Select(r => r.UserToFavourite) .Skip(skip) .Take(count); + + public int GetTotalUsersFavouritedByUser(GameUser user) + => this._realm.All() + .Count(r => r.User == user); public bool FavouriteUser(GameUser userToFavourite, GameUser userFavouriting) { diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Users.cs b/Refresh.GameServer/Database/GameDatabaseContext.Users.cs index 4a824d2f..6fbfe922 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Users.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Users.cs @@ -7,6 +7,7 @@ using Refresh.GameServer.Types; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Photos; +using Refresh.GameServer.Types.Relations; using Refresh.GameServer.Types.Roles; using Refresh.GameServer.Types.UserData; @@ -300,11 +301,11 @@ public void DeleteUser(GameUser user) foreach (GamePhotoSubject subject in user.PhotosWithMe) subject.User = null; - this._realm.RemoveRange(user.FavouriteLevelRelations); - this._realm.RemoveRange(user.UsersFavourited); - this._realm.RemoveRange(user.UsersFavouritingMe); - this._realm.RemoveRange(user.QueueLevelRelations); - this._realm.RemoveRange(user.PhotosByMe); + this._realm.RemoveRange(this._realm.All().Where(r => r.User == user)); + this._realm.RemoveRange(this._realm.All().Where(r => r.UserToFavourite == user)); + this._realm.RemoveRange(this._realm.All().Where(r => r.UserFavouriting == user)); + this._realm.RemoveRange(this._realm.All().Where(r => r.User == user)); + this._realm.RemoveRange(this._realm.All().Where(p => p.Publisher == user)); foreach (GameLevel level in this._realm.All().Where(l => l.Publisher == user)) { diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index d17936fc..c11496e3 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -164,7 +164,8 @@ protected override void Migrate(Migration migration, ulong oldVersion) if (oldVersion < 100) newUser.EmailAddress = oldUser.EmailAddress?.ToLowerInvariant(); // Version was bumped here to delete invalid favourite level relations - if (oldVersion < 101) migration.NewRealm.RemoveRange(newUser.FavouriteLevelRelations.Where(r => r.Level == null)); + // TODO: fix this migration + // if (oldVersion < 101) migration.NewRealm.RemoveRange(newUser.FavouriteLevelRelations.Where(r => r.Level == null)); // In version 102 we split the Vita icon hash from the PS3 icon hash if (oldVersion < 102) newUser.VitaIconHash = "0"; diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index 13334036..802df395 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -3,6 +3,7 @@ using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; using Refresh.GameServer.Endpoints.ApiV3.DataTypes; +using Refresh.GameServer.Endpoints.Game.Levels.FilterSettings; using Refresh.GameServer.Types; using Refresh.GameServer.Types.Data; using Refresh.GameServer.Types.Levels; @@ -66,7 +67,7 @@ public class GameUserResponse : IDataConvertableFrom Handle = SerializedUserHandle.FromUser(old, dataContext), CommentCount = old.ProfileComments.Count, CommentsEnabled = true, - FavouriteLevelCount = old.IsManaged ? old.FavouriteLevelRelations.Count() : 0, + FavouriteLevelCount = old.IsManaged ? dataContext.Database.GetTotalLevelsFavouritedByUser(old) : 0, FavouriteUserCount = old.IsManaged ? old.UsersFavourited.Count() : 0, QueuedLevelCount = old.IsManaged ? old.QueueLevelRelations.Count() : 0, HeartCount = old.IsManaged ? old.UsersFavouritingMe.Count() : 0, @@ -150,10 +151,11 @@ public class GameUserResponse : IDataConvertableFrom response.FavouriteUsers = new SerializedMinimalFavouriteUserList(users.Select(u => SerializedUserHandle.FromUser(u, dataContext)).ToList(), users.Count); //Fill out PSP favourite levels - List favouriteLevels = old.FavouriteLevelRelations - .AsEnumerable() - .Where(l => l.Level._GameVersion == (int)TokenGame.LittleBigPlanetPSP) - .Select(f => GameMinimalLevelResponse.FromOld(f.Level, dataContext)).ToList()!; + List favouriteLevels = dataContext.Database + .GetLevelsFavouritedByUser(old, 20, 0, new LevelFilterSettings(dataContext.Game), dataContext.User) + .Items + .Where(l => l._GameVersion == (int)TokenGame.LittleBigPlanetPSP) + .Select(l => GameMinimalLevelResponse.FromOld(l, dataContext)).ToList()!; response.FavouriteLevels = new SerializedMinimalFavouriteLevelList(new SerializedMinimalLevelList(favouriteLevels, favouriteLevels.Count, favouriteLevels.Count)); break; } diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index 191535ee..cfa5991e 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -63,9 +63,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser #nullable disable public IList ProfileComments { get; } - [Backlink(nameof(FavouriteLevelRelation.User))] - public IQueryable FavouriteLevelRelations { get; } - [Backlink(nameof(QueueLevelRelation.User))] public IQueryable QueueLevelRelations { get; } From 674bea5e4f19348858d280ab331a13a41e7a6800 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 21:39:55 -0400 Subject: [PATCH 07/15] Remove relation backlinks in GameUser --- .../Database/GameDatabaseContext.Activity.cs | 6 ++-- .../Database/GameDatabaseContext.Relations.cs | 23 ++++++++------ .../DataTypes/Response/GameUserResponse.cs | 8 ++--- Refresh.GameServer/Types/UserData/GameUser.cs | 11 +------ .../Tests/Relations/QueueTests.cs | 30 +++++++++++-------- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Activity.cs b/Refresh.GameServer/Database/GameDatabaseContext.Activity.cs index e4d82f93..c407295e 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Activity.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Activity.cs @@ -17,7 +17,7 @@ public DatabaseList GetUserRecentActivity(ActivityQueryParameters paramet if (parameters.User != null) { - List favouriteUsers = parameters.User.UsersFavourited.AsEnumerable().Select(f => (ObjectId?)f.UserToFavourite.UserId).ToList(); + List favouriteUsers = this.GetUsersFavouritedByUser(parameters.User, 1000, 0).Select(f => (ObjectId?)f.UserId).ToList(); List userFriends = this.GetUsersMutuals(parameters.User).Select(u => (ObjectId?)u.UserId).ToList(); query = query.Where(e => @@ -61,9 +61,9 @@ private IEnumerable GetRecentActivity(ActivityQueryParameters parameters) if (parameters is { ExcludeFavouriteUsers: true, User: not null }) { - List favouriteUsers = parameters.User.UsersFavourited.ToList(); + List favouriteUsers = this.GetUsersFavouritedByUser(parameters.User, 1000, 0).ToList(); - query = query.Where(e => favouriteUsers.All(r => r.UserToFavourite.UserId != e.User.UserId && r.UserToFavourite.UserId != e.StoredObjectId)); + query = query.Where(e => favouriteUsers.All(r => r.UserId != e.User.UserId && r.UserId != e.StoredObjectId)); } if (parameters is { ExcludeMyself: true, User: not null }) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs index 05826fc7..138447d7 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs @@ -77,12 +77,8 @@ public bool AreUsersMutual(GameUser user1, GameUser user2) => [Pure] public IEnumerable GetUsersMutuals(GameUser user) { - // this might be the shittiest query i've ever written. - return user.UsersFavourited.AsEnumerable() - .Select(relation => relation.UserToFavourite) - .Where(f => f.UsersFavourited.AsEnumerable() - .Select(otherUserRelation => otherUserRelation.UserToFavourite).AsEnumerable() - .Contains(user)); + return this.GetUsersFavouritedByUser(user, 1000, 0).AsEnumerable() + .Where(u => this.IsUserFavouritedByUser(user, u)); } [Pure] @@ -94,8 +90,12 @@ public IEnumerable GetUsersFavouritedByUser(GameUser user, int count, .Take(count); public int GetTotalUsersFavouritedByUser(GameUser user) - => this._realm.All() - .Count(r => r.User == user); + => this._realm.All() + .Count(r => r.UserFavouriting == user); + + public int GetTotalUsersFavouritingUser(GameUser user) + => this._realm.All() + .Count(r => r.UserToFavourite == user); public bool FavouriteUser(GameUser userToFavourite, GameUser userFavouriting) { @@ -152,6 +152,11 @@ public DatabaseList GetLevelsQueuedByUser(GameUser user, int count, i .FilterByLevelFilterSettings(accessor, levelFilterSettings) .FilterByGameVersion(levelFilterSettings.GameVersion), skip, count); + [Pure] + public int GetTotalLevelsQueuedByUser(GameUser user) + => this._realm.All() + .Count(r => r.User == user); + public bool QueueLevel(GameLevel level, GameUser user) { if (this.IsLevelQueuedByUser(level, user)) return false; @@ -180,7 +185,7 @@ public bool DequeueLevel(GameLevel level, GameUser user) public void ClearQueue(GameUser user) { - this._realm.Write(() => this._realm.RemoveRange(user.QueueLevelRelations)); + this._realm.Write(() => this._realm.RemoveRange(this._realm.All().Where(r => r.User == user))); } #endregion diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index 802df395..f527a52f 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -1,7 +1,5 @@ using System.Xml.Serialization; -using Bunkum.Core.Storage; using Refresh.GameServer.Authentication; -using Refresh.GameServer.Database; using Refresh.GameServer.Endpoints.ApiV3.DataTypes; using Refresh.GameServer.Endpoints.Game.Levels.FilterSettings; using Refresh.GameServer.Types; @@ -68,9 +66,9 @@ public class GameUserResponse : IDataConvertableFrom CommentCount = old.ProfileComments.Count, CommentsEnabled = true, FavouriteLevelCount = old.IsManaged ? dataContext.Database.GetTotalLevelsFavouritedByUser(old) : 0, - FavouriteUserCount = old.IsManaged ? old.UsersFavourited.Count() : 0, - QueuedLevelCount = old.IsManaged ? old.QueueLevelRelations.Count() : 0, - HeartCount = old.IsManaged ? old.UsersFavouritingMe.Count() : 0, + FavouriteUserCount = old.IsManaged ? dataContext.Database.GetTotalUsersFavouritedByUser(old) : 0, + QueuedLevelCount = old.IsManaged ? dataContext.Database.GetTotalLevelsQueuedByUser(old) : 0, + HeartCount = old.IsManaged ? dataContext.Database.GetTotalUsersFavouritingUser(old) : 0, PhotosByMeCount = old.IsManaged ? old.PhotosByMe.Count() : 0, PhotosWithMeCount = old.IsManaged ? old.PhotosWithMe.Count() : 0, diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index cfa5991e..26fa0227 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -62,15 +62,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser #nullable disable public IList ProfileComments { get; } - - [Backlink(nameof(QueueLevelRelation.User))] - public IQueryable QueueLevelRelations { get; } - - [Backlink(nameof(FavouriteUserRelation.UserToFavourite))] - public IQueryable UsersFavouritingMe { get; } - - [Backlink(nameof(FavouriteUserRelation.UserFavouriting))] - public IQueryable UsersFavourited { get; } [Backlink(nameof(GameLevel.Publisher))] public IQueryable PublishedLevels { get; } @@ -80,8 +71,8 @@ public partial class GameUser : IRealmObject, IRateLimitUser [Backlink(nameof(GamePhotoSubject.User))] public IQueryable PhotosWithMe { get; } - [Backlink(nameof(GameNotification.User))] + [Backlink(nameof(GameNotification.User))] public IQueryable Notifications { get; } public IList IpVerificationRequests { get; } diff --git a/RefreshTests.GameServer/Tests/Relations/QueueTests.cs b/RefreshTests.GameServer/Tests/Relations/QueueTests.cs index 420b2ae4..21f2f759 100644 --- a/RefreshTests.GameServer/Tests/Relations/QueueTests.cs +++ b/RefreshTests.GameServer/Tests/Relations/QueueTests.cs @@ -1,8 +1,8 @@ using Refresh.GameServer.Authentication; +using Refresh.GameServer.Database; +using Refresh.GameServer.Endpoints.Game.Levels.FilterSettings; using Refresh.GameServer.Types.Levels; -using Refresh.GameServer.Types.Lists; using Refresh.GameServer.Types.UserData; -using RefreshTests.GameServer.Extensions; namespace RefreshTests.GameServer.Tests.Relations; @@ -23,8 +23,9 @@ public void QueueAndUnqueueLevel() context.Database.Refresh(); //Ensure we only have one queued level, and that it is the level we queued - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(1)); - Assert.That(user.QueueLevelRelations.First().Level.LevelId, Is.EqualTo(level.LevelId)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(1)); + Assert.That(context.Database.GetLevelsQueuedByUser(user, 1, 0, new LevelFilterSettings(TokenGame.LittleBigPlanet2), user) + .Items.First().LevelId, Is.EqualTo(level.LevelId)); //Remove the level from the queue message = client.PostAsync($"/lbp/lolcatftw/remove/user/{level.LevelId}", new ReadOnlyMemoryContent(Array.Empty())).Result; @@ -32,7 +33,7 @@ public void QueueAndUnqueueLevel() context.Database.Refresh(); //Ensure the queue is cleared - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(0)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(0)); } [Test] @@ -53,9 +54,13 @@ public void ClearQueue() context.Database.Refresh(); //Ensure we have both levels queued - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(2)); - Assert.That(user.QueueLevelRelations.AsEnumerable().Any(q => q.Level.LevelId == level1.LevelId)); - Assert.That(user.QueueLevelRelations.AsEnumerable().Any(q => q.Level.LevelId == level2.LevelId)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(2)); + + DatabaseList queue = context.Database.GetLevelsQueuedByUser(user, 2, 0, + new LevelFilterSettings(TokenGame.LittleBigPlanet2), user); + + Assert.That(queue.Items.Any(q => q.LevelId == level1.LevelId)); + Assert.That(queue.Items.Any(q => q.LevelId == level2.LevelId)); //Clear the queue message = client.PostAsync($"/lbp/lolcatftw/clear", new ReadOnlyMemoryContent(Array.Empty())).Result; @@ -63,7 +68,7 @@ public void ClearQueue() context.Database.Refresh(); //Ensure the queue is cleared - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(0)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(0)); } [Test] @@ -106,8 +111,9 @@ public void CantQueueLevelTwice() context.Database.Refresh(); //Ensure we only have one queued level, and that it is the level we queued - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(1)); - Assert.That(user.QueueLevelRelations.First().Level.LevelId, Is.EqualTo(level.LevelId)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(1)); + Assert.That(context.Database.GetLevelsQueuedByUser(user, 1, 0, new LevelFilterSettings(TokenGame.LittleBigPlanet2), user) + .Items.First().LevelId, Is.EqualTo(level.LevelId)); } [Test] @@ -124,6 +130,6 @@ public void CantDequeueLevelTwice() context.Database.Refresh(); //Ensure we have 0 queued levels - Assert.That(user.QueueLevelRelations.Count(), Is.EqualTo(0)); + Assert.That(context.Database.GetTotalLevelsQueuedByUser(user), Is.EqualTo(0)); } } \ No newline at end of file From 44a661a51b24faa94cbd8ab285d660126e290d16 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 21:45:15 -0400 Subject: [PATCH 08/15] Turn GameIpVerificationRequest into an IRealmObject --- .../Database/GameDatabaseContext.Tokens.cs | 13 +++++-------- .../Types/UserData/GameIpVerificationRequest.cs | 4 +++- Refresh.GameServer/Types/UserData/GameUser.cs | 2 -- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Tokens.cs b/Refresh.GameServer/Database/GameDatabaseContext.Tokens.cs index 81cf2b08..690c8747 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Tokens.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Tokens.cs @@ -142,13 +142,14 @@ public void AddIpVerificationRequest(GameUser user, string ipAddress) { GameIpVerificationRequest request = new() { + User = user, IpAddress = ipAddress, CreatedAt = this._time.Now, }; this._realm.Write(() => { - user.IpVerificationRequests.Add(request); + this._realm.Add(request); }); } @@ -157,22 +158,18 @@ public void SetApprovedIp(GameUser user, string ipAddress) this._realm.Write(() => { user.CurrentVerifiedIp = ipAddress; - user.IpVerificationRequests.Clear(); + this._realm.RemoveRange(this._realm.All().Where(r => r.User == user)); }); } public void DenyIpVerificationRequest(GameUser user, string ipAddress) { - IEnumerable requests = user.IpVerificationRequests.Where(r => r.IpAddress == ipAddress); this._realm.Write(() => { - foreach (GameIpVerificationRequest request in requests) - { - user.IpVerificationRequests.Remove(request); - } + this._realm.RemoveRange(this._realm.All().Where(r => r.User == user && r.IpAddress == ipAddress)); }); } public DatabaseList GetIpVerificationRequestsForUser(GameUser user, int count, int skip) - => new(user.IpVerificationRequests, skip, count); + => new(this._realm.All().Where(r => r.User == user), skip, count); } \ No newline at end of file diff --git a/Refresh.GameServer/Types/UserData/GameIpVerificationRequest.cs b/Refresh.GameServer/Types/UserData/GameIpVerificationRequest.cs index 13b18ff7..2188d38e 100644 --- a/Refresh.GameServer/Types/UserData/GameIpVerificationRequest.cs +++ b/Refresh.GameServer/Types/UserData/GameIpVerificationRequest.cs @@ -4,8 +4,10 @@ namespace Refresh.GameServer.Types.UserData; #nullable disable -public partial class GameIpVerificationRequest : IEmbeddedObject +public partial class GameIpVerificationRequest : IRealmObject { + public GameUser User { get; set; } + public string IpAddress { get; set; } public DateTimeOffset CreatedAt { get; set; } } \ No newline at end of file diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index 26fa0227..e74c8a1c 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -74,8 +74,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser [Backlink(nameof(GameNotification.User))] public IQueryable Notifications { get; } - - public IList IpVerificationRequests { get; } #nullable restore public string BetaPlanetsHash { get; set; } = "0"; From 0da583e868b691872f0011f6dfc072262d41683c Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 21:48:59 -0400 Subject: [PATCH 09/15] Remove backlink for notifications --- Refresh.GameServer/Types/UserData/GameUser.cs | 3 --- RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index e74c8a1c..2e997da9 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -71,9 +71,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser [Backlink(nameof(GamePhotoSubject.User))] public IQueryable PhotosWithMe { get; } - - [Backlink(nameof(GameNotification.User))] - public IQueryable Notifications { get; } #nullable restore public string BetaPlanetsHash { get; set; } = "0"; diff --git a/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs b/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs index 0cb61487..e3b1e6d7 100644 --- a/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs +++ b/RefreshTests.GameServer/Tests/Levels/ScoreLeaderboardTests.cs @@ -426,7 +426,7 @@ public void OvertakeNotificationsWorkProperly() if (lastBestScore == null) continue; GameUser notificationRecipient = lastBestScore.Players.First(); - GameNotification? notification = notificationRecipient.Notifications.FirstOrDefault(); + GameNotification? notification = context.Database.GetNotificationsByUser(notificationRecipient, 1, 0).Items.FirstOrDefault(); if (lastBestScore.Score <= 0) { @@ -444,7 +444,7 @@ public void OvertakeNotificationsWorkProperly() if (context.Database.GetTotalPlaysForLevel(level) <= 2) continue; notificationRecipient = users[i - 2]; - Assert.That(notificationRecipient.Notifications.Any(), Is.False); + Assert.That(context.Database.GetNotificationCountByUser(notificationRecipient), Is.Zero); } Assert.That(testedSent && testedNotSent, Is.True); From 2048ff34d1319e56050fb88743af13f7561a60df Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 21:56:53 -0400 Subject: [PATCH 10/15] Remove photo backlinks from user --- .../Database/GameDatabaseContext.Photos.cs | 14 +++++++++++++- .../Database/GameDatabaseContext.Users.cs | 6 ++++-- .../Game/DataTypes/Response/GameUserResponse.cs | 4 ++-- Refresh.GameServer/Types/UserData/GameUser.cs | 6 ------ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs b/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs index 8bee6bfd..5e26e43a 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs @@ -75,16 +75,28 @@ public DatabaseList GetRecentPhotos(int count, int skip) => public DatabaseList GetPhotosByUser(GameUser user, int count, int skip) => new(this._realm.All().Where(p => p.Publisher == user) .OrderByDescending(p => p.TakenAt), skip, count); + + [Pure] + public int GetTotalPhotosByUser(GameUser user) + => this._realm.All() + .Count(p => p.Publisher == user); [Pure] public DatabaseList GetPhotosWithUser(GameUser user, int count, int skip) => new(this._realm.All() - .AsEnumerable() // FIXME: client-side enumeration + .AsEnumerable() .Where(p => p.Subjects.FirstOrDefault(s => Equals(s.User, user)) != null) .OrderByDescending(p => p.TakenAt) .Skip(skip) .Take(count)); + + [Pure] + public int GetTotalPhotosWithUser(GameUser user) + => this._realm.All() + // FIXME: client-side enumeration + .AsEnumerable() + .Count(p => p.Subjects.FirstOrDefault(s => Equals(s.User, user)) != null); [Pure] public DatabaseList GetPhotosInLevel(GameLevel level, int count, int skip) => diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Users.cs b/Refresh.GameServer/Database/GameDatabaseContext.Users.cs index 6fbfe922..8547a3dd 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Users.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Users.cs @@ -298,8 +298,10 @@ public void DeleteUser(GameUser user) user.PsnAuthenticationAllowed = false; user.RpcnAuthenticationAllowed = false; - foreach (GamePhotoSubject subject in user.PhotosWithMe) - subject.User = null; + // TODO: unit tests for this + foreach (GamePhoto photo in this.GetPhotosWithUser(user, int.MaxValue, 0).Items) + foreach (GamePhotoSubject subject in photo.Subjects.Where(s => s.User?.UserId == user.UserId)) + subject.User = null; this._realm.RemoveRange(this._realm.All().Where(r => r.User == user)); this._realm.RemoveRange(this._realm.All().Where(r => r.UserToFavourite == user)); diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index f527a52f..8f02f905 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -69,8 +69,8 @@ public class GameUserResponse : IDataConvertableFrom FavouriteUserCount = old.IsManaged ? dataContext.Database.GetTotalUsersFavouritedByUser(old) : 0, QueuedLevelCount = old.IsManaged ? dataContext.Database.GetTotalLevelsQueuedByUser(old) : 0, HeartCount = old.IsManaged ? dataContext.Database.GetTotalUsersFavouritingUser(old) : 0, - PhotosByMeCount = old.IsManaged ? old.PhotosByMe.Count() : 0, - PhotosWithMeCount = old.IsManaged ? old.PhotosWithMe.Count() : 0, + PhotosByMeCount = old.IsManaged ? dataContext.Database.GetTotalPhotosByUser(old) : 0, + PhotosWithMeCount = old.IsManaged ? dataContext.Database.GetTotalPhotosWithUser(old) : 0, EntitledSlots = MaximumLevels, EntitledSlotsLBP2 = MaximumLevels, diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index 2e997da9..b55c2b8a 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -65,12 +65,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser [Backlink(nameof(GameLevel.Publisher))] public IQueryable PublishedLevels { get; } - - [Backlink(nameof(GamePhoto.Publisher))] - public IQueryable PhotosByMe { get; } - - [Backlink(nameof(GamePhotoSubject.User))] - public IQueryable PhotosWithMe { get; } #nullable restore public string BetaPlanetsHash { get; set; } = "0"; From f233227bba63eca9454f143d972282f2c3d580a1 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 22:00:38 -0400 Subject: [PATCH 11/15] Remove published levels backlink from user --- .../Database/GameDatabaseContext.Levels.cs | 8 ++++++++ .../Game/DataTypes/Response/GameUserResponse.cs | 12 ++++++------ Refresh.GameServer/Types/UserData/GameUser.cs | 3 --- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Levels.cs b/Refresh.GameServer/Database/GameDatabaseContext.Levels.cs index 8d87e9a8..9ddd4c07 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Levels.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Levels.cs @@ -363,6 +363,14 @@ public DatabaseList SearchForLevels(int count, int skip, GameUser? us [Pure] public int GetTotalLevelCount() => this._realm.All().Count(l => l._Source == (int)GameLevelSource.User); + public int GetTotalLevelsPublishedByUser(GameUser user) + => this._realm.All() + .Count(r => r.Publisher == user); + + public int GetTotalLevelsPublishedByUser(GameUser user, TokenGame game) + => this._realm.All() + .Count(r => r._GameVersion == (int)game); + [Pure] public int GetTotalTeamPickCount(TokenGame game) => this._realm.All().FilterByGameVersion(game).Count(l => l.TeamPicked); diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs index 8f02f905..e301c816 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameUserResponse.cs @@ -109,21 +109,21 @@ public class GameUserResponse : IDataConvertableFrom { case TokenGame.LittleBigPlanet3: { //Match all LBP3 levels - response.UsedSlotsLBP3 = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.LittleBigPlanet3); + response.UsedSlotsLBP3 = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.LittleBigPlanet3); response.FreeSlotsLBP3 = MaximumLevels - response.UsedSlotsLBP3; //Fill out LBP2/LBP1 levels goto case TokenGame.LittleBigPlanet2; } case TokenGame.LittleBigPlanet2: { //Match all LBP2 levels - response.UsedSlotsLBP2 = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.LittleBigPlanet2); + response.UsedSlotsLBP2 = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.LittleBigPlanet2); response.FreeSlotsLBP2 = MaximumLevels - response.UsedSlotsLBP2; //Fill out LBP1 levels goto case TokenGame.LittleBigPlanet1; } case TokenGame.LittleBigPlanetVita: { //Match all LBP Vita levels - response.UsedSlotsLBP2 = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.LittleBigPlanetVita); + response.UsedSlotsLBP2 = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.LittleBigPlanetVita); response.FreeSlotsLBP2 = MaximumLevels - response.UsedSlotsLBP2; //Apply Vita-specific icon hash @@ -132,13 +132,13 @@ public class GameUserResponse : IDataConvertableFrom } case TokenGame.LittleBigPlanet1: { //Match all LBP1 levels - response.UsedSlots = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.LittleBigPlanet1); + response.UsedSlots = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.LittleBigPlanet1); response.FreeSlots = MaximumLevels - response.UsedSlots; break; } case TokenGame.LittleBigPlanetPSP: { //Match all LBP PSP levels - response.UsedSlots = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.LittleBigPlanetPSP); + response.UsedSlots = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.LittleBigPlanetPSP); response.FreeSlots = MaximumLevels - response.UsedSlots; // Apply PSP-specific icon hash @@ -160,7 +160,7 @@ public class GameUserResponse : IDataConvertableFrom case TokenGame.BetaBuild: { // only beta levels - response.UsedSlots = old.PublishedLevels.Count(x => x._GameVersion == (int)TokenGame.BetaBuild); + response.UsedSlots = dataContext.Database.GetTotalLevelsPublishedByUser(old, TokenGame.BetaBuild); response.FreeSlots = MaximumLevels - response.UsedSlotsLBP2; // use the same values for LBP3 and LBP2 since they're all shared under one count diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index b55c2b8a..5f0069e2 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -62,9 +62,6 @@ public partial class GameUser : IRealmObject, IRateLimitUser #nullable disable public IList ProfileComments { get; } - - [Backlink(nameof(GameLevel.Publisher))] - public IQueryable PublishedLevels { get; } #nullable restore public string BetaPlanetsHash { get; set; } = "0"; From ade818c8856505727ecc1fc0dcad87c1d21364b2 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 16 May 2024 22:06:35 -0400 Subject: [PATCH 12/15] Remove relation backlink from comments --- .../Database/GameDatabaseContext.Relations.cs | 3 +++ .../Endpoints/Game/CommentEndpoints.cs | 10 ++++++---- Refresh.GameServer/Types/Comments/GameComment.cs | 14 +++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs index 138447d7..7e20a950 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs @@ -358,6 +358,9 @@ public int GetUniquePlaysForLevel(GameLevel level) => public RatingType? GetRatingByUser(GameComment comment, GameUser user) => this.GetCommentRelationByUser(comment, user)?.RatingType; + public int GetTotalRatingsForComment(GameComment comment, RatingType type) => + this._realm.All().Count(r => r.Comment == comment && r._RatingType == (int)type); + public bool RateComment(GameUser user, GameComment comment, RatingType ratingType) { if (ratingType == RatingType.Neutral) diff --git a/Refresh.GameServer/Endpoints/Game/CommentEndpoints.cs b/Refresh.GameServer/Endpoints/Game/CommentEndpoints.cs index 503d2acd..85b55124 100644 --- a/Refresh.GameServer/Endpoints/Game/CommentEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/CommentEndpoints.cs @@ -7,6 +7,7 @@ using Refresh.GameServer.Extensions; using Refresh.GameServer.Time; using Refresh.GameServer.Types.Comments; +using Refresh.GameServer.Types.Data; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Lists; using Refresh.GameServer.Types.Reviews; @@ -35,7 +36,7 @@ public Response PostProfileComment(RequestContext context, GameDatabaseContext d [GameEndpoint("userComments/{username}", ContentType.Xml)] [NullStatusCode(NotFound)] [MinimumRole(GameUserRole.Restricted)] - public SerializedCommentList? GetProfileComments(RequestContext context, GameDatabaseContext database, GameUser user, string username) + public SerializedCommentList? GetProfileComments(RequestContext context, GameDatabaseContext database, GameUser user, DataContext dataContext, string username) { GameUser? profile = database.GetUserByUsername(username); if (profile == null) return null; @@ -43,7 +44,7 @@ public Response PostProfileComment(RequestContext context, GameDatabaseContext d (int skip, int count) = context.GetPageData(); List comments = database.GetProfileComments(profile, count, skip).ToList(); - foreach (GameComment comment in comments) comment.PrepareForSerialization(user); + foreach (GameComment comment in comments) comment.PrepareForSerialization(user, dataContext); return new SerializedCommentList(comments); } @@ -89,7 +90,8 @@ public Response PostLevelComment(RequestContext context, GameDatabaseContext dat [GameEndpoint("comments/{slotType}/{id}", ContentType.Xml)] [NullStatusCode(NotFound)] [MinimumRole(GameUserRole.Restricted)] - public SerializedCommentList? GetLevelComments(RequestContext context, GameDatabaseContext database, GameUser user, string slotType, int id) + public SerializedCommentList? GetLevelComments(RequestContext context, GameDatabaseContext database, GameUser user, DataContext dataContext, + string slotType, int id) { GameLevel? level = database.GetLevelByIdAndType(slotType, id); if (level == null) return null; @@ -97,7 +99,7 @@ public Response PostLevelComment(RequestContext context, GameDatabaseContext dat (int skip, int count) = context.GetPageData(); List comments = database.GetLevelComments(level, count, skip).ToList(); - foreach(GameComment comment in comments) comment.PrepareForSerialization(user); + foreach(GameComment comment in comments) comment.PrepareForSerialization(user, dataContext); return new SerializedCommentList(comments); } diff --git a/Refresh.GameServer/Types/Comments/GameComment.cs b/Refresh.GameServer/Types/Comments/GameComment.cs index db1a8a70..0dd6ba68 100644 --- a/Refresh.GameServer/Types/Comments/GameComment.cs +++ b/Refresh.GameServer/Types/Comments/GameComment.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using Realms; using Refresh.GameServer.Database; -using Refresh.GameServer.Types.Relations; +using Refresh.GameServer.Types.Data; using Refresh.GameServer.Types.Reviews; using Refresh.GameServer.Types.UserData; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. @@ -21,9 +21,6 @@ public partial class GameComment : IRealmObject, ISequentialId /// Timestamp in Unix milliseconds /// [XmlElement("timestamp")] public long Timestamp { get; set; } - - [Backlink(nameof(CommentRelation.Comment))] - public IQueryable CommentRelations { get; } #region LBP Serialization Quirks @@ -33,15 +30,14 @@ public partial class GameComment : IRealmObject, ISequentialId [XmlElement("thumbsdown")] [Ignored] public int? ThumbsDown { get; set; } [XmlElement("yourthumb")] [Ignored] public int? YourThumb { get; set; } - public void PrepareForSerialization(GameUser user) + public void PrepareForSerialization(GameUser user, DataContext dataContext) { this.Handle = this.Author.Username; - this.ThumbsUp = this.CommentRelations.Count(r => r._RatingType == (int)RatingType.Yay); - this.ThumbsDown = this.CommentRelations.Count(r => r._RatingType == (int)RatingType.Boo); + this.ThumbsUp = dataContext.Database.GetTotalRatingsForComment(this, RatingType.Yay); + this.ThumbsDown = dataContext.Database.GetTotalRatingsForComment(this, RatingType.Boo); - this.YourThumb = (int?)(this.CommentRelations - .FirstOrDefault(r => r.User == user)?.RatingType ?? 0); + this.YourThumb = (int?)dataContext.Database.GetRatingByUser(this, user) ?? 0; } #endregion From ec49fd12f8260543f511f8c9ac6e0f6fa39f5755 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 28 May 2024 21:01:44 -0400 Subject: [PATCH 13/15] Remove migrations from legacy database --- .../Database/GameDatabaseContext.cs | 2 +- .../GameDatabaseProvider.cs | 311 +----------------- 2 files changed, 4 insertions(+), 309 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.cs b/Refresh.GameServer/Database/GameDatabaseContext.cs index fb493013..ef494e48 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.cs @@ -13,7 +13,7 @@ public partial class GameDatabaseContext : RealmDatabaseContext private readonly IDateTimeProvider _time; - internal GameDatabaseContext(IDateTimeProvider time) + public GameDatabaseContext(IDateTimeProvider time) { this._time = time; } diff --git a/Refresh.LegacyDatabase/GameDatabaseProvider.cs b/Refresh.LegacyDatabase/GameDatabaseProvider.cs index 91908321..bd60a9b2 100644 --- a/Refresh.LegacyDatabase/GameDatabaseProvider.cs +++ b/Refresh.LegacyDatabase/GameDatabaseProvider.cs @@ -94,313 +94,8 @@ protected override GameDatabaseContext CreateContext() protected override void Migrate(Migration migration, ulong oldVersion) { - // Get the current unix timestamp for when we add timestamps to objects - long timestampMilliseconds = this._time.TimestampMilliseconds; - - // DO NOT USE FOR NEW MIGRATIONS! LBP almost never actually uses seconds for timestamps. - // This is from a mistake made early in development where this was not understood by me. - // Unless you are certain second timestamps are used, use the millisecond timestamps set above. - long timestampSeconds = this._time.TimestampSeconds; - - IQueryable? oldUsers = migration.OldRealm.DynamicApi.All("GameUser"); - IQueryable? newUsers = migration.NewRealm.All(); - - for (int i = 0; i < newUsers.Count(); i++) - { - dynamic oldUser = oldUsers.ElementAt(i); - GameUser newUser = newUsers.ElementAt(i); - - if (oldVersion < 3) - { - newUser.Description = ""; - newUser.Location = new GameLocation { X = 0, Y = 0, }; - } - - //In version 4, GameLocation went from TopLevel -> Embedded, and UserPins was added - if (oldVersion < 4) newUser.Pins = new UserPins(); - - // In version 12, users were given IconHashes - if (oldVersion < 12) newUser.IconHash = "0"; - - // In version 13, users were given PlanetsHashes - if (oldVersion < 13) newUser.Lbp2PlanetsHash = "0"; - - // In version 23, users were given bcrypt passwords - if (oldVersion < 23) newUser.PasswordBcrypt = null; - - // In version 26, users were given join dates - if (oldVersion < 26) newUser.JoinDate = DateTimeOffset.MinValue; - - // In version 40, we switched to Realm source generators which requires some values to be reset - if (oldVersion < 40) - { - newUser.IconHash = oldUser.IconHash; - newUser.Description = oldUser.Description; - newUser.Lbp2PlanetsHash = oldUser.PlanetsHash; - } - - // In version 67, users switched to dates to store their join date - if (oldVersion < 67) newUser.JoinDate = DateTimeOffset.FromUnixTimeMilliseconds(oldUser.JoinDate); - - // In version 69 (nice), users were given last login dates. For now, we'll set that to now. - if(oldVersion < 69 /*nice*/) newUser.LastLoginDate = DateTimeOffset.Now; - - // In version 72, users got settings for permissions regarding certain platforms. - // To avoid breakage, we set them to true for existing users. - if (oldVersion < 72) - { - newUser.PsnAuthenticationAllowed = true; - newUser.RpcnAuthenticationAllowed = true; - } - - // In version 81, we split out planet hashes for LBP2, LBP Vita, and LBP3 - // Since we've supported LBP2 as the primary game so far, set LBP2's hash to the old hash - if (oldVersion < 81) - { - newUser.Lbp2PlanetsHash = oldUser.PlanetsHash; - newUser.Lbp3PlanetsHash = oldUser.PlanetsHash; // Planets are forwards compatible - } - - // Fix vita - if (oldVersion < 82) - { - newUser.VitaPlanetsHash = "0"; - } - - // In version 94, we added an option to redirect grief reports to photos - if (oldVersion < 94) newUser.RedirectGriefReportsToPhotos = false; - - // In version 100, we started enforcing lowercase email addresses - if (oldVersion < 100) newUser.EmailAddress = oldUser.EmailAddress?.ToLowerInvariant(); - - // Version was bumped here to delete invalid favourite level relations - if (oldVersion < 101) migration.NewRealm.RemoveRange(newUser.FavouriteLevelRelations.Where(r => r.Level == null)); - - // In version 102 we split the Vita icon hash from the PS3 icon hash - if (oldVersion < 102) newUser.VitaIconHash = "0"; - - //In version 117, we started tracking the amount of data the user has uploaded to the server - if (oldVersion < 117) - { - newUser.FilesizeQuotaUsage = migration.NewRealm.All() - .AsEnumerable() - .Where(a => a.OriginalUploader?.UserId == newUser.UserId) - .Sum(a => a.SizeInBytes); - } - } - - IQueryable? oldLevels = migration.OldRealm.DynamicApi.All("GameLevel"); - IQueryable? newLevels = migration.NewRealm.All(); - - for (int i = 0; i < newLevels.Count(); i++) - { - dynamic oldLevel = oldLevels.ElementAt(i); - GameLevel newLevel = newLevels.ElementAt(i); - - // In version 10, GameLevels switched to int-based ids. - if (oldVersion < 10) - { - newLevel.LevelId = i + 1; - } - - // In version 11, timestamps were added to levels. - if (oldVersion < 11) - { - // Since we dont have a reference point for when the level was actually uploaded, default to now - newLevel.PublishDate = timestampSeconds; - newLevel.UpdateDate = timestampSeconds; - } - - // In version 14, level timestamps were fixed - if (oldVersion < 14) - { - newLevel.PublishDate = oldLevel.PublishDate * 1000; - newLevel.UpdateDate = oldLevel.UpdateDate * 1000; - } - - // In version 40, we switched to Realm source generators which requires some values to be reset - if (oldVersion < 40) - { - newLevel.Title = oldLevel.Title; - newLevel.IconHash = oldLevel.IconHash; - newLevel.Description = oldLevel.Description; - newLevel.RootResource = oldLevel.RootResource; - } - - // In version 57, we implemented minimum and maximum players which are 1 and 4 by default - if (oldVersion < 57) - { - newLevel.MinPlayers = 1; - newLevel.MaxPlayers = 4; - } - - // In version 79, we started tracking the version in which the level was uploaded from - // Set all levels to LBP2 by default, since that's the version we've supported up until now. - if (oldVersion < 79) - { - newLevel._GameVersion = (int)TokenGame.LittleBigPlanet2; - } - - // In version 92, we started storing both user and story levels. - // Set all existing levels to user levels, since that's what has existed up until now. - if (oldVersion < 92) - { - newLevel._Source = (int)GameLevelSource.User; - } - } - - // In version 22, tokens added expiry and types so just wipe them all - if (oldVersion < 22) migration.NewRealm.RemoveAll(); - - // In version 35, tokens added platforms and games - if (oldVersion < 35) migration.NewRealm.RemoveAll(); - - // IQueryable? oldEvents = migration.OldRealm.DynamicApi.All("Event"); - IQueryable? newEvents = migration.NewRealm.All(); - - List eventsToNuke = new(); - for (int i = 0; i < newEvents.Count(); i++) - { - // dynamic oldEvent = oldEvents.ElementAt(i); - Event newEvent = newEvents.ElementAt(i); - - // In version 30, events were given timestamps - if (oldVersion < 30) newEvent.Timestamp = timestampSeconds; - - // Fixes events with broken timestamps - if (oldVersion < 32 && newEvent.Timestamp == 0) newEvent.Timestamp = timestampSeconds; - - // Converts events to use millisecond timestamps - if (oldVersion < 33 && newEvent.Timestamp < 1000000000000) newEvent.Timestamp *= 1000; - - // fixup for dumb bad code not clearing score events when levels are deleted - if (oldVersion < 96 && newEvent.StoredDataType == EventDataType.Score) - { - GameSubmittedScore? score = migration.NewRealm.All().FirstOrDefault(s => s.ScoreId == newEvent.StoredObjectId); - if(score == null) eventsToNuke.Add(newEvent); - } - } - - // realm won't let you use an IEnumerable in RemoveRange. too bad! - foreach (Event eventToNuke in eventsToNuke) - { - migration.NewRealm.Remove(eventToNuke); - } - - // IQueryable? oldTokens = migration.OldRealm.DynamicApi.All("Token"); - IQueryable? newTokens = migration.NewRealm.All(); - - for (int i = 0; i < newTokens.Count(); i++) - { - // dynamic oldToken = oldTokens.ElementAt(i); - Token newToken = newTokens.ElementAt(i); - - if (oldVersion < 36) newToken.LoginDate = DateTimeOffset.FromUnixTimeMilliseconds(timestampMilliseconds); - } - - IQueryable? oldComments = migration.OldRealm.DynamicApi.All("GameComment"); - IQueryable? newComments = migration.NewRealm.All(); - - for (int i = 0; i < newComments.Count(); i++) - { - dynamic oldComment = oldComments.ElementAt(i); - GameComment newComment = newComments.ElementAt(i); - - // In version 40, we switched to Realm source generators which requires some values to be reset - if (oldVersion < 40) - { - newComment.Content = oldComment.Content; - } - } - - IQueryable? oldPhotos = migration.OldRealm.DynamicApi.All("GamePhoto"); - IQueryable? newPhotos = migration.NewRealm.All(); - - for (int i = 0; i < oldPhotos.Count(); i++) - { - dynamic oldPhoto = oldPhotos.ElementAt(i); - GamePhoto newPhoto = newPhotos.ElementAt(i); - - // In version 52, the timestamp on photos were corrected - if (oldVersion < 52) - { - newPhoto.TakenAt = DateTimeOffset.FromUnixTimeSeconds(oldPhoto.TakenAt.ToUnixTimeMilliseconds()); - } - - if (oldVersion < 110) - { - newPhoto.LargeAsset = migration.NewRealm.Find(oldPhoto.LargeHash.StartsWith("psp/") ? oldPhoto.LargeHash.Substring(4) : oldPhoto.LargeHash); - newPhoto.MediumAsset = migration.NewRealm.Find(oldPhoto.MediumHash.StartsWith("psp/") ? oldPhoto.MediumHash.Substring(4) : oldPhoto.MediumHash); - newPhoto.SmallAsset = migration.NewRealm.Find(oldPhoto.SmallHash.StartsWith("psp/") ? oldPhoto.SmallHash.Substring(4) : oldPhoto.SmallHash); - } - } - - IQueryable? oldAssets = migration.OldRealm.DynamicApi.All("GameAsset"); - IQueryable? newAssets = migration.NewRealm.All(); - - for (int i = 0; i < newAssets.Count(); i++) - { - dynamic oldAsset = oldAssets.ElementAt(i); - GameAsset newAsset = newAssets.ElementAt(i); - - if (oldVersion < 88) - { - // We don't have any more advanced heuristics here, - // but TGA files are the only asset currently affected by the tracking of `IsPSP`, - // and PSP is the only game to upload TGA files. - newAsset.IsPSP = newAsset.AssetType == GameAssetType.Tga; - } - } - - // Remove all scores with a null level, as in version 92 we started tracking story leaderboards differently - if (oldVersion < 92) migration.NewRealm.RemoveRange(migration.NewRealm.All().Where(s => s.Level == null)); - - IQueryable? oldScores = migration.OldRealm.DynamicApi.All("GameSubmittedScore"); - IQueryable? newScores = migration.NewRealm.All(); - - for (int i = 0; i < newScores.Count(); i++) - { - dynamic oldScore = oldScores.ElementAt(i); - GameSubmittedScore newScore = newScores.ElementAt(i); - - if (oldVersion < 92) - { - newScore.Game = newScore.Level.GameVersion; - } - - // In version 104 we started tracking the platform - if (oldVersion < 104) - { - // Determine the most reasonable platform for the score's game - TokenPlatform platform = newScore.Game switch - { - TokenGame.LittleBigPlanet1 => TokenPlatform.PS3, - TokenGame.LittleBigPlanet2 => TokenPlatform.PS3, - TokenGame.LittleBigPlanet3 => TokenPlatform.PS3, - TokenGame.LittleBigPlanetVita => TokenPlatform.Vita, - TokenGame.LittleBigPlanetPSP => TokenPlatform.PSP, - TokenGame.BetaBuild => TokenPlatform.RPCS3, - TokenGame.Website => throw new InvalidOperationException($"what? score id {newScore.ScoreId} by {newScore.Players[0].Username} is fucked"), - _ => throw new ArgumentOutOfRangeException(), - }; - - newScore.Platform = platform; - } - } - - IQueryable? oldPlayLevelRelations = migration.OldRealm.DynamicApi.All("PlayLevelRelation"); - IQueryable? newPlayLevelRelations = migration.NewRealm.All(); - - for (int i = 0; i < newPlayLevelRelations.Count(); i++) - { - dynamic oldPlayLevelRelation = oldPlayLevelRelations.ElementAt(i); - PlayLevelRelation newPlayLevelRelation = newPlayLevelRelations.ElementAt(i); - - //In version 93, we added a count to PlayLevelRelation - if (oldVersion < 93) - { - newPlayLevelRelation.Count = 1; - } - } + if (oldVersion != SchemaVersion) + throw new InvalidOperationException($"The legacy database must be fully up to date before migration." + + $"Expected version is {SchemaVersion}, but the database file was at {oldVersion}."); } } \ No newline at end of file From 325375e7e1c417b940b18eb5426f9210dcf08c5a Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 28 May 2024 21:02:29 -0400 Subject: [PATCH 14/15] Update legacy schema version to 124 --- Refresh.LegacyDatabase/GameDatabaseProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refresh.LegacyDatabase/GameDatabaseProvider.cs b/Refresh.LegacyDatabase/GameDatabaseProvider.cs index bd60a9b2..d32e578f 100644 --- a/Refresh.LegacyDatabase/GameDatabaseProvider.cs +++ b/Refresh.LegacyDatabase/GameDatabaseProvider.cs @@ -35,7 +35,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 121; + protected override ulong SchemaVersion => 124; protected override string Filename => "refreshGameServer.realm"; From a2717b724a80b73fd7213e2091b9b2350f09e364 Mon Sep 17 00:00:00 2001 From: jvyden Date: Mon, 10 Jun 2024 18:26:57 -0400 Subject: [PATCH 15/15] Legacy database v127 --- Refresh.LegacyDatabase/GameDatabaseProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refresh.LegacyDatabase/GameDatabaseProvider.cs b/Refresh.LegacyDatabase/GameDatabaseProvider.cs index d32e578f..366d2729 100644 --- a/Refresh.LegacyDatabase/GameDatabaseProvider.cs +++ b/Refresh.LegacyDatabase/GameDatabaseProvider.cs @@ -35,7 +35,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 124; + protected override ulong SchemaVersion => 127; protected override string Filename => "refreshGameServer.realm";