Implement basic filters and LBP2 CrossController fixes (#758)

* implement basic filters and lbp2cc fixes

* lbp3 hide lbp2cc slots

* hide lbp2cc levels from hearted and most played categories in lbp3 and basic filters for lbp3

---------

Co-authored-by: jackcaver <jackcaver@users.noreply.github.com>
Co-authored-by: koko <koko@drones.gay>
This commit is contained in:
jackcaver 2023-05-04 20:42:31 +06:00 committed by GitHub
parent ab353c502f
commit 9deff7ce63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 233 additions and 67 deletions

View file

@ -170,7 +170,16 @@ public class CollectionController : ControllerBase
} }
[HttpGet("searches/{endpointName}")] [HttpGet("searches/{endpointName}")]
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize) 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(); GameTokenEntity token = this.GetToken();
@ -182,24 +191,52 @@ public class CollectionController : ControllerBase
Logger.Debug("Found category " + category, LogArea.Category); Logger.Debug("Found category " + category, LogArea.Category);
List<SlotBase> slots; List<SlotEntity> slots;
int totalSlots; int totalSlots;
if (category is CategoryWithUser categoryWithUser) if (category is CategoryWithUser categoryWithUser)
{ {
slots = (await categoryWithUser.GetSlots(this.database, user, pageStart, pageSize) slots = (await categoryWithUser.GetSlots(this.database, user, pageStart, pageSize)
.ToListAsync()) .ToListAsync());
.ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
totalSlots = categoryWithUser.GetTotalSlots(this.database, user); totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
} }
else else
{ {
slots = category.GetSlots(this.database, pageStart, pageSize) slots = category.GetSlots(this.database, pageStart, pageSize)
.ToList() .ToList();
.ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
totalSlots = category.GetTotalSlots(this.database); totalSlots = category.GetTotalSlots(this.database);
} }
return this.Ok(new GenericSlotResponse("results", slots, totalSlots, pageStart + pageSize)); 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;
} }
} }

View file

@ -8,6 +8,7 @@ using LBPUnion.ProjectLighthouse.Types.Serialization;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
@ -24,15 +25,32 @@ public class SearchController : ControllerBase
} }
[HttpGet("searchLBP3")] [HttpGet("searchLBP3")]
public Task<IActionResult> SearchSlotsLBP3([FromQuery] int pageSize, [FromQuery] int pageStart, [FromQuery] string textFilter) public Task<IActionResult> SearchSlotsLBP3([FromQuery] int pageSize, [FromQuery] int pageStart, [FromQuery] string textFilter,
=> this.SearchSlots(textFilter, pageSize, pageStart, "results"); [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);
[HttpGet("search")] [HttpGet("search")]
public async Task<IActionResult> SearchSlots( public async Task<IActionResult> SearchSlots(
[FromQuery] string query, [FromQuery] string query,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] int pageStart, [FromQuery] int pageStart,
string? keyName = "slots" 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
) )
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -46,7 +64,7 @@ public class SearchController : ControllerBase
string[] keywords = query.Split(" "); string[] keywords = query.Split(" ");
IQueryable<SlotEntity> dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true) IQueryable<SlotEntity> dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true)
.Where(s => s.Type == SlotType.User) .Where(s => s.Type == SlotType.User && s.CrossControllerRequired == crosscontrol)
.OrderBy(s => !s.TeamPick) .OrderBy(s => !s.TeamPick)
.ThenByDescending(s => s.FirstUploaded) .ThenByDescending(s => s.FirstUploaded)
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable .Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
@ -61,14 +79,48 @@ public class SearchController : ControllerBase
s.SlotId.ToString().Equals(keyword) s.SlotId.ToString().Equals(keyword)
); );
List<SlotBase> slots = (await dbQuery.Skip(Math.Max(0, pageStart - 1)) List<SlotEntity> slots = (await dbQuery.Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()) .ToListAsync());
.ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
return this.Ok(new GenericSlotResponse(keyName, slots, await dbQuery.CountAsync(), 0)); 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));
} }
// /LITTLEBIGPLANETPS3_XML?pageStart=1&pageSize=10&resultTypes[]=slot&resultTypes[]=playlist&resultTypes[]=user&adventure=dontCare&textFilter=qwer // /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;
}
} }

View file

@ -29,7 +29,7 @@ public class SlotsController : ControllerBase
} }
[HttpGet("slots/by")] [HttpGet("slots/by")]
public async Task<IActionResult> SlotsBy([FromQuery(Name = "u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize) public async Task<IActionResult> SlotsBy([FromQuery(Name = "u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] bool crosscontrol = false)
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -42,12 +42,13 @@ public class SlotsController : ControllerBase
List<SlotBase> slots = (await this.database.Slots.Where(s => s.CreatorId == targetUserId) List<SlotBase> slots = (await this.database.Slots.Where(s => s.CreatorId == targetUserId)
.ByGameVersion(token.GameVersion, token.UserId == targetUserId) .ByGameVersion(token.GameVersion, token.UserId == targetUserId)
.Where(match => match.CrossControllerRequired == crosscontrol)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, usedSlots)) .Take(Math.Min(pageSize, usedSlots))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, usedSlots); int start = pageStart + Math.Min(pageSize, usedSlots);
int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId); int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId && s.CrossControllerRequired == crosscontrol);
return this.Ok(new GenericSlotResponse("slots", slots, total, start)); return this.Ok(new GenericSlotResponse("slots", slots, total, start));
} }
@ -137,19 +138,24 @@ public class SlotsController : ControllerBase
( (
[FromQuery] int pageStart, [FromQuery] int pageStart,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] int players = 1,
[FromQuery] string? gameFilterType = null, [FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null, [FromQuery] string? labelFilter0 = null,
[FromQuery] bool? move = null, [FromQuery] string? labelFilter1 = null,
[FromQuery] int? page = null [FromQuery] string? labelFilter2 = null,
[FromQuery] string? move = null,
[FromQuery] int? page = null,
[FromQuery] bool crosscontrol = false
) )
{ {
if (page != null) pageStart = (int)page * 30; if (page != null) pageStart = (int)page * 30;
// bit of a better placeholder until we can track average user interaction with /stream endpoint // bit of a better placeholder until we can track average user interaction with /stream endpoint
return await this.ThumbsSlots(pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisMonth"); return await this.ThumbsSlots(pageStart, Math.Min(pageSize, 30), players, gameFilterType, "thisMonth",
labelFilter0, labelFilter1, labelFilter2, move, crosscontrol);
} }
[HttpGet("slots")] [HttpGet("slots")]
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] bool crosscontrol = false)
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -158,6 +164,7 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion; GameVersion gameVersion = token.GameVersion;
List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true) List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true)
.Where(s => s.CrossControllerRequired == crosscontrol)
.OrderByDescending(s => s.FirstUploaded) .OrderByDescending(s => s.FirstUploaded)
.ThenByDescending(s => s.SlotId) .ThenByDescending(s => s.SlotId)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
@ -257,26 +264,51 @@ public class SlotsController : ControllerBase
} }
[HttpGet("slots/mmpicks")] [HttpGet("slots/mmpicks")]
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize) 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
)
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
if (pageSize <= 0) return this.BadRequest(); if (pageSize <= 0) return this.BadRequest();
List<SlotBase> slots = (await this.database.Slots.Where(s => s.TeamPick) List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.ByGameVersion(token.GameVersion, false, true) .Where(s => s.TeamPick && s.CrossControllerRequired == crosscontrol)
.OrderByDescending(s => s.LastUpdated) .OrderByDescending(s => s.LastUpdated)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.TeamPickCountForGame(this.database, token.GameVersion); int total = await StatisticsHelper.TeamPickCountForGame(this.database, token.GameVersion, crosscontrol);
return this.Ok(new GenericSlotResponse(slots, total, start)); return this.Ok(new GenericSlotResponse(slots, total, start));
} }
[HttpGet("slots/lbp2luckydip")] [HttpGet("slots/lbp2luckydip")]
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed) 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
)
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -285,10 +317,11 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion; GameVersion gameVersion = token.GameVersion;
const double biasFactor = .8f; const double biasFactor = .8f;
List<SlotBase> slots = (await this.database.Slots.ByGameVersion(gameVersion, false, true) 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)) .OrderByDescending(s => EF.Functions.Random() * (s.FirstUploaded * biasFactor))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
@ -301,17 +334,22 @@ public class SlotsController : ControllerBase
( (
[FromQuery] int pageStart, [FromQuery] int pageStart,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] int players,
[FromQuery] string? gameFilterType = null, [FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null, [FromQuery] string? dateFilterType = null,
[FromQuery] bool? move = null, [FromQuery] string? labelFilter0 = null,
[FromQuery] string? dateFilterType = null [FromQuery] string? labelFilter1 = null,
[FromQuery] string? labelFilter2 = null,
[FromQuery] string? move = null,
[FromQuery] bool crosscontrol = false
) )
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
if (pageSize <= 0) return this.BadRequest(); if (pageSize <= 0) return this.BadRequest();
List<SlotBase> slots = (await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.Where(s => s.CrossControllerRequired == crosscontrol)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,
@ -322,7 +360,7 @@ public class SlotsController : ControllerBase
.Select(s => s.Slot) .Select(s => s.Slot)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
@ -335,10 +373,14 @@ public class SlotsController : ControllerBase
( (
[FromQuery] int pageStart, [FromQuery] int pageStart,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] int players,
[FromQuery] string? gameFilterType = null, [FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null, [FromQuery] string? labelFilter0 = null,
[FromQuery] bool? move = null, [FromQuery] string? labelFilter1 = null,
[FromQuery] string? dateFilterType = null [FromQuery] string? labelFilter2 = null,
[FromQuery] string? move = null,
[FromQuery] string? dateFilterType = null,
[FromQuery] bool crosscontrol = false
) )
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -356,11 +398,12 @@ public class SlotsController : ControllerBase
string colName = $"Plays{game}Unique"; string colName = $"Plays{game}Unique";
List<SlotBase> slots = (await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.Where(s => s.CrossControllerRequired == crosscontrol)
.OrderByDescending(s => EF.Property<int>(s, colName)) .OrderByDescending(s => EF.Property<int>(s, colName))
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
@ -373,17 +416,22 @@ public class SlotsController : ControllerBase
( (
[FromQuery] int pageStart, [FromQuery] int pageStart,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] int players,
[FromQuery] string? gameFilterType = null, [FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null, [FromQuery] string? labelFilter0 = null,
[FromQuery] bool? move = null, [FromQuery] string? labelFilter1 = null,
[FromQuery] string? dateFilterType = null [FromQuery] string? labelFilter2 = null,
[FromQuery] string? move = null,
[FromQuery] string? dateFilterType = null,
[FromQuery] bool crosscontrol = false
) )
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
if (pageSize <= 0) return this.BadRequest(); if (pageSize <= 0) return this.BadRequest();
List<SlotBase> slots = (await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) List<SlotBase> slots = this.filterSlots((await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.Where(s => s.CrossControllerRequired == crosscontrol)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,
@ -393,7 +441,7 @@ public class SlotsController : ControllerBase
.Select(s => s.Slot) .Select(s => s.Slot)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30)) .Take(Math.Min(pageSize, 30))
.ToListAsync()).ToSerializableList(s => SlotBase.CreateFromEntity(s, token)); .ToListAsync()), players, labelFilter0, labelFilter1, labelFilter2, move).ToSerializableList(s => SlotBase.CreateFromEntity(s, token));
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion); int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
@ -408,8 +456,12 @@ public class SlotsController : ControllerBase
[FromQuery] int pageStart, [FromQuery] int pageStart,
[FromQuery] int pageSize, [FromQuery] int pageSize,
[FromQuery] string? gameFilterType = null, [FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null, [FromQuery] int players = 1,
[FromQuery] bool? move = null [FromQuery] string? labelFilter0 = null,
[FromQuery] string? labelFilter1 = null,
[FromQuery] string? labelFilter2 = null,
[FromQuery] string? move = null,
[FromQuery] bool crosscontrol = false
) )
{ {
GameTokenEntity token = this.GetToken(); GameTokenEntity token = this.GetToken();
@ -438,24 +490,44 @@ public class SlotsController : ControllerBase
.OrderByDescending(kvp => kvp.Value) .OrderByDescending(kvp => kvp.Value)
.Select(kvp => kvp.Key); .Select(kvp => kvp.Key);
List<SlotBase> slots = new(); List<SlotEntity> slots = new();
foreach (int slotId in orderedPlayersBySlotId) foreach (int slotId in orderedPlayersBySlotId)
{ {
SlotEntity? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true) SlotEntity? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true)
.Where(s => s.SlotId == slotId) .Where(s => s.SlotId == slotId && s.CrossControllerRequired == crosscontrol)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (slot == null) continue; // shouldn't happen ever unless the room is borked if (slot == null) continue; // shouldn't happen ever unless the room is borked
slots.Add(SlotBase.CreateFromEntity(slot, token)); slots.Add(slot);
} }
slots = this.filterSlots(slots, players, labelFilter0, labelFilter1, labelFilter2, move);
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots); int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
int total = playersBySlotId.Count; int total = playersBySlotId.Count;
return this.Ok(new GenericSlotResponse(slots, total, start)); 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) private static GameVersion getGameFilter(string? gameFilterType, GameVersion version)
{ {

View file

@ -35,9 +35,9 @@ public class CustomCategory : Category
public sealed override string Description { get; set; } public sealed override string Description { get; set; }
public sealed override string IconHash { get; set; } public sealed override string IconHash { get; set; }
public sealed override string Endpoint { get; set; } public sealed override string Endpoint { get; set; }
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]); public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0] && !s.CrossControllerRequired);
public override IQueryable<SlotEntity> GetSlots public override IQueryable<SlotEntity> GetSlots
(DatabaseContext database, int pageStart, int pageSize) (DatabaseContext database, int pageStart, int pageSize)
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3).Where(s => this.SlotIds.Contains(s.SlotId)); => 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 int GetTotalSlots(DatabaseContext database) => this.SlotIds.Count;
} }

View file

@ -17,7 +17,7 @@ public class HeartedCategory : CategoryWithUser
public override string Endpoint { get; set; } = "hearted"; 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 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) => database.HeartedLevels.Where(h => h.UserId == user.UserId)
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3 && !h.Slot.CrossControllerRequired)
.OrderByDescending(h => h.HeartedLevelId) .OrderByDescending(h => h.HeartedLevelId)
.Include(h => h.Slot.Creator) .Include(h => h.Slot.Creator)
.Select(h => h.Slot) .Select(h => h.Slot)
@ -26,7 +26,7 @@ public class HeartedCategory : CategoryWithUser
public override IQueryable<SlotEntity> GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize) public override IQueryable<SlotEntity> GetSlots(DatabaseContext database, UserEntity user, int pageStart, int pageSize)
=> database.HeartedLevels.Where(h => h.UserId == user.UserId) => database.HeartedLevels.Where(h => h.UserId == user.UserId)
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3) .Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3 && !h.Slot.CrossControllerRequired)
.OrderByDescending(h => h.HeartedLevelId) .OrderByDescending(h => h.HeartedLevelId)
.Include(h => h.Slot.Creator) .Include(h => h.Slot.Creator)
.Select(h => h.Slot) .Select(h => h.Slot)

View file

@ -16,7 +16,7 @@ public class HighestRatedCategory : Category
public override string IconHash { get; set; } = "g820603"; public override string IconHash { get; set; } = "g820603";
public override string Endpoint { get; set; } = "thumbs"; public override string Endpoint { get; set; } = "thumbs";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => public override SlotEntity? GetPreviewSlot(DatabaseContext database) =>
database.Slots.Where(s => s.Type == SlotType.User) database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,
@ -28,6 +28,7 @@ public class HighestRatedCategory : Category
public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) => public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) =>
database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.Where(s => !s.CrossControllerRequired)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,

