mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-31 01:08:41 +00:00
Refactor serialization system (#702)
* Initial work for serialization refactor * Experiment with new naming conventions * Mostly implement user and slot serialization. Still needs to be fine tuned to match original implementation Many things are left in a broken state like website features/api endpoints/lbp3 categories * Fix release building * Migrate scores, reviews, and more to new serialization system. Many things are still broken but progress is steadily being made * Fix Api responses and migrate serialization for most types * Make serialization better and fix bugs Fix recursive PrepareSerialization when recursive item is set during root item's PrepareSerialization, items, should be properly indexed in order but it's only tested to 1 level of recursion * Fix review serialization * Fix user serialization producing malformed SQL query * Remove DefaultIfEmpty query * MariaDB doesn't like double nested queries * Fix LBP1 tag counter * Implement lbp3 categories and add better deserialization handling * Implement expression tree caching to speed up reflection and write new serializer tests * Remove Game column from UserEntity and rename DatabaseContextModelSnapshot.cs back to DatabaseModelSnapshot.cs * Make UserEntity username not required * Fix recursive serialization of lists and add relevant unit tests * Actually commit the migration * Fix LocationTests to use new deserialization class * Fix comments not serializing the right author username * Replace all occurrences of StatusCode with their respective ASP.NET named result instead of StatusCode(403) everything is now in the form of Forbid() * Fix SlotBase.ConvertToEntity and LocationTests * Fix compilation error * Give Location a default value in GameUserSlot and GameUser * Reimplement stubbed website functions * Convert grief reports to new serialization system * Update DatabaseModelSnapshot and bump dotnet tool version * Remove unused directives * Fix broken type reference * Fix rated comments on website * Don't include banned users in website comments * Optimize score submission * Fix slot id calculating in in-game comment posting * Move serialization interfaces to types folder and add more documentation * Allow uploading of versus scores
This commit is contained in:
parent
307b2135a3
commit
329ab66043
248 changed files with 4993 additions and 2896 deletions
|
@ -5,7 +5,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -41,7 +41,7 @@ public class ClientConfigurationController : ControllerBase
|
|||
|
||||
[HttpGet("t_conf")]
|
||||
[Produces("text/xml")]
|
||||
public IActionResult Conf() => this.Ok("<t_enable>false</t_enable>");
|
||||
public IActionResult Conf() => this.Ok(new TelemetryConfigResponse());
|
||||
|
||||
[HttpGet("ChallengeConfig.xml")]
|
||||
[Produces("text/xml")]
|
||||
|
@ -54,8 +54,8 @@ public class ClientConfigurationController : ControllerBase
|
|||
[Produces("text/xml")]
|
||||
public async Task<IActionResult> GetPrivacySettings()
|
||||
{
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
PrivacySettings ps = new()
|
||||
{
|
||||
|
@ -63,15 +63,15 @@ public class ClientConfigurationController : ControllerBase
|
|||
ProfileVisibility = user.ProfileVisibility.ToSerializedString(),
|
||||
};
|
||||
|
||||
return this.Ok(ps.Serialize());
|
||||
return this.Ok(ps);
|
||||
}
|
||||
|
||||
[HttpPost("privacySettings")]
|
||||
[Produces("text/xml")]
|
||||
public async Task<IActionResult> SetPrivacySetting()
|
||||
{
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
PrivacySettings? settings = await this.DeserializeBody<PrivacySettings>();
|
||||
if (settings == null) return this.BadRequest();
|
||||
|
@ -100,6 +100,6 @@ public class ClientConfigurationController : ControllerBase
|
|||
ProfileVisibility = user.ProfileVisibility.ToSerializedString(),
|
||||
};
|
||||
|
||||
return this.Ok(ps.Serialize());
|
||||
return this.Ok(ps);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,10 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -29,7 +29,7 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("rateComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Return bad request if both are true or both are false
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
@ -44,7 +44,7 @@ public class CommentController : ControllerBase
|
|||
[HttpGet("userComments/{username}")]
|
||||
public async Task<IActionResult> GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0 || pageStart < 0) return this.BadRequest();
|
||||
|
||||
|
@ -77,45 +77,37 @@ public class CommentController : ControllerBase
|
|||
where blockedProfile.UserId == token.UserId
|
||||
select blockedProfile.BlockedUserId).ToListAsync();
|
||||
|
||||
List<Comment> comments = await this.database.Comments.Where(p => p.TargetId == targetId && p.Type == type)
|
||||
List<GameComment> comments = await this.database.Comments.Where(p => p.TargetId == targetId && p.Type == type)
|
||||
.OrderByDescending(p => p.Timestamp)
|
||||
.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))
|
||||
.Select(c => GameComment.CreateFromEntity(c, token.UserId))
|
||||
.ToListAsync();
|
||||
|
||||
string outputXml = comments.Aggregate
|
||||
(string.Empty, (current, comment) => current + comment.Serialize(this.getReaction(token.UserId, comment.CommentId).Result));
|
||||
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
|
||||
}
|
||||
|
||||
private async Task<int> getReaction(int userId, int commentId)
|
||||
{
|
||||
return await this.database.Reactions.Where(r => r.UserId == userId)
|
||||
.Where(r => r.TargetId == commentId)
|
||||
.Select(r => r.Rating)
|
||||
.FirstOrDefaultAsync();
|
||||
return this.Ok(new CommentListResponse(comments));
|
||||
}
|
||||
|
||||
[HttpPost("postUserComment/{username}")]
|
||||
[HttpPost("postComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> PostComment(string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Comment? comment = await this.DeserializeBody<Comment>();
|
||||
GameComment? comment = await this.DeserializeBody<GameComment>();
|
||||
if (comment == null) 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);
|
||||
|
||||
CommentType type = username == null ? CommentType.Level : CommentType.Profile;
|
||||
|
||||
int targetId;
|
||||
if (type == CommentType.Level)
|
||||
{
|
||||
slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
targetId = await this.database.Slots.Where(s => s.SlotId == slotId)
|
||||
.Where(s => s.CommentsEnabled && !s.Hidden)
|
||||
.Select(s => s.SlotId)
|
||||
|
@ -138,11 +130,11 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("deleteComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||
CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||
if (comment == null) return this.NotFound();
|
||||
|
||||
if (comment.Deleted) return this.Ok();
|
||||
|
@ -169,7 +161,7 @@ public class CommentController : ControllerBase
|
|||
canDelete = comment.PosterUserId == token.UserId || slotCreator == token.UserId;
|
||||
}
|
||||
|
||||
if (!canDelete) return this.StatusCode(403, "");
|
||||
if (!canDelete) return this.Forbid();
|
||||
|
||||
comment.Deleted = true;
|
||||
comment.DeletedBy = await this.database.UsernameFromGameToken(token);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
@ -10,5 +11,5 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
|||
public class DeveloperController : Controller
|
||||
{
|
||||
[HttpGet("developer_videos")]
|
||||
public IActionResult DeveloperVideos() => this.Ok("<videos></videos>");
|
||||
public IActionResult DeveloperVideos() => this.Ok(new GameDeveloperVideos());
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
||||
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -29,17 +29,17 @@ public class FriendsController : ControllerBase
|
|||
[HttpPost("npdata")]
|
||||
public async Task<IActionResult> NPData()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
NPData? npData = await this.DeserializeBody<NPData>();
|
||||
if (npData == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(npData);
|
||||
|
||||
List<User> friends = new();
|
||||
List<UserEntity> friends = new();
|
||||
foreach (string friendName in npData.Friends ?? new List<string>())
|
||||
{
|
||||
User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName);
|
||||
UserEntity? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName);
|
||||
if (friend == null) continue;
|
||||
|
||||
friends.Add(friend);
|
||||
|
@ -48,7 +48,7 @@ public class FriendsController : ControllerBase
|
|||
List<int> blockedUsers = new();
|
||||
foreach (string blockedUserName in npData.BlockedUsers ?? new List<string>())
|
||||
{
|
||||
User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName);
|
||||
UserEntity? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName);
|
||||
if (blockedUser == null) continue;
|
||||
|
||||
blockedUsers.Add(blockedUser.UserId);
|
||||
|
@ -61,30 +61,35 @@ public class FriendsController : ControllerBase
|
|||
|
||||
UserFriendStore.UpdateFriendData(friendStore);
|
||||
|
||||
string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username));
|
||||
List<MinimalUserProfile> minimalFriends =
|
||||
friends.Select(u => new MinimalUserProfile
|
||||
{
|
||||
UserHandle = new NpHandle(u.Username, ""),
|
||||
}).ToList();
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized)));
|
||||
return this.Ok(new FriendResponse(minimalFriends));
|
||||
}
|
||||
|
||||
[HttpGet("myFriends")]
|
||||
public async Task<IActionResult> MyFriends()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
UserFriendData? friendStore = UserFriendStore.GetUserFriendData(token.UserId);
|
||||
|
||||
if (friendStore == null)
|
||||
return this.Ok(LbpSerializer.BlankElement("myFriends"));
|
||||
GenericUserResponse<GameUser> response = new("myFriends", new List<GameUser>());
|
||||
|
||||
if (friendStore == null)
|
||||
return this.Ok(response);
|
||||
|
||||
string friends = "";
|
||||
foreach (int friendId in friendStore.FriendIds)
|
||||
{
|
||||
User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == friendId);
|
||||
UserEntity? friend = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == friendId);
|
||||
if (friend == null) continue;
|
||||
|
||||
friends += friend.Serialize(token.GameVersion);
|
||||
response.Users.Add(GameUser.CreateFromEntity(friend, token.GameVersion));
|
||||
}
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("myFriends", friends));
|
||||
return this.Ok(response);
|
||||
}
|
||||
}
|
|
@ -64,12 +64,12 @@ public class LoginController : ControllerBase
|
|||
if (username == null)
|
||||
{
|
||||
Logger.Warn("Unable to determine username, rejecting login", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
await this.database.RemoveExpiredTokens();
|
||||
|
||||
User? user;
|
||||
UserEntity? user;
|
||||
|
||||
switch (npTicket.Platform)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ public class LoginController : ControllerBase
|
|||
if (user == null)
|
||||
{
|
||||
// Check if there is an account with that username already
|
||||
User? targetUsername = await this.database.Users.FirstOrDefaultAsync(u => u.Username == npTicket.Username);
|
||||
UserEntity? targetUsername = await this.database.Users.FirstOrDefaultAsync(u => u.Username == npTicket.Username);
|
||||
if (targetUsername != null)
|
||||
{
|
||||
ulong targetPlatform = npTicket.Platform == Platform.RPCS3
|
||||
|
@ -102,7 +102,7 @@ public class LoginController : ControllerBase
|
|||
if (targetPlatform != 0)
|
||||
{
|
||||
Logger.Warn($"New user tried to login but their name is already taken, username={username}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
// if there is already a pending link request don't create another
|
||||
|
@ -111,9 +111,9 @@ public class LoginController : ControllerBase
|
|||
p.PlatformId == npTicket.UserId &&
|
||||
p.UserId == targetUsername.UserId);
|
||||
|
||||
if (linkAttemptExists) return this.StatusCode(403, "");
|
||||
if (linkAttemptExists) return this.Forbid();
|
||||
|
||||
PlatformLinkAttempt linkAttempt = new()
|
||||
PlatformLinkAttemptEntity linkAttempt = new()
|
||||
{
|
||||
Platform = npTicket.Platform,
|
||||
UserId = targetUsername.UserId,
|
||||
|
@ -124,13 +124,13 @@ public class LoginController : ControllerBase
|
|||
this.database.PlatformLinkAttempts.Add(linkAttempt);
|
||||
await this.database.SaveChangesAsync();
|
||||
Logger.Success($"User '{npTicket.Username}' tried to login but platform isn't linked, platform={npTicket.Platform}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
if (!ServerConfiguration.Instance.Authentication.AutomaticAccountCreation)
|
||||
{
|
||||
Logger.Warn($"Unknown user tried to connect username={username}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
// create account for user if they don't exist
|
||||
user = await this.database.CreateUser(username, "$");
|
||||
|
@ -162,7 +162,7 @@ public class LoginController : ControllerBase
|
|||
{
|
||||
Logger.Warn($"{npTicket.Platform} user changed their name to a name that is already taken," +
|
||||
$" oldName='{user.Username}', newName='{npTicket.Username}'", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
Logger.Info($"User's username has changed, old='{user.Username}', new='{npTicket.Username}', platform={npTicket.Platform}", LogArea.Login);
|
||||
user.Username = username;
|
||||
|
@ -176,26 +176,26 @@ public class LoginController : ControllerBase
|
|||
await this.database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
GameToken? token = await this.database.GameTokens.Include(t => t.User)
|
||||
GameTokenEntity? token = await this.database.GameTokens.Include(t => t.User)
|
||||
.FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == npTicket.Username && t.TicketHash == npTicket.TicketHash);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
Logger.Warn($"Rejecting duplicate ticket from {username}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
token = await this.database.AuthenticateUser(user, npTicket, ipAddress);
|
||||
if (token == null)
|
||||
{
|
||||
Logger.Warn($"Unable to find/generate a token for username {npTicket.Username}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
if (user.IsBanned)
|
||||
{
|
||||
Logger.Error($"User {npTicket.Username} tried to login but is banned", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
Logger.Success($"Successfully logged in user {user.Username} as {token.GameVersion} client", LogArea.Login);
|
||||
|
@ -214,7 +214,7 @@ public class LoginController : ControllerBase
|
|||
AuthTicket = "MM_AUTH=" + token.UserToken,
|
||||
ServerBrand = VersionHelper.EnvVer,
|
||||
TitleStorageUrl = ServerConfiguration.Instance.GameApiExternalUrl,
|
||||
}.Serialize()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -25,10 +25,10 @@ public class LogoutController : ControllerBase
|
|||
[HttpPost]
|
||||
public async Task<IActionResult> OnPost()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
user.LastLogout = TimeHelper.TimestampMillis;
|
||||
|
||||
|
|
|
@ -28,18 +28,18 @@ public class EnterLevelController : ControllerBase
|
|||
[HttpPost("play/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> PlayLevel(string slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
// don't count plays for developer slots
|
||||
if (slotType == "developer") return this.Ok();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.BadRequest();
|
||||
|
||||
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId);
|
||||
VisitedLevel? v;
|
||||
IQueryable<VisitedLevelEntity> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId);
|
||||
VisitedLevelEntity? v;
|
||||
if (!visited.Any())
|
||||
{
|
||||
switch (token.GameVersion)
|
||||
|
@ -57,7 +57,7 @@ public class EnterLevelController : ControllerBase
|
|||
default: return this.BadRequest();
|
||||
}
|
||||
|
||||
v = new VisitedLevel
|
||||
v = new VisitedLevelEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = token.UserId,
|
||||
|
@ -98,22 +98,22 @@ public class EnterLevelController : ControllerBase
|
|||
[HttpPost("enterLevel/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> EnterLevel(string slotType, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") return this.Ok();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId);
|
||||
VisitedLevel? v;
|
||||
IQueryable<VisitedLevelEntity> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId);
|
||||
VisitedLevelEntity? v;
|
||||
if (!visited.Any())
|
||||
{
|
||||
slot.PlaysLBP1Unique++;
|
||||
|
||||
v = new VisitedLevel
|
||||
v = new VisitedLevelEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = token.UserId,
|
||||
|
|
|
@ -37,10 +37,10 @@ public class MatchController : ControllerBase
|
|||
[Produces("text/plain")]
|
||||
public async Task<IActionResult> Match()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
#region Parse match data
|
||||
|
||||
|
@ -113,7 +113,7 @@ public class MatchController : ControllerBase
|
|||
List<int> users = new();
|
||||
foreach (string playerUsername in createRoom.Players)
|
||||
{
|
||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (player != null) users.Add(player.UserId);
|
||||
else return this.BadRequest();
|
||||
|
@ -129,10 +129,10 @@ public class MatchController : ControllerBase
|
|||
|
||||
if (room != null)
|
||||
{
|
||||
List<User> users = new();
|
||||
List<UserEntity> users = new();
|
||||
foreach (string playerUsername in updatePlayersInRoom.Players)
|
||||
{
|
||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
UserEntity? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (player != null) users.Add(player);
|
||||
else return this.BadRequest();
|
||||
|
|
|
@ -46,7 +46,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
[HttpGet("announce")]
|
||||
public async Task<IActionResult> Announce()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
|
@ -81,7 +81,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
[HttpPost("filter")]
|
||||
public async Task<IActionResult> Filter()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
string message = await this.ReadBodyAsync();
|
||||
|
||||
|
@ -92,7 +92,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
|
||||
if (await this.database.Users.AnyAsync(u => u.EmailAddress == email)) return this.Ok();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null || user.EmailAddressVerified) return this.Ok();
|
||||
|
||||
user.EmailAddress = email;
|
||||
|
|
|
@ -8,6 +8,7 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Moderation;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Moderation.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
@ -29,11 +30,11 @@ public class ReportController : ControllerBase
|
|||
[HttpPost("grief")]
|
||||
public async Task<IActionResult> Report()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
GriefReport? report = await this.DeserializeBody<GriefReport>();
|
||||
GameGriefReport? report = await this.DeserializeBody<GameGriefReport>();
|
||||
if (report == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(report);
|
||||
|
@ -46,18 +47,20 @@ public class ReportController : ControllerBase
|
|||
|
||||
if (report.XmlPlayers.Any(p => !this.database.IsUsernameValid(p.Name))) return this.BadRequest();
|
||||
|
||||
report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle));
|
||||
report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[]));
|
||||
report.Timestamp = TimeHelper.TimestampMillis;
|
||||
report.ReportingPlayerId = token.UserId;
|
||||
GriefReportEntity reportEntity = GameGriefReport.ConvertToEntity(report);
|
||||
|
||||
this.database.Reports.Add(report);
|
||||
reportEntity.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle));
|
||||
reportEntity.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[]));
|
||||
reportEntity.Timestamp = TimeHelper.TimestampMillis;
|
||||
reportEntity.ReportingPlayerId = token.UserId;
|
||||
|
||||
this.database.Reports.Add(reportEntity);
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
await WebhookHelper.SendWebhook(
|
||||
title: "New grief report",
|
||||
description: $"Submitted by {username}\n" +
|
||||
$"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{report.ReportId}).",
|
||||
$"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{reportEntity.ReportId}).",
|
||||
dest: WebhookHelper.WebhookDestination.Moderation
|
||||
);
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ using LBPUnion.ProjectLighthouse.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
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;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -34,17 +34,17 @@ public class PhotosController : ControllerBase
|
|||
[HttpPost("uploadPhoto")]
|
||||
public async Task<IActionResult> UploadPhoto()
|
||||
{
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();
|
||||
if (user.GetUploadedPhotoCount(this.database) >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();
|
||||
|
||||
Photo? photo = await this.DeserializeBody<Photo>();
|
||||
GamePhoto? photo = await this.DeserializeBody<GamePhoto>();
|
||||
if (photo == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(photo);
|
||||
|
||||
foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId))
|
||||
foreach (PhotoEntity p in this.database.Photos.Where(p => p.CreatorId == user.UserId))
|
||||
{
|
||||
if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded
|
||||
if (p.MediumHash == photo.MediumHash) return this.Ok();
|
||||
|
@ -52,20 +52,28 @@ public class PhotosController : ControllerBase
|
|||
if (p.PlanHash == photo.PlanHash) return this.Ok();
|
||||
}
|
||||
|
||||
photo.CreatorId = user.UserId;
|
||||
photo.Creator = user;
|
||||
PhotoEntity photoEntity = new()
|
||||
{
|
||||
CreatorId = user.UserId,
|
||||
Creator = user,
|
||||
SmallHash = photo.SmallHash,
|
||||
MediumHash = photo.MediumHash,
|
||||
LargeHash = photo.LargeHash,
|
||||
PlanHash = photo.PlanHash,
|
||||
Timestamp = photo.Timestamp,
|
||||
};
|
||||
|
||||
if (photo.XmlLevelInfo?.RootLevel != null)
|
||||
if (photo.LevelInfo?.RootLevel != null)
|
||||
{
|
||||
bool validLevel = false;
|
||||
PhotoSlot photoSlot = photo.XmlLevelInfo;
|
||||
PhotoSlot photoSlot = photo.LevelInfo;
|
||||
if (photoSlot.SlotType is SlotType.Pod or SlotType.Local) photoSlot.SlotId = 0;
|
||||
switch (photoSlot.SlotType)
|
||||
{
|
||||
case SlotType.User:
|
||||
{
|
||||
// We'll grab the slot by the RootLevel and see what happens from here.
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel));
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel));
|
||||
if (slot == null) break;
|
||||
|
||||
if (!string.IsNullOrEmpty(slot.RootLevel)) validLevel = true;
|
||||
|
@ -76,7 +84,7 @@ public class PhotosController : ControllerBase
|
|||
case SlotType.Local:
|
||||
case SlotType.Developer:
|
||||
{
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId);
|
||||
if (slot != null)
|
||||
photoSlot.SlotId = slot.SlotId;
|
||||
else
|
||||
|
@ -92,23 +100,23 @@ public class PhotosController : ControllerBase
|
|||
break;
|
||||
}
|
||||
|
||||
if (validLevel) photo.SlotId = photo.XmlLevelInfo.SlotId;
|
||||
if (validLevel) photoEntity.SlotId = photoSlot.SlotId;
|
||||
}
|
||||
|
||||
if (photo.XmlSubjects?.Count > 4) return this.BadRequest();
|
||||
if (photo.Subjects?.Count > 4) return this.BadRequest();
|
||||
|
||||
if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp;
|
||||
|
||||
this.database.Photos.Add(photo);
|
||||
this.database.Photos.Add(photoEntity);
|
||||
|
||||
// Save to get photo ID for the PhotoSubject foreign keys
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
if (photo.XmlSubjects != null)
|
||||
if (photo.Subjects != null)
|
||||
{
|
||||
// Check for duplicate photo subjects
|
||||
List<string> subjectUserIds = new(4);
|
||||
foreach (PhotoSubject subject in photo.PhotoSubjects)
|
||||
foreach (GamePhotoSubject subject in photo.Subjects)
|
||||
{
|
||||
if (subjectUserIds.Contains(subject.Username) && !string.IsNullOrEmpty(subject.Username))
|
||||
return this.BadRequest();
|
||||
|
@ -116,17 +124,23 @@ public class PhotosController : ControllerBase
|
|||
subjectUserIds.Add(subject.Username);
|
||||
}
|
||||
|
||||
foreach (PhotoSubject subject in photo.XmlSubjects.Where(subject => !string.IsNullOrEmpty(subject.Username)))
|
||||
foreach (GamePhotoSubject subject in photo.Subjects.Where(subject => !string.IsNullOrEmpty(subject.Username)))
|
||||
{
|
||||
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
|
||||
subject.UserId = await this.database.Users.Where(u => u.Username == subject.Username)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (subject.User == null) continue;
|
||||
if (subject.UserId == 0) continue;
|
||||
|
||||
PhotoSubjectEntity subjectEntity = new()
|
||||
{
|
||||
PhotoId = photoEntity.PhotoId,
|
||||
UserId = subject.UserId,
|
||||
};
|
||||
|
||||
subject.UserId = subject.User.UserId;
|
||||
subject.PhotoId = photo.PhotoId;
|
||||
Logger.Debug($"Adding PhotoSubject (userid {subject.UserId}) to db", LogArea.Photos);
|
||||
|
||||
this.database.PhotoSubjects.Add(subject);
|
||||
this.database.PhotoSubjects.Add(subjectEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,16 +169,15 @@ public class PhotosController : ControllerBase
|
|||
|
||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
|
||||
List<Photo> photos = await this.database.Photos.Include(p => p.Creator)
|
||||
.Include(p => p.PhotoSubjects)
|
||||
.ThenInclude(ps => ps.User)
|
||||
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))
|
||||
.Select(p => GamePhoto.CreateFromEntity(p))
|
||||
.ToListAsync();
|
||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id, SlotHelper.ParseType(slotType)));
|
||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
}
|
||||
|
||||
[HttpGet("photos/by")]
|
||||
|
@ -175,16 +188,14 @@ public class PhotosController : ControllerBase
|
|||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
List<Photo> photos = await this.database.Photos.Include(p => p.Creator)
|
||||
.Include(p => p.PhotoSubjects)
|
||||
.ThenInclude(ps => ps.User)
|
||||
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))
|
||||
.Select(p => GamePhoto.CreateFromEntity(p))
|
||||
.ToListAsync();
|
||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize());
|
||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
}
|
||||
|
||||
[HttpGet("photos/with")]
|
||||
|
@ -195,32 +206,30 @@ public class PhotosController : ControllerBase
|
|||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
List<Photo> photos = await this.database.Photos.Include(p => p.Creator)
|
||||
.Include(p => p.PhotoSubjects)
|
||||
.ThenInclude(ps => ps.User)
|
||||
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))
|
||||
.Select(p => GamePhoto.CreateFromEntity(p))
|
||||
.ToListAsync();
|
||||
string response = photos.Aggregate(string.Empty, (current, photo) => current + photo.Serialize());
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||
return this.Ok(new PhotoListResponse(photos));
|
||||
}
|
||||
|
||||
[HttpPost("deletePhoto/{id:int}")]
|
||||
public async Task<IActionResult> DeletePhoto(int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id);
|
||||
PhotoEntity? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id);
|
||||
if (photo == null) return this.NotFound();
|
||||
|
||||
// If user isn't photo creator then check if they own the level
|
||||
if (photo.CreatorId != token.UserId)
|
||||
{
|
||||
Slot? photoSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId && s.Type == SlotType.User);
|
||||
if (photoSlot == null || photoSlot.CreatorId != token.UserId) return this.StatusCode(401, "");
|
||||
SlotEntity? photoSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId && s.Type == SlotType.User);
|
||||
if (photoSlot == null || photoSlot.CreatorId != token.UserId) return this.Unauthorized();
|
||||
}
|
||||
|
||||
HashSet<string> photoResources = new(){photo.LargeHash, photo.SmallHash, photo.MediumHash, photo.PlanHash,};
|
||||
|
|
|
@ -4,7 +4,6 @@ using System.IO.Pipelines;
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Resources;
|
||||
|
@ -22,7 +21,7 @@ public class ResourcesController : ControllerBase
|
|||
{
|
||||
|
||||
[HttpPost("showModerated")]
|
||||
public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources"));
|
||||
public IActionResult ShowModerated() => this.Ok(new ResourceList());
|
||||
|
||||
[HttpPost("filterResources")]
|
||||
[HttpPost("showNotUploaded")]
|
||||
|
@ -31,11 +30,9 @@ public class ResourcesController : ControllerBase
|
|||
ResourceList? resourceList = await this.DeserializeBody<ResourceList>();
|
||||
if (resourceList?.Resources == null) return this.BadRequest();
|
||||
|
||||
string resources = resourceList.Resources.Where
|
||||
(s => !FileHelper.ResourceExists(s))
|
||||
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
|
||||
resourceList.Resources = resourceList.Resources.Where(r => !FileHelper.ResourceExists(r)).ToArray();
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("resources", resources));
|
||||
return this.Ok(resourceList);
|
||||
}
|
||||
|
||||
[HttpGet("r/{hash}")]
|
||||
|
|
|
@ -3,13 +3,13 @@ using LBPUnion.ProjectLighthouse.Configuration;
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
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;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -32,16 +32,18 @@ public class CollectionController : ControllerBase
|
|||
[HttpGet("playlists/{playlistId:int}/slots")]
|
||||
public async Task<IActionResult> GetPlaylistSlots(int playlistId)
|
||||
{
|
||||
Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
PlaylistEntity? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
if (targetPlaylist == null) return this.BadRequest();
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.Include(s => s.Creator)
|
||||
.Where(s => targetPlaylist.SlotIds.Contains(s.SlotId));
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
List<SlotBase> slots = await this.database.Slots.Where(s => targetPlaylist.SlotIds.Contains(s.SlotId))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
|
||||
int total = targetPlaylist.SlotIds.Length;
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, 0));
|
||||
}
|
||||
|
||||
[HttpPost("playlists/{playlistId:int}")]
|
||||
|
@ -50,9 +52,9 @@ public class CollectionController : ControllerBase
|
|||
[HttpPost("playlists/{playlistId:int}/order_slots")]
|
||||
public async Task<IActionResult> UpdatePlaylist(int playlistId, int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
PlaylistEntity? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
if (targetPlaylist == null) return this.BadRequest();
|
||||
|
||||
if (token.UserId != targetPlaylist.CreatorId) return this.BadRequest();
|
||||
|
@ -65,7 +67,7 @@ public class CollectionController : ControllerBase
|
|||
return this.Ok(this.GetUserPlaylists(token.UserId));
|
||||
}
|
||||
|
||||
Playlist? newPlaylist = await this.DeserializeBody<Playlist>("playlist", "levels");
|
||||
GamePlaylist? newPlaylist = await this.DeserializeBody<GamePlaylist>("playlist", "levels");
|
||||
|
||||
if (newPlaylist == null) return this.BadRequest();
|
||||
|
||||
|
@ -97,41 +99,48 @@ public class CollectionController : ControllerBase
|
|||
return this.Ok(this.GetUserPlaylists(token.UserId));
|
||||
}
|
||||
|
||||
private string GetUserPlaylists(int userId)
|
||||
private async Task<PlaylistResponse> GetUserPlaylists(int userId)
|
||||
{
|
||||
string response = Enumerable.Aggregate(
|
||||
this.database.Playlists.Include(p => p.Creator).Where(p => p.CreatorId == userId),
|
||||
string.Empty,
|
||||
(current, slot) => current + slot.Serialize());
|
||||
List<GamePlaylist> playlists = await this.database.Playlists.Include(p => p.Creator)
|
||||
.Where(p => p.CreatorId == userId)
|
||||
.Select(p => GamePlaylist.CreateFromEntity(p))
|
||||
.ToListAsync();
|
||||
int total = this.database.Playlists.Count(p => p.CreatorId == userId);
|
||||
|
||||
return LbpSerializer.TaggedStringElement("playlists", response, new Dictionary<string, object>
|
||||
return new PlaylistResponse
|
||||
{
|
||||
{"total", total},
|
||||
{"hint_start", total+1},
|
||||
});
|
||||
Playlists = playlists,
|
||||
Total = total,
|
||||
HintStart = total+1,
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("playlists")]
|
||||
public async Task<IActionResult> CreatePlaylist()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
int playlistCount = await this.database.Playlists.CountAsync(p => p.CreatorId == token.UserId);
|
||||
|
||||
if (playlistCount > ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota) return this.BadRequest();
|
||||
|
||||
Playlist? playlist = await this.DeserializeBody<Playlist>("playlist");
|
||||
GamePlaylist? playlist = await this.DeserializeBody<GamePlaylist>("playlist");
|
||||
|
||||
if (playlist == null) return this.BadRequest();
|
||||
|
||||
playlist.CreatorId = token.UserId;
|
||||
PlaylistEntity playlistEntity = new()
|
||||
{
|
||||
CreatorId = token.UserId,
|
||||
Description = playlist.Description,
|
||||
Name = playlist.Name,
|
||||
SlotIds = playlist.SlotIds,
|
||||
};
|
||||
|
||||
this.database.Playlists.Add(playlist);
|
||||
this.database.Playlists.Add(playlistEntity);
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Ok(this.GetUserPlaylists(token.UserId));
|
||||
return this.Ok(await this.GetUserPlaylists(token.UserId));
|
||||
}
|
||||
|
||||
[HttpGet("user/{username}/playlists")]
|
||||
|
@ -140,101 +149,60 @@ public class CollectionController : ControllerBase
|
|||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
return this.Ok(this.GetUserPlaylists(targetUserId));
|
||||
return this.Ok(await this.GetUserPlaylists(targetUserId));
|
||||
}
|
||||
|
||||
[HttpGet("searches")]
|
||||
[HttpGet("genres")]
|
||||
public async Task<IActionResult> GenresAndSearches()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
string categoriesSerialized = CategoryHelper.Categories.Aggregate
|
||||
(
|
||||
string.Empty,
|
||||
(current, category) =>
|
||||
{
|
||||
string serialized;
|
||||
List<GameCategory> categories = new();
|
||||
|
||||
if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user);
|
||||
else serialized = category.Serialize(this.database);
|
||||
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 current + serialized;
|
||||
}
|
||||
);
|
||||
|
||||
categoriesSerialized += LbpSerializer.StringElement("text_search", LbpSerializer.StringElement("url", "/slots/searchLBP3"));
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"categories",
|
||||
categoriesSerialized,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"hint", ""
|
||||
},
|
||||
{
|
||||
"hint_start", 1
|
||||
},
|
||||
{
|
||||
"total", CategoryHelper.Categories.Count
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
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)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
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<Slot> slots;
|
||||
List<SlotBase> slots;
|
||||
int totalSlots;
|
||||
|
||||
if (category is CategoryWithUser categoryWithUser)
|
||||
{
|
||||
slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList();
|
||||
slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize)
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToList();
|
||||
totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
slots = category.GetSlots(this.database, pageStart, pageSize).ToList();
|
||||
slots = category.GetSlots(this.database, pageStart, pageSize)
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToList();
|
||||
totalSlots = category.GetTotalSlots(this.database);
|
||||
}
|
||||
|
||||
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"results",
|
||||
slotsSerialized,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"total", totalSlots
|
||||
},
|
||||
{
|
||||
"hint_start", pageStart + pageSize
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
return this.Ok(new GenericSlotResponse("results", slots, totalSlots, pageStart + pageSize));
|
||||
}
|
||||
}
|
|
@ -43,9 +43,9 @@ public class LevelTagsController : ControllerBase
|
|||
[HttpPost("tag/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> PostTag([FromForm(Name = "t")] string tagName, [FromRoute] string slotType, [FromRoute] int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync();
|
||||
SlotEntity? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync();
|
||||
if (slot == null) return this.BadRequest();
|
||||
|
||||
if (!LabelHelper.IsValidTag(tagName)) return this.BadRequest();
|
||||
|
@ -56,7 +56,7 @@ public class LevelTagsController : ControllerBase
|
|||
|
||||
if (slotType != "user") return this.BadRequest();
|
||||
|
||||
RatedLevel? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId);
|
||||
RatedLevelEntity? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId);
|
||||
if (rating == null) return this.BadRequest();
|
||||
|
||||
rating.TagLBP1 = tagName;
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
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.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -43,35 +43,27 @@ public class ListController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IEnumerable<Slot> queuedLevels = this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.Queue)
|
||||
List<SlotBase> queuedLevels = await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.Queue)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.AsEnumerable();
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token)).ToListAsync();
|
||||
|
||||
string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Serialize(gameVersion));
|
||||
int total = await this.database.QueuedLevels.CountAsync(q => q.UserId == token.UserId);
|
||||
int start = pageStart + Math.Min(pageSize, 30);
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement("slots", response, new Dictionary<string, object>
|
||||
{
|
||||
{ "total", await this.database.QueuedLevels.CountAsync(q => q.UserId == token.UserId) },
|
||||
{ "hint_start", pageStart + Math.Min(pageSize, 30) },
|
||||
})
|
||||
);
|
||||
return this.Ok(new GenericSlotResponse(queuedLevels, total, start));
|
||||
}
|
||||
|
||||
[HttpPost("lolcatftw/add/user/{id:int}")]
|
||||
public async Task<IActionResult> AddQueuedLevel(int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
await this.database.QueueLevel(token.UserId, slot);
|
||||
|
@ -82,9 +74,9 @@ public class ListController : ControllerBase
|
|||
[HttpPost("lolcatftw/remove/user/{id:int}")]
|
||||
public async Task<IActionResult> RemoveQueuedLevel(int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
await this.database.UnqueueLevel(token.UserId, slot);
|
||||
|
@ -95,7 +87,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("lolcatftw/clear")]
|
||||
public async Task<IActionResult> ClearQueuedLevels()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == token.UserId));
|
||||
|
||||
|
@ -120,30 +112,23 @@ public class ListController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.Forbid();
|
||||
|
||||
User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.StatusCode(403, "");
|
||||
|
||||
IEnumerable<Slot> heartedLevels = this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.FavouriteSlots)
|
||||
List<SlotBase> heartedLevels = await this.filterListByRequest(gameFilterType, dateFilterType, token.GameVersion, username, ListFilterType.FavouriteSlots)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.AsEnumerable();
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Serialize(gameVersion));
|
||||
int total = await this.database.HeartedLevels.CountAsync(q => q.UserId == targetUser.UserId);
|
||||
int start = pageStart + Math.Min(pageSize, 30);
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement("favouriteSlots", response, new Dictionary<string, object>
|
||||
{
|
||||
{ "total", await this.database.HeartedLevels.CountAsync(q => q.UserId == targetUser.UserId) },
|
||||
{ "hint_start", pageStart + Math.Min(pageSize, 30) },
|
||||
})
|
||||
);
|
||||
return this.Ok(new GenericSlotResponse("favouriteSlots", heartedLevels, total, start));
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -151,13 +136,13 @@ public class ListController : ControllerBase
|
|||
[HttpPost("favourite/slot/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> AddFavouriteSlot(string slotType, int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
if (slotType == "developer")
|
||||
|
@ -174,13 +159,13 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/slot/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> RemoveFavouriteSlot(string slotType, int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
if (slotType == "developer")
|
||||
|
@ -204,29 +189,31 @@ public class ListController : ControllerBase
|
|||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.StatusCode(403, "");
|
||||
if (targetUserId == 0) return this.Forbid();
|
||||
|
||||
IEnumerable<Playlist> heartedPlaylists = 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);
|
||||
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)
|
||||
.Select(p => GamePlaylist.CreateFromEntity(p))
|
||||
.ToListAsync();
|
||||
|
||||
string response = heartedPlaylists.Aggregate(string.Empty, (current, p) => current + p.Serialize());
|
||||
int total = await this.database.HeartedPlaylists.CountAsync(p => p.UserId == targetUserId);
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement("favouritePlaylists", response, new Dictionary<string, object>
|
||||
{
|
||||
{ "total", this.database.HeartedPlaylists.Count(p => p.UserId == targetUserId) },
|
||||
{ "hint_start", pageStart + Math.Min(pageSize, 30) },
|
||||
})
|
||||
);
|
||||
return this.Ok(new GenericPlaylistResponse<GamePlaylist>("favouritePlaylists", heartedPlaylists)
|
||||
{
|
||||
Total = total,
|
||||
HintStart = pageStart + Math.Min(pageSize, 30),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("favourite/playlist/{playlistId:int}")]
|
||||
public async Task<IActionResult> AddFavouritePlaylist(int playlistId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
PlaylistEntity? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
if (playlist == null) return this.NotFound();
|
||||
|
||||
await this.database.HeartPlaylist(token.UserId, playlist);
|
||||
|
@ -237,9 +224,9 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/playlist/{playlistId:int}")]
|
||||
public async Task<IActionResult> RemoveFavouritePlaylist(int playlistId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
PlaylistEntity? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
if (playlist == null) return this.NotFound();
|
||||
|
||||
await this.database.UnheartPlaylist(token.UserId, playlist);
|
||||
|
@ -256,40 +243,34 @@ public class ListController : ControllerBase
|
|||
[HttpGet("favouriteUsers/{username}")]
|
||||
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.StatusCode(403, "");
|
||||
UserEntity? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.Forbid();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
IEnumerable<User> heartedProfiles = this.database.HeartedProfiles.Include
|
||||
(q => q.HeartedUser)
|
||||
.OrderBy(q => q.HeartedProfileId)
|
||||
.Where(q => q.UserId == targetUser.UserId)
|
||||
.Select(q => q.HeartedUser)
|
||||
List<GameUser> heartedProfiles = await this.database.HeartedProfiles.Include
|
||||
(h => h.HeartedUser)
|
||||
.OrderBy(h => h.HeartedProfileId)
|
||||
.Where(h => h.UserId == targetUser.UserId)
|
||||
.Select(h => h.HeartedUser)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.AsEnumerable();
|
||||
.Select(h => GameUser.CreateFromEntity(h, token.GameVersion))
|
||||
.ToListAsync();
|
||||
|
||||
string response = heartedProfiles.Aggregate(string.Empty, (current, u) => current + u.Serialize(token.GameVersion));
|
||||
int total = await this.database.HeartedProfiles.CountAsync(h => h.UserId == targetUser.UserId);
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.TaggedStringElement("favouriteUsers", response, new Dictionary<string, object>
|
||||
{
|
||||
{ "total", await this.database.HeartedProfiles.CountAsync(q => q.UserId == targetUser.UserId) },
|
||||
{ "hint_start", pageStart + Math.Min(pageSize, 30) },
|
||||
})
|
||||
);
|
||||
return this.Ok(new GenericUserResponse<GameUser>("favouriteUsers", heartedProfiles, total, pageStart + Math.Min(pageSize, 30)));
|
||||
}
|
||||
|
||||
[HttpPost("favourite/user/{username}")]
|
||||
public async Task<IActionResult> AddFavouriteUser(string username)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (heartedUser == null) return this.NotFound();
|
||||
|
||||
await this.database.HeartUser(token.UserId, heartedUser);
|
||||
|
@ -300,9 +281,9 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/user/{username}")]
|
||||
public async Task<IActionResult> RemoveFavouriteUser(string username)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
UserEntity? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (heartedUser == null) return this.NotFound();
|
||||
|
||||
await this.database.UnheartUser(token.UserId, heartedUser);
|
||||
|
@ -313,7 +294,7 @@ public class ListController : ControllerBase
|
|||
#endregion
|
||||
|
||||
#region Filtering
|
||||
enum ListFilterType // used to collapse code that would otherwise be two separate functions
|
||||
internal enum ListFilterType // used to collapse code that would otherwise be two separate functions
|
||||
{
|
||||
Queue,
|
||||
FavouriteSlots,
|
||||
|
@ -337,7 +318,7 @@ public class ListController : ControllerBase
|
|||
};
|
||||
}
|
||||
|
||||
private IQueryable<Slot> filterListByRequest(string? gameFilterType, string? dateFilterType, GameVersion version, string username, ListFilterType filterType)
|
||||
private IQueryable<SlotEntity> filterListByRequest(string? gameFilterType, string? dateFilterType, GameVersion version, string username, ListFilterType filterType)
|
||||
{
|
||||
if (version is GameVersion.LittleBigPlanetPSP or GameVersion.Unknown)
|
||||
{
|
||||
|
@ -358,7 +339,7 @@ public class ListController : ControllerBase
|
|||
|
||||
if (filterType == ListFilterType.Queue)
|
||||
{
|
||||
IQueryable<QueuedLevel> whereQueuedLevels;
|
||||
IQueryable<QueuedLevelEntity> whereQueuedLevels;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
|
@ -374,7 +355,7 @@ public class ListController : ControllerBase
|
|||
return whereQueuedLevels.OrderByDescending(q => q.QueuedLevelId).Include(q => q.Slot.Creator).Select(q => q.Slot).ByGameVersion(gameVersion, false, false, true);
|
||||
}
|
||||
|
||||
IQueryable<HeartedLevel> whereHeartedLevels;
|
||||
IQueryable<HeartedLevelEntity> whereHeartedLevels;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
|
|
|
@ -5,12 +5,12 @@ using LBPUnion.ProjectLighthouse.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Resources;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -37,12 +37,12 @@ public class PublishController : ControllerBase
|
|||
[HttpPost("startPublish")]
|
||||
public async Task<IActionResult> StartPublish()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
Slot? slot = await this.DeserializeBody<Slot>();
|
||||
GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();
|
||||
if (slot == null)
|
||||
{
|
||||
Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish);
|
||||
|
@ -55,7 +55,7 @@ public class PublishController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel;
|
||||
if (slot.Resources?.Length == 0) slot.Resources = new[]{slot.RootLevel,};
|
||||
|
||||
if (slot.Resources == null)
|
||||
{
|
||||
|
@ -63,10 +63,12 @@ public class PublishController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
|
||||
int usedSlots = await this.database.Slots.CountAsync(s => s.CreatorId == token.UserId && s.GameVersion == token.GameVersion);
|
||||
|
||||
// Republish logic
|
||||
if (slot.SlotId != 0)
|
||||
{
|
||||
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||
SlotEntity? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||
if (oldSlot == null)
|
||||
{
|
||||
Logger.Warn("Rejecting level republish, could not find old slot", LogArea.Publish);
|
||||
|
@ -78,18 +80,18 @@ public class PublishController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
}
|
||||
else if (user.GetUsedSlotsForGame(token.GameVersion) > user.EntitledSlots)
|
||||
else if (usedSlots > user.EntitledSlots)
|
||||
{
|
||||
return this.StatusCode(403, "");
|
||||
return this.Forbid();
|
||||
}
|
||||
|
||||
slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here
|
||||
HashSet<string> resources = new(slot.Resources)
|
||||
{
|
||||
slot.IconHash,
|
||||
};
|
||||
resources = resources.Where(hash => !FileHelper.ResourceExists(hash)).ToHashSet();
|
||||
|
||||
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"));
|
||||
return this.Ok(new SlotResourceResponse(resources.ToList()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -98,12 +100,12 @@ public class PublishController : ControllerBase
|
|||
[HttpPost("publish")]
|
||||
public async Task<IActionResult> Publish([FromQuery] string? game)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
Slot? slot = await this.DeserializeBody<Slot>();
|
||||
GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();
|
||||
|
||||
if (slot == null)
|
||||
{
|
||||
|
@ -111,6 +113,12 @@ public class PublishController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
|
||||
if (slot.Resources == null)
|
||||
{
|
||||
Logger.Warn("Rejecting level upload, resource list is null", LogArea.Publish);
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
slot.Description = CensorHelper.FilterMessage(slot.Description);
|
||||
|
||||
if (slot.Description.Length > 512)
|
||||
|
@ -168,7 +176,7 @@ public class PublishController : ControllerBase
|
|||
// Republish logic
|
||||
if (slot.SlotId != 0)
|
||||
{
|
||||
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||
SlotEntity? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||
if (oldSlot == null)
|
||||
{
|
||||
Logger.Warn("Rejecting level republish, wasn't able to find old slot", LogArea.Publish);
|
||||
|
@ -190,36 +198,32 @@ public class PublishController : ControllerBase
|
|||
// Delete the useless rootLevel that lbp3 just uploaded
|
||||
if (slotVersion == GameVersion.LittleBigPlanet3)
|
||||
FileHelper.DeleteResource(slot.RootLevel);
|
||||
|
||||
slot.GameVersion = oldSlot.GameVersion;
|
||||
slot.RootLevel = oldSlot.RootLevel;
|
||||
slot.ResourceCollection = oldSlot.ResourceCollection;
|
||||
else
|
||||
{
|
||||
oldSlot.GameVersion = slot.GameVersion;
|
||||
oldSlot.RootLevel = slot.RootLevel;
|
||||
oldSlot.Resources = slot.Resources;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slot.CreatorId = oldSlot.CreatorId;
|
||||
slot.SlotId = oldSlot.SlotId;
|
||||
oldSlot.Name = slot.Name;
|
||||
oldSlot.Description = slot.Description;
|
||||
oldSlot.Location = slot.Location;
|
||||
oldSlot.IconHash = slot.IconHash;
|
||||
oldSlot.BackgroundHash = slot.BackgroundHash;
|
||||
oldSlot.AuthorLabels = slot.AuthorLabels;
|
||||
oldSlot.Shareable = slot.IsShareable;
|
||||
oldSlot.Resources = slot.Resources;
|
||||
oldSlot.InitiallyLocked = slot.InitiallyLocked;
|
||||
oldSlot.Lbp1Only = slot.IsLbp1Only;
|
||||
oldSlot.IsAdventurePlanet = slot.IsAdventurePlanet;
|
||||
oldSlot.LevelType = slot.LevelType;
|
||||
oldSlot.SubLevel = slot.IsSubLevel;
|
||||
oldSlot.MoveRequired = slot.IsMoveRequired;
|
||||
oldSlot.CrossControllerRequired = slot.IsCrossControlRequired;
|
||||
|
||||
#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;
|
||||
|
||||
#endregion
|
||||
|
||||
slot.FirstUploaded = oldSlot.FirstUploaded;
|
||||
slot.LastUpdated = TimeHelper.TimestampMillis;
|
||||
|
||||
slot.TeamPick = oldSlot.TeamPick;
|
||||
oldSlot.LastUpdated = TimeHelper.TimestampMillis;
|
||||
|
||||
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
||||
{
|
||||
|
@ -227,20 +231,23 @@ public class PublishController : ControllerBase
|
|||
slot.MaximumPlayers = 4;
|
||||
}
|
||||
|
||||
slot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4);
|
||||
slot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4);
|
||||
oldSlot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4);
|
||||
oldSlot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4);
|
||||
|
||||
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
|
||||
await this.database.SaveChangesAsync();
|
||||
return this.Ok(oldSlot.Serialize(token.GameVersion));
|
||||
return this.Ok(SlotBase.CreateFromEntity(oldSlot, this.GetToken()));
|
||||
}
|
||||
|
||||
if (user.GetUsedSlotsForGame(slotVersion) > user.EntitledSlots)
|
||||
int usedSlots = await this.database.Slots.CountAsync(s => s.CreatorId == token.UserId && s.GameVersion == slotVersion);
|
||||
|
||||
if (usedSlots > user.EntitledSlots)
|
||||
{
|
||||
Logger.Warn("Rejecting level upload, too many published slots", LogArea.Publish);
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
SlotEntity slotEntity = SlotBase.ConvertToEntity(slot);
|
||||
|
||||
slot.CreatorId = user.UserId;
|
||||
slot.FirstUploaded = TimeHelper.TimestampMillis;
|
||||
slot.LastUpdated = TimeHelper.TimestampMillis;
|
||||
|
@ -254,7 +261,7 @@ public class PublishController : ControllerBase
|
|||
slot.MinimumPlayers = Math.Clamp(slot.MinimumPlayers, 1, 4);
|
||||
slot.MaximumPlayers = Math.Clamp(slot.MaximumPlayers, 1, 4);
|
||||
|
||||
this.database.Slots.Add(slot);
|
||||
this.database.Slots.Add(slotEntity);
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
if (user.LevelVisibility == PrivacyType.All)
|
||||
|
@ -265,18 +272,18 @@ public class PublishController : ControllerBase
|
|||
|
||||
Logger.Success($"Successfully published level {slot.Name} (id: {slot.SlotId}) by {user.Username} (id: {user.UserId})", LogArea.Publish);
|
||||
|
||||
return this.Ok(slot.Serialize(token.GameVersion));
|
||||
return this.Ok(SlotBase.CreateFromEntity(slotEntity, this.GetToken()));
|
||||
}
|
||||
|
||||
[HttpPost("unpublish/{id:int}")]
|
||||
public async Task<IActionResult> Unpublish(int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
if (slot.CreatorId != token.UserId) return this.StatusCode(403, "");
|
||||
if (slot.CreatorId != token.UserId) return this.Forbid();
|
||||
|
||||
this.database.Slots.Remove(slot);
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -30,15 +30,15 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("rate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
SlotEntity? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.Forbid();
|
||||
|
||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId);
|
||||
RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel
|
||||
ratedLevel = new RatedLevelEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = token.UserId,
|
||||
|
@ -59,15 +59,15 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("dpadrate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.Forbid();
|
||||
|
||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId);
|
||||
RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel
|
||||
ratedLevel = new RatedLevelEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = token.UserId,
|
||||
|
@ -79,7 +79,7 @@ public class ReviewController : ControllerBase
|
|||
|
||||
ratedLevel.Rating = Math.Clamp(rating, -1, 1);
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId);
|
||||
ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId);
|
||||
if (review != null) review.Thumb = ratedLevel.Rating;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
@ -90,20 +90,20 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("postReview/user/{slotId:int}")]
|
||||
public async Task<IActionResult> PostReview(int slotId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
Review? newReview = await this.DeserializeBody<Review>();
|
||||
GameReview? newReview = await this.DeserializeBody<GameReview>();
|
||||
if (newReview == null) return this.BadRequest();
|
||||
|
||||
newReview.Text = CensorHelper.FilterMessage(newReview.Text);
|
||||
|
||||
if (newReview.Text.Length > 512) return this.BadRequest();
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId);
|
||||
ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId);
|
||||
|
||||
if (review == null)
|
||||
{
|
||||
review = new Review
|
||||
review = new ReviewEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
ReviewerId = token.UserId,
|
||||
|
@ -121,10 +121,10 @@ public class ReviewController : ControllerBase
|
|||
review.Timestamp = TimeHelper.TimestampMillis;
|
||||
|
||||
// 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 == token.UserId);
|
||||
RatedLevelEntity? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel
|
||||
ratedLevel = new RatedLevelEntity
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = token.UserId,
|
||||
|
@ -144,58 +144,32 @@ public class ReviewController : ControllerBase
|
|||
[HttpGet("reviewsFor/user/{slotId:int}")]
|
||||
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
SlotEntity? 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)
|
||||
List<GameReview> reviews = await 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(Math.Max(0, pageStart - 1))
|
||||
.Take(pageSize);
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(r => GameReview.CreateFromEntity(r, token))
|
||||
.ToListAsync();
|
||||
|
||||
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 == token.UserId);
|
||||
return current + review.Serialize(yourThumb);
|
||||
}
|
||||
);
|
||||
string response = LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"reviews",
|
||||
inner,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"hint_start", pageStart + pageSize
|
||||
},
|
||||
{
|
||||
"hint", reviewList.LastOrDefault()?.Timestamp ?? 0
|
||||
},
|
||||
}
|
||||
);
|
||||
return this.Ok(response);
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart + Math.Min(pageSize, 30)));
|
||||
}
|
||||
|
||||
[HttpGet("reviewsBy/{username}")]
|
||||
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -205,61 +179,32 @@ public class ReviewController : ControllerBase
|
|||
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
IEnumerable<Review?> reviews = this.database.Reviews.ByGameVersion(gameVersion, true)
|
||||
.Include(r => r.Reviewer)
|
||||
.Include(r => r.Slot)
|
||||
List<GameReview> reviews = await this.database.Reviews.ByGameVersion(gameVersion, true)
|
||||
.Where(r => r.ReviewerId == targetUserId)
|
||||
.OrderByDescending(r => r.Timestamp)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(pageSize);
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(r => GameReview.CreateFromEntity(r, token))
|
||||
.ToListAsync();
|
||||
|
||||
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 == token.UserId);
|
||||
return current + review.Serialize(ratedReview);
|
||||
}
|
||||
);
|
||||
|
||||
string response = LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"reviews",
|
||||
inner,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"hint_start", pageStart
|
||||
},
|
||||
{
|
||||
"hint", reviewList.LastOrDefault()?.Timestamp ?? 0 // Seems to be the timestamp of oldest
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return this.Ok(response);
|
||||
return this.Ok(new ReviewResponse(reviews, reviews.LastOrDefault()?.Timestamp ?? TimeHelper.TimestampMillis, pageStart));
|
||||
}
|
||||
|
||||
[HttpPost("rateReview/user/{slotId:int}/{username}")]
|
||||
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
int reviewerId = await this.database.UserIdFromUsername(username);
|
||||
if (reviewerId == 0) return this.StatusCode(400, "");
|
||||
if (reviewerId == 0) return this.BadRequest();
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
if (review == null) return this.StatusCode(400, "");
|
||||
ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
if (review == null) return this.BadRequest();
|
||||
|
||||
RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId);
|
||||
RatedReviewEntity? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId);
|
||||
if (ratedReview == null)
|
||||
{
|
||||
ratedReview = new RatedReview
|
||||
ratedReview = new RatedReviewEntity
|
||||
{
|
||||
ReviewId = review.ReviewId,
|
||||
UserId = token.UserId,
|
||||
|
@ -301,18 +246,18 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
|
||||
public async Task<IActionResult> DeleteReview(int slotId, string username)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync();
|
||||
if (creatorId == 0) return this.StatusCode(400, "");
|
||||
if (creatorId == 0) return this.BadRequest();
|
||||
|
||||
if (token.UserId != creatorId) return this.StatusCode(403, "");
|
||||
if (token.UserId != creatorId) return this.Unauthorized();
|
||||
|
||||
int reviewerId = await this.database.UserIdFromUsername(username);
|
||||
if (reviewerId == 0) return this.StatusCode(400, "");
|
||||
if (reviewerId == 0) return this.BadRequest();
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
if (review == null) return this.StatusCode(400, "");
|
||||
ReviewEntity? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
if (review == null) return this.BadRequest();
|
||||
|
||||
review.Deleted = true;
|
||||
review.DeletedBy = DeletedBy.LevelAuthor;
|
||||
|
|
|
@ -4,12 +4,12 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -34,7 +34,7 @@ public class ScoreController : ControllerBase
|
|||
[HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")]
|
||||
public async Task<IActionResult> SubmitScore(string slotType, int id, int childId)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
|
@ -44,16 +44,15 @@ public class ScoreController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
|
||||
Score? score = await this.DeserializeBody<Score>();
|
||||
GameScore? score = await this.DeserializeBody<GameScore>();
|
||||
if (score == null)
|
||||
{
|
||||
Logger.Warn($"Rejecting score upload, score is null (slotType={slotType}, slotId={id}, user={username})", LogArea.Score);
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
// This only seems to happens on lbp2 versus levels, not sure why
|
||||
if (score.PlayerIdCollection.Contains(':'))
|
||||
score.PlayerIdCollection = score.PlayerIdCollection.Replace(':', ',');
|
||||
// Workaround for parsing player ids of versus levels
|
||||
if (score.PlayerIds.Length == 1 && score.PlayerIds[0].Contains(':')) score.PlayerIds = score.PlayerIds[0].Split(":");
|
||||
|
||||
if (score.PlayerIds.Length == 0)
|
||||
{
|
||||
|
@ -89,15 +88,14 @@ public class ScoreController : ControllerBase
|
|||
|
||||
SanitizationHelper.SanitizeStringsInClass(score);
|
||||
|
||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
int slotId = id;
|
||||
|
||||
score.SlotId = id;
|
||||
score.ChildSlotId = childId;
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null)
|
||||
{
|
||||
Logger.Warn($"Rejecting score upload, slot is null (slotId={score.SlotId}, slotType={slotType}, reqId={id}, user={username})", LogArea.Score);
|
||||
Logger.Warn($"Rejecting score upload, slot is null (slotId={slotId}, slotType={slotType}, reqId={id}, user={username})", LogArea.Score);
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
|
@ -115,46 +113,56 @@ public class ScoreController : ControllerBase
|
|||
break;
|
||||
case GameVersion.LittleBigPlanetPSP:
|
||||
case GameVersion.Unknown:
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
default:
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
Score playerScore = new()
|
||||
{
|
||||
PlayerIdCollection = string.Join(',', score.PlayerIds),
|
||||
Type = score.Type,
|
||||
Points = score.Points,
|
||||
SlotId = score.SlotId,
|
||||
ChildSlotId = score.ChildSlotId,
|
||||
};
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == playerScore.SlotId)
|
||||
string playerIdCollection = string.Join(',', score.PlayerIds);
|
||||
|
||||
ScoreEntity? existingScore = await this.database.Scores.Where(s => s.SlotId == slot.SlotId)
|
||||
.Where(s => s.ChildSlotId == 0 || s.ChildSlotId == childId)
|
||||
.Where(s => s.PlayerIdCollection == playerScore.PlayerIdCollection)
|
||||
.Where(s => s.Type == playerScore.Type);
|
||||
if (existingScore.Any())
|
||||
.Where(s => s.PlayerIdCollection == playerIdCollection)
|
||||
.Where(s => s.Type == score.Type)
|
||||
.FirstOrDefaultAsync();
|
||||
if (existingScore != null)
|
||||
{
|
||||
Score first = existingScore.First(s => s.SlotId == playerScore.SlotId);
|
||||
playerScore.ScoreId = first.ScoreId;
|
||||
playerScore.Points = Math.Max(first.Points, playerScore.Points);
|
||||
this.database.Entry(first).CurrentValues.SetValues(playerScore);
|
||||
existingScore.Points = Math.Max(existingScore.Points, score.Points);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScoreEntity playerScore = new()
|
||||
{
|
||||
PlayerIdCollection = playerIdCollection,
|
||||
Type = score.Type,
|
||||
Points = score.Points,
|
||||
SlotId = slotId,
|
||||
ChildSlotId = childId,
|
||||
};
|
||||
this.database.Scores.Add(playerScore);
|
||||
}
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
string myRanking = this.getScores(score.SlotId, score.Type, username, -1, 5, "scoreboardSegment", childId: score.ChildSlotId);
|
||||
|
||||
return this.Ok(myRanking);
|
||||
return this.Ok(this.getScores(new LeaderboardOptions
|
||||
{
|
||||
RootName = "scoreboardSegment",
|
||||
PageSize = 5,
|
||||
PageStart = -1,
|
||||
SlotId = slotId,
|
||||
ChildSlotId = childId,
|
||||
ScoreType = score.Type,
|
||||
TargetUsername = username,
|
||||
TargetPlayerIds = null,
|
||||
}));
|
||||
}
|
||||
|
||||
[HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")]
|
||||
[HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")]
|
||||
public async Task<IActionResult> FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -180,7 +188,17 @@ public class ScoreController : ControllerBase
|
|||
if (friendUsername != null) friendNames.Add(friendUsername);
|
||||
}
|
||||
|
||||
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, "scores", friendNames.ToArray(), childId));
|
||||
return this.Ok(this.getScores(new LeaderboardOptions
|
||||
{
|
||||
RootName = "scores",
|
||||
PageSize = pageSize,
|
||||
PageStart = pageStart,
|
||||
SlotId = slotId,
|
||||
ChildSlotId = childId,
|
||||
ScoreType = type,
|
||||
TargetUsername = username,
|
||||
TargetPlayerIds = friendNames.ToArray(),
|
||||
}));
|
||||
}
|
||||
|
||||
[HttpGet("topscores/{slotType}/{slotId:int}/{type:int}")]
|
||||
|
@ -188,7 +206,7 @@ public class ScoreController : ControllerBase
|
|||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public async Task<IActionResult> TopScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -198,79 +216,60 @@ public class ScoreController : ControllerBase
|
|||
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, childId: childId));
|
||||
return this.Ok(this.getScores(new LeaderboardOptions
|
||||
{
|
||||
RootName = "scores",
|
||||
PageSize = pageSize,
|
||||
PageStart = pageStart,
|
||||
SlotId = slotId,
|
||||
ChildSlotId = childId,
|
||||
ScoreType = type,
|
||||
TargetUsername = username,
|
||||
TargetPlayerIds = null,
|
||||
}));
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
private string getScores
|
||||
(
|
||||
int slotId,
|
||||
int type,
|
||||
string username,
|
||||
int pageStart = -1,
|
||||
int pageSize = 5,
|
||||
string rootName = "scores",
|
||||
string[]? playerIds = null,
|
||||
int? childId = 0
|
||||
)
|
||||
private class LeaderboardOptions
|
||||
{
|
||||
public int SlotId { get; set; }
|
||||
public int ScoreType { get; set; }
|
||||
public string TargetUsername { get; set; } = "";
|
||||
public int PageStart { get; set; } = -1;
|
||||
public int PageSize { get; set; } = 5;
|
||||
public string RootName { get; set; } = "scores";
|
||||
public string[]? TargetPlayerIds;
|
||||
public int? ChildSlotId;
|
||||
}
|
||||
|
||||
private ScoreboardResponse getScores(LeaderboardOptions options)
|
||||
{
|
||||
|
||||
// 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)
|
||||
.Where(s => s.ChildSlotId == 0 || s.ChildSlotId == childId)
|
||||
var rankedScores = this.database.Scores.Where(s => s.SlotId == options.SlotId && s.Type == options.ScoreType)
|
||||
.Where(s => s.ChildSlotId == 0 || s.ChildSlotId == options.ChildSlotId)
|
||||
.AsEnumerable()
|
||||
.Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Split(",").Contains(id)))
|
||||
.Where(s => options.TargetPlayerIds == null ||
|
||||
options.TargetPlayerIds.Any(id => s.PlayerIdCollection.Split(",").Contains(id)))
|
||||
.OrderByDescending(s => s.Points)
|
||||
.ThenBy(s => s.ScoreId)
|
||||
.ToList()
|
||||
.Select
|
||||
(
|
||||
(s, rank) => new
|
||||
{
|
||||
Score = s,
|
||||
Rank = rank + 1,
|
||||
}
|
||||
);
|
||||
.Select((s, rank) => new
|
||||
{
|
||||
Score = s,
|
||||
Rank = rank + 1,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
||||
// 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.Split(",").Contains(username)).MaxBy(rs => rs.Score.Points);
|
||||
var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Split(",").Contains(options.TargetUsername)).MaxBy(rs => rs.Score.Points);
|
||||
|
||||
// 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));
|
||||
var pagedScores = rankedScores.Skip(options.PageStart != -1 || myScore == null ? options.PageStart - 1 : myScore.Rank - 3).Take(Math.Min(options.PageSize, 30));
|
||||
|
||||
string serializedScores = pagedScores.Aggregate
|
||||
(
|
||||
string.Empty,
|
||||
(current, rs) =>
|
||||
{
|
||||
rs.Score.Rank = rs.Rank;
|
||||
return current + rs.Score.Serialize();
|
||||
}
|
||||
);
|
||||
List<GameScore> gameScores = pagedScores.Select(ps => GameScore.CreateFromEntity(ps.Score, ps.Rank)).ToList();
|
||||
|
||||
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;
|
||||
return new ScoreboardResponse(options.RootName, gameScores, myScore?.Score.Points ?? 0, myScore?.Rank ?? 0, rankedScores.Count);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -35,7 +35,7 @@ public class SearchController : ControllerBase
|
|||
string? keyName = "slots"
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class SearchController : ControllerBase
|
|||
|
||||
string[] keywords = query.Split(" ");
|
||||
|
||||
IQueryable<Slot> 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)
|
||||
.OrderBy(s => !s.TeamPick)
|
||||
.ThenByDescending(s => s.FirstUploaded)
|
||||
|
@ -61,11 +61,12 @@ public class SearchController : ControllerBase
|
|||
s.SlotId.ToString().Equals(keyword)
|
||||
);
|
||||
|
||||
List<Slot> slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)).Take(Math.Min(pageSize, 30)).ToListAsync();
|
||||
List<SlotBase> slots = await dbQuery.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, this.GetToken()))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement(keyName, response, "total", dbQuery.Count()));
|
||||
return this.Ok(new GenericSlotResponse(keyName, slots, await dbQuery.CountAsync(), 0));
|
||||
}
|
||||
|
||||
// /LITTLEBIGPLANETPS3_XML?pageStart=1&pageSize=10&resultTypes[]=slot&resultTypes[]=playlist&resultTypes[]=user&adventure=dontCare&textFilter=qwer
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#nullable enable
|
||||
using System.Security.Cryptography;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -28,113 +28,102 @@ public class SlotsController : ControllerBase
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
private static string generateSlotsResponse(string slotAggregate, int start, int total) =>
|
||||
LbpSerializer.TaggedStringElement("slots",
|
||||
slotAggregate,
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"hint_start", start
|
||||
},
|
||||
{
|
||||
"total", total
|
||||
},
|
||||
});
|
||||
|
||||
[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)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
int usedSlots = this.database.Slots.Count(s => s.CreatorId == targetUserId);
|
||||
|
||||
string response = Enumerable.Aggregate
|
||||
(
|
||||
this.database.Slots.Where(s => s.CreatorId == targetUserId)
|
||||
.ByGameVersion(gameVersion, token.UserId == targetUserId, true)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, usedSlots)),
|
||||
string.Empty,
|
||||
(current, slot) => current + slot.Serialize(token.GameVersion)
|
||||
);
|
||||
List<SlotBase> slots = await this.database.Slots.Where(s => s.CreatorId == targetUserId)
|
||||
.ByGameVersion(token.GameVersion, token.UserId == targetUserId)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, usedSlots))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
int start = pageStart + Math.Min(pageSize, usedSlots);
|
||||
int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId);
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
|
||||
return this.Ok(new GenericSlotResponse("slots", slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slotList")]
|
||||
public async Task<IActionResult> GetSlotListAlt([FromQuery] int[] s)
|
||||
public async Task<IActionResult> GetSlotListAlt([FromQuery(Name = "s")] int[] slotIds)
|
||||
{
|
||||
List<string?> serializedSlots = new();
|
||||
foreach (int slotId in s)
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
List<SlotBase> slots = new();
|
||||
foreach (int slotId in slotIds)
|
||||
{
|
||||
Slot? 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.Include(t => t.Creator).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();
|
||||
if (slot == null)
|
||||
{
|
||||
serializedSlots.Add($"<slot type=\"developer\"><id>{slotId}</id></slot>");
|
||||
slots.Add(new GameDeveloperSlot
|
||||
{
|
||||
SlotId = slotId,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
serializedSlots.Add(slot.Serialize());
|
||||
|
||||
slots.Add(SlotBase.CreateFromEntity(slot, token));
|
||||
}
|
||||
string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => slot == null ? current : current + slot);
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", serialized, "total", serializedSlots.Count));
|
||||
return this.Ok(new GenericSlotResponse(slots, slots.Count, 0));
|
||||
}
|
||||
|
||||
[HttpGet("slots/developer")]
|
||||
public async Task<IActionResult> StoryPlayers()
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
List<int> activeSlotIds = RoomHelper.Rooms.Where(r => r.Slot.SlotType == SlotType.Developer).Select(r => r.Slot.SlotId).ToList();
|
||||
|
||||
List<string> serializedSlots = new();
|
||||
List<SlotBase> slots = new();
|
||||
|
||||
foreach (int id in activeSlotIds)
|
||||
{
|
||||
int placeholderSlotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == placeholderSlotId);
|
||||
serializedSlots.Add(slot.SerializeDevSlot());
|
||||
SlotEntity slot = await this.database.Slots.FirstAsync(s => s.SlotId == placeholderSlotId);
|
||||
|
||||
slots.Add(SlotBase.CreateFromEntity(slot, token));
|
||||
}
|
||||
|
||||
string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => current + slot);
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("slots", serialized));
|
||||
return this.Ok(new GenericSlotResponse(slots));
|
||||
}
|
||||
|
||||
[HttpGet("s/developer/{id:int}")]
|
||||
public async Task<IActionResult> SDev(int id)
|
||||
{
|
||||
int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId);
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
return this.Ok(slot.SerializeDevSlot());
|
||||
int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
SlotEntity slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId);
|
||||
|
||||
return this.Ok(SlotBase.CreateFromEntity(slot, token));
|
||||
}
|
||||
|
||||
[HttpGet("s/user/{id:int}")]
|
||||
public async Task<IActionResult> SUser(int id)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
Slot? slot = await this.database.Slots.ByGameVersion(gameVersion, true, true).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
SlotEntity? 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 == token.UserId);
|
||||
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == token.UserId);
|
||||
Review? review = await this.database.Reviews.Include(r => r.Slot).FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == token.UserId);
|
||||
return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review, true));
|
||||
return this.Ok(SlotBase.CreateFromEntity(slot, token, SerializationMode.Full));
|
||||
}
|
||||
|
||||
[HttpGet("slots/cool")]
|
||||
|
@ -163,27 +152,28 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots")]
|
||||
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
List<SlotBase> slots = await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/like/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -191,10 +181,10 @@ public class SlotsController : ControllerBase
|
|||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
Slot? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
SlotEntity? targetSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (targetSlot == null) return this.BadRequest();
|
||||
|
||||
string[] tags = targetSlot.LevelTags;
|
||||
string[] tags = targetSlot.LevelTags(this.database);
|
||||
|
||||
List<int> slotIdsWithTag = this.database.RatedLevels
|
||||
.Where(r => r.TagLBP1.Length > 0)
|
||||
|
@ -202,105 +192,108 @@ public class SlotsController : ControllerBase
|
|||
.Select(r => r.SlotId)
|
||||
.ToList();
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
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));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = slotIdsWithTag.Count;
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/highestRated")]
|
||||
public async Task<IActionResult> HighestRatedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IEnumerable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.AsEnumerable()
|
||||
List<SlotBase> slots = await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.ToAsyncEnumerable()
|
||||
.OrderByDescending(s => s.RatingLBP1)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCount(this.database);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/tag")]
|
||||
public async Task<IActionResult> SimilarSlots([FromQuery] string tag, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
List<int> slotIdsWithTag = await this.database.RatedLevels.Where(r => r.TagLBP1.Length > 0)
|
||||
.Where(r => r.TagLBP1 == tag)
|
||||
.Select(s => s.SlotId)
|
||||
.ToListAsync();
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId))
|
||||
.ByGameVersion(gameVersion, false, true)
|
||||
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));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = slotIdsWithTag.Count;
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mmpicks")]
|
||||
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.Where(s => s.TeamPick)
|
||||
.ByGameVersion(gameVersion, false, true)
|
||||
List<SlotBase> slots = await this.database.Slots.Where(s => s.TeamPick)
|
||||
.ByGameVersion(token.GameVersion, false, true)
|
||||
.OrderByDescending(s => s.LastUpdated)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.TeamPickCountForGame(this.database, token.GameVersion);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/lbp2luckydip")]
|
||||
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IEnumerable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30));
|
||||
List<SlotBase> slots = await this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.OrderBy(_ => EF.Functions.Random())
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/thumbs")]
|
||||
|
@ -314,24 +307,25 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsEnumerable()
|
||||
List<SlotBase> slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsAsyncEnumerable()
|
||||
.OrderByDescending(s => s.Thumbsup)
|
||||
.ThenBy(_ => rand.Next())
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostUniquePlays")]
|
||||
|
@ -345,20 +339,19 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsEnumerable()
|
||||
.OrderByDescending
|
||||
(
|
||||
List<SlotBase> slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsAsyncEnumerable()
|
||||
.OrderByDescending(
|
||||
// probably not the best way to do this?
|
||||
s =>
|
||||
{
|
||||
return this.getGameFilter(gameFilterType, token.GameVersion) switch
|
||||
return getGameFilter(gameFilterType, token.GameVersion) switch
|
||||
{
|
||||
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
|
||||
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
|
||||
|
@ -366,17 +359,17 @@ public class SlotsController : ControllerBase
|
|||
GameVersion.LittleBigPlanetVita => s.PlaysLBP2Unique,
|
||||
_ => s.PlaysUnique,
|
||||
};
|
||||
}
|
||||
)
|
||||
})
|
||||
.ThenBy(_ => rand.Next())
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
[HttpGet("slots/mostHearted")]
|
||||
|
@ -390,24 +383,23 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
Random rand = new();
|
||||
|
||||
IEnumerable<Slot> slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsEnumerable()
|
||||
List<SlotBase> slots = await this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion)
|
||||
.AsAsyncEnumerable()
|
||||
.OrderByDescending(s => s.Hearts)
|
||||
.ThenBy(_ => rand.Next())
|
||||
.ThenBy(_ => RandomNumberGenerator.GetInt32(int.MaxValue))
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
.Take(Math.Min(pageSize, 30))
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.ToListAsync();
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = await StatisticsHelper.SlotCountForGame(this.database, token.GameVersion);
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
// /slots/busiest?pageStart=1&pageSize=30&gameFilterType=both&players=1&move=true
|
||||
|
@ -421,7 +413,7 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] bool? move = null
|
||||
)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -447,42 +439,45 @@ public class SlotsController : ControllerBase
|
|||
.OrderByDescending(kvp => kvp.Value)
|
||||
.Select(kvp => kvp.Key);
|
||||
|
||||
List<Slot> slots = new();
|
||||
List<SlotBase> slots = new();
|
||||
|
||||
foreach (int slotId in orderedPlayersBySlotId)
|
||||
{
|
||||
Slot? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
SlotBase? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.Where(s => s.SlotId == slotId)
|
||||
.Select(s => SlotBase.CreateFromEntity(s, token))
|
||||
.FirstOrDefaultAsync();
|
||||
if (slot == null) continue; // shouldn't happen ever unless the room is borked
|
||||
|
||||
slots.Add(slot);
|
||||
}
|
||||
|
||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
int start = pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots);
|
||||
int total = playersBySlotId.Count;
|
||||
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
return this.Ok(new GenericSlotResponse(slots, total, start));
|
||||
}
|
||||
|
||||
|
||||
private GameVersion getGameFilter(string? gameFilterType, GameVersion version)
|
||||
private static GameVersion getGameFilter(string? gameFilterType, GameVersion version)
|
||||
{
|
||||
if (version == GameVersion.LittleBigPlanetVita) return GameVersion.LittleBigPlanetVita;
|
||||
if (version == GameVersion.LittleBigPlanetPSP) return GameVersion.LittleBigPlanetPSP;
|
||||
|
||||
return gameFilterType switch
|
||||
return version switch
|
||||
{
|
||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||
null => GameVersion.LittleBigPlanet1,
|
||||
_ => GameVersion.Unknown,
|
||||
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<Slot> filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version)
|
||||
private IQueryable<SlotEntity> filterByRequest(string? gameFilterType, string? dateFilterType, GameVersion version)
|
||||
{
|
||||
if (version == GameVersion.LittleBigPlanetVita || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown)
|
||||
{
|
||||
|
@ -498,9 +493,9 @@ public class SlotsController : ControllerBase
|
|||
_ => 0,
|
||||
};
|
||||
|
||||
GameVersion gameVersion = this.getGameFilter(gameFilterType, version);
|
||||
GameVersion gameVersion = getGameFilter(gameFilterType, version);
|
||||
|
||||
IQueryable<Slot> whereSlots;
|
||||
IQueryable<SlotEntity> whereSlots;
|
||||
|
||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||
if (gameFilterType == "both")
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
|
@ -33,11 +33,7 @@ public class StatisticsController : ControllerBase
|
|||
int totalSlotCount = await StatisticsHelper.SlotCountForGame(this.database, this.GetToken().GameVersion);
|
||||
int mmPicksCount = await StatisticsHelper.TeamPickCountForGame(this.database, this.GetToken().GameVersion);
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
LbpSerializer.StringElement
|
||||
("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount))
|
||||
);
|
||||
return this.Ok(new PlanetStatsResponse(totalSlotCount, mmPicksCount));
|
||||
}
|
||||
|
||||
[HttpGet("planetStats/totalLevelCount")]
|
||||
|
|
|
@ -4,12 +4,14 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
||||
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 LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -30,57 +32,41 @@ public class UserController : ControllerBase
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
private async Task<string?> getSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
|
||||
{
|
||||
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
return user?.Serialize(gameVersion);
|
||||
}
|
||||
|
||||
private async Task<string?> getSerializedUserPicture(string username)
|
||||
{
|
||||
// use an anonymous type to only fetch certain columns
|
||||
var partialUser = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select(u => new
|
||||
{
|
||||
u.Username,
|
||||
u.IconHash,
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
if (partialUser == null) return null;
|
||||
|
||||
string user = LbpSerializer.TaggedStringElement("npHandle", partialUser.Username, "icon", partialUser.IconHash);
|
||||
return LbpSerializer.TaggedStringElement("user", user, "type", "user");
|
||||
}
|
||||
|
||||
[HttpGet("user/{username}")]
|
||||
public async Task<IActionResult> GetUser(string username)
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
string? user = await this.getSerializedUser(username, token.GameVersion);
|
||||
UserEntity? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (user == null) return this.NotFound();
|
||||
|
||||
return this.Ok(user);
|
||||
return this.Ok(GameUser.CreateFromEntity(user, this.GetToken().GameVersion));
|
||||
}
|
||||
|
||||
[HttpGet("users")]
|
||||
public async Task<IActionResult> GetUserAlt([FromQuery] string[] u)
|
||||
public async Task<IActionResult> GetUserAlt([FromQuery(Name = "u")] string[] userList)
|
||||
{
|
||||
List<string?> serializedUsers = new();
|
||||
foreach (string userId in u) serializedUsers.Add(await this.getSerializedUserPicture(userId));
|
||||
List<MinimalUserProfile> minimalUserList = new();
|
||||
foreach (string username in userList)
|
||||
{
|
||||
MinimalUserProfile? profile = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select(u => new MinimalUserProfile
|
||||
{
|
||||
UserHandle = new NpHandle(u.Username, u.IconHash),
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
if (profile == null) continue;
|
||||
minimalUserList.Add(profile);
|
||||
}
|
||||
|
||||
string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user);
|
||||
|
||||
return this.Ok(LbpSerializer.StringElement("users", serialized));
|
||||
return this.Ok(new MinimalUserListResponse(minimalUserList));
|
||||
}
|
||||
|
||||
[HttpPost("updateUser")]
|
||||
public async Task<IActionResult> UpdateUser()
|
||||
{
|
||||
GameToken token = this.GetToken();
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
UserUpdate? update = await this.DeserializeBody<UserUpdate>("updateUser", "user");
|
||||
|
||||
|
@ -118,17 +104,17 @@ public class UserController : ControllerBase
|
|||
|
||||
if (update.Slots != null)
|
||||
{
|
||||
update.Slots = update.Slots.Where(s => s.Type == SlotType.User)
|
||||
.Where(s => s.Location != null)
|
||||
.Where(s => s.SlotId != 0).ToList();
|
||||
foreach (UserUpdateSlot? updateSlot in update.Slots)
|
||||
{
|
||||
// ReSharper disable once MergeIntoNegatedPattern
|
||||
if (updateSlot.Type != SlotType.User || updateSlot.Location == null || updateSlot.SlotId == 0) continue;
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId);
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId);
|
||||
if (slot == null) continue;
|
||||
|
||||
if (slot.CreatorId != token.UserId) continue;
|
||||
|
||||
slot.Location = updateSlot.Location;
|
||||
slot.Location = updateSlot.Location!;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +145,11 @@ public class UserController : ControllerBase
|
|||
case GameVersion.Unknown:
|
||||
default: // The rest do not support custom earths.
|
||||
{
|
||||
throw new ArgumentException($"invalid gameVersion {token.GameVersion} for setting earth");
|
||||
string bodyString = await this.ReadBodyAsync();
|
||||
Logger.Warn($"User with invalid gameVersion '{token.GameVersion}' tried to set earth hash: \n" +
|
||||
$"body: '{bodyString}'",
|
||||
LogArea.Resources);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,10 +160,11 @@ public class UserController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpPost("update_my_pins")]
|
||||
[Produces("text/json")]
|
||||
public async Task<IActionResult> UpdateMyPins()
|
||||
{
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
UserEntity? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
string bodyString = await this.ReadBodyAsync();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ public class SetLastContactMiddleware : MiddlewareDBContext
|
|||
if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML"))
|
||||
{
|
||||
// We begin by grabbing a token from the request, if this is a LBPPS3_XML request of course.
|
||||
GameToken? gameToken = await database.GameTokenFromRequest(context.Request);
|
||||
GameTokenEntity? gameToken = await database.GameTokenFromRequest(context.Request);
|
||||
|
||||
if (gameToken?.GameVersion == GameVersion.LittleBigPlanet1)
|
||||
// Ignore UserFromGameToken null because user must exist for a token to exist
|
||||
|
|
|
@ -42,7 +42,7 @@ public class GameServerStartup
|
|||
(
|
||||
options =>
|
||||
{
|
||||
options.OutputFormatters.Add(new XmlOutputFormatter());
|
||||
options.OutputFormatters.Add(new LbpOutputFormatter());
|
||||
options.OutputFormatters.Add(new JsonOutputFormatter());
|
||||
}
|
||||
);
|
||||
|
|
|
@ -35,7 +35,7 @@ public class TokenAuthHandler : AuthenticationHandler<AuthenticationSchemeOption
|
|||
{
|
||||
if (!this.Context.Request.Cookies.ContainsKey(cookie)) return AuthenticateResult.Fail("No auth cookie");
|
||||
|
||||
GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request);
|
||||
GameTokenEntity? gameToken = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (gameToken == null) return AuthenticateResult.Fail("No game token");
|
||||
|
||||
this.Context.Items["Token"] = gameToken;
|
||||
|
|
|
@ -20,6 +20,6 @@ public static class CategoryHelper
|
|||
Categories.Add(new LuckyDipCategory());
|
||||
|
||||
using DatabaseContext database = new();
|
||||
foreach (DatabaseCategory category in database.CustomCategories) Categories.Add(new CustomCategory(category));
|
||||
foreach (DatabaseCategoryEntity category in database.CustomCategories) Categories.Add(new CustomCategory(category));
|
||||
}
|
||||
}
|
|
@ -2,18 +2,19 @@
|
|||
using System.Diagnostics;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
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 Slot? GetPreviewSlot(DatabaseContext database, User user);
|
||||
public override Slot? GetPreviewSlot(DatabaseContext database)
|
||||
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);
|
||||
|
@ -22,7 +23,7 @@ public abstract class CategoryWithUser : Category
|
|||
return null;
|
||||
}
|
||||
|
||||
public abstract int GetTotalSlots(DatabaseContext database, User user);
|
||||
public abstract int GetTotalSlots(DatabaseContext database, UserEntity user);
|
||||
public override int GetTotalSlots(DatabaseContext database)
|
||||
{
|
||||
#if DEBUG
|
||||
|
@ -32,14 +33,14 @@ public abstract class CategoryWithUser : Category
|
|||
return -1;
|
||||
}
|
||||
|
||||
public abstract IQueryable<Slot> GetSlots(DatabaseContext database, User user, int pageStart, int pageSize);
|
||||
public override IList<Slot> GetSlots(DatabaseContext database, int pageStart, int pageSize)
|
||||
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<Slot>();
|
||||
return new List<SlotEntity>();
|
||||
}
|
||||
|
||||
public new string Serialize(DatabaseContext database)
|
||||
|
@ -48,35 +49,13 @@ public abstract class CategoryWithUser : Category
|
|||
return string.Empty;
|
||||
}
|
||||
|
||||
public string Serialize(DatabaseContext database, User user)
|
||||
public GameCategory Serialize(DatabaseContext database, UserEntity user)
|
||||
{
|
||||
Slot? previewSlot = this.GetPreviewSlot(database, user);
|
||||
|
||||
string previewResults = "";
|
||||
if (previewSlot != null)
|
||||
previewResults = LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"results",
|
||||
previewSlot.Serialize(),
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"total", this.GetTotalSlots(database, user)
|
||||
},
|
||||
{
|
||||
"hint_start", "2"
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return LbpSerializer.StringElement
|
||||
(
|
||||
"category",
|
||||
LbpSerializer.StringElement("name", this.Name) +
|
||||
LbpSerializer.StringElement("description", this.Description) +
|
||||
LbpSerializer.StringElement("url", this.IngameEndpoint) +
|
||||
(previewSlot == null ? "" : previewResults) +
|
||||
LbpSerializer.StringElement("icon", this.IconHash)
|
||||
);
|
||||
List<SlotBase> slots = new()
|
||||
{
|
||||
SlotBase.CreateFromEntity(this.GetPreviewSlot(database, user), GameVersion.LittleBigPlanet3, user.UserId),
|
||||
};
|
||||
int totalSlots = this.GetTotalSlots(database, user);
|
||||
return GameCategory.CreateFromEntity(this, new GenericSlotResponse(slots, totalSlots, 2));
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ public class CustomCategory : Category
|
|||
this.SlotIds = slotIds.ToList();
|
||||
}
|
||||
|
||||
public CustomCategory(DatabaseCategory category)
|
||||
public CustomCategory(DatabaseCategoryEntity category)
|
||||
{
|
||||
this.Name = category.Name;
|
||||
this.Description = category.Description;
|
||||
|
@ -35,8 +35,8 @@ public class CustomCategory : Category
|
|||
public sealed override string Description { get; set; }
|
||||
public sealed override string IconHash { get; set; }
|
||||
public sealed override string Endpoint { get; set; }
|
||||
public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]);
|
||||
public override IQueryable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.FirstOrDefault(s => s.SlotId == this.SlotIds[0]);
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3).Where(s => this.SlotIds.Contains(s.SlotId));
|
||||
public override int GetTotalSlots(DatabaseContext database) => this.SlotIds.Count;
|
||||
|
|
|
@ -15,7 +15,7 @@ public class HeartedCategory : CategoryWithUser
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database, User 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)
|
||||
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3)
|
||||
.OrderByDescending(h => h.HeartedLevelId)
|
||||
|
@ -24,7 +24,7 @@ public class HeartedCategory : CategoryWithUser
|
|||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.FirstOrDefault();
|
||||
|
||||
public override IQueryable<Slot> GetSlots(DatabaseContext database, User 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)
|
||||
.Where(h => h.Slot.Type == SlotType.User && !h.Slot.Hidden && h.Slot.GameVersion <= GameVersion.LittleBigPlanet3)
|
||||
.OrderByDescending(h => h.HeartedLevelId)
|
||||
|
@ -34,5 +34,5 @@ public class HeartedCategory : CategoryWithUser
|
|||
.Skip(Math.Max(0, pageStart))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
|
||||
public override int GetTotalSlots(DatabaseContext database, User user) => database.HeartedLevels.Count(h => h.UserId == user.UserId);
|
||||
public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.HeartedLevels.Count(h => h.UserId == user.UserId);
|
||||
}
|
|
@ -14,8 +14,8 @@ public class HighestRatedCategory : Category
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Thumbsup);
|
||||
public override IEnumerable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Thumbsup);
|
||||
public override IEnumerable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.AsEnumerable()
|
||||
|
|
|
@ -14,8 +14,8 @@ public class LuckyDipCategory : Category
|
|||
public override string Description { get; set; } = "Randomized uploaded content";
|
||||
public override string IconHash { get; set; } = "g820605";
|
||||
public override string Endpoint { get; set; } = "lbp2luckydip";
|
||||
public override Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault();
|
||||
public override IQueryable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(_ => EF.Functions.Random()).FirstOrDefault();
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.OrderByDescending(_ => EF.Functions.Random())
|
||||
|
|
|
@ -14,8 +14,8 @@ public class MostHeartedCategory : Category
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Hearts);
|
||||
public override IEnumerable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).AsEnumerable().MaxBy(s => s.Hearts);
|
||||
public override IEnumerable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.AsEnumerable()
|
||||
|
|
|
@ -13,12 +13,12 @@ public class MostPlayedCategory : Category
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database) => database.Slots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots
|
||||
.Where(s => s.Type == SlotType.User)
|
||||
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
|
||||
.ThenByDescending(s => s.PlaysLBP1 + s.PlaysLBP2 + s.PlaysLBP3)
|
||||
.FirstOrDefault();
|
||||
public override IQueryable<Slot> GetSlots
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.OrderByDescending(s => s.PlaysLBP1Unique + s.PlaysLBP2Unique + s.PlaysLBP3Unique)
|
||||
|
|
|
@ -13,8 +13,8 @@ public class NewestLevelsCategory : Category
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault();
|
||||
public override IQueryable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault();
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
|
|
|
@ -15,7 +15,7 @@ public class QueueCategory : CategoryWithUser
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database, User user)
|
||||
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)
|
||||
|
@ -24,7 +24,7 @@ public class QueueCategory : CategoryWithUser
|
|||
.ByGameVersion(GameVersion.LittleBigPlanet3, false, false, true)
|
||||
.FirstOrDefault();
|
||||
|
||||
public override IQueryable<Slot> GetSlots(DatabaseContext database, User user, int pageStart, int pageSize)
|
||||
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)
|
||||
.OrderByDescending(q => q.QueuedLevelId)
|
||||
|
@ -34,5 +34,5 @@ public class QueueCategory : CategoryWithUser
|
|||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 20));
|
||||
|
||||
public override int GetTotalSlots(DatabaseContext database, User user) => database.QueuedLevels.Count(q => q.UserId == user.UserId);
|
||||
public override int GetTotalSlots(DatabaseContext database, UserEntity user) => database.QueuedLevels.Count(q => q.UserId == user.UserId);
|
||||
}
|
|
@ -13,8 +13,8 @@ public class TeamPicksCategory : Category
|
|||
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 Slot? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick);
|
||||
public override IQueryable<Slot> GetSlots
|
||||
public override SlotEntity? GetPreviewSlot(DatabaseContext database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault(s => s.TeamPick);
|
||||
public override IQueryable<SlotEntity> GetSlots
|
||||
(DatabaseContext database, int pageStart, int pageSize)
|
||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||
.OrderByDescending(s => s.FirstUploaded)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc;
|
||||
|
||||
|
@ -9,7 +10,7 @@ namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Misc;
|
|||
/// </summary>
|
||||
[XmlRoot("resources")]
|
||||
[XmlType("resources")]
|
||||
public class ResourceList
|
||||
public class ResourceList : ILbpSerializable
|
||||
{
|
||||
[XmlElement("resource")]
|
||||
public string[]? Resources;
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
||||
|
||||
[Keyless]
|
||||
public class ClientsConnected
|
||||
{
|
||||
public bool Lbp1 { get; set; }
|
||||
public bool Lbp2 { get; set; }
|
||||
public bool LbpMe { get; set; }
|
||||
public bool Lbp3Ps3 { get; set; }
|
||||
public bool Lbp3Ps4 { get; set; }
|
||||
|
||||
public string Serialize()
|
||||
=> LbpSerializer.StringElement
|
||||
(
|
||||
"clientsConnected",
|
||||
LbpSerializer.StringElement("lbp1", this.Lbp1) +
|
||||
LbpSerializer.StringElement("lbp2", this.Lbp2) +
|
||||
LbpSerializer.StringElement("lbpme", this.LbpMe) +
|
||||
LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) +
|
||||
LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4)
|
||||
);
|
||||
}
|
|
@ -1,24 +1,16 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
||||
|
||||
[XmlRoot("privacySettings")]
|
||||
[XmlType("privacySettings")]
|
||||
public class PrivacySettings
|
||||
public class PrivacySettings : ILbpSerializable
|
||||
{
|
||||
[XmlElement("levelVisiblity")]
|
||||
public string? LevelVisibility { get; set; }
|
||||
|
||||
[XmlElement("profileVisiblity")]
|
||||
public string? ProfileVisibility { get; set; }
|
||||
|
||||
public string Serialize()
|
||||
=> LbpSerializer.StringElement
|
||||
(
|
||||
"privacySettings",
|
||||
LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) +
|
||||
LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility)
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue