Split GameAPI and Website into their own projects

This commit is contained in:
jvyden 2022-05-14 17:28:08 -04:00
parent bb03a01246
commit 14154faaf8
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
116 changed files with 484 additions and 287 deletions

View file

@ -0,0 +1,120 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Categories;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class CollectionController : ControllerBase
{
private readonly Database database;
public CollectionController(Database database)
{
this.database = database;
}
[HttpGet("user/{username}/playlists")]
public IActionResult GetUserPlaylists(string username) => this.Ok();
[HttpGet("searches")]
[HttpGet("genres")]
public async Task<IActionResult> GenresAndSearches()
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
string categoriesSerialized = CollectionHelper.Categories.Aggregate
(
string.Empty,
(current, category) =>
{
string serialized;
if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user);
else serialized = category.Serialize(this.database);
return current + serialized;
}
);
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"categories",
categoriesSerialized,
new Dictionary<string, object>
{
{
"hint", ""
},
{
"hint_start", 1
},
{
"total", CollectionHelper.Categories.Count
},
}
)
);
}
[HttpGet("searches/{endpointName}")]
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize)
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
if (category == null) return this.NotFound();
Logger.LogDebug("Found category " + category, LogArea.Category);
List<Slot> slots;
int totalSlots;
if (category is CategoryWithUser categoryWithUser)
{
slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList();
totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
}
else
{
slots = category.GetSlots(this.database, pageStart, pageSize).ToList();
totalSlots = category.GetTotalSlots(this.database);
}
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameToken.GameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"results",
slotsSerialized,
new Dictionary<string, object>
{
{
"total", totalSlots
},
{
"hint_start", pageStart + pageSize
},
}
)
);
}
}

View file

@ -0,0 +1,25 @@
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/tags")]
[Produces("text/plain")]
public class LevelTagsController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
string[] tags = Enum.GetNames(typeof(LevelTags));
int i = 0;
foreach (string tag in tags)
{
tags[i] = $"TAG_{tag.Replace("_", "-")}";
i++;
}
return this.Ok(string.Join(",", tags));
}
}

View file

@ -0,0 +1,212 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class ListController : ControllerBase
{
private readonly Database database;
public ListController(Database database)
{
this.database = database;
}
#region Levels
#region Level Queue (lolcatftw)
[HttpGet("slots/lolcatftw/{username}")]
public async Task<IActionResult> GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
IEnumerable<QueuedLevel> queuedLevels = this.database.QueuedLevels.Include(q => q.User)
.Include(q => q.Slot)
.Include(q => q.Slot.Location)
.Include(q => q.Slot.Creator)
.Where(q => q.Slot.GameVersion <= gameVersion)
.Where(q => q.User.Username == username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30))
.AsEnumerable();
string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize(gameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("lolcatftw/add/user/{id:int}")]
public async Task<IActionResult> AddQueuedLevel(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
await this.database.QueueLevel(user, slot);
return this.Ok();
}
[HttpPost("lolcatftw/remove/user/{id:int}")]
public async Task<IActionResult> RemoveQueuedLevel(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
await this.database.UnqueueLevel(user, slot);
return this.Ok();
}
[HttpPost("lolcatftw/clear")]
public async Task<IActionResult> ClearQueuedLevels()
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId));
await this.database.SaveChangesAsync();
return this.Ok();
}
#endregion
#region Hearted Levels
[HttpGet("favouriteSlots/{username}")]
public async Task<IActionResult> GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
IEnumerable<HeartedLevel> heartedLevels = this.database.HeartedLevels.Include(q => q.User)
.Include(q => q.Slot)
.Include(q => q.Slot.Location)
.Include(q => q.Slot.Creator)
.Where(q => q.Slot.GameVersion <= gameVersion)
.Where(q => q.User.Username == username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30))
.AsEnumerable();
string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize(gameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("favourite/slot/user/{id:int}")]
public async Task<IActionResult> AddFavouriteSlot(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
await this.database.HeartLevel(user, slot);
return this.Ok();
}
[HttpPost("unfavourite/slot/user/{id:int}")]
public async Task<IActionResult> RemoveFavouriteSlot(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
await this.database.UnheartLevel(user, slot);
return this.Ok();
}
#endregion
#endregion Levels
#region Users
[HttpGet("favouriteUsers/{username}")]
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
IEnumerable<HeartedProfile> heartedProfiles = this.database.HeartedProfiles.Include
(q => q.User)
.Include(q => q.HeartedUser)
.Include(q => q.HeartedUser.Location)
.Where(q => q.User.Username == username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30))
.AsEnumerable();
string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("favourite/user/{username}")]
public async Task<IActionResult> AddFavouriteUser(string username)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (heartedUser == null) return this.NotFound();
await this.database.HeartUser(user, heartedUser);
return this.Ok();
}
[HttpPost("unfavourite/user/{username}")]
public async Task<IActionResult> RemoveFavouriteUser(string username)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (heartedUser == null) return this.NotFound();
await this.database.UnheartUser(user, heartedUser);
return this.Ok();
}
#endregion
}