View file

@ -14,10 +14,11 @@ public class LuckyDipCategory : Category
public override string Description { get; set; } = "A random selection of content"; public override string Description { get; set; } = "A random selection of content";
public override string IconHash { get; set; } = "g820605"; public override string IconHash { get; set; } = "g820605";
public override string Endpoint { get; set; } = "lbp2luckydip"; public override string Endpoint { get; set; } = "lbp2luckydip";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault(); 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 public override IQueryable<SlotEntity> GetSlots
(DatabaseContext database, int pageStart, int pageSize) (DatabaseContext database, int pageStart, int pageSize)
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.Where(s => !s.CrossControllerRequired)
.OrderByDescending(_ => EF.Functions.Random()) .OrderByDescending(_ => EF.Functions.Random())
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 20)); .Take(Math.Min(pageSize, 20));

View file

@ -16,7 +16,7 @@ public class MostHeartedCategory : Category
public override string Endpoint { get; set; } = "mostHearted"; public override string Endpoint { get; set; } = "mostHearted";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => public override SlotEntity? GetPreviewSlot(DatabaseContext database) =>
database.Slots.Where(s => s.Type == SlotType.User) database.Slots.Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,
@ -28,6 +28,7 @@ public class MostHeartedCategory : Category
public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) => public override IEnumerable<SlotEntity> GetSlots(DatabaseContext database, int pageStart, int pageSize) =>
database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.Where(s => !s.CrossControllerRequired)
.Select(s => new SlotMetadata .Select(s => new SlotMetadata
{ {
Slot = s, Slot = s,

View file

@ -14,13 +14,14 @@ public class MostPlayedCategory : Category
public override string IconHash { get; set; } = "g820608"; public override string IconHash { get; set; } = "g820608";
public override string Endpoint { get; set; } = "mostUniquePlays"; public override string Endpoint { get; set; } = "mostUniquePlays";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots
.Where(s => s.Type == SlotType.User) .Where(s => s.Type == SlotType.User && !s.CrossControllerRequired)
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique) .OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3) .ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3)
.FirstOrDefault(); .FirstOrDefault();
public override IQueryable<SlotEntity> GetSlots public override IQueryable<SlotEntity> GetSlots
(DatabaseContext database, int pageStart, int pageSize) (DatabaseContext database, int pageStart, int pageSize)
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.Where(s => !s.CrossControllerRequired)
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique) .OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3) .ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))

