Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement hearting and queueing levels through ApiV3 #681

Merged
merged 16 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Refresh.GameServer/Database/GameDatabaseContext.Relations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
public required bool TeamPicked { get; set; }
public required DateTimeOffset? DateTeamPicked { get; set; }
public required GameLevelType LevelType { get; set; }
public required GameSlotType SlotType { get; set; }
public required bool IsLocked { get; set; }
public required bool IsSubLevel { get; set; }
public required bool IsCopyable { get; set; }
Expand All @@ -57,7 +58,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
public static ApiGameLevelResponse? FromOld(GameLevel? level, DataContext dataContext)
{
if (level == null) return null;

return new ApiGameLevelResponse
{
IsAdventure = level.IsAdventure,
Expand Down Expand Up @@ -85,6 +86,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
RootLevelHash = level.RootResource,
GameVersion = level.GameVersion,
LevelType = level.LevelType,
SlotType = level.SlotType,
IsCopyable = level.IsCopyable,
IsLocked = level.IsLocked,
IsSubLevel = level.IsSubLevel,
Expand Down
82 changes: 82 additions & 0 deletions Refresh.GameServer/Endpoints/ApiV3/LevelApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Refresh.GameServer.Types.Levels;
using Refresh.GameServer.Types.Levels.Categories;
using Refresh.GameServer.Types.UserData;
using Refresh.GameServer.Types.Roles;

namespace Refresh.GameServer.Endpoints.ApiV3;

Expand Down Expand Up @@ -52,6 +53,8 @@ public ApiListResponse<ApiLevelCategoryResponse> 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<ApiGameLevelResponse> 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. " +
Expand Down Expand Up @@ -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<ApiGameLevelRelationsResponse> 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)
};
jvyden marked this conversation as resolved.
Show resolved Hide resolved
}

[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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ internal FavouriteSlotsByUserCategory() : base("hearted", "favouriteSlots", true
public override DatabaseList<GameLevel>? 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);
Expand Down
Loading