View file

@ -0,0 +1,236 @@
#nullable enable
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Files;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class PublishController : ControllerBase
{
private readonly Database database;
public PublishController(Database database)
{
this.database = database;
}
/// <summary>
/// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded
/// </summary>
[HttpPost("startPublish")]
public async Task<IActionResult> StartPublish()
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
Slot? slot = await this.getSlotFromBody();
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest();
if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel;
// Republish logic
if (slot.SlotId != 0)
{
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
if (oldSlot == null) return this.NotFound();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
}
else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
{
return this.StatusCode(403, "");
}
slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here
string resources = slot.Resources.Where
(hash => !FileHelper.ResourceExists(hash))
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user"));
}
/// <summary>
/// Endpoint actually used to publish a level
/// </summary>
[HttpPost("publish")]
public async Task<IActionResult> Publish()
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
Slot? slot = await this.getSlotFromBody();
if (slot == null) return this.BadRequest();
if (slot.Location == null) return this.BadRequest();
if (slot.Description.Length > 200) return this.BadRequest();
if (slot.Name.Length > 100) return this.BadRequest();
if (slot.Resources.Any(resource => !FileHelper.ResourceExists(resource)))
{
return this.BadRequest();
}
LbpFile? rootLevel = LbpFile.FromHash(slot.RootLevel);
if (rootLevel == null) return this.BadRequest();
if (rootLevel.FileType != LbpFileType.Level) return this.BadRequest();
// Republish logic
if (slot.SlotId != 0)
{
Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
if (oldSlot == null) return this.NotFound();
if (oldSlot.Location == null) throw new ArgumentNullException();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
oldSlot.Location.X = slot.Location.X;
oldSlot.Location.Y = slot.Location.Y;
slot.CreatorId = oldSlot.CreatorId;
slot.LocationId = oldSlot.LocationId;
slot.SlotId = oldSlot.SlotId;
#region Set plays
slot.PlaysLBP1 = oldSlot.PlaysLBP1;
slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete;
slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique;
slot.PlaysLBP2 = oldSlot.PlaysLBP2;
slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete;
slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique;
slot.PlaysLBP3 = oldSlot.PlaysLBP3;
slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete;
slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique;
slot.PlaysLBPVita = oldSlot.PlaysLBPVita;
slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete;
slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique;
#endregion
slot.FirstUploaded = oldSlot.FirstUploaded;
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
slot.TeamPick = oldSlot.TeamPick;
// Only update a slot's gameVersion if the level was actually change
if (oldSlot.RootLevel != slot.RootLevel)
{
slot.GameVersion = gameToken.GameVersion;
}
else
{
slot.GameVersion = oldSlot.GameVersion;
}
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
{
slot.MinimumPlayers = 1;
slot.MaximumPlayers = 4;
}
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
await this.database.SaveChangesAsync();
return this.Ok(oldSlot.Serialize(gameToken.GameVersion));
}
if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
{
return this.StatusCode(403, "");
}
//TODO: parse location in body
Location l = new()
{
X = slot.Location.X,
Y = slot.Location.Y,
};
this.database.Locations.Add(l);
await this.database.SaveChangesAsync();
slot.LocationId = l.Id;
slot.CreatorId = user.UserId;
slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
slot.GameVersion = gameToken.GameVersion;
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
{
slot.MinimumPlayers = 1;
slot.MaximumPlayers = 4;
}
this.database.Slots.Add(slot);
await this.database.SaveChangesAsync();
await WebhookHelper.SendWebhook
(
"New level published!",
$"**{user.Username}** just published a new level: [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId})\n{slot.Description}"
);
return this.Ok(slot.Serialize(gameToken.GameVersion));
}
[HttpPost("unpublish/{id:int}")]
public async Task<IActionResult> Unpublish(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
if (slot.Location == null) throw new ArgumentNullException();
if (slot.CreatorId != user.UserId) return this.StatusCode(403, "");
this.database.Locations.Remove(slot.Location);
this.database.Slots.Remove(slot);
await this.database.SaveChangesAsync();
return this.Ok();
}
private async Task<Slot?> getSlotFromBody()
{
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Slot));
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
SanitizationHelper.SanitizeStringsInClass(slot);
return slot;
}
}