View file

@ -13,10 +13,11 @@ public class NewestLevelsCategory : Category
public override string Description { get; set; } = "The most recently published content"; public override string Description { get; set; } = "The most recently published content";
public override string IconHash { get; set; } = "g820623"; public override string IconHash { get; set; } = "g820623";
public override string Endpoint { get; set; } = "newest"; public override string Endpoint { get; set; } = "newest";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault(); 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 public override IQueryable<SlotEntity> GetSlots
(DatabaseContext database, int pageStart, int pageSize) (DatabaseContext database, int pageStart, int pageSize)
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.Where(s => !s.CrossControllerRequired)
.OrderByDescending(s => s.FirstUploaded) .OrderByDescending(s => s.FirstUploaded)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 20)); .Take(Math.Min(pageSize, 20));

View file

@ -13,12 +13,12 @@ public class TeamPicksCategory : Category
public override string Description { get; set; } = "Community Team Picks"; public override string Description { get; set; } = "Community Team Picks";
public override string IconHash { get; set; } = "g820626"; public override string IconHash { get; set; } = "g820626";
public override string Endpoint { get; set; } = "team_picks"; public override string Endpoint { get; set; } = "team_picks";
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick); public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick && !s.CrossControllerRequired);
public override IQueryable<SlotEntity> GetSlots public override IQueryable<SlotEntity> GetSlots
(DatabaseContext database, int pageStart, int pageSize) (DatabaseContext database, int pageStart, int pageSize)
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
.OrderByDescending(s => s.FirstUploaded) .OrderByDescending(s => s.FirstUploaded)
.Where(s => s.TeamPick) .Where(s => s.TeamPick && !s.CrossControllerRequired)
.Skip(Math.Max(0, pageStart - 1)) .Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 20)); .Take(Math.Min(pageSize, 20));
public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.TeamPick); public override int GetTotalSlots(DatabaseContext database) => database.Slots.Count(s => s.TeamPick);

View file

@ -27,7 +27,7 @@ public static class StatisticsHelper
public static async Task<int> TeamPickCount(DatabaseContext database) => await database.Slots.CountAsync(s => s.TeamPick); public static async Task<int> TeamPickCount(DatabaseContext database) => await database.Slots.CountAsync(s => s.TeamPick);
public static async Task<int> TeamPickCountForGame(DatabaseContext database, GameVersion gameVersion) => await database.Slots.ByGameVersion(gameVersion).CountAsync(s => s.TeamPick); public static async Task<int> TeamPickCountForGame(DatabaseContext database, GameVersion gameVersion, bool? crosscontrol = null) => await database.Slots.ByGameVersion(gameVersion).CountAsync(s => s.TeamPick && (crosscontrol == null || s.CrossControllerRequired == crosscontrol));
public static async Task<int> PhotoCount(DatabaseContext database) => await database.Photos.CountAsync(); public static async Task<int> PhotoCount(DatabaseContext database) => await database.Photos.CountAsync();

View file

@ -194,7 +194,7 @@ public class GameUser : ILbpSerializable, INeedsPreparationForSerialization
if (this.TargetGame == GameVersion.LittleBigPlanetVita) if (this.TargetGame == GameVersion.LittleBigPlanetVita)
{ {
this.Lbp2EntitledSlots = entitledSlots; this.Lbp2EntitledSlots = entitledSlots;
this.Lbp2UsedSlots = await SlotCount(GameVersion.LittleBigPlanet2).CountAsync(); this.Lbp2UsedSlots = await SlotCount(GameVersion.LittleBigPlanetVita).CountAsync();
} }
else else
{ {
@ -203,7 +203,7 @@ public class GameUser : ILbpSerializable, INeedsPreparationForSerialization
this.CrossControlEntitledSlots = entitledSlots; this.CrossControlEntitledSlots = entitledSlots;
this.Lbp3EntitledSlots = entitledSlots; this.Lbp3EntitledSlots = entitledSlots;
this.Lbp1UsedSlots = await SlotCount(GameVersion.LittleBigPlanet1).CountAsync(); this.Lbp1UsedSlots = await SlotCount(GameVersion.LittleBigPlanet1).CountAsync();
this.Lbp2UsedSlots = await SlotCount(GameVersion.LittleBigPlanet2).CountAsync(); this.Lbp2UsedSlots = await SlotCount(GameVersion.LittleBigPlanet2).CountAsync(s => !s.CrossControllerRequired);
this.Lbp3UsedSlots = await SlotCount(GameVersion.LittleBigPlanet3).CountAsync(); this.Lbp3UsedSlots = await SlotCount(GameVersion.LittleBigPlanet3).CountAsync();
this.Lbp1FreeSlots = this.Lbp1EntitledSlots - this.Lbp1UsedSlots; this.Lbp1FreeSlots = this.Lbp1EntitledSlots - this.Lbp1UsedSlots;

View file

@ -119,7 +119,7 @@ public class GameUserSlot : SlotBase, INeedsPreparationForSerialization
public bool IsMoveRequired { get; set; } public bool IsMoveRequired { get; set; }
public bool ShouldSerializeIsMoveRequired() => this.SerializationMode == SerializationMode.Full; public bool ShouldSerializeIsMoveRequired() => this.SerializationMode == SerializationMode.Full;
[XmlElement("crossControlRequired")] [XmlElement("vitaCrossControlRequired")]
public bool IsCrossControlRequired { get; set; } public bool IsCrossControlRequired { get; set; }
public bool ShouldSerializeIsCrossControlRequired() => this.SerializationMode == SerializationMode.Full; public bool ShouldSerializeIsCrossControlRequired() => this.SerializationMode == SerializationMode.Full;