diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs index 1a8f91e4..48c4eb4b 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Relations.cs @@ -16,7 +16,7 @@ public partial class GameDatabaseContext // Relations { #region Favouriting Levels [Pure] - private bool IsLevelFavouritedByUser(GameLevel level, GameUser user) => this.FavouriteLevelRelations + public bool IsLevelFavouritedByUser(GameLevel level, GameUser user) => this.FavouriteLevelRelations .FirstOrDefault(r => r.Level == level && r.User == user) != null; [Pure] @@ -143,7 +143,7 @@ public bool UnfavouriteUser(GameUser userToFavourite, GameUser userFavouriting) #region Queueing [Pure] - private bool IsLevelQueuedByUser(GameLevel level, GameUser user) => this.QueueLevelRelations + public bool IsLevelQueuedByUser(GameLevel level, GameUser user) => this.QueueLevelRelations .FirstOrDefault(r => r.Level == level && r.User == user) != null; [Pure] diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelRelationsResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelRelationsResponse.cs new file mode 100644 index 00000000..9f10208b --- /dev/null +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelRelationsResponse.cs @@ -0,0 +1,9 @@ +namespace Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response.Levels; + +[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] +public class ApiGameLevelRelationsResponse : IApiResponse +{ + public required bool IsHearted { get; set; } + public required bool IsQueued { get; set; } + public required int MyPlaysCount { get; set; } +} \ No newline at end of file diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelResponse.cs index 9006adf8..9a3b885f 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/Levels/ApiGameLevelResponse.cs @@ -48,6 +48,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom GetCategories(RequestContext co [DocQueryParam("game", "Filters levels to a specific game version. Allowed values: lbp1-3, vita, psp, beta")] [DocQueryParam("seed", "The random seed to use for randomization. Uses 0 if not specified.")] [DocQueryParam("players", "Filters levels to those accommodating the specified number of players.")] + [DocQueryParam("username", "If set, certain categories like 'hearted' or 'byUser' will return the levels of " + + "the user with this username instead of your own. Optional.")] public ApiListResponse GetLevels(RequestContext context, GameDatabaseContext database, MatchService matchService, CategoryService categories, GameUser? user, IDataStore dataStore, [DocSummary("The name of the category you'd like to retrieve levels from. " + @@ -174,4 +177,83 @@ public ApiOkResponse SetLevelAsOverrideByHash(RequestContext context, GameDataba return new ApiOkResponse(); } + + + [ApiV3Endpoint("levels/id/{id}/relations"), MinimumRole(GameUserRole.Restricted)] + [DocSummary("Gets your relations to a level by it's ID")] + [DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)] + public ApiResponse GetLevelRelationsOfUser(RequestContext context, GameDatabaseContext database, GameUser user, + [DocSummary("The ID of the level")] int id) + { + GameLevel? level = database.GetLevelById(id); + if (level == null) return ApiNotFoundError.LevelMissingError; + + return new ApiGameLevelRelationsResponse + { + IsHearted = database.IsLevelFavouritedByUser(level, user), + IsQueued = database.IsLevelQueuedByUser(level, user), + MyPlaysCount = database.GetTotalPlaysForLevelByUser(level, user) + }; + } + + [ApiV3Endpoint("levels/id/{id}/heart", HttpMethods.Post)] + [DocSummary("Adds a specific level by it's ID to your hearted levels")] + [DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)] + public ApiOkResponse FavouriteLevel(RequestContext context, GameDatabaseContext database, GameUser user, + [DocSummary("The ID of the level")] int id) + { + GameLevel? level = database.GetLevelById(id); + if (level == null) return ApiNotFoundError.LevelMissingError; + + database.FavouriteLevel(level, user); + return new ApiOkResponse(); + } + + [ApiV3Endpoint("levels/id/{id}/unheart", HttpMethods.Post)] + [DocSummary("Removes a specific level by it's ID from your hearted levels")] + [DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)] + public ApiOkResponse UnheartLevel(RequestContext context, GameDatabaseContext database, GameUser user, + [DocSummary("The ID of the level")] int id) + { + GameLevel? level = database.GetLevelById(id); + if (level == null) return ApiNotFoundError.LevelMissingError; + + database.UnfavouriteLevel(level, user); + return new ApiOkResponse(); + } + + [ApiV3Endpoint("levels/id/{id}/queue", HttpMethods.Post)] + [DocSummary("Adds a specific level by it's ID to your queue")] + [DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)] + public ApiOkResponse QueueLevel(RequestContext context, GameDatabaseContext database, GameUser user, + [DocSummary("The ID of the level")] int id) + { + GameLevel? level = database.GetLevelById(id); + if (level == null) return ApiNotFoundError.LevelMissingError; + + database.QueueLevel(level, user); + return new ApiOkResponse(); + } + + [ApiV3Endpoint("levels/id/{id}/dequeue", HttpMethods.Post)] + [DocSummary("Removes a specific level by it's ID from your queue")] + [DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)] + public ApiOkResponse DequeueLevel(RequestContext context, GameDatabaseContext database, GameUser user, + [DocSummary("The ID of the level")] int id) + { + GameLevel? level = database.GetLevelById(id); + if (level == null) return ApiNotFoundError.LevelMissingError; + + database.DequeueLevel(level, user); + return new ApiOkResponse(); + } + + [ApiV3Endpoint("levels/queued/clear", HttpMethods.Post)] + [DocSummary("Clears your level queue")] + public ApiOkResponse ClearQueuedLevels(RequestContext context, GameDatabaseContext database, + IDataStore dataStore, GameUser user, DataContext dataContext) + { + database.ClearQueue(user); + return new ApiOkResponse(); + } } \ No newline at end of file diff --git a/Refresh.GameServer/Types/Levels/Categories/ByUserLevelCategory.cs b/Refresh.GameServer/Types/Levels/Categories/ByUserLevelCategory.cs index 6757eb7c..36cc0c74 100644 --- a/Refresh.GameServer/Types/Levels/Categories/ByUserLevelCategory.cs +++ b/Refresh.GameServer/Types/Levels/Categories/ByUserLevelCategory.cs @@ -24,7 +24,7 @@ internal ByUserLevelCategory() : base("byUser", "by", true) LevelFilterSettings levelFilterSettings, GameUser? user) { // Prefer username from query, but fallback to user passed into this category if it's missing - string? username = context.QueryString["u"]; + string? username = context.QueryString["u"] ?? context.QueryString["username"]; if (username != null) user = dataContext.Database.GetUserByUsername(username); if (user == null) return null; diff --git a/Refresh.GameServer/Types/Levels/Categories/FavouriteSlotsByUserCategory.cs b/Refresh.GameServer/Types/Levels/Categories/FavouriteSlotsByUserCategory.cs index 72e893a2..8425df7b 100644 --- a/Refresh.GameServer/Types/Levels/Categories/FavouriteSlotsByUserCategory.cs +++ b/Refresh.GameServer/Types/Levels/Categories/FavouriteSlotsByUserCategory.cs @@ -21,6 +21,10 @@ internal FavouriteSlotsByUserCategory() : base("hearted", "favouriteSlots", true public override DatabaseList? Fetch(RequestContext context, int skip, int count, DataContext dataContext, LevelFilterSettings levelFilterSettings, GameUser? user) { + // Prefer username from query, but fallback to user passed into this category if it's missing + string? username = context.QueryString["u"] ?? context.QueryString["username"]; + if (username != null) user = dataContext.Database.GetUserByUsername(username); + if (user == null) return null; return dataContext.Database.GetLevelsFavouritedByUser(user, count, skip, levelFilterSettings, dataContext.User);