View file

@ -0,0 +1,326 @@
#nullable enable
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Reviews;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class ReviewController : ControllerBase
{
private readonly Database database;
public ReviewController(Database database)
{
this.database = database;
}
// LBP1 rating
[HttpPost("rate/user/{slotId}")]
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
if (slot == null) return this.StatusCode(403, "");
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
if (ratedLevel == null)
{
ratedLevel = new RatedLevel
{
SlotId = slotId,
UserId = user.UserId,
Rating = 0,
};
this.database.RatedLevels.Add(ratedLevel);
}
ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0);
await this.database.SaveChangesAsync();
return this.Ok();
}
// LBP2 and beyond rating
[HttpPost("dpadrate/user/{slotId:int}")]
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
if (slot == null) return this.StatusCode(403, "");
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
if (ratedLevel == null)
{
ratedLevel = new RatedLevel
{
SlotId = slotId,
UserId = user.UserId,
RatingLBP1 = 0,
};
this.database.RatedLevels.Add(ratedLevel);
}
ratedLevel.Rating = Math.Clamp(rating, -1, 1);
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
if (review != null) review.Thumb = ratedLevel.Rating;
await this.database.SaveChangesAsync();
return this.Ok();
}
[HttpPost("postReview/user/{slotId:int}")]
public async Task<IActionResult> PostReview(int slotId)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Review? newReview = await this.getReviewFromBody();
if (newReview == null) return this.BadRequest();
if (newReview.Text.Length > 100) return this.BadRequest();
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
if (review == null)
{
review = new Review
{
SlotId = slotId,
ReviewerId = user.UserId,
DeletedBy = DeletedBy.None,
ThumbsUp = 0,
ThumbsDown = 0,
};
this.database.Reviews.Add(review);
}
review.Thumb = newReview.Thumb;
review.LabelCollection = newReview.LabelCollection;
review.Text = newReview.Text;
review.Deleted = false;
review.Timestamp = TimeHelper.UnixTimeMilliseconds();
// sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??)
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
if (ratedLevel == null)
{
ratedLevel = new RatedLevel
{
SlotId = slotId,
UserId = user.UserId,
RatingLBP1 = 0,
};
this.database.RatedLevels.Add(ratedLevel);
}
ratedLevel.Rating = newReview.Thumb;
await this.database.SaveChangesAsync();
return this.Ok();
}
[HttpGet("reviewsFor/user/{slotId:int}")]
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
GameVersion gameVersion = gameToken.GameVersion;
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
if (slot == null) return this.BadRequest();
IQueryable<Review?> reviews = this.database.Reviews.ByGameVersion(gameVersion, true)
.Where(r => r.SlotId == slotId)
.Include(r => r.Reviewer)
.Include(r => r.Slot)
.OrderByDescending(r => r.ThumbsUp - r.ThumbsDown)
.ThenByDescending(r => r.Timestamp)
.Skip(pageStart - 1)
.Take(pageSize);
List<Review?> reviewList = reviews.ToList();
string inner = reviewList.Aggregate
(
string.Empty,
(current, review) =>
{
if (review == null) return current;
RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
return current + review.Serialize(null, yourThumb);
}
);
string response = LbpSerializer.TaggedStringElement
(
"reviews",
inner,
new Dictionary<string, object>
{
{
"hint_start", pageStart + pageSize
},
{
"hint", reviewList.LastOrDefault()!.Timestamp // not sure
},
}
);
return this.Ok(response);
}
[HttpGet("reviewsBy/{username}")]
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
GameVersion gameVersion = gameToken.GameVersion;
IEnumerable<Review?> reviews = this.database.Reviews.ByGameVersion(gameVersion, true)
.Include(r => r.Reviewer)
.Include(r => r.Slot)
.Where(r => r.Reviewer!.Username == username)
.OrderByDescending(r => r.Timestamp)
.Skip(pageStart - 1)
.Take(pageSize);
List<Review?> reviewList = reviews.ToList();
string inner = reviewList.Aggregate
(
string.Empty,
(current, review) =>
{
if (review == null) return current;
RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
return current + review.Serialize(null, ratedReview);
}
);
string response = LbpSerializer.TaggedStringElement
(
"reviews",
inner,
new Dictionary<string, object>
{
{
"hint_start", pageStart
},
{
"hint", reviewList.LastOrDefault()!.Timestamp // Seems to be the timestamp of oldest
},
}
);
return this.Ok(response);
}
[HttpPost("rateReview/user/{slotId:int}/{username}")]
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (reviewer == null) return this.StatusCode(400, "");
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
if (review == null) return this.StatusCode(400, "");
RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
if (ratedReview == null)
{
ratedReview = new RatedReview
{
ReviewId = review.ReviewId,
UserId = user.UserId,
Thumb = 0,
};
this.database.RatedReviews.Add(ratedReview);
await this.database.SaveChangesAsync();
}
int oldRating = ratedReview.Thumb;
ratedReview.Thumb = Math.Clamp(rating, -1, 1);
if (oldRating == ratedReview.Thumb) return this.Ok();
// if the user's rating changed then we recount the review's ratings to ensure accuracy
List<RatedReview> reactions = await this.database.RatedReviews.Where(r => r.ReviewId == review.ReviewId).ToListAsync();
int yay = 0;
int boo = 0;
foreach (RatedReview r in reactions)
{
switch (r.Thumb)
{
case -1:
boo++;
break;
case 1:
yay++;
break;
}
}
review.ThumbsDown = boo;
review.ThumbsUp = yay;
await this.database.SaveChangesAsync();
return this.Ok();
}
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
public async Task<IActionResult> DeleteReview(int slotId, string username)
{
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (reviewer == null) return this.StatusCode(403, "");
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
if (review == null) return this.StatusCode(403, "");
review.Deleted = true;
review.DeletedBy = DeletedBy.LevelAuthor;
await this.database.SaveChangesAsync();
return this.Ok();
}
private async Task<Review?> getReviewFromBody()
{
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Review));
Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString));
SanitizationHelper.SanitizeStringsInClass(review);
return review;
}
}

