mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-29 16:38:37 +00:00
Rewrite gameserver slot filter system (#763)
* Initial implementation of new slot sorting and filtering system * Initial implementation of filtering for lbp3 community tab * Add support for organization on lbp3 * Add playlist and user categories * Implement unit tests for all filters Refactor more systems to use PaginationData * Fix PlayerCountFilter test * Add more unit tests and integration tests for the filter system * Fix LBP2 move filter and gameFilterType * Fix sort by likes in LBP3 category * Add sort for total plays * Remove extra whitespace and make styling more consistent * Order hearted and queued levels by primary key ID * Fix query without order warnings
This commit is contained in:
parent
de228cb242
commit
0c1e350fa3
106 changed files with 4040 additions and 1183 deletions
|
@ -4,6 +4,7 @@ using LBPUnion.ProjectLighthouse.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
@ -42,12 +43,10 @@ public class CommentController : ControllerBase
|
|||
|
||||
[HttpGet("comments/{slotType}/{slotId:int}")]
|
||||
[HttpGet("userComments/{username}")]
|
||||
public async Task<IActionResult> GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId)
|
||||
public async Task<IActionResult> GetComments(string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0 || pageStart < 0) return this.BadRequest();
|
||||
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
@ -55,6 +54,8 @@ public class CommentController : ControllerBase
|
|||
int targetId;
|
||||
CommentType type = username == null ? CommentType.Level : CommentType.Profile;
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
if (type == CommentType.Level)
|
||||
{
|
||||
targetId = await this.database.Slots.Where(s => s.SlotId == slotId)
|
||||
|
@ -82,8 +83,7 @@ public class CommentController : ControllerBase
|
|||
.Where(p => !blockedUsers.Contains(p.PosterUserId))
|
||||
.Include(c => c.Poster)
|
||||
.Where(p => p.Poster.PermissionLevel != PermissionLevel.Banned)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(c => GameComment.CreateFromEntity(c, token.UserId));
|
||||
|
||||
return this.Ok(new CommentListResponse(comments));
|
||||
|
|
|
@ -10,7 +10,7 @@ using LBPUnion.ProjectLighthouse.Types.Users;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Login;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
|
@ -13,7 +13,7 @@ using LBPUnion.ProjectLighthouse.Types.Users;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Login;
|
||||
|
||||
[ApiController]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/login")]
|
|
@ -6,7 +6,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Login;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
|
@ -64,5 +64,4 @@ public class ReportController : ControllerBase
|
|||
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ using LBPUnion.ProjectLighthouse.Logging;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
|
@ -159,54 +160,53 @@ public class PhotosController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("photos/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> SlotPhotos([FromQuery] int pageStart, [FromQuery] int pageSize, string slotType, int id)
|
||||
public async Task<IActionResult> SlotPhotos(string slotType, int id)
|
||||
{
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<GamePhoto> photos = (await this.database.Photos.Include(p => p.PhotoSubjects)
|
||||
.Where(p => p.SlotId == id)
|
||||
.OrderByDescending(s => s.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(GamePhoto.CreateFromEntity);
|
||||
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
}
|
||||
|
||||
[HttpGet("photos/by")]
|
||||
public async Task<IActionResult> UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> UserPhotosBy(string user)
|
||||
{
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<GamePhoto> photos = (await this.database.Photos.Include(p => p.PhotoSubjects)
|
||||
.Where(p => p.CreatorId == targetUserId)
|
||||
.OrderByDescending(s => s.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(GamePhoto.CreateFromEntity);
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
}
|
||||
|
||||
[HttpGet("photos/with")]
|
||||
public async Task<IActionResult> UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> UserPhotosWith(string user)
|
||||
{
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<GamePhoto> photos = (await this.database.Photos.Include(p => p.PhotoSubjects)
|
||||
.Where(p => p.PhotoSubjects.Any(ps => ps.UserId == targetUserId))
|
||||
.OrderByDescending(s => s.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(GamePhoto.CreateFromEntity);
|
||||
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class CategoryController : ControllerBase
|
||||
{
|
||||
private readonly DatabaseContext database;
|
||||
|
||||
public CategoryController(DatabaseContext database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpGet("searches")]
|
||||
[HttpGet("genres")]
|
||||
public async Task<IActionResult> GenresAndSearches()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
pageData.TotalElements = CategoryHelper.Categories.Count;
|
||||
|
||||
if (!int.TryParse(this.Request.Query["num_categories_with_results"], out int results)) results = 5;
|
||||
|
||||
List<GameCategory> categories = new();
|
||||
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
foreach (Category category in CategoryHelper.Categories.Skip(Math.Max(0, pageData.PageStart - 1))
|
||||
.Take(Math.Min(pageData.PageSize, pageData.MaxElements))
|
||||
.ToList())
|
||||
{
|
||||
int numResults = results > 0 ? 1 : 0;
|
||||
categories.Add(await category.Serialize(this.database, token, queryBuilder, numResults));
|
||||
results--;
|
||||
}
|
||||
|
||||
return this.Ok(new CategoryListResponse(categories, pageData.TotalElements, "", pageData.HintStart));
|
||||
}
|
||||
|
||||
[HttpGet("searches/{endpointName}")]
|
||||
public async Task<IActionResult> GetCategorySlots(string endpointName)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
|
||||
if (category == null) return this.NotFound();
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
Logger.Debug("Found category " + category, LogArea.Category);
|
||||
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
GenericSerializableList returnList = category switch
|
||||
{
|
||||
SlotCategory gc => await this.GetSlotCategory(gc, token, queryBuilder, pageData),
|
||||
PlaylistCategory pc => await this.GetPlaylistCategory(pc, token, pageData),
|
||||
UserCategory uc => await this.GetUserCategory(uc, token, pageData),
|
||||
_ => new GenericSerializableList(),
|
||||
};
|
||||
|
||||
return this.Ok(returnList);
|
||||
}
|
||||
|
||||
private async Task<GenericSerializableList> GetUserCategory(UserCategory userCategory, GameTokenEntity token, PaginationData pageData)
|
||||
{
|
||||
int totalUsers = await userCategory.GetItems(this.database, token).CountAsync();
|
||||
pageData.TotalElements = totalUsers;
|
||||
IQueryable<UserEntity> userQuery = userCategory.GetItems(this.database, token).ApplyPagination(pageData);
|
||||
|
||||
List<ILbpSerializable> users =
|
||||
(await userQuery.ToListAsync()).ToSerializableList<UserEntity, ILbpSerializable>(GameUser
|
||||
.CreateFromEntity);
|
||||
return new GenericSerializableList(users, pageData);
|
||||
}
|
||||
|
||||
private async Task<GenericSerializableList> GetPlaylistCategory(PlaylistCategory playlistCategory, GameTokenEntity token, PaginationData pageData)
|
||||
{
|
||||
int totalPlaylists = await playlistCategory.GetItems(this.database, token).CountAsync();
|
||||
pageData.TotalElements = totalPlaylists;
|
||||
IQueryable<PlaylistEntity> playlistQuery = playlistCategory.GetItems(this.database, token).ApplyPagination(pageData);
|
||||
|
||||
List<ILbpSerializable> playlists =
|
||||
(await playlistQuery.ToListAsync()).ToSerializableList<PlaylistEntity, ILbpSerializable>(GamePlaylist
|
||||
.CreateFromEntity);
|
||||
return new GenericSerializableList(playlists, pageData);
|
||||
}
|
||||
|
||||
private async Task<GenericSerializableList> GetSlotCategory(SlotCategory slotCategory, GameTokenEntity token, SlotQueryBuilder queryBuilder, PaginationData pageData)
|
||||
{
|
||||
int totalSlots = await slotCategory.GetItems(this.database, token, queryBuilder).CountAsync();
|
||||
pageData.TotalElements = totalSlots;
|
||||
IQueryable<SlotEntity> slotQuery = slotCategory.GetItems(this.database, token, queryBuilder).ApplyPagination(pageData);
|
||||
|
||||
if (bool.TryParse(this.Request.Query["includePlayed"], out bool includePlayed) && !includePlayed)
|
||||
{
|
||||
slotQuery = slotQuery.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Played = this.database.VisitedLevels.Any(v => v.SlotId == s.SlotId && v.UserId == token.UserId),
|
||||
})
|
||||
.Where(s => !s.Played)
|
||||
.Select(s => s.Slot);
|
||||
}
|
||||
|
||||
if (this.Request.Query.ContainsKey("sort"))
|
||||
{
|
||||
string sort = (string?)this.Request.Query["sort"] ?? "";
|
||||
slotQuery = sort switch
|
||||
{
|
||||
"relevance" => slotQuery.ApplyOrdering(new SlotSortBuilder<SlotEntity>()
|
||||
.AddSort(new UniquePlaysTotalSort())
|
||||
.AddSort(new LastUpdatedSort())),
|
||||
"likes" => slotQuery.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
ThumbsUp = this.database.RatedLevels.Count(r => r.SlotId == s.SlotId && r.Rating == 1),
|
||||
})
|
||||
.OrderByDescending(s => s.ThumbsUp)
|
||||
.Select(s => s.Slot),
|
||||
"hearts" => slotQuery.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Hearts = this.database.HeartedLevels.Count(h => h.SlotId == s.SlotId),
|
||||
})
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.Select(s => s.Slot),
|
||||
"date" => slotQuery.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort())),
|
||||
"plays" => slotQuery.ApplyOrdering(
|
||||
new SlotSortBuilder<SlotEntity>().AddSort(new UniquePlaysTotalSort()).AddSort(new TotalPlaysSort())),
|
||||
_ => slotQuery,
|
||||
};
|
||||
}
|
||||
|
||||
List<ILbpSerializable> slots =
|
||||
(await slotQuery.ToListAsync()).ToSerializableList<SlotEntity, ILbpSerializable>(s =>
|
||||
SlotBase.CreateFromEntity(s, token));
|
||||
return new GenericSerializableList(slots, pageData);
|
||||
}
|
||||
}
|
|
@ -2,13 +2,8 @@
|
|||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -148,95 +143,4 @@ public class CollectionController : ControllerBase
|
|||
|
||||
return this.Ok(await this.GetUserPlaylists(targetUserId));
|
||||
}
|
||||
|
||||
[HttpGet("searches")]
|
||||
[HttpGet("genres")]
|
||||
public async Task<IActionResult> GenresAndSearches()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
List<GameCategory> categories = new();
|
||||
|
||||
foreach (Category category in CategoryHelper.Categories.ToList())
|
||||
{
|
||||
if(category is CategoryWithUser categoryWithUser) categories.Add(categoryWithUser.Serialize(this.database, user));
|
||||
else categories.Add(category.Serialize(this.database));
|
||||
}
|
||||
|
||||
return this.Ok(new CategoryListResponse(categories, CategoryHelper.Categories.Count, 0, 1));
|
||||
}
|
||||
|
||||
[HttpGet("searches/{endpointName}")]
|
||||
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize,
|
||||
[FromQuery] int players = 0,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? labelFilter3 = null,
|
||||
[FromQuery] string? labelFilter4 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] string? adventure = null
|
||||
)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
|
||||
if (category == null) return this.NotFound();
|
||||
|
||||
Logger.Debug("Found category " + category, LogArea.Category);
|
||||
|
||||
List<SlotEntity> slots;
|
||||
int totalSlots;
|
||||
|
||||
if (category is CategoryWithUser categoryWithUser)
|
||||
{
|
||||
slots = (await categoryWithUser.GetSlots(this.database, user, pageStart, pageSize)
|
||||
.ToListAsync());
|
||||
totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
slots = category.GetSlots(this.database, pageStart, pageSize)
|
||||
.ToList();
|
||||
totalSlots = category.GetTotalSlots(this.database);
|
||||
}
|
||||
|
||||
slots = this.filterSlots(slots, players + 1, labelFilter0, labelFilter1, labelFilter2, labelFilter3, labelFilter4, move, adventure);
|
||||
|
||||
return this.Ok(new GenericSlotResponse("results", slots.ToSerializableList(s => SlotBase.CreateFromEntity(s, token)), totalSlots, pageStart + pageSize));
|
||||
}
|
||||
|
||||
private List<SlotEntity> filterSlots(List<SlotEntity> slots, int players, string? labelFilter0 = null, string? labelFilter1 = null, string? labelFilter2 = null, string? labelFilter3 = null, string? labelFilter4 = null, string? move = null, string? adventure = null)
|
||||
{
|
||||
slots.RemoveAll(s => s.MinimumPlayers != players);
|
||||
|
||||
if (labelFilter0 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter0));
|
||||
if (labelFilter1 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter1));
|
||||
if (labelFilter2 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter2));
|
||||
if (labelFilter3 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter3));
|
||||
if (labelFilter4 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter4));
|
||||
|
||||
if (move == "noneCan")
|
||||
slots.RemoveAll(s => s.MoveRequired);
|
||||
if (move == "allMust")
|
||||
slots.RemoveAll(s => !s.MoveRequired);
|
||||
|
||||
if (adventure == "noneCan")
|
||||
slots.RemoveAll(s => s.IsAdventurePlanet);
|
||||
if (adventure == "allMust")
|
||||
slots.RemoveAll(s => !s.IsAdventurePlanet);
|
||||
|
||||
return slots;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
@ -22,6 +24,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
|||
public class ListController : ControllerBase
|
||||
{
|
||||
private readonly DatabaseContext database;
|
||||
|
||||
public ListController(DatabaseContext database)
|
||||
{
|
||||
this.database = database;
|
||||
|
@ -32,31 +35,30 @@ public class ListController : ControllerBase
|
|||
#region Level Queue (lolcatftw)
|
||||
|
||||
[HttpGet("slots/lolcatftw/{username}")]
|
||||
public async Task<IActionResult> GetQueuedLevels
|
||||
(
|
||||
string username,
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] int? players = null,
|
||||
[FromQuery] bool? move = null,
|
||||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
public async Task<IActionResult> GetQueuedLevels(string username)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<SlotBase> queuedLevels = (await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.Queue)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync())
|
||||
.ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
int total = await this.database.QueuedLevels.CountAsync(q => q.UserId == token.UserId);
|
||||
int start = pageStart + Math.Min(pageSize, 30);
|
||||
pageData.TotalElements = await this.database.QueuedLevels.CountAsync(q => q.UserId == targetUserId);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(queuedLevels, total, start));
|
||||
IQueryable<SlotEntity> baseQuery = this.database.QueuedLevels.Where(h => h.UserId == targetUserId)
|
||||
.OrderByDescending(q => q.QueuedLevelId)
|
||||
.Include(q => q.Slot)
|
||||
.Select(q => q.Slot);
|
||||
|
||||
List<SlotBase> queuedLevels = await baseQuery.GetSlots(token,
|
||||
this.FilterFromRequest(token),
|
||||
pageData,
|
||||
new SlotSortBuilder<SlotEntity>());
|
||||
|
||||
return this.Ok(new GenericSlotResponse(queuedLevels, pageData));
|
||||
}
|
||||
|
||||
[HttpPost("lolcatftw/add/user/{id:int}")]
|
||||
|
@ -102,37 +104,33 @@ public class ListController : ControllerBase
|
|||
#region Hearted Levels
|
||||
|
||||
[HttpGet("favouriteSlots/{username}")]
|
||||
public async Task<IActionResult> GetFavouriteSlots
|
||||
(
|
||||
string username,
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] int? players = null,
|
||||
[FromQuery] bool? move = null,
|
||||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
public async Task<IActionResult> GetFavouriteSlots(string username)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.Forbid();
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
List<SlotBase> heartedLevels = (await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.FavouriteSlots)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
|
||||
pageData.TotalElements = await this.database.HeartedLevels.CountAsync(h => h.UserId == targetUserId);
|
||||
|
||||
int total = await this.database.HeartedLevels.CountAsync(q => q.UserId == targetUser.UserId);
|
||||
int start = pageStart + Math.Min(pageSize, 30);
|
||||
IQueryable<SlotEntity> baseQuery = this.database.HeartedLevels.Where(h => h.UserId == targetUserId)
|
||||
.OrderByDescending(h => h.HeartedLevelId)
|
||||
.Include(h => h.Slot)
|
||||
.Select(h => h.Slot);
|
||||
|
||||
return this.Ok(new GenericSlotResponse("favouriteSlots", heartedLevels, total, start));
|
||||
List<SlotBase> heartedLevels = await baseQuery.GetSlots(token,
|
||||
this.FilterFromRequest(token),
|
||||
pageData,
|
||||
new SlotSortBuilder<SlotEntity>());
|
||||
|
||||
return this.Ok(new GenericSlotResponse("favouriteSlots", heartedLevels, pageData));
|
||||
}
|
||||
|
||||
private const int FirstLbp2DeveloperSlotId = 124806; // This is the first known level slot GUID in LBP2. Feel free to change it if a lower one is found.
|
||||
private const int firstLbp2DeveloperSlotId = 124806; // This is the first known level slot GUID in LBP2. Feel free to change it if a lower one is found.
|
||||
|
||||
[HttpPost("favourite/slot/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> AddFavouriteSlot(string slotType, int id)
|
||||
|
@ -148,7 +146,7 @@ public class ListController : ControllerBase
|
|||
|
||||
if (slotType == "developer")
|
||||
{
|
||||
GameVersion slotGameVersion = (slot.InternalSlotId < FirstLbp2DeveloperSlotId) ? GameVersion.LittleBigPlanet1 : token.GameVersion;
|
||||
GameVersion slotGameVersion = (slot.InternalSlotId < firstLbp2DeveloperSlotId) ? GameVersion.LittleBigPlanet1 : token.GameVersion;
|
||||
slot.GameVersion = slotGameVersion;
|
||||
}
|
||||
|
||||
|
@ -171,7 +169,7 @@ public class ListController : ControllerBase
|
|||
|
||||
if (slotType == "developer")
|
||||
{
|
||||
GameVersion slotGameVersion = (slot.InternalSlotId < FirstLbp2DeveloperSlotId) ? GameVersion.LittleBigPlanet1 : token.GameVersion;
|
||||
GameVersion slotGameVersion = (slot.InternalSlotId < firstLbp2DeveloperSlotId) ? GameVersion.LittleBigPlanet1 : token.GameVersion;
|
||||
slot.GameVersion = slotGameVersion;
|
||||
}
|
||||
|
||||
|
@ -185,26 +183,27 @@ public class ListController : ControllerBase
|
|||
#region Hearted Playlists
|
||||
|
||||
[HttpGet("favouritePlaylists/{username}")]
|
||||
public async Task<IActionResult> GetFavouritePlaylists(string username, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> GetFavouritePlaylists(string username)
|
||||
{
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.Forbid();
|
||||
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<GamePlaylist> heartedPlaylists = (await this.database.HeartedPlaylists.Where(p => p.UserId == targetUserId)
|
||||
.Include(p => p.Playlist)
|
||||
.Include(p => p.Playlist.Creator)
|
||||
.OrderByDescending(p => p.HeartedPlaylistId)
|
||||
.Select(p => p.Playlist)
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(GamePlaylist.CreateFromEntity);
|
||||
|
||||
int total = await this.database.HeartedPlaylists.CountAsync(p => p.UserId == targetUserId);
|
||||
pageData.TotalElements = await this.database.HeartedPlaylists.CountAsync(p => p.UserId == targetUserId);
|
||||
|
||||
return this.Ok(new GenericPlaylistResponse<GamePlaylist>("favouritePlaylists", heartedPlaylists)
|
||||
{
|
||||
Total = total,
|
||||
HintStart = pageStart + Math.Min(pageSize, 30),
|
||||
Total = pageData.TotalElements,
|
||||
HintStart = pageData.HintStart,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -241,26 +240,27 @@ public class ListController : ControllerBase
|
|||
#region Users
|
||||
|
||||
[HttpGet("favouriteUsers/{username}")]
|
||||
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||
public async Task<IActionResult> GetFavouriteUsers(string username)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.Forbid();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
pageData.TotalElements = await this.database.HeartedProfiles.CountAsync(h => h.UserId == targetUserId);
|
||||
|
||||
List<GameUser> heartedProfiles = (await this.database.HeartedProfiles.Include(h => h.HeartedUser)
|
||||
.OrderBy(h => h.HeartedProfileId)
|
||||
.Where(h => h.UserId == targetUser.UserId)
|
||||
.Where(h => h.UserId == targetUserId)
|
||||
.Select(h => h.HeartedUser)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(u => GameUser.CreateFromEntity(u, token.GameVersion));
|
||||
|
||||
int total = await this.database.HeartedProfiles.CountAsync(h => h.UserId == targetUser.UserId);
|
||||
|
||||
return this.Ok(new GenericUserResponse<GameUser>("favouriteUsers", heartedProfiles, total, pageStart + Math.Min(pageSize, 30)));
|
||||
return this.Ok(new GenericUserResponse<GameUser>("favouriteUsers", heartedProfiles, pageData));
|
||||
}
|
||||
|
||||
[HttpPost("favourite/user/{username}")]
|
||||
|
@ -288,85 +288,5 @@ public class ListController : ControllerBase
|
|||
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering
|
||||
internal enum ListFilterType // used to collapse code that would otherwise be two separate functions
|
||||
{
|
||||
Queue,
|
||||
FavouriteSlots,
|
||||
}
|
||||
|
||||
private static GameVersion getGameFilter(string? gameFilterType, GameVersion version)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
GameVersion.LittleBigPlanetVita => GameVersion.LittleBigPlanetVita,
|
||||
GameVersion.LittleBigPlanetPSP => GameVersion.LittleBigPlanetPSP,
|
||||
_ => gameFilterType switch
|
||||
{
|
||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||
null => GameVersion.LittleBigPlanet1,
|
||||
_ => GameVersion.Unknown,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<SlotEntity> filterListByRequest(string? gameFilterType, string? dateFilterType, GameVersion version, string username, ListFilterType filterType)
|
||||
{
|
||||
if (version is GameVersion.LittleBigPlanetPSP or GameVersion.Unknown)
|
||||
{
|
||||
return this.database.Slots.ByGameVersion(version, false, true);
|
||||
}
|
||||
|
||||
long oldestTime = dateFilterType switch
|
||||
{
|
||||
"thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(),
|
||||
"thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
GameVersion gameVersion = getGameFilter(gameFilterType, version);
|
||||
|
||||
// The filtering only cares if this isn't equal to 'both'
|
||||
if (version == GameVersion.LittleBigPlanetVita) gameFilterType = "lbp2";
|
||||
|
||||
if (filterType == ListFilterType.Queue)
|
||||
{
|
||||
IQueryable<QueuedLevelEntity> whereQueuedLevels;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
// Get game versions less than the current version
|
||||
// Needs support for LBP3 ("both" = LBP1+2)
|
||||
whereQueuedLevels = this.database.QueuedLevels.Where(q => q.User.Username == username)
|
||||
.Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion <= gameVersion && q.Slot.FirstUploaded >= oldestTime);
|
||||
else
|
||||
// Get game versions exactly equal to gamefiltertype
|
||||
whereQueuedLevels = this.database.QueuedLevels.Where(q => q.User.Username == username)
|
||||
.Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion == gameVersion && q.Slot.FirstUploaded >= oldestTime);
|
||||
|
||||
return whereQueuedLevels.OrderByDescending(q => q.QueuedLevelId).Include(q => q.Slot.Creator).Select(q => q.Slot).ByGameVersion(gameVersion, false, false, true);
|
||||
}
|
||||
|
||||
IQueryable<HeartedLevelEntity> whereHeartedLevels;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
// Get game versions less than the current version
|
||||
// Needs support for LBP3 ("both" = LBP1+2)
|
||||
whereHeartedLevels = this.database.HeartedLevels.Where(h => h.User.Username == username)
|
||||
.Where(h => (h.Slot.Type == SlotType.User || h.Slot.Type == SlotType.Developer) && !h.Slot.Hidden && h.Slot.GameVersion <= gameVersion && h.Slot.FirstUploaded >= oldestTime);
|
||||
else
|
||||
// Get game versions exactly equal to gamefiltertype
|
||||
whereHeartedLevels = this.database.HeartedLevels.Where(h => h.User.Username == username)
|
||||
.Where(h => (h.Slot.Type == SlotType.User || h.Slot.Type == SlotType.Developer) && !h.Slot.Hidden && h.Slot.GameVersion == gameVersion && h.Slot.FirstUploaded >= oldestTime);
|
||||
|
||||
return whereHeartedLevels.OrderByDescending(h => h.HeartedLevelId).Include(h => h.Slot.Creator).Select(h => h.Slot).ByGameVersion(gameVersion, false, false, true);
|
||||
}
|
||||
#endregion Filtering
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -28,7 +28,7 @@ public class ReviewController : ControllerBase
|
|||
|
||||
// LBP1 rating
|
||||
[HttpPost("rate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
|
||||
public async Task<IActionResult> Rate(int slotId, int rating)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class ReviewController : ControllerBase
|
|||
|
||||
// LBP2 and beyond rating
|
||||
[HttpPost("dpadrate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
|
||||
public async Task<IActionResult> DPadRate(int slotId, int rating)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
|
@ -142,54 +142,47 @@ public class ReviewController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("reviewsFor/user/{slotId:int}")]
|
||||
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
public async Task<IActionResult> ReviewsFor(int slotId)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.BadRequest();
|
||||
|
||||
List<GameReview> reviews = (await this.database.Reviews.ByGameVersion(gameVersion, true)
|
||||
List<GameReview> reviews = (await this.database.Reviews
|
||||
.Where(r => r.SlotId == slotId)
|
||||
.OrderByDescending(r => r.ThumbsUp - r.ThumbsDown)
|
||||
.ThenByDescending(r => r.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(r => GameReview.CreateFromEntity(r, token));
|
||||
|
||||
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart + Math.Min(pageSize, 30)));
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageData.HintStart));
|
||||
}
|
||||
|
||||
[HttpGet("reviewsBy/{username}")]
|
||||
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
public async Task<IActionResult> ReviewsBy(string username)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
List<GameReview> reviews = (await this.database.Reviews.ByGameVersion(gameVersion, true)
|
||||
List<GameReview> reviews = (await this.database.Reviews
|
||||
.Where(r => r.ReviewerId == targetUserId)
|
||||
.OrderByDescending(r => r.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(r => GameReview.CreateFromEntity(r, token));
|
||||
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart));
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageData.HintStart));
|
||||
}
|
||||
|
||||
[HttpPost("rateReview/user/{slotId:int}/{username}")]
|
||||
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
|
||||
public async Task<IActionResult> RateReview(int slotId, string username, int rating = 0)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
|
@ -25,102 +27,29 @@ public class SearchController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("searchLBP3")]
|
||||
public Task<IActionResult> SearchSlotsLBP3([FromQuery] int pageSize, [FromQuery] int pageStart, [FromQuery] string textFilter,
|
||||
[FromQuery] int? players = 0,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? labelFilter3 = null,
|
||||
[FromQuery] string? labelFilter4 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] string? adventure = null)
|
||||
=> this.SearchSlots(textFilter, pageSize, pageStart, "results", false, players+1, labelFilter0, labelFilter1, labelFilter2, labelFilter3, labelFilter4, move, adventure);
|
||||
public Task<IActionResult> SearchSlotsLBP3([FromQuery] string textFilter)
|
||||
=> this.SearchSlots(textFilter, "results");
|
||||
|
||||
[HttpGet("search")]
|
||||
public async Task<IActionResult> SearchSlots(
|
||||
[FromQuery] string query,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int pageStart,
|
||||
string? keyName = "slots",
|
||||
bool crosscontrol = false,
|
||||
[FromQuery] int? players = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? labelFilter3 = null,
|
||||
[FromQuery] string? labelFilter4 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] string? adventure = null
|
||||
)
|
||||
public async Task<IActionResult> SearchSlots([FromQuery] string query, string? keyName = "slots")
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query)) return this.BadRequest();
|
||||
|
||||
query = query.ToLower();
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
string[] keywords = query.Split(" ");
|
||||
queryBuilder.AddFilter(new TextFilter(query));
|
||||
|
||||
IQueryable<SlotEntity> dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.Where(s => s.Type == SlotType.User && s.CrossControllerRequired == crosscontrol)
|
||||
.OrderBy(s => !s.TeamPick)
|
||||
.ThenByDescending(s => s.FirstUploaded)
|
||||
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
|
||||
pageData.TotalElements = await this.database.Slots.Where(queryBuilder.Build()).CountAsync();
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (string keyword in keywords)
|
||||
dbQuery = dbQuery.Where
|
||||
(
|
||||
s => s.Name.ToLower().Contains(keyword) ||
|
||||
s.Description.ToLower().Contains(keyword) ||
|
||||
s.Creator!.Username.ToLower().Contains(keyword) ||
|
||||
s.SlotId.ToString().Equals(keyword)
|
||||
);
|
||||
List<SlotBase> slots = await this.database.Slots.Include(s => s.Creator)
|
||||
.GetSlots(token, queryBuilder, pageData, new SlotSortBuilder<SlotEntity>());
|
||||
|
||||
List<SlotEntity> slots = (await dbQuery.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync());
|
||||
|
||||
slots = filterSlots(slots, players, labelFilter0, labelFilter1, labelFilter2, labelFilter3, labelFilter4, move, adventure);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(keyName, slots.ToSerializableList(s => SlotBase.CreateFromEntity(s, token)), await dbQuery.CountAsync(), 0));
|
||||
return this.Ok(new GenericSlotResponse(keyName, slots, pageData));
|
||||
}
|
||||
|
||||
// /LITTLEBIGPLANETPS3_XML?pageStart=1&pageSize=10&resultTypes[]=slot&resultTypes[]=playlist&resultTypes[]=user&adventure=dontCare&textFilter=qwer
|
||||
|
||||
private List<SlotEntity> filterSlots(List<SlotEntity> slots, int? players = null, string? labelFilter0 = null, string? labelFilter1 = null, string? labelFilter2 = null, string? labelFilter3 = null, string? labelFilter4 = null, string? move = null, string? adventure = null)
|
||||
{
|
||||
if (players != null)
|
||||
slots.RemoveAll(s => s.MinimumPlayers != players);
|
||||
|
||||
if (labelFilter0 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter0));
|
||||
if (labelFilter1 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter1));
|
||||
if (labelFilter2 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter2));
|
||||
if (labelFilter3 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter3));
|
||||
if (labelFilter4 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter4));
|
||||
|
||||
if (move == "false")
|
||||
slots.RemoveAll(s => s.MoveRequired);
|
||||
if (move == "only")
|
||||
slots.RemoveAll(s => !s.MoveRequired);
|
||||
|
||||
if (move == "noneCan")
|
||||
slots.RemoveAll(s => s.MoveRequired);
|
||||
if (move == "allMust")
|
||||
slots.RemoveAll(s => !s.MoveRequired);
|
||||
|
||||
if (adventure == "noneCan")
|
||||
slots.RemoveAll(s => s.IsAdventurePlanet);
|
||||
if (adventure == "allMust")
|
||||
slots.RemoveAll(s => !s.IsAdventurePlanet);
|
||||
|
||||
return slots;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using System.Linq.Expressions;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts.Metadata;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
|
@ -23,34 +29,31 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
|||
public class SlotsController : ControllerBase
|
||||
{
|
||||
private readonly DatabaseContext database;
|
||||
|
||||
public SlotsController(DatabaseContext database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpGet("slots/by")]
|
||||
public async Task<IActionResult> SlotsBy([FromQuery(Name = "u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] bool crosscontrol = false)
|
||||
public async Task<IActionResult> SlotsBy([FromQuery(Name = "u")] string username)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
int usedSlots = this.database.Slots.Count(s => s.CreatorId == targetUserId);
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<SlotBase> slots = (await this.database.Slots.Where(s => s.CreatorId == targetUserId)
|
||||
.ByGameVersion(token.GameVersion, token.UserId == targetUserId)
|
||||
.Where(match => match.CrossControllerRequired == crosscontrol)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, usedSlots))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, usedSlots);
|
||||
int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId && s.CrossControllerRequired == crosscontrol);
|
||||
pageData.TotalElements = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId);
|
||||
|
||||
return this.Ok(new GenericSlotResponse("slots", slots, total, start));
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token).AddFilter(new CreatorFilter(targetUserId));
|
||||
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort());
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse("slots", slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slotList")]
|
||||
|
@ -61,7 +64,7 @@ public class SlotsController : ControllerBase
|
|||
List<SlotBase> slots = new();
|
||||
foreach (int slotId in slotIds)
|
||||
{
|
||||
SlotEntity? slot = await this.database.Slots.Include(t => t.Creator).Where(t => t.SlotId == slotId && t.Type == SlotType.User).FirstOrDefaultAsync();
|
||||
SlotEntity? slot = await this.database.Slots.Where(t => t.SlotId == slotId && t.Type == SlotType.User).FirstOrDefaultAsync();
|
||||
if (slot == null)
|
||||
{
|
||||
slot = await this.database.Slots.Where(t => t.InternalSlotId == slotId && t.Type == SlotType.Developer).FirstOrDefaultAsync();
|
||||
|
@ -102,7 +105,7 @@ public class SlotsController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("s/developer/{id:int}")]
|
||||
public async Task<IActionResult> SDev(int id)
|
||||
public async Task<IActionResult> DeveloperSlot(int id)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
|
@ -113,13 +116,12 @@ public class SlotsController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("s/user/{id:int}")]
|
||||
public async Task<IActionResult> SUser(int id)
|
||||
public async Task<IActionResult> UserSlot(int id)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
SlotEntity? slot = await this.database.Slots.ByGameVersion(gameVersion, true, true).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.Where(this.GetDefaultFilters(token).Build())
|
||||
.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
|
@ -127,66 +129,40 @@ public class SlotsController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("slots/cool")]
|
||||
public async Task<IActionResult> Lbp1CoolSlots([FromQuery] int page)
|
||||
{
|
||||
const int pageSize = 30;
|
||||
return await this.CoolSlots((page - 1) * pageSize, pageSize);
|
||||
}
|
||||
public async Task<IActionResult> Lbp1CoolSlots() => await this.CoolSlots();
|
||||
|
||||
[HttpGet("slots/lbp2cool")]
|
||||
public async Task<IActionResult> CoolSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int players = 1,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] int? page = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
{
|
||||
if (page != null) pageStart = (int)page * 30;
|
||||
// bit of a better placeholder until we can track average user interaction with /stream endpoint
|
||||
return await this.ThumbsSlots(pageStart, Math.Min(pageSize, 30), players, gameFilterType, "thisMonth",
|
||||
labelFilter0, labelFilter1, labelFilter2, move, crosscontrol);
|
||||
}
|
||||
public async Task<IActionResult> CoolSlots() => await this.ThumbsSlots();
|
||||
|
||||
[HttpGet("slots")]
|
||||
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] bool crosscontrol = false)
|
||||
public async Task<IActionResult> NewestSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.Where(s => s.CrossControllerRequired == crosscontrol)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
.ThenByDescending(s => s.SlotId)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new FirstUploadedSort());
|
||||
sortBuilder.AddSort(new SlotIdSort());
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/like/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
if (slotType != "user") return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
SlotEntity? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (targetSlot == null) return this.BadRequest();
|
||||
|
||||
|
@ -198,275 +174,180 @@ public class SlotsController : ControllerBase
|
|||
.Select(r => r.SlotId)
|
||||
.ToList();
|
||||
|
||||
List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.Where(s => slotIdsWithTag.Contains(s.SlotId))
|
||||
.OrderByDescending(s => s.PlaysLBP1)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
pageData.TotalElements = slotIdsWithTag.Count;
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = slotIdsWithTag.Count;
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token).AddFilter(0, new SlotIdFilter(slotIdsWithTag));
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new PlaysForGameSort(GameVersion.LittleBigPlanet1));
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/highestRated")]
|
||||
public async Task<IActionResult> HighestRatedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> HighestRatedSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
RatingLbp1 = this.database.RatedLevels.Where(r => r.SlotId == s.SlotId).Average(r => (double?)r.RatingLBP1) ?? 3.0,
|
||||
})
|
||||
.OrderByDescending(s => s.RatingLbp1)
|
||||
.Select(s => s.Slot)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCount(this.database);
|
||||
SlotSortBuilder<SlotMetadata> sortBuilder = new();
|
||||
sortBuilder.AddSort(new RatingLBP1Sort());
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
Expression<Func<SlotEntity, SlotMetadata>> selectorFunc = s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
RatingLbp1 = this.database.RatedLevels.Where(r => r.SlotId == s.SlotId)
|
||||
.Average(r => (double?)r.RatingLBP1) ?? 3.0,
|
||||
};
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder, selectorFunc);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/tag")]
|
||||
public async Task<IActionResult> SimilarSlots([FromQuery] string tag, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> SimilarSlots([FromQuery] string tag)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<int> slotIdsWithTag = await this.database.RatedLevels.Where(r => r.TagLBP1.Length > 0)
|
||||
.Where(r => r.TagLBP1 == tag)
|
||||
.Select(s => s.SlotId)
|
||||
.ToListAsync();
|
||||
|
||||
List<SlotBase> slots = (await this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId))
|
||||
.ByGameVersion(token.GameVersion, false, true)
|
||||
.OrderByDescending(s => s.PlaysLBP1)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
pageData.TotalElements = slotIdsWithTag.Count;
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = slotIdsWithTag.Count;
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new PlaysForGameSort(GameVersion.LittleBigPlanet1));
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, this.FilterFromRequest(token), pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mmpicks")]
|
||||
public async Task<IActionResult> TeamPickedSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? dateFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> TeamPickedSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.Where(s => s.TeamPick && s.CrossControllerRequired == crosscontrol)
|
||||
.OrderByDescending(s => s.LastUpdated)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.TeamPickCountForGame(this.database, token.GameVersion, crosscontrol);
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token).AddFilter(new TeamPickFilter());
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new LastUpdatedSort());
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/lbp2luckydip")]
|
||||
public async Task<IActionResult> LuckyDipSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int seed,
|
||||
[FromQuery] int players = 1,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? dateFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> LuckyDipSlots([FromQuery] int seed)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
const double biasFactor = .8f;
|
||||
List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.Where(s => s.CrossControllerRequired == crosscontrol)
|
||||
.OrderByDescending(s => EF.Functions.Random() * (s.FirstUploaded * biasFactor))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new RandomFirstUploadedSort());
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/thumbs")]
|
||||
public async Task<IActionResult> ThumbsSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? dateFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> ThumbsSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.Where(s => s.CrossControllerRequired == crosscontrol)
|
||||
.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
ThumbsUp = this.database.RatedLevels.Count(r => r.SlotId == s.SlotId && r.Rating == 1),
|
||||
})
|
||||
.OrderByDescending(s => s.ThumbsUp)
|
||||
.ThenBy(_ => EF.Functions.Random())
|
||||
.Select(s => s.Slot)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
SlotSortBuilder<SlotMetadata> sortBuilder = new();
|
||||
sortBuilder.AddSort(new ThumbsUpSort());
|
||||
|
||||
Expression<Func<SlotEntity, SlotMetadata>> selectorFunc = s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
ThumbsUp = this.database.RatedLevels.Count(r => r.SlotId == s.SlotId && r.Rating == 1),
|
||||
};
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder, selectorFunc);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostUniquePlays")]
|
||||
public async Task<IActionResult> MostUniquePlaysSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] string? dateFilterType = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> MostUniquePlaysSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
string game = getGameFilter(gameFilterType, token.GameVersion) switch
|
||||
{
|
||||
GameVersion.LittleBigPlanet1 => "LBP1",
|
||||
GameVersion.LittleBigPlanet2 => "LBP2",
|
||||
GameVersion.LittleBigPlanet3 => "LBP3",
|
||||
GameVersion.LittleBigPlanetVita => "LBP2",
|
||||
_ => "",
|
||||
};
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
string colName = $"Plays{game}Unique";
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.Where(s => s.CrossControllerRequired == crosscontrol)
|
||||
.OrderByDescending(s => EF.Property<int>(s, colName))
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||
sortBuilder.AddSort(new UniquePlaysForGameSort(token.GameVersion));
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostHearted")]
|
||||
public async Task<IActionResult> MostHeartedSlots
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] int players,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] string? dateFilterType = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> MostHeartedSlots()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.Where(s => s.CrossControllerRequired == crosscontrol)
|
||||
.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Hearts = this.database.HeartedLevels.Count(r => r.SlotId == s.SlotId),
|
||||
})
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.Select(s => s.Slot)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
SlotSortBuilder<SlotMetadata> sortBuilder = new();
|
||||
sortBuilder.AddSort(new HeartsSort());
|
||||
|
||||
Expression<Func<SlotEntity, SlotMetadata>> selectorFunc = s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Hearts = this.database.HeartedLevels.Count(r => r.SlotId == s.SlotId),
|
||||
};
|
||||
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder, selectorFunc);
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
|
||||
// /slots/busiest?pageStart=1&pageSize=30&gameFilterType=both&players=1&move=true
|
||||
[HttpGet("slots/busiest")]
|
||||
public async Task<IActionResult> BusiestLevels
|
||||
(
|
||||
[FromQuery] int pageStart,
|
||||
[FromQuery] int pageSize,
|
||||
[FromQuery] string? gameFilterType = null,
|
||||
[FromQuery] int players = 1,
|
||||
[FromQuery] string? labelFilter0 = null,
|
||||
[FromQuery] string? labelFilter1 = null,
|
||||
[FromQuery] string? labelFilter2 = null,
|
||||
[FromQuery] string? move = null,
|
||||
[FromQuery] bool crosscontrol = false
|
||||
)
|
||||
public async Task<IActionResult> BusiestLevels()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
PaginationData pageData = this.Request.GetPaginationData();
|
||||
|
||||
Dictionary<int, int> playersBySlotId = new();
|
||||
|
||||
|
@ -484,98 +365,15 @@ public class SlotsController : ControllerBase
|
|||
playersBySlotId.Add(room.Slot.SlotId, playerCount);
|
||||
}
|
||||
|
||||
IEnumerable<int> orderedPlayersBySlotId = playersBySlotId
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.OrderByDescending(kvp => kvp.Value)
|
||||
.Select(kvp => kvp.Key);
|
||||
|
||||
List<SlotEntity> slots = new();
|
||||
pageData.TotalElements = playersBySlotId.Count;
|
||||
|
||||
foreach (int slotId in orderedPlayersBySlotId)
|
||||
{
|
||||
SlotEntity? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.Where(s => s.SlotId == slotId && s.CrossControllerRequired == crosscontrol)
|
||||
.FirstOrDefaultAsync();
|
||||
if (slot == null) continue; // shouldn't happen ever unless the room is borked
|
||||
|
||||
slots.Add(slot);
|
||||
}
|
||||
List<int> orderedPlayersBySlotId = playersBySlotId.OrderByDescending(kvp => kvp.Value).Select(kvp => kvp.Key).ToList();
|
||||
|
||||
slots = this.filterSlots(slots, players, labelFilter0, labelFilter1, labelFilter2, move);
|
||||
SlotQueryBuilder queryBuilder = this.FilterFromRequest(token);
|
||||
queryBuilder.AddFilter(0, new SlotIdFilter(orderedPlayersBySlotId));
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = playersBySlotId.Count;
|
||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, new SlotSortBuilder<SlotEntity>());
|
||||
|
||||
return this.Ok(new GenericSlotResponse(slots.ToSerializableList(s => SlotBase.CreateFromEntity(s, token)), total, start));
|
||||
}
|
||||
|
||||
private List<SlotEntity> filterSlots(List<SlotEntity> slots, int players, string? labelFilter0 = null, string? labelFilter1 = null, string? labelFilter2 = null, string? move = null)
|
||||
{
|
||||
slots.RemoveAll(s => s.MinimumPlayers != players);
|
||||
|
||||
if (labelFilter0 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter0));
|
||||
if (labelFilter1 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter1));
|
||||
if (labelFilter2 != null)
|
||||
slots.RemoveAll(s => !s.AuthorLabels.Split(',').ToList().Contains(labelFilter2));
|
||||
|
||||
if (move == "false")
|
||||
slots.RemoveAll(s => s.MoveRequired);
|
||||
if (move == "only")
|
||||
slots.RemoveAll(s => !s.MoveRequired);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static GameVersion getGameFilter(string? gameFilterType, GameVersion version)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
GameVersion.LittleBigPlanetVita => GameVersion.LittleBigPlanetVita,
|
||||
GameVersion.LittleBigPlanetPSP => GameVersion.LittleBigPlanetPSP,
|
||||
_ => gameFilterType switch
|
||||
{
|
||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||
null => GameVersion.LittleBigPlanet1,
|
||||
_ => GameVersion.Unknown,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<SlotEntity> filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version)
|
||||
{
|
||||
if (version == GameVersion.LittleBigPlanetVita || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown)
|
||||
{
|
||||
return this.database.Slots.ByGameVersion(version, false, true);
|
||||
}
|
||||
|
||||
string _dateFilterType = dateFilterType ?? "";
|
||||
|
||||
long oldestTime = _dateFilterType switch
|
||||
{
|
||||
"thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(),
|
||||
"thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
GameVersion gameVersion = getGameFilter(gameFilterType, version);
|
||||
|
||||
IQueryable<SlotEntity> whereSlots;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
// Get game versions less than the current version
|
||||
// Needs support for LBP3 ("both" = LBP1+2)
|
||||
whereSlots = this.database.Slots.Where(s => s.Type == SlotType.User && !s.Hidden && s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
||||
else
|
||||
// Get game versions exactly equal to gamefiltertype
|
||||
whereSlots = this.database.Slots.Where(s => s.Type == SlotType.User && !s.Hidden && s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
||||
|
||||
return whereSlots.Include(s => s.Creator);
|
||||
return this.Ok(new GenericSlotResponse(slots, pageData));
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
|
@ -14,7 +16,6 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
|||
[Produces("text/plain")]
|
||||
public class StatisticsController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly DatabaseContext database;
|
||||
|
||||
public StatisticsController(DatabaseContext database)
|
||||
|
@ -23,7 +24,7 @@ public class StatisticsController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("playersInPodCount")]
|
||||
public IActionResult PlayersInPodCount() => this.Ok(StatisticsHelper.RoomCountForPlatform(this.GetToken().Platform).ToString());
|
||||
public IActionResult PlayersInPodCount() => this.Ok(StatisticsHelper.RoomCountForPlatform(this.GetToken().Platform).ToString());
|
||||
|
||||
[HttpGet("totalPlayerCount")]
|
||||
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatchesForGame(this.database, this.GetToken().GameVersion)).ToString());
|
||||
|
@ -32,12 +33,19 @@ public IActionResult PlayersInPodCount() => this.Ok(StatisticsHelper.RoomCountFo
|
|||
[Produces("text/xml")]
|
||||
public async Task<IActionResult> PlanetStats()
|
||||
{
|
||||
int totalSlotCount = await StatisticsHelper.SlotCountForGame(this.database, this.GetToken().GameVersion);
|
||||
int mmPicksCount = await StatisticsHelper.TeamPickCountForGame(this.database, this.GetToken().GameVersion);
|
||||
SlotQueryBuilder defaultFilter = this.GetDefaultFilters(this.GetToken());
|
||||
int totalSlotCount = await StatisticsHelper.SlotCount(this.database, defaultFilter);
|
||||
defaultFilter.AddFilter(new TeamPickFilter());
|
||||
int mmPicksCount = await StatisticsHelper.SlotCount(this.database, defaultFilter);
|
||||
|
||||
return this.Ok(new PlanetStatsResponse(totalSlotCount, mmPicksCount));
|
||||
}
|
||||
|
||||
[HttpGet("planetStats/totalLevelCount")]
|
||||
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCountForGame(this.database, this.GetToken().GameVersion)).ToString());
|
||||
public async Task<IActionResult> TotalLevelCount()
|
||||
{
|
||||
SlotQueryBuilder defaultFilter = this.GetDefaultFilters(this.GetToken());
|
||||
int totalSlotCount = await StatisticsHelper.SlotCount(this.database, defaultFilter);
|
||||
return this.Ok(totalSlotCount.ToString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
|
||||
public static class ControllerExtensions
|
||||
{
|
||||
private static GameVersion GetGameFilter(string? gameFilterType, GameVersion version)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
GameVersion.LittleBigPlanetVita => GameVersion.LittleBigPlanetVita,
|
||||
GameVersion.LittleBigPlanetPSP => GameVersion.LittleBigPlanetPSP,
|
||||
_ => gameFilterType switch
|
||||
{
|
||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||
null => GameVersion.LittleBigPlanet1,
|
||||
_ => GameVersion.Unknown,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public static SlotQueryBuilder GetDefaultFilters(this ControllerBase controller, GameTokenEntity token) =>
|
||||
new SlotQueryBuilder().AddFilter(new GameVersionFilter(token.GameVersion))
|
||||
.AddFilter(new SubLevelFilter(token.UserId))
|
||||
.AddFilter(new HiddenSlotFilter())
|
||||
.AddFilter(new SlotTypeFilter(SlotType.User));
|
||||
|
||||
public static SlotQueryBuilder FilterFromRequest(this ControllerBase controller, GameTokenEntity token)
|
||||
{
|
||||
SlotQueryBuilder queryBuilder = new();
|
||||
|
||||
List<string> authorLabels = new();
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string? label = controller.Request.Query[$"labelFilter{i}"];
|
||||
if (label == null) continue;
|
||||
authorLabels.Add(label);
|
||||
}
|
||||
|
||||
if (authorLabels.Count > 0) queryBuilder.AddFilter(new AuthorLabelFilter(authorLabels.ToArray()));
|
||||
|
||||
if (int.TryParse(controller.Request.Query["players"], out int minPlayers) && minPlayers >= 1)
|
||||
{
|
||||
// LBP3 starts counting at 0
|
||||
if (token.GameVersion == GameVersion.LittleBigPlanet3) minPlayers++;
|
||||
|
||||
queryBuilder.AddFilter(new PlayerCountFilter(minPlayers));
|
||||
}
|
||||
|
||||
if (controller.Request.Query.ContainsKey("textFilter"))
|
||||
{
|
||||
string textFilter = (string?)controller.Request.Query["textFilter"] ?? "";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(textFilter)) queryBuilder.AddFilter(new TextFilter(textFilter));
|
||||
}
|
||||
|
||||
if (controller.Request.Query.ContainsKey("dateFilterType"))
|
||||
{
|
||||
string dateFilter = (string?)controller.Request.Query["dateFilterType"] ?? "";
|
||||
long oldestTime = dateFilter switch
|
||||
{
|
||||
"thisWeek" => DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeMilliseconds(),
|
||||
"thisMonth" => DateTimeOffset.UtcNow.AddDays(-31).ToUnixTimeMilliseconds(),
|
||||
_ => 0,
|
||||
};
|
||||
if (oldestTime != 0) queryBuilder.AddFilter(new FirstUploadedFilter(oldestTime));
|
||||
}
|
||||
|
||||
if (token.GameVersion != GameVersion.LittleBigPlanet3)
|
||||
{
|
||||
if (controller.Request.Query.ContainsKey("move"))
|
||||
{
|
||||
string moveFilter = (string?)controller.Request.Query["move"] ?? "";
|
||||
// By default this will include levels with move so we don't handle true
|
||||
switch (moveFilter)
|
||||
{
|
||||
case "false":
|
||||
queryBuilder.AddFilter(new ExcludeMovePackFilter());
|
||||
break;
|
||||
case "only":
|
||||
queryBuilder.AddFilter(new MovePackFilter());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bool.TryParse(controller.Request.Query["move"], out bool movePack) && !movePack)
|
||||
queryBuilder.AddFilter(new ExcludeMovePackFilter());
|
||||
|
||||
if (bool.TryParse(controller.Request.Query["crosscontrol"], out bool crossControl) && crossControl)
|
||||
queryBuilder.AddFilter(new CrossControlFilter());
|
||||
|
||||
GameVersion targetVersion = token.GameVersion;
|
||||
|
||||
if (controller.Request.Query.ContainsKey("gameFilterType"))
|
||||
{
|
||||
string gameFilter = (string?)controller.Request.Query["gameFilterType"] ?? "";
|
||||
GameVersion filterVersion = GetGameFilter(gameFilter, targetVersion);
|
||||
// Don't serve lbp3 levels to lbp2 just cause of the game filter
|
||||
if (filterVersion <= targetVersion)
|
||||
{
|
||||
targetVersion = filterVersion;
|
||||
}
|
||||
}
|
||||
queryBuilder.AddFilter(new GameVersionFilter(targetVersion));
|
||||
}
|
||||
else if (token.GameVersion == GameVersion.LittleBigPlanet3)
|
||||
{
|
||||
void ParseLbp3Query(string key, Action allMust, Action noneCan, Action dontCare)
|
||||
{
|
||||
if (!controller.Request.Query.ContainsKey(key)) return;
|
||||
|
||||
string value = (string?)controller.Request.Query[key] ?? "dontCare";
|
||||
switch (value)
|
||||
{
|
||||
case "allMust":
|
||||
allMust();
|
||||
break;
|
||||
case "noneCan":
|
||||
noneCan();
|
||||
break;
|
||||
case "dontCare":
|
||||
dontCare();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ParseLbp3Query("adventure",
|
||||
() => queryBuilder.AddFilter(new AdventureFilter()),
|
||||
() => queryBuilder.AddFilter(new ExcludeAdventureFilter()),
|
||||
() =>
|
||||
{ });
|
||||
|
||||
ParseLbp3Query("move",
|
||||
() => queryBuilder.AddFilter(new MovePackFilter()),
|
||||
() => queryBuilder.AddFilter(new ExcludeMovePackFilter()),
|
||||
() =>
|
||||
{ });
|
||||
|
||||
string[]? ParseLbp3ArrayQuery(string key)
|
||||
{
|
||||
return !controller.Request.Query.TryGetValue($"{key}[]", out StringValues keys)
|
||||
? null
|
||||
: keys.Where(s => s != null).Select(s => s!).ToArray();
|
||||
}
|
||||
|
||||
string[]? gameFilters = ParseLbp3ArrayQuery("gameFilter");
|
||||
if (gameFilters != null)
|
||||
{
|
||||
queryBuilder.AddFilter(new GameVersionListFilter(gameFilters
|
||||
.Select(s => GetGameFilter(s, token.GameVersion))
|
||||
.ToArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
queryBuilder.AddFilter(new GameVersionFilter(GameVersion.LittleBigPlanet3));
|
||||
}
|
||||
|
||||
string[]? resultFilters = ParseLbp3ArrayQuery("resultType");
|
||||
if (resultFilters != null)
|
||||
{
|
||||
queryBuilder.AddFilter(new ResultTypeFilter(resultFilters));
|
||||
}
|
||||
}
|
||||
|
||||
if (token.GameVersion != GameVersion.LittleBigPlanet1)
|
||||
queryBuilder.AddFilter(new ExcludeLBP1OnlyFilter(token.UserId, token.GameVersion));
|
||||
|
||||
queryBuilder.AddFilter(new SubLevelFilter(token.UserId));
|
||||
queryBuilder.AddFilter(new HiddenSlotFilter());
|
||||
queryBuilder.AddFilter(new SlotTypeFilter(SlotType.User));
|
||||
|
||||
return queryBuilder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using System.Linq.Expressions;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
|
||||
public static class DatabaseContextExtensions
|
||||
{
|
||||
public static async Task<List<SlotBase>> GetSlots
|
||||
(
|
||||
this IQueryable<SlotEntity> queryable,
|
||||
GameTokenEntity token,
|
||||
SlotQueryBuilder queryBuilder,
|
||||
PaginationData pageData,
|
||||
ISortBuilder<SlotEntity> sortBuilder
|
||||
) =>
|
||||
(await queryable.Where(queryBuilder.Build())
|
||||
.ApplyOrdering(sortBuilder)
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
|
||||
public static async Task<List<SlotBase>> GetSlots
|
||||
(
|
||||
this DatabaseContext database,
|
||||
GameTokenEntity token,
|
||||
SlotQueryBuilder queryBuilder,
|
||||
PaginationData pageData,
|
||||
ISortBuilder<SlotEntity> sortBuilder
|
||||
) =>
|
||||
(await database.Slots.Where(queryBuilder.Build())
|
||||
.ApplyOrdering(sortBuilder)
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
|
||||
public static async Task<List<SlotBase>> GetSlots
|
||||
(
|
||||
this DatabaseContext database,
|
||||
GameTokenEntity token,
|
||||
SlotQueryBuilder queryBuilder,
|
||||
PaginationData pageData,
|
||||
ISortBuilder<SlotMetadata> sortBuilder,
|
||||
Expression<Func<SlotEntity, SlotMetadata>> selectorFunction
|
||||
) =>
|
||||
(await database.Slots.Where(queryBuilder.Build())
|
||||
.AsQueryable()
|
||||
.Select(selectorFunction)
|
||||
.ApplyOrdering(sortBuilder)
|
||||
.Select(s => s.Slot)
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
|
||||
}
|
|
@ -15,6 +15,8 @@ public static class CategoryHelper
|
|||
Categories.Add(new NewestLevelsCategory());
|
||||
Categories.Add(new MostPlayedCategory());
|
||||
Categories.Add(new HighestRatedCategory());
|
||||
Categories.Add(new MyHeartedCreatorsCategory());
|
||||
Categories.Add(new MyPlaylistsCategory());
|
||||
Categories.Add(new QueueCategory());
|
||||
Categories.Add(new HeartedCategory());
|
||||
Categories.Add(new LuckyDipCategory());
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
#nullable enable
|
||||
using System.Diagnostics;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public abstract class CategoryWithUser : Category
|
||||
{
|
||||
public abstract SlotEntity? GetPreviewSlot(DatabaseContext database, UserEntity user);
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.Error("tried to get preview slot without user on CategoryWithUser", LogArea.Category);
|
||||
if (Debugger.IsAttached) Debugger.Break();
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract int GetTotalSlots(DatabaseContext database, UserEntity user);
|
||||
public override int GetTotalSlots(DatabaseContext database)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.Error("tried to get total slots without user on CategoryWithUser", LogArea.Category);
|
||||
if (Debugger.IsAttached) Debugger.Break();
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
public abstract IQueryable<SlotEntity> GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize);
|
||||
public override IList<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.Error("tried to get slots without user on CategoryWithUser", LogArea.Category);
|
||||
if (Debugger.IsAttached) Debugger.Break();
|
||||
#endif
|
||||
return new List<SlotEntity>();
|
||||
}
|
||||
|
||||
public new string Serialize(DatabaseContext database)
|
||||
{
|
||||
Logger.Error("tried to serialize without user on CategoryWithUser", LogArea.Category);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public GameCategory Serialize(DatabaseContext database, UserEntity user)
|
||||
{
|
||||
List<SlotBase> slots = new();
|
||||
SlotEntity? previewSlot = this.GetPreviewSlot(database, user);
|
||||
if (previewSlot != null)
|
||||
slots.Add(SlotBase.CreateFromEntity(previewSlot, GameVersion.LittleBigPlanet3, user.UserId));
|
||||
|
||||
int totalSlots = this.GetTotalSlots(database, user);
|
||||
return GameCategory.CreateFromEntity(this, new GenericSlotResponse(slots, totalSlots, 2));
|
||||
}
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class CustomCategory : Category
|
||||
public class CustomCategory : SlotCategory
|
||||
{
|
||||
|
||||
public List<int> SlotIds;
|
||||
private readonly List<int> slotIds;
|
||||
public CustomCategory(string name, string description, string endpoint, string icon, IEnumerable<int> slotIds)
|
||||
{
|
||||
this.Name = name;
|
||||
|
@ -18,7 +17,7 @@ public class CustomCategory : Category
|
|||
this.IconHash = icon;
|
||||
this.Endpoint = endpoint;
|
||||
|
||||
this.SlotIds = slotIds.ToList();
|
||||
this.slotIds = slotIds.ToList();
|
||||
}
|
||||
|
||||
public CustomCategory(DatabaseCategoryEntity category)
|
||||
|
@ -28,16 +27,19 @@ public class CustomCategory : Category
|
|||
this.IconHash = category.IconHash;
|
||||
this.Endpoint = category.Endpoint;
|
||||
|
||||
this.SlotIds = category.SlotIds.ToList();
|
||||
this.slotIds = category.SlotIds.ToList();
|
||||
}
|
||||
|
||||
public sealed override string Name { get; set; }
|
||||
public sealed override string Description { get; set; }
|
||||
public sealed override string IconHash { get; set; }
|
||||
public sealed override string Endpoint { get; set; }
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0] && !s.CrossControllerRequired);
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3).Where(s => this.SlotIds.Contains(s.SlotId) && !s.CrossControllerRequired);
|
||||
public override int GetTotalSlots(DatabaseContext database) => this.SlotIds.Count;
|
||||
|
||||
public override string Tag => "custom_category";
|
||||
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity entity, SlotQueryBuilder queryBuilder)
|
||||
{
|
||||
queryBuilder.Clone().AddFilter(new SlotIdFilter(this.slotIds));
|
||||
return database.Slots.Where(queryBuilder.Build());
|
||||
}
|
||||
}
|
|
@ -1,38 +1,22 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class HeartedCategory : CategoryWithUser
|
||||
public class HeartedCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "My Hearted Content";
|
||||
public override string Description { get; set; } = "Content you've hearted";
|
||||
public override string IconHash { get; set; } = "g820611";
|
||||
public override string Endpoint { get; set; } = "hearted";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database, UserEntity user) // note: developer slots act up in LBP3 when listed here, so I omitted it
|
||||
=> database.HeartedLevels.Where(h => h.UserId == user.UserId)
|
||||
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3 && !h.Slot.CrossControllerRequired)
|
||||
.OrderByDescending(h => h.HeartedLevelId)
|
||||
.Include(h => h.Slot.Creator)
|
||||
.Select(h => h.Slot)
|
||||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.FirstOrDefault();
|
||||
public override string Endpoint { get; set; } = "hearted_levels";
|
||||
public override string Tag => "my_hearted_levels";
|
||||
|
||||
public override IQueryable<SlotEntity> GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize)
|
||||
=> database.HeartedLevels.Where(h => h.UserId == user.UserId)
|
||||
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3 && !h.Slot.CrossControllerRequired)
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.HeartedLevels.Where(h => h.UserId == token.UserId)
|
||||
.OrderByDescending(h => h.HeartedLevelId)
|
||||
.Include(h => h.Slot.Creator)
|
||||
.Select(h => h.Slot)
|
||||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.Skip(Math.Max(0, pageStart))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
|
||||
public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.HeartedLevels.Count(h => h.UserId == user.UserId);
|
||||
.Where(queryBuilder.Build());
|
||||
}
|
|
@ -1,43 +1,27 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class HighestRatedCategory : Category
|
||||
public class HighestRatedCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Highest Rated";
|
||||
public override string Description { get; set; } = "Community Highest Rated content";
|
||||
public override string IconHash { get; set; } = "g820603";
|
||||
public override string Endpoint { get; set; } = "thumbs";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) =>
|
||||
database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
|
||||
.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
ThumbsUp = database.RatedLevels.Count(r => r.SlotId == s.SlotId && r.Rating == 1),
|
||||
})
|
||||
.OrderByDescending(s => s.ThumbsUp)
|
||||
.Select(s => s.Slot)
|
||||
.FirstOrDefault();
|
||||
public override string Tag => "highest_rated";
|
||||
|
||||
public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) =>
|
||||
database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.Where(s => !s.CrossControllerRequired)
|
||||
.Select(s => new SlotMetadata
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
ThumbsUp = database.RatedLevels.Count(r => r.SlotId == s.SlotId && r.Rating == 1),
|
||||
})
|
||||
.OrderByDescending(s => s.ThumbsUp)
|
||||
.ThenBy(_ => EF.Functions.Random())
|
||||
.Select(s => s.Slot)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||
.Where(queryBuilder.Build());
|
||||
}
|
|
@ -1,26 +1,22 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class LuckyDipCategory : Category
|
||||
public class LuckyDipCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Lucky Dip";
|
||||
public override string Description { get; set; } = "A random selection of content";
|
||||
public override string IconHash { get; set; } = "g820605";
|
||||
public override string Endpoint { get; set; } = "lbp2luckydip";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault();
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.Where(s => !s.CrossControllerRequired)
|
||||
.OrderByDescending(_ => EF.Functions.Random())
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||
public override string Endpoint { get; set; } = "lucky_dip";
|
||||
public override string Tag => "lucky_dip";
|
||||
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Where(queryBuilder.Build())
|
||||
.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort()));
|
||||
}
|
|
@ -1,42 +1,29 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts.Metadata;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class MostHeartedCategory : Category
|
||||
public class MostHeartedCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Most Hearted";
|
||||
public override string Description { get; set; } = "The Most Hearted Content";
|
||||
public override string IconHash { get; set; } = "g820607";
|
||||
public override string Endpoint { get; set; } = "mostHearted";
|
||||
public override string Endpoint { get; set; } = "most_hearted";
|
||||
public override string Tag => "most_hearted";
|
||||
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) =>
|
||||
database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
|
||||
.Select(s => new SlotMetadata
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Hearts = database.HeartedLevels.Count(r => r.SlotId == s.SlotId),
|
||||
})
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.ApplyOrdering(new SlotSortBuilder<SlotMetadata>().AddSort(new HeartsSort()))
|
||||
.Select(s => s.Slot)
|
||||
.FirstOrDefault();
|
||||
|
||||
public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) =>
|
||||
database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.Where(s => !s.CrossControllerRequired)
|
||||
.Select(s => new SlotMetadata
|
||||
{
|
||||
Slot = s,
|
||||
Hearts = database.HeartedLevels.Count(r => r.SlotId == s.SlotId),
|
||||
})
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.Select(s => s.Slot)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||
.Where(queryBuilder.Build());
|
||||
}
|
|
@ -1,30 +1,21 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class MostPlayedCategory : Category
|
||||
public class MostPlayedCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Most Played";
|
||||
public override string Description { get; set; } = "The most played content";
|
||||
public override string IconHash { get; set; } = "g820608";
|
||||
public override string Endpoint { get; set; } = "mostUniquePlays";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots
|
||||
.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
|
||||
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
|
||||
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3)
|
||||
.FirstOrDefault();
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.Where(s => !s.CrossControllerRequired)
|
||||
public override string Endpoint { get; set; } = "most_played";
|
||||
public override string Tag => "most_played";
|
||||
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Where(queryBuilder.Build())
|
||||
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
|
||||
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class MyHeartedCreatorsCategory : UserCategory
|
||||
{
|
||||
public override string Name { get; set; } = "My Hearted Creators";
|
||||
public override string Description { get; set; } = "Creators you've hearted";
|
||||
public override string IconHash { get; set; } = "g820612";
|
||||
public override string Endpoint { get; set; } = "favourite_creators";
|
||||
public override string Tag => "favourite_creators";
|
||||
|
||||
public override IQueryable<UserEntity> GetItems(DatabaseContext database, GameTokenEntity token) =>
|
||||
database.HeartedProfiles.Where(h => h.UserId == token.UserId)
|
||||
.OrderByDescending(h => h.UserId)
|
||||
.Include(h => h.HeartedUser)
|
||||
.Select(h => h.HeartedUser);
|
||||
}
|
|
@ -1,25 +1,22 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class NewestLevelsCategory : Category
|
||||
public class NewestLevelsCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Newest Levels";
|
||||
public override string Description { get; set; } = "The most recently published content";
|
||||
public override string IconHash { get; set; } = "g820623";
|
||||
public override string Endpoint { get; set; } = "newest";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired).OrderByDescending(s => s.FirstUploaded).FirstOrDefault();
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.Where(s => !s.CrossControllerRequired)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||
public override string Tag => "newest";
|
||||
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Where(queryBuilder.Build())
|
||||
.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort()));
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public abstract class PlaylistCategory : Category
|
||||
{
|
||||
public override string[] Types { get; } = { "playlist", };
|
||||
|
||||
public abstract IQueryable<PlaylistEntity> GetItems(DatabaseContext database, GameTokenEntity token);
|
||||
|
||||
public override async Task<GameCategory> Serialize(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder, int numResults = 1)
|
||||
{
|
||||
List<ILbpSerializable> playlists =
|
||||
(await this.GetItems(database, token).Take(numResults).ToListAsync())
|
||||
.ToSerializableList<PlaylistEntity, ILbpSerializable>(GamePlaylist.CreateFromEntity);
|
||||
|
||||
int totalPlaylists = await this.GetItems(database, token).CountAsync();
|
||||
return GameCategory.CreateFromEntity(this, new GenericSerializableList(playlists, totalPlaylists, numResults + 1));
|
||||
}
|
||||
}
|
|
@ -1,38 +1,22 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class QueueCategory : CategoryWithUser
|
||||
public class QueueCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "My Queue";
|
||||
public override string Description { get; set; } = "Your queued content";
|
||||
public override string IconHash { get; set; } = "g820614";
|
||||
public override string Endpoint { get; set; } = "queue";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database, UserEntity user)
|
||||
=> database.QueuedLevels.Where(q => q.UserId == user.UserId)
|
||||
.Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion <= GameVersion.LittleBigPlanet3)
|
||||
.OrderByDescending(q => q.QueuedLevelId)
|
||||
.Include(q => q.Slot.Creator)
|
||||
.Select(q => q.Slot)
|
||||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.FirstOrDefault();
|
||||
public override string Tag => "my_queue";
|
||||
|
||||
public override IQueryable<SlotEntity> GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize)
|
||||
=> database.QueuedLevels.Where(q => q.UserId == user.UserId)
|
||||
.Where(q => q.Slot.Type == SlotType.User && !q.Slot.Hidden && q.Slot.GameVersion <= GameVersion.LittleBigPlanet3)
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.QueuedLevels.Where(q => q.UserId == token.UserId)
|
||||
.OrderByDescending(q => q.QueuedLevelId)
|
||||
.Include(q => q.Slot.Creator)
|
||||
.Select(q => q.Slot)
|
||||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
|
||||
public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.QueuedLevels.Count(q => q.UserId == user.UserId);
|
||||
.Where(queryBuilder.Build());
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public abstract class SlotCategory : Category
|
||||
{
|
||||
public override string[] Types { get; } = { "slot", "adventure", };
|
||||
|
||||
public abstract IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder);
|
||||
|
||||
public override async Task<GameCategory> Serialize(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder, int numResults = 1)
|
||||
{
|
||||
List<ILbpSerializable> slots =
|
||||
(await this.GetItems(database, token, queryBuilder).Take(numResults).ToListAsync())
|
||||
.ToSerializableList<SlotEntity, ILbpSerializable>(s => SlotBase.CreateFromEntity(s, token));
|
||||
|
||||
int totalSlots = await this.GetItems(database, token, queryBuilder).CountAsync();
|
||||
return GameCategory.CreateFromEntity(this, new GenericSerializableList(slots, totalSlots, numResults+1));
|
||||
}
|
||||
}
|
|
@ -1,25 +1,23 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public class TeamPicksCategory : Category
|
||||
public class TeamPicksCategory : SlotCategory
|
||||
{
|
||||
public override string Name { get; set; } = "Team Picks";
|
||||
public override string Description { get; set; } = "Community Team Picks";
|
||||
public override string IconHash { get; set; } = "g820626";
|
||||
public override string Endpoint { get; set; } = "team_picks";
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick && !s.CrossControllerRequired);
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
.Where(s => s.TeamPick && !s.CrossControllerRequired)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.TeamPick);
|
||||
public override string Tag => "team_picks";
|
||||
|
||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||
database.Slots.Where(queryBuilder.Clone().AddFilter(new TeamPickFilter()).Build())
|
||||
.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort()));
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
||||
public abstract class UserCategory : Category
|
||||
{
|
||||
public override string[] Types { get; } = { "user", };
|
||||
|
||||
public abstract IQueryable<UserEntity> GetItems(DatabaseContext database, GameTokenEntity token);
|
||||
|
||||
public override async Task<GameCategory> Serialize(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder, int numResults = 1)
|
||||
{
|
||||
List<ILbpSerializable> users =
|
||||
(await this.GetItems(database, token).Take(numResults).ToListAsync())
|
||||
.ToSerializableList<UserEntity, ILbpSerializable>(GameUser.CreateFromEntity);
|
||||
|
||||
int totalUsers = await this.GetItems(database, token).CountAsync();
|
||||
return GameCategory.CreateFromEntity(this, new GenericSerializableList(users, totalUsers, numResults + 1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types;
|
||||
|
||||
public class MyPlaylistsCategory : PlaylistCategory
|
||||
{
|
||||
public override string Name { get; set; } = "My Playlists";
|
||||
public override string Description { get; set; } = "Your playlists";
|
||||
public override string IconHash { get; set; } = "g820613";
|
||||
public override string Endpoint { get; set; } = "my_playlists";
|
||||
public override string Tag => "my_playlists";
|
||||
public override string[] Types { get; } = { "playlist", };
|
||||
|
||||
public override IQueryable<PlaylistEntity> GetItems(DatabaseContext database, GameTokenEntity token) =>
|
||||
database.Playlists.Where(p => p.CreatorId == token.UserId).OrderByDescending(p => p.PlaylistId);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue