From bc266ed4053cd7343f75bccbdcc88b1f6bc5f3de Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Sun, 15 Oct 2023 22:20:34 -0700 Subject: [PATCH 1/6] oops realm no support .Sum :( --- .../Game/DataTypes/Response/GameLevelResponse.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index b70e44e7..62db3e88 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -7,6 +7,7 @@ using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Levels.SkillRewards; using Refresh.GameServer.Types.Matching; +using Refresh.GameServer.Types.Relations; using Refresh.GameServer.Types.Reviews; using Refresh.GameServer.Types.UserData; @@ -80,6 +81,12 @@ public class GameLevelResponse : IDataConvertableFrom p.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), From 98b7e77e1e0341b914746fd22515388370999855 Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Sun, 15 Oct 2023 22:36:24 -0700 Subject: [PATCH 2/6] Add ability to redirect photos to grief reports Closes #187 --- .../Database/GameDatabaseContext.Photos.cs | 9 ++-- .../Database/GameDatabaseProvider.cs | 5 +- .../DataTypes/Request/ApiUpdateUserRequest.cs | 2 + .../Response/ApiExtendedGameUserResponse.cs | 3 ++ .../Endpoints/Game/ReportingEndpoints.cs | 53 +++++++++++++++++-- Refresh.GameServer/Services/CommandService.cs | 5 ++ .../Types/Photos/SerializedPhotoSubject.cs | 3 ++ Refresh.GameServer/Types/UserData/GameUser.cs | 5 ++ 8 files changed, 77 insertions(+), 8 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs b/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs index eb520306..1396b7fb 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Photos.cs @@ -19,15 +19,16 @@ public void UploadPhoto(SerializedPhoto photo, GameUser publisher) PlanHash = photo.PlanHash, Publisher = publisher, - LevelName = photo.Level.Title, - LevelType = photo.Level.Type, - LevelId = photo.Level.LevelId, + LevelName = photo.Level?.Title ?? "", + LevelType = photo.Level?.Type ?? "", + //If level is null, default to level ID 0 + LevelId = photo.Level?.LevelId ?? 0, TakenAt = DateTimeOffset.FromUnixTimeSeconds(Math.Clamp(photo.Timestamp, this._time.EarliestDate, this._time.TimestampSeconds)), PublishedAt = this._time.Now, }; - if (photo.Level.Type == "user") + if (photo.Level?.Type == "user") newPhoto.Level = this.GetLevelById(photo.Level.LevelId); float[] bounds = new float[SerializedPhotoSubject.FloatCount]; diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index 23dfd7fb..3a70012b 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -32,7 +32,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 93; + protected override ulong SchemaVersion => 94; protected override string Filename => "refreshGameServer.realm"; @@ -159,6 +159,9 @@ protected override void Migrate(Migration migration, ulong oldVersion) { newUser.VitaPlanetsHash = "0"; } + + // In version 94, we added an option to redirect grief reports to photos + if (oldVersion < 94) newUser.RedirectGriefReportsToPhotos = false; } IQueryable? oldLevels = migration.OldRealm.DynamicApi.All("GameLevel"); diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Request/ApiUpdateUserRequest.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Request/ApiUpdateUserRequest.cs index a487b843..2e470d95 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Request/ApiUpdateUserRequest.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Request/ApiUpdateUserRequest.cs @@ -9,5 +9,7 @@ public class ApiUpdateUserRequest public bool? PsnAuthenticationAllowed { get; set; } public bool? RpcnAuthenticationAllowed { get; set; } + public bool? RedirectGriefReportsToPhotos { get; set; } + public string? EmailAddress { get; set; } } \ No newline at end of file diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs index 35a61528..fba50578 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiExtendedGameUserResponse.cs @@ -26,6 +26,8 @@ public class ApiExtendedGameUserResponse : IApiResponse, IDataConvertableFrom 4 } || body.ScreenElements is { Player.Length: > 4 }) - { + GameLevel? level = database.GetLevelById(body.LevelId); + + //If the level is specified but its invalid, return BadRequest + if (body.LevelId != 0 && level == null) return BadRequest; + + if (user.RedirectGriefReportsToPhotos) + { + List subjects = new(); + if (body.Players != null) + subjects.AddRange(body.Players.Select(player => new SerializedPhotoSubject + { + Username = player.Username, + DisplayName = player.Username, + //TODO: im not sure what BoundsList expects, seems to be some kind of float value, + // but im not sure how to get that from a Rect + BoundsList = "", + })); + + database.UploadPhoto(new SerializedPhoto + { + Timestamp = time.TimestampSeconds, + AuthorName = user.Username, + SmallHash = body.JpegHash, + MediumHash = body.JpegHash, + LargeHash = body.JpegHash, + PlanHash = "0", + //If the level id is 0 or we couldn't find the level null, dont fill out the `Level` field + Level = body.LevelId == 0 || level == null ? null : new SerializedPhotoLevel + { + LevelId = level.LevelId, + Title = level.Title, + Type = level.Source switch { + GameLevelSource.User => "user", + GameLevelSource.Story => "developer", + _ => throw new ArgumentOutOfRangeException(), + }, + }, + PhotoSubjects = subjects, + }, user); + + return OK; } + + //Basic validation + if (body.Players is { Length: > 4 } || body.ScreenElements is { Player.Length: > 4 }) + return BadRequest; database.AddGriefReport(body); diff --git a/Refresh.GameServer/Services/CommandService.cs b/Refresh.GameServer/Services/CommandService.cs index d6433e22..e174f1ee 100644 --- a/Refresh.GameServer/Services/CommandService.cs +++ b/Refresh.GameServer/Services/CommandService.cs @@ -100,6 +100,11 @@ public void HandleCommand(CommandInvocation command, GameDatabaseContext databas break; } + case "togglegriefphotos": { + user.RedirectGriefReportsToPhotos = !user.RedirectGriefReportsToPhotos; + + break; + } #if DEBUG case "tokengame": { diff --git a/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs b/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs index 6dd5ef29..fbb7839c 100644 --- a/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs +++ b/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs @@ -22,6 +22,9 @@ public class SerializedPhotoSubject public static void ParseBoundsList(ReadOnlySpan input, float[] floats) { + //Dont parse out empty inputs + if (input.IsEmpty) return; + byte start = 0; byte floatIndex = 0; diff --git a/Refresh.GameServer/Types/UserData/GameUser.cs b/Refresh.GameServer/Types/UserData/GameUser.cs index a2768c90..14a58392 100644 --- a/Refresh.GameServer/Types/UserData/GameUser.cs +++ b/Refresh.GameServer/Types/UserData/GameUser.cs @@ -80,6 +80,11 @@ public partial class GameUser : IRealmObject, IRateLimitUser public bool RpcnAuthenticationAllowed { get; set; } public bool PsnAuthenticationAllowed { get; set; } + /// + /// If `true`, turn all grief reports into photo uploads + /// + public bool RedirectGriefReportsToPhotos { get; set; } + [Ignored] public GameUserRole Role { get => (GameUserRole)this._Role; From 59c21971bec14b7f59b85bf4c7a1ece933fe1054 Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Sun, 15 Oct 2023 22:45:47 -0700 Subject: [PATCH 3/6] Add support for PSP photo uploading We need to prepend the hash with `psp/` since the TGA is a PSP asset --- Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs index df7fc4cb..6cd0f210 100644 --- a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs @@ -4,6 +4,7 @@ using Bunkum.Listener.Protocol; using Bunkum.Protocols.Http; using Refresh.GameServer.Database; +using Refresh.GameServer.Extensions; using Refresh.GameServer.Time; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Photos; @@ -35,14 +36,16 @@ public Response UploadReport(RequestContext context, GameDatabaseContext databas // but im not sure how to get that from a Rect BoundsList = "", })); + + string hash = context.IsPSP() ? "psp/" + body.JpegHash : body.JpegHash; database.UploadPhoto(new SerializedPhoto { Timestamp = time.TimestampSeconds, AuthorName = user.Username, - SmallHash = body.JpegHash, - MediumHash = body.JpegHash, - LargeHash = body.JpegHash, + SmallHash = hash, + MediumHash = hash, + LargeHash = hash, PlanHash = "0", //If the level id is 0 or we couldn't find the level null, dont fill out the `Level` field Level = body.LevelId == 0 || level == null ? null : new SerializedPhotoLevel From 2b5df184085b48bc1b8b4b7cf8643f401f98a30e Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Sun, 15 Oct 2023 23:00:50 -0700 Subject: [PATCH 4/6] Split toggle grief photos command into 2 separate on/off commands --- Refresh.GameServer/Services/CommandService.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Refresh.GameServer/Services/CommandService.cs b/Refresh.GameServer/Services/CommandService.cs index e174f1ee..fd0c0e6a 100644 --- a/Refresh.GameServer/Services/CommandService.cs +++ b/Refresh.GameServer/Services/CommandService.cs @@ -100,8 +100,13 @@ public void HandleCommand(CommandInvocation command, GameDatabaseContext databas break; } - case "togglegriefphotos": { - user.RedirectGriefReportsToPhotos = !user.RedirectGriefReportsToPhotos; + case "griefphotoson": { + user.RedirectGriefReportsToPhotos = true; + + break; + } + case "griefphotosoff": { + user.RedirectGriefReportsToPhotos = false; break; } From 7e75e80ed98e229752c33c62f5fc1aa7d620f5df Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Mon, 16 Oct 2023 00:09:35 -0700 Subject: [PATCH 5/6] Add description to GameReport --- Refresh.GameServer/Database/GameDatabaseProvider.cs | 2 +- Refresh.GameServer/Types/Report/GameReport.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index 3a70012b..6fc65a10 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -32,7 +32,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 94; + protected override ulong SchemaVersion => 95; protected override string Filename => "refreshGameServer.realm"; diff --git a/Refresh.GameServer/Types/Report/GameReport.cs b/Refresh.GameServer/Types/Report/GameReport.cs index 8a855632..ab80aceb 100644 --- a/Refresh.GameServer/Types/Report/GameReport.cs +++ b/Refresh.GameServer/Types/Report/GameReport.cs @@ -51,7 +51,10 @@ public InfoBubble[] InfoBubble [XmlElement("levelId")] public int LevelId { get; set; } - + + [XmlElement("description")] + public string Description { get; set; } + [XmlElement("griefStateHash")] public string GriefStateHash { get; set; } From 404018d19740527b48ed08808388a9c0fa605380 Mon Sep 17 00:00:00 2001 From: Beyley Thomas Date: Mon, 16 Oct 2023 00:10:00 -0700 Subject: [PATCH 6/6] Properly fill out BoundsList, and remove parsing hack --- .../Endpoints/Game/ReportingEndpoints.cs | 20 +++++++++++++++---- .../Types/Photos/SerializedPhotoSubject.cs | 3 --- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs index 6cd0f210..e2436470 100644 --- a/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/ReportingEndpoints.cs @@ -3,6 +3,7 @@ using Bunkum.Core.Responses; using Bunkum.Listener.Protocol; using Bunkum.Protocols.Http; +using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; using Refresh.GameServer.Extensions; using Refresh.GameServer.Time; @@ -16,10 +17,19 @@ namespace Refresh.GameServer.Endpoints.Game; public class ReportingEndpoints : EndpointGroup { [GameEndpoint("grief", HttpMethods.Post, ContentType.Xml)] - public Response UploadReport(RequestContext context, GameDatabaseContext database, GameReport body, GameUser user, IDateTimeProvider time) + public Response UploadReport(RequestContext context, GameDatabaseContext database, GameReport body, GameUser user, IDateTimeProvider time, Token token) { GameLevel? level = database.GetLevelById(body.LevelId); + Size imageSize = token.TokenGame switch { + TokenGame.LittleBigPlanet1 => new Size(640, 360), + TokenGame.LittleBigPlanet2 => new Size(640, 360), + TokenGame.LittleBigPlanet3 => new Size(640, 360), + TokenGame.LittleBigPlanetVita => new Size(512, 290), + TokenGame.LittleBigPlanetPSP => new Size(480, 272), + _ => throw new ArgumentOutOfRangeException(nameof(token), $"Token game {token.TokenGame} is not allowed for grief upload!"), + }; + //If the level is specified but its invalid, return BadRequest if (body.LevelId != 0 && level == null) return BadRequest; @@ -32,9 +42,11 @@ public Response UploadReport(RequestContext context, GameDatabaseContext databas { Username = player.Username, DisplayName = player.Username, - //TODO: im not sure what BoundsList expects, seems to be some kind of float value, - // but im not sure how to get that from a Rect - BoundsList = "", + // ReSharper disable PossibleLossOfFraction YES I KNOW THESE ARE INTEGERS + BoundsList = $"{(float)(player.Rectangle.Left - imageSize.Width / 2) / (imageSize.Width / 2)}," + + $"{(float)(player.Rectangle.Top - imageSize.Height / 2) / (imageSize.Height / 2)}," + + $"{(float)(player.Rectangle.Right - imageSize.Width / 2) / (imageSize.Width / 2)}," + + $"{(float)(player.Rectangle.Bottom - imageSize.Height / 2) / (imageSize.Height / 2)}", })); string hash = context.IsPSP() ? "psp/" + body.JpegHash : body.JpegHash; diff --git a/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs b/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs index fbb7839c..6dd5ef29 100644 --- a/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs +++ b/Refresh.GameServer/Types/Photos/SerializedPhotoSubject.cs @@ -22,9 +22,6 @@ public class SerializedPhotoSubject public static void ParseBoundsList(ReadOnlySpan input, float[] floats) { - //Dont parse out empty inputs - if (input.IsEmpty) return; - byte start = 0; byte floatIndex = 0;