View file

@ -0,0 +1,169 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class ScoreController : ControllerBase
{
private readonly Database database;
public ScoreController(Database database)
{
this.database = database;
}
[HttpPost("scoreboard/user/{id:int}")]
public async Task<IActionResult> SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
{
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
if (userAndToken == null) return this.StatusCode(403, "");
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Score));
Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString));
if (score == null) return this.BadRequest();
SanitizationHelper.SanitizeStringsInClass(score);
score.SlotId = id;
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
if (slot == null) return this.BadRequest();
switch (gameToken.GameVersion)
{
case GameVersion.LittleBigPlanet1:
slot.PlaysLBP1Complete++;
break;
case GameVersion.LittleBigPlanet2:
slot.PlaysLBP2Complete++;
break;
case GameVersion.LittleBigPlanet3:
slot.PlaysLBP3Complete++;
break;
case GameVersion.LittleBigPlanetVita:
slot.PlaysLBPVitaComplete++;
break;
}
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId)
.Where(s => s.PlayerIdCollection == score.PlayerIdCollection)
.Where(s => s.Type == score.Type);
if (existingScore.Any())
{
Score first = existingScore.First(s => s.SlotId == score.SlotId);
score.ScoreId = first.ScoreId;
score.Points = Math.Max(first.Points, score.Points);
this.database.Entry(first).CurrentValues.SetValues(score);
}
else
{
this.database.Scores.Add(score);
}
await this.database.SaveChangesAsync();
string myRanking = this.getScores(score.SlotId, score.Type, user, -1, 5, "scoreboardSegment");
return this.Ok(myRanking);
}
[HttpGet("friendscores/user/{slotId:int}/{type:int}")]
public IActionResult FriendScores(int slotId, int type)
//=> await TopScores(slotId, type);
=> this.Ok(LbpSerializer.BlankElement("scores"));
[HttpGet("topscores/user/{slotId:int}/{type:int}")]
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public async Task<IActionResult> TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
{
// Get username
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
return this.Ok(this.getScores(slotId, type, user, pageStart, pageSize));
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private string getScores
(
int slotId,
int type,
User user,
int pageStart = -1,
int pageSize = 5,
string rootName = "scores"
)
{
// This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT
var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type)
.OrderByDescending(s => s.Points)
.ToList()
.Select
(
(s, rank) => new
{
Score = s,
Rank = rank + 1,
}
);
// Find your score, since even if you aren't in the top list your score is pinned
var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Contains(user.Username)).OrderByDescending(rs => rs.Score.Points).FirstOrDefault();
// Paginated viewing: if not requesting pageStart, get results around user
var pagedScores = rankedScores.Skip(pageStart != -1 || myScore == null ? pageStart - 1 : myScore.Rank - 3).Take(Math.Min(pageSize, 30));
string serializedScores = pagedScores.Aggregate
(
string.Empty,
(current, rs) =>
{
rs.Score.Rank = rs.Rank;
return current + rs.Score.Serialize();
}
);
string res;
if (myScore == null) res = LbpSerializer.StringElement(rootName, serializedScores);
else
res = LbpSerializer.TaggedStringElement
(
rootName,
serializedScores,
new Dictionary<string, object>
{
{
"yourScore", myScore.Score.Points
},
{
"yourRank", myScore.Rank
}, //This is the numerator of your position globally in the side menu.
{
"totalNumScores", rankedScores.Count()
}, // This is the denominator of your position globally in the side menu.
}
);
return res;
}
}

View file

@ -0,0 +1,56 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class SearchController : ControllerBase
{
private readonly Database database;
public SearchController(Database database)
{
this.database = database;
}
[HttpGet("slots/search")]
public async Task<IActionResult> SearchSlots([FromQuery] string query, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request);
if (gameToken == null) return this.StatusCode(403, "");
if (string.IsNullOrWhiteSpace(query)) return this.BadRequest();
query = query.ToLower();
string[] keywords = query.Split(" ");
IQueryable<Slot> dbQuery = this.database.Slots.Include
(s => s.Creator)
.Include(s => s.Location)
.OrderBy(s => !s.TeamPick)
.ThenByDescending(s => s.FirstUploaded)
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
// 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<Slot> slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync();
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(gameToken.GameVersion));
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count()));
}
}

View file

@ -0,0 +1,402 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Reviews;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.GameAPI.Controllers.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
public class SlotsController : ControllerBase
{
private readonly Database database;
public SlotsController(Database database)
{
this.database = database;
}
[HttpGet("slots/by")]
public async Task<IActionResult> SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u);
if (user == null) return this.NotFound();
string response = Enumerable.Aggregate
(
this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId, true)
.Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)),
string.Empty,
(current, slot) => current + slot.Serialize(token.GameVersion)
);
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", user.UsedSlots
},
}
)
);
}
[HttpGet("s/user/{id:int}")]
public async Task<IActionResult> SUser(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
Slot? slot = await this.database.Slots.ByGameVersion(gameVersion, true, true).FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == user.UserId);
return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review));
}
[HttpGet("slots/cool")]
public async Task<IActionResult> Lbp1CoolSlots([FromQuery] int page)
{
const int pageSize = 30;
return await this.CoolSlots((page - 1) * pageSize, pageSize);
}
[HttpGet("slots/lbp2cool")]
public async Task<IActionResult> CoolSlots
(
[FromQuery] int pageStart,
[FromQuery] int pageSize,
[FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null,
[FromQuery] bool? move = null,
[FromQuery] int? page = null
)
{
int _pageStart = pageStart;
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), gameFilterType, players, move, "thisWeek");
}
[HttpGet("slots")]
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
.OrderByDescending(s => s.FirstUploaded)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.SlotCount()
},
}
)
);
}
[HttpGet("slots/mmpicks")]
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
.Where(s => s.TeamPick)
.OrderByDescending(s => s.LastUpdated)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.TeamPickCount()
},
}
)
);
}
[HttpGet("slots/lbp2luckydip")]
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
IEnumerable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.SlotCount()
},
}
)
);
}
[HttpGet("slots/thumbs")]
public async Task<IActionResult> ThumbsSlots
(
[FromQuery] int pageStart,
[FromQuery] int pageSize,
[FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null,
[FromQuery] bool? move = null,
[FromQuery] string? dateFilterType = null
)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
Random rand = new();
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable()
.OrderByDescending(s => s.Thumbsup)
.ThenBy(_ => rand.Next())
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.SlotCount()
},
}
)
);
}
[HttpGet("slots/mostUniquePlays")]
public async Task<IActionResult> MostUniquePlaysSlots
(
[FromQuery] int pageStart,
[FromQuery] int pageSize,
[FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null,
[FromQuery] bool? move = null,
[FromQuery] string? dateFilterType = null
)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
Random rand = new();
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable()
.OrderByDescending
(
// probably not the best way to do this?
s =>
{
return this.getGameFilter(gameFilterType, token.GameVersion) switch
{
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique,
GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique,
_ => s.PlaysUnique,
};
}
)
.ThenBy(_ => rand.Next())
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.SlotCount()
},
}
)
);
}
[HttpGet("slots/mostHearted")]
public async Task<IActionResult> MostHeartedSlots
(
[FromQuery] int pageStart,
[FromQuery] int pageSize,
[FromQuery] string? gameFilterType = null,
[FromQuery] int? players = null,
[FromQuery] bool? move = null,
[FromQuery] string? dateFilterType = null
)
{
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
Random rand = new();
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
.AsEnumerable()
.OrderByDescending(s => s.Hearts)
.ThenBy(_ => rand.Next())
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, 30));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
return this.Ok
(
LbpSerializer.TaggedStringElement
(
"slots",
response,
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
},
{
"total", await StatisticsHelper.SlotCount()
},
}
)
);
}
private GameVersion getGameFilter(string? gameFilterType, GameVersion version)
{
if (version == GameVersion.LittleBigPlanetVita) return GameVersion.LittleBigPlanetVita;
if (version == GameVersion.LittleBigPlanetPSP) return GameVersion.LittleBigPlanetPSP;
return gameFilterType switch
{
"lbp1" => GameVersion.LittleBigPlanet1,
"lbp2" => GameVersion.LittleBigPlanet2,
"lbp3" => GameVersion.LittleBigPlanet3,
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
null => GameVersion.LittleBigPlanet1,
_ => GameVersion.Unknown,
};
}
private IQueryable<Slot> 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 = this.getGameFilter(gameFilterType, version);
IQueryable<Slot> 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.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
else
// Get game versions exactly equal to gamefiltertype
whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
}
}