diff --git a/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs index 36e536dd..b3a48c21 100644 --- a/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs @@ -31,6 +31,8 @@ public class SlotEndpoints : ApiEndpointController [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public async Task GetSlots([FromQuery] int limit = 20, [FromQuery] int skip = 0) { + if (skip < 0) skip = 0; + if (limit < 0) limit = 0; limit = Math.Min(ServerStatics.PageSize, limit); IEnumerable minimalSlots = (await this.database.Slots.OrderByDescending(s => s.FirstUploaded).Skip(skip).Take(limit).ToListAsync()).Select diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs index 5d037fa0..3b3ae1d0 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs @@ -4,7 +4,6 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 6c1a829a..968948a4 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -26,12 +26,12 @@ public class CommentController : ControllerBase [HttpPost("rateComment/{slotType}/{slotId:int}")] public async Task RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); + if (username == null && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest(); - bool success = await this.database.RateComment(user, commentId, rating); + bool success = await this.database.RateComment(token.UserId, commentId, rating); if (!success) return this.BadRequest(); return this.Ok(); @@ -41,8 +41,10 @@ public class CommentController : ControllerBase [HttpGet("userComments/{username}")] public async Task GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + if (pageSize <= 0) return this.BadRequest(); int targetId = slotId; CommentType type = CommentType.Level; @@ -75,29 +77,28 @@ public class CommentController : ControllerBase (c => c.Poster) .Where(c => c.TargetId == targetId && c.Type == type) .OrderByDescending(c => c.Timestamp) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .ToListAsync(); string outputXml = comments.Aggregate - (string.Empty, (current, comment) => current + comment.Serialize(this.getReaction(user.UserId, comment.CommentId).Result)); + (string.Empty, (current, comment) => current + comment.Serialize(this.getReaction(token.UserId, comment.CommentId).Result)); return this.Ok(LbpSerializer.StringElement("comments", outputXml)); } private async Task getReaction(int userId, int commentId) { - Reaction? reaction = await this.database.Reactions.FirstOrDefaultAsync(r => r.UserId == userId && r.TargetId == commentId); - if (reaction == null) return 0; - - return reaction.Rating; + return await this.database.Reactions.Where(r => r.UserId == userId && r.TargetId == commentId) + .Select(r => r.Rating) + .FirstOrDefaultAsync(); } [HttpPost("postUserComment/{username}")] [HttpPost("postComment/{slotType}/{slotId:int}")] public async Task PostComment(string? username, string? slotType, int slotId) { - User? poster = await this.database.UserFromGameRequest(this.Request); - if (poster == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -119,7 +120,7 @@ public class CommentController : ControllerBase if (slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, targetId, SlotType.Developer); - bool success = await this.database.PostComment(poster, targetId, type, comment.Message); + bool success = await this.database.PostComment(token.UserId, targetId, type, comment.Message); if (success) return this.Ok(); return this.BadRequest(); @@ -129,8 +130,8 @@ public class CommentController : ControllerBase [HttpPost("deleteComment/{slotType}/{slotId:int}")] public async Task DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId); if (comment == null) return this.NotFound(); @@ -140,12 +141,12 @@ public class CommentController : ControllerBase if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); // if you are not the poster - if (comment.PosterUserId != user.UserId) + if (comment.PosterUserId != token.UserId) { if (comment.Type == CommentType.Profile) { // if you aren't the poster and aren't the profile owner - if (comment.TargetId != user.UserId) + if (comment.TargetId != token.UserId) { return this.StatusCode(403, ""); } @@ -154,7 +155,7 @@ public class CommentController : ControllerBase { Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId); // if you aren't the creator of the level - if (slot == null || slot.CreatorId != user.UserId || slotId != slot.SlotId) + if (slot == null || slot.CreatorId != token.UserId || slotId != slot.SlotId) { return this.StatusCode(403, ""); } @@ -162,7 +163,7 @@ public class CommentController : ControllerBase } comment.Deleted = true; - comment.DeletedBy = user.Username; + comment.DeletedBy = await this.database.UsernameFromGameToken(token); comment.DeletedType = "user"; await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs index 20ffda5c..9a06ca02 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs @@ -26,8 +26,8 @@ public class FriendsController : ControllerBase [HttpPost("npdata")] public async Task NPData() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -56,8 +56,8 @@ public class FriendsController : ControllerBase blockedUsers.Add(blockedUser.UserId); } - UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId); - if (friendStore == null) friendStore = UserFriendStore.CreateUserFriendData(user.UserId); + UserFriendData? friendStore = UserFriendStore.GetUserFriendData(token.UserId); + if (friendStore == null) friendStore = UserFriendStore.CreateUserFriendData(token.UserId); friendStore.FriendIds = friends.Select(u => u.UserId).ToList(); friendStore.BlockedIds = blockedUsers; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs index 2dc6e363..734aa54f 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs @@ -2,7 +2,6 @@ using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -23,38 +22,35 @@ public class EnterLevelController : ControllerBase [HttpPost("play/user/{slotId}")] public async Task PlayLevel(int slotId) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); - GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return this.StatusCode(403, ""); - GameVersion gameVersion = token.GameVersion; - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId); + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId); VisitedLevel? v; if (!visited.Any()) { switch (gameVersion) { case GameVersion.LittleBigPlanet2: + case GameVersion.LittleBigPlanetVita: slot.PlaysLBP2Unique++; break; case GameVersion.LittleBigPlanet3: slot.PlaysLBP3Unique++; break; - case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVitaUnique++; - break; default: return this.BadRequest(); } - v = new VisitedLevel(); - v.SlotId = slotId; - v.UserId = user.UserId; + v = new VisitedLevel + { + SlotId = slotId, + UserId = token.UserId, + }; this.database.VisitedLevels.Add(v); } else @@ -67,6 +63,7 @@ public class EnterLevelController : ControllerBase switch (gameVersion) { case GameVersion.LittleBigPlanet2: + case GameVersion.LittleBigPlanetVita: slot.PlaysLBP2++; v.PlaysLBP2++; break; @@ -74,12 +71,9 @@ public class EnterLevelController : ControllerBase slot.PlaysLBP3++; v.PlaysLBP3++; break; - case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVita++; - v.PlaysLBPVita++; - break; case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException(); case GameVersion.Unknown: + case GameVersion.LittleBigPlanet1: default: return this.BadRequest(); } @@ -93,21 +87,23 @@ public class EnterLevelController : ControllerBase [HttpGet("enterLevel/{id:int}")] public async Task EnterLevel(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId); + IQueryable visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == token.UserId); VisitedLevel? v; if (!visited.Any()) { slot.PlaysLBP1Unique++; - v = new VisitedLevel(); - v.SlotId = id; - v.UserId = user.UserId; + v = new VisitedLevel + { + SlotId = id, + UserId = token.UserId, + }; this.database.VisitedLevels.Add(v); } else diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs index c6c8097a..30062231 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs @@ -86,9 +86,9 @@ public class MatchController : ControllerBase // Check how many people are online in release builds, disabled for debug for ..well debugging. #if DEBUG - if (matchData is FindBestRoom diveInData) + else if (matchData is FindBestRoom diveInData) #else - if (matchData is FindBestRoom diveInData && MatchHelper.UserLocations.Count > 1) + else if (matchData is FindBestRoom diveInData && MatchHelper.UserLocations.Count > 1) #endif { FindBestRoomResponse? response = RoomHelper.FindBestRoom @@ -102,7 +102,7 @@ public class MatchController : ControllerBase return this.Ok($"[{{\"StatusCode\":200}},{serialized}]"); } - if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) + else if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) { List users = new(); foreach (string playerUsername in createRoom.Players) @@ -117,7 +117,7 @@ public class MatchController : ControllerBase RoomHelper.CreateRoom(users, gameToken.GameVersion, gameToken.Platform, createRoom.RoomSlot); } - if (matchData is UpdatePlayersInRoom updatePlayersInRoom) + else if (matchData is UpdatePlayersInRoom updatePlayersInRoom) { Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId); @@ -133,7 +133,7 @@ public class MatchController : ControllerBase } room.PlayerIds = users.Select(u => u.UserId).ToList(); - RoomHelper.CleanupRooms(null, room); + await RoomHelper.CleanupRooms(null, room); } } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index e2048035..abd863ce 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -4,8 +4,6 @@ using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; -using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; @@ -39,8 +37,9 @@ along with this program. If not, see ."; [HttpGet("eula")] public async Task Eula() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + // ReSharper disable once ConvertIfStatementToReturnStatement + if (token == null) return this.StatusCode(403, ""); return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}"); } @@ -48,38 +47,30 @@ along with this program. If not, see ."; [HttpGet("announce")] public async Task Announce() { - #if !DEBUG - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - #else - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (userAndToken == null) return this.StatusCode(403, ""); - - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - #endif + string username = await this.database.UsernameFromGameToken(token); string announceText = ServerConfiguration.Instance.AnnounceText; - announceText = announceText.Replace("%user", user.Username); - announceText = announceText.Replace("%id", user.UserId.ToString()); + announceText = announceText.Replace("%user", username); + announceText = announceText.Replace("%id", token.UserId.ToString()); return this.Ok ( announceText + #if DEBUG "\n\n---DEBUG INFO---\n" + - $"user.UserId: {user.UserId}\n" + - $"token.Approved: {gameToken.Approved}\n" + - $"token.Used: {gameToken.Used}\n" + - $"token.UserLocation: {gameToken.UserLocation}\n" + - $"token.GameVersion: {gameToken.GameVersion}\n" + - $"token.ExpiresAt: {gameToken.ExpiresAt.ToString(CultureInfo.CurrentCulture)}\n" + + $"user.UserId: {token.UserId}\n" + + $"token.Approved: {token.Approved}\n" + + $"token.Used: {token.Used}\n" + + $"token.UserLocation: {token.UserLocation}\n" + + $"token.GameVersion: {token.GameVersion}\n" + + $"token.ExpiresAt: {token.ExpiresAt.ToString(CultureInfo.CurrentCulture)}\n" + "---DEBUG INFO---" + #endif - (announceText != "" ? "\n" : "") + (string.IsNullOrWhiteSpace(announceText) ? "" : "\n") ); } @@ -92,15 +83,17 @@ along with this program. If not, see ."; [HttpPost("filter")] public async Task Filter() { - User? user = await this.database.UserFromGameRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + if (token == null) return this.StatusCode(403, ""); string response = await new StreamReader(this.Request.Body).ReadToEndAsync(); string scannedText = CensorHelper.ScanMessage(response); - Logger.Info($"{user.Username}: {response} / {scannedText}", LogArea.Filter); + string username = await this.database.UsernameFromGameToken(token); + + Logger.Info($"{username}: {response} / {scannedText}", LogArea.Filter); return this.Ok(scannedText); } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs index 7452c77c..5334261c 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs @@ -2,6 +2,7 @@ using System.Xml.Serialization; using Discord; using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Logging; @@ -63,7 +64,7 @@ public class PhotosController : ControllerBase case SlotType.User: { Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.SlotId == photoSlot.SlotId); - if (slot != null) validLevel = slot.RootLevel == photoSlot.RootLevel; + if (slot != null && !string.IsNullOrEmpty(slot.RootLevel)) validLevel = true; break; } case SlotType.Pod: @@ -89,8 +90,19 @@ public class PhotosController : ControllerBase if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp; + // Check for duplicate photo subjects + List subjectUserIds = new(4); foreach (PhotoSubject subject in photo.Subjects) { + if (subjectUserIds.Contains(subject.Username) && !string.IsNullOrEmpty(subject.Username)) return this.BadRequest(); + + subjectUserIds.Add(subject.Username); + } + + foreach (PhotoSubject subject in photo.Subjects) + { + if (string.IsNullOrEmpty(subject.Username)) continue; + subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); if (subject.User == null) continue; @@ -103,18 +115,7 @@ public class PhotosController : ControllerBase await this.database.SaveChangesAsync(); - // Check for duplicate photo subjects - List subjectUserIds = new(4); - foreach (PhotoSubject subject in photo.Subjects) - { - if (subjectUserIds.Contains(subject.UserId)) return this.BadRequest(); - - subjectUserIds.Add(subject.UserId); - } - - photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray(); - - // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); + photo.PhotoSubjectIds = photo.Subjects.Where(s => s.UserId != 0).Select(subject => subject.PhotoSubjectId.ToString()).ToArray(); Logger.Debug($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LogArea.Photos); @@ -139,8 +140,10 @@ public class PhotosController : ControllerBase [HttpGet("photos/{slotType}/{id:int}")] public async Task SlotPhotos([FromQuery] int pageStart, [FromQuery] int pageSize, string slotType, int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + if (pageSize <= 0) return this.BadRequest(); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); @@ -149,7 +152,7 @@ public class PhotosController : ControllerBase List photos = await this.database.Photos.Include(p => p.Creator) .Where(p => p.SlotId == id) .OrderByDescending(s => s.Timestamp) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .ToListAsync(); string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id, SlotHelper.ParseType(slotType))); @@ -159,15 +162,19 @@ public class PhotosController : ControllerBase [HttpGet("photos/by")] public async Task UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) { - User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (userFromQuery == null) return this.NotFound(); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + if (pageSize <= 0) return this.BadRequest(); + + int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync(); + if (targetUserId == 0) return this.NotFound(); List photos = await this.database.Photos.Include (p => p.Creator) - .Where(p => p.CreatorId == userFromQuery.UserId) + .Where(p => p.CreatorId == targetUserId) .OrderByDescending(s => s.Timestamp) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .ToListAsync(); string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize()); @@ -177,19 +184,31 @@ public class PhotosController : ControllerBase [HttpGet("photos/with")] public async Task UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize) { - User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (userFromQuery == null) return this.NotFound(); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - List photos = new(); - foreach (Photo photo in this.database.Photos.Include - (p => p.Creator)) photos.AddRange(photo.Subjects.Where(subject => subject.User.UserId == userFromQuery.UserId).Select(_ => photo)); + if (pageSize <= 0) return this.BadRequest(); - string response = photos.OrderByDescending - (s => s.Timestamp) - .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - .Aggregate(string.Empty, (s, photo) => s + photo.Serialize()); + int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync(); + if (targetUserId == 0) return this.NotFound(); + + List photoSubjectIds = new(); + photoSubjectIds.AddRange(this.database.PhotoSubjects.Where(p => p.UserId == targetUserId).Select(p => p.PhotoSubjectId)); + + var list = this.database.Photos.Select(p => new + { + p.PhotoId, + p.PhotoSubjectCollection, + }).ToList(); + List photoIds = (from v in list where photoSubjectIds.Any(ps => v.PhotoSubjectCollection.Contains(ps.ToString())) select v.PhotoId).ToList(); + + string response = Enumerable.Aggregate( + this.database.Photos.Where(p => photoIds.Any(id => p.PhotoId == id) && p.CreatorId != targetUserId) + .OrderByDescending(s => s.Timestamp) + .Skip(Math.Max(0, pageStart - 1)) + .Take(Math.Min(pageSize, 30)), + string.Empty, + (current, photo) => current + photo.Serialize()); return this.Ok(LbpSerializer.StringElement("photos", response)); } @@ -197,13 +216,18 @@ public class PhotosController : ControllerBase [HttpPost("deletePhoto/{id:int}")] public async Task DeletePhoto(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id); if (photo == null) return this.NotFound(); - if (photo.CreatorId != user.UserId) return this.StatusCode(401, ""); + if (photo.CreatorId != token.UserId) return this.StatusCode(401, ""); + foreach (string idStr in photo.PhotoSubjectIds) + { + if (!int.TryParse(idStr, out int subjectId)) throw new InvalidCastException(idStr + " is not a valid number."); + this.database.PhotoSubjects.RemoveWhere(p => p.PhotoSubjectId == subjectId); + } this.database.Photos.Remove(photo); await this.database.SaveChangesAsync(); return this.Ok(); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs index 84a94038..19c189e4 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs @@ -3,9 +3,8 @@ using System.Buffers; using System.IO.Pipelines; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Files; -using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -32,8 +31,8 @@ public class ResourcesController : ControllerBase [HttpPost("showNotUploaded")] public async Task FilterResources() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -52,8 +51,8 @@ public class ResourcesController : ControllerBase [HttpGet("r/{hash}")] public async Task GetResource(string hash) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); string path = FileHelper.GetResourcePath(hash); @@ -67,8 +66,8 @@ public class ResourcesController : ControllerBase [HttpPost("upload/{hash}")] public async Task UploadResource(string hash) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); string assetsDirectory = FileHelper.ResourcePath; string path = FileHelper.GetResourcePath(hash); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs index e577b9d1..ed152b9a 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs @@ -1,5 +1,4 @@ #nullable enable -using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; @@ -31,6 +30,8 @@ public class ListController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; IEnumerable queuedLevels = this.database.QueuedLevels.Where(q => q.User.Username == username) @@ -38,7 +39,7 @@ public class ListController : ControllerBase .Include(q => q.Slot.Location) .Select(q => q.Slot) .ByGameVersion(gameVersion) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .AsEnumerable(); @@ -54,13 +55,13 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/add/user/{id:int}")] public async Task AddQueuedLevel(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - await this.database.QueueLevel(user, slot); + await this.database.QueueLevel(token.UserId, slot); return this.Ok(); } @@ -68,13 +69,13 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/remove/user/{id:int}")] public async Task RemoveQueuedLevel(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - await this.database.UnqueueLevel(user, slot); + await this.database.UnqueueLevel(token.UserId, slot); return this.Ok(); } @@ -82,10 +83,10 @@ public class ListController : ControllerBase [HttpPost("lolcatftw/clear")] public async Task ClearQueuedLevels() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId)); + this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == token.UserId)); await this.database.SaveChangesAsync(); @@ -102,14 +103,19 @@ public class ListController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; - IEnumerable heartedLevels = this.database.HeartedLevels.Where(q => q.User.Username == username) + User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (targetUser == null) return this.StatusCode(403, ""); + + IEnumerable heartedLevels = this.database.HeartedLevels.Where(q => q.UserId == targetUser.UserId) .Include(q => q.Slot.Creator) .Include(q => q.Slot.Location) .Select(q => q.Slot) .ByGameVersion(gameVersion) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .AsEnumerable(); @@ -119,7 +125,7 @@ public class ListController : ControllerBase ( LbpSerializer.TaggedStringElement("favouriteSlots", response, new Dictionary { - { "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username) }, + { "total", this.database.HeartedLevels.Count(q => q.UserId == targetUser.UserId) }, { "hint_start", pageStart + Math.Min(pageSize, 30) }, }) ); @@ -128,13 +134,13 @@ public class ListController : ControllerBase [HttpPost("favourite/slot/user/{id:int}")] public async Task AddFavouriteSlot(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - await this.database.HeartLevel(user, slot); + await this.database.HeartLevel(token.UserId, slot); return this.Ok(); } @@ -142,13 +148,13 @@ public class ListController : ControllerBase [HttpPost("unfavourite/slot/user/{id:int}")] public async Task RemoveFavouriteSlot(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); - await this.database.UnheartLevel(user, slot); + await this.database.UnheartLevel(token.UserId, slot); return this.Ok(); } @@ -165,22 +171,27 @@ public class ListController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); - IEnumerable heartedProfiles = this.database.HeartedProfiles.Include - (q => q.User) - .Include(q => q.HeartedUser) + User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (targetUser == null) return this.StatusCode(403, ""); + + if (pageSize <= 0) return this.BadRequest(); + + IEnumerable heartedProfiles = this.database.HeartedProfiles.Include + (q => q.HeartedUser) .Include(q => q.HeartedUser.Location) - .Where(q => q.User.Username == username) - .Skip(pageStart - 1) + .Select(q => q.HeartedUser) + .Where(q => q.UserId == targetUser.UserId) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .AsEnumerable(); - string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion)); + string response = heartedProfiles.Aggregate(string.Empty, (current, u) => current + u.Serialize(token.GameVersion)); return this.Ok ( LbpSerializer.TaggedStringElement("favouriteUsers", response, new Dictionary { - { "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username) }, + { "total", this.database.HeartedProfiles.Count(q => q.UserId == targetUser.UserId) }, { "hint_start", pageStart + Math.Min(pageSize, 30) }, }) ); @@ -189,13 +200,13 @@ public class ListController : ControllerBase [HttpPost("favourite/user/{username}")] public async Task AddFavouriteUser(string username) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); - await this.database.HeartUser(user, heartedUser); + await this.database.HeartUser(token.UserId, heartedUser); return this.Ok(); } @@ -203,13 +214,13 @@ public class ListController : ControllerBase [HttpPost("unfavourite/user/{username}")] public async Task RemoveFavouriteUser(string username) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound(); - await this.database.UnheartUser(user, heartedUser); + await this.database.UnheartUser(token.UserId, heartedUser); return this.Ok(); } diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index cd45cfca..657e0c9c 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -185,10 +185,6 @@ public class PublishController : ControllerBase slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete; slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique; - slot.PlaysLBPVita = oldSlot.PlaysLBPVita; - slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete; - slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique; - #endregion slot.FirstUploaded = oldSlot.FirstUploaded; @@ -249,15 +245,15 @@ public class PublishController : ControllerBase [HttpPost("unpublish/{id:int}")] public async Task Unpublish(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); if (slot == null) return this.NotFound(); if (slot.Location == null) throw new ArgumentNullException(); - if (slot.CreatorId != user.UserId) return this.StatusCode(403, ""); + if (slot.CreatorId != token.UserId) return this.StatusCode(403, ""); this.database.Locations.Remove(slot.Location); this.database.Slots.Remove(slot); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs index eb6cb62d..3902017b 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs @@ -30,19 +30,19 @@ public class ReviewController : ControllerBase [HttpPost("rate/user/{slotId}")] public async Task Rate(int slotId, [FromQuery] int rating) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { ratedLevel = new RatedLevel { SlotId = slotId, - UserId = user.UserId, + UserId = token.UserId, Rating = 0, }; this.database.RatedLevels.Add(ratedLevel); @@ -59,19 +59,19 @@ public class ReviewController : ControllerBase [HttpPost("dpadrate/user/{slotId:int}")] public async Task DPadRate(int slotId, [FromQuery] int rating) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.StatusCode(403, ""); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { ratedLevel = new RatedLevel { SlotId = slotId, - UserId = user.UserId, + UserId = token.UserId, RatingLBP1 = 0, }; this.database.RatedLevels.Add(ratedLevel); @@ -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 == user.UserId); + Review? 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,22 +90,22 @@ public class ReviewController : ControllerBase [HttpPost("postReview/user/{slotId:int}")] public async Task PostReview(int slotId) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); Review? newReview = await this.getReviewFromBody(); if (newReview == null) return this.BadRequest(); if (newReview.Text.Length > 512) return this.BadRequest(); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == token.UserId); if (review == null) { review = new Review { SlotId = slotId, - ReviewerId = user.UserId, + ReviewerId = token.UserId, DeletedBy = DeletedBy.None, ThumbsUp = 0, ThumbsDown = 0, @@ -119,13 +119,13 @@ public class ReviewController : ControllerBase review.Timestamp = TimeHelper.UnixTimeMilliseconds(); // sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??) - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == token.UserId); if (ratedLevel == null) { ratedLevel = new RatedLevel { SlotId = slotId, - UserId = user.UserId, + UserId = token.UserId, RatingLBP1 = 0, }; this.database.RatedLevels.Add(ratedLevel); @@ -141,15 +141,12 @@ public class ReviewController : ControllerBase [HttpGet("reviewsFor/user/{slotId:int}")] public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (userAndToken == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - - GameVersion gameVersion = gameToken.GameVersion; + GameVersion gameVersion = token.GameVersion; Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); if (slot == null) return this.BadRequest(); @@ -160,7 +157,7 @@ public class ReviewController : ControllerBase .Include(r => r.Slot) .OrderByDescending(r => r.ThumbsUp - r.ThumbsDown) .ThenByDescending(r => r.Timestamp) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(pageSize); List reviewList = reviews.ToList(); @@ -172,7 +169,7 @@ public class ReviewController : ControllerBase { if (review == null) return current; - RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); return current + review.Serialize(null, yourThumb); } ); @@ -196,22 +193,23 @@ public class ReviewController : ControllerBase [HttpGet("reviewsBy/{username}")] public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (userAndToken == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; + GameVersion gameVersion = token.GameVersion; - GameVersion gameVersion = gameToken.GameVersion; + int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + + if (targetUserId == 0) return this.BadRequest(); IEnumerable reviews = this.database.Reviews.ByGameVersion(gameVersion, true) .Include(r => r.Reviewer) .Include(r => r.Slot) - .Where(r => r.Reviewer!.Username == username) + .Where(r => r.ReviewerId == targetUserId) .OrderByDescending(r => r.Timestamp) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(pageSize); List reviewList = reviews.ToList(); @@ -223,7 +221,7 @@ public class ReviewController : ControllerBase { if (review == null) return current; - RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); return current + review.Serialize(null, ratedReview); } ); @@ -249,22 +247,22 @@ public class ReviewController : ControllerBase [HttpPost("rateReview/user/{slotId:int}/{username}")] public async Task RateReview(int slotId, string username, [FromQuery] int rating = 0) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (reviewer == null) return this.StatusCode(400, ""); + int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + if (reviewerId == 0) return this.StatusCode(400, ""); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); if (review == null) return this.StatusCode(400, ""); - RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == token.UserId); if (ratedReview == null) { ratedReview = new RatedReview { ReviewId = review.ReviewId, - UserId = user.UserId, + UserId = token.UserId, Thumb = 0, }; this.database.RatedReviews.Add(ratedReview); @@ -276,12 +274,12 @@ public class ReviewController : ControllerBase if (oldRating == ratedReview.Thumb) return this.Ok(); // if the user's rating changed then we recount the review's ratings to ensure accuracy - List reactions = await this.database.RatedReviews.Where(r => r.ReviewId == review.ReviewId).ToListAsync(); + List reactions = await this.database.RatedReviews.Where(r => r.ReviewId == reviewerId).Select(r => r.Thumb).ToListAsync(); int yay = 0; int boo = 0; - foreach (RatedReview r in reactions) + foreach (int r in reactions) { - switch (r.Thumb) + switch (r) { case -1: boo++; @@ -303,11 +301,19 @@ public class ReviewController : ControllerBase [HttpPost("deleteReview/user/{slotId:int}/{username}")] public async Task DeleteReview(int slotId, string username) { - User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - if (reviewer == null) return this.StatusCode(403, ""); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId); - if (review == null) return this.StatusCode(403, ""); + int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync(); + if (creatorId == 0) return this.StatusCode(400, ""); + + if (token.UserId != creatorId) return this.StatusCode(403, ""); + + int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); + if (reviewerId == 0) return this.StatusCode(400, ""); + + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId); + if (review == null) return this.StatusCode(400, ""); review.Deleted = true; review.DeletedBy = DeletedBy.LevelAuthor; diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs index 34696cf3..5a1c22df 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs @@ -1,13 +1,11 @@ #nullable enable using System.Diagnostics.CodeAnalysis; using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; @@ -27,16 +25,13 @@ public class ScoreController : ControllerBase [HttpPost("scoreboard/{slotType}/{id:int}")] public async Task SubmitScore(string slotType, int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false) { - (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (userAndToken == null) return this.StatusCode(403, ""); + string username = await this.database.UsernameFromGameToken(token); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); - // ReSharper disable once PossibleInvalidOperationException - User user = userAndToken.Value.Item1; - GameToken gameToken = userAndToken.Value.Item2; - this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -53,20 +48,18 @@ public class ScoreController : ControllerBase Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId); if (slot == null) return this.BadRequest(); - switch (gameToken.GameVersion) + switch (token.GameVersion) { case GameVersion.LittleBigPlanet1: slot.PlaysLBP1Complete++; break; case GameVersion.LittleBigPlanet2: + case GameVersion.LittleBigPlanetVita: slot.PlaysLBP2Complete++; break; case GameVersion.LittleBigPlanet3: slot.PlaysLBP3Complete++; break; - case GameVersion.LittleBigPlanetVita: - slot.PlaysLBPVitaComplete++; - break; } IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId) @@ -87,7 +80,7 @@ public class ScoreController : ControllerBase await this.database.SaveChangesAsync(); - string myRanking = this.getScores(score.SlotId, score.Type, user, -1, 5, "scoreboardSegment"); + string myRanking = this.getScores(score.SlotId, score.Type, username, -1, 5, "scoreboardSegment"); return this.Ok(myRanking); } @@ -102,15 +95,18 @@ public class ScoreController : ControllerBase public async Task TopScores(string slotType, int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) { // Get username - User? user = await this.database.UserFromGameRequest(this.Request); + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); - if (user == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + + string username = await this.database.UsernameFromGameToken(token); if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest(); if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); - return this.Ok(this.getScores(slotId, type, user, pageStart, pageSize)); + return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize)); } [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] @@ -118,12 +114,13 @@ public class ScoreController : ControllerBase ( int slotId, int type, - User user, + string username, int pageStart = -1, int pageSize = 5, string rootName = "scores" ) { + // This is hella ugly but it technically assigns the proper rank to a score // var needed for Anonymous type returned from SELECT var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type) @@ -139,7 +136,7 @@ public class ScoreController : ControllerBase ); // Find your score, since even if you aren't in the top list your score is pinned - var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Contains(user.Username)).OrderByDescending(rs => rs.Score.Points).FirstOrDefault(); + var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Contains(username)).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)); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs index fab980fc..401b3bd3 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs @@ -2,7 +2,6 @@ using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -34,6 +33,8 @@ public class SearchController : ControllerBase GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request); if (gameToken == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + if (string.IsNullOrWhiteSpace(query)) return this.BadRequest(); query = query.ToLower(); @@ -56,7 +57,7 @@ public class SearchController : ControllerBase s.SlotId.ToString().Equals(keyword) ); - List slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync(); + List slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)).Take(Math.Min(pageSize, 30)).ToListAsync(); string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(gameToken.GameVersion)); diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs index e02b8f81..968699b5 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs @@ -30,16 +30,18 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; - User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u); - if (user == null) return this.NotFound(); + int targetUserId = await this.database.Users.Where(dbUser => dbUser.Username == u).Select(dbUser => dbUser.UserId).FirstOrDefaultAsync(); + if (targetUserId == 0) return this.NotFound(); string response = Enumerable.Aggregate ( - this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId, true) - .Where(s => s.Creator!.Username == user.Username) - .Skip(pageStart - 1) + this.database.Slots.ByGameVersion(gameVersion, token.UserId == targetUserId, true) + .Where(s => s.CreatorId == targetUserId) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)), string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion) @@ -57,7 +59,7 @@ public class SlotsController : ControllerBase "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { - "total", user.UsedSlots + "total", await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId) }, } ) @@ -93,9 +95,6 @@ public class SlotsController : ControllerBase [HttpGet("slots/developer")] public async Task StoryPlayers() { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); @@ -107,7 +106,7 @@ public class SlotsController : ControllerBase { 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(false)); + serializedSlots.Add(slot.SerializeDevSlot()); } string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => current + slot); @@ -118,9 +117,6 @@ public class SlotsController : ControllerBase [HttpGet("s/developer/{id:int}")] public async Task SDev(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); @@ -133,9 +129,6 @@ public class SlotsController : ControllerBase [HttpGet("s/user/{id:int}")] public async Task SUser(int id) { - User? user = await this.database.UserFromGameRequest(this.Request); - if (user == null) return this.StatusCode(403, ""); - GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); @@ -145,10 +138,10 @@ public class SlotsController : ControllerBase if (slot == null) return this.NotFound(); - RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); - VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); - Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == user.UserId); - return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review)); + 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.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == token.UserId); + return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review, true)); } [HttpGet("slots/cool")] @@ -181,11 +174,13 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) .OrderByDescending(s => s.FirstUploaded) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); @@ -215,12 +210,14 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; IQueryable slots = this.database.Slots.ByGameVersion(gameVersion, false, true) .Where(s => s.TeamPick) .OrderByDescending(s => s.LastUpdated) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion)); @@ -249,6 +246,8 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + GameVersion gameVersion = token.GameVersion; IEnumerable slots = this.database.Slots.ByGameVersion(gameVersion, false, true).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30)); @@ -288,13 +287,15 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + Random rand = new(); IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) .AsEnumerable() .OrderByDescending(s => s.Thumbsup) .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); @@ -332,6 +333,8 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + Random rand = new(); IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) @@ -346,13 +349,13 @@ public class SlotsController : ControllerBase GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique, GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique, GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique, - GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique, + GameVersion.LittleBigPlanetVita => s.PlaysLBP2Unique, _ => s.PlaysUnique, }; } ) .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); @@ -390,13 +393,15 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + Random rand = new(); IEnumerable slots = this.filterByRequest(gameFilterType, dateFilterType, token.GameVersion) .AsEnumerable() .OrderByDescending(s => s.Hearts) .ThenBy(_ => rand.Next()) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)); string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion)); @@ -434,6 +439,8 @@ public class SlotsController : ControllerBase GameToken? token = await this.database.GameTokenFromRequest(this.Request); if (token == null) return this.StatusCode(403, ""); + if (pageSize <= 0) return this.BadRequest(); + Dictionary playersBySlotId = new(); foreach (Room room in RoomHelper.Rooms) @@ -451,7 +458,7 @@ public class SlotsController : ControllerBase } IEnumerable orderedPlayersBySlotId = playersBySlotId - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) .OrderByDescending(kvp => kvp.Value) .Select(kvp => kvp.Key); diff --git a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs index 495bf66f..e109bdb5 100644 --- a/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs +++ b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs @@ -1,10 +1,10 @@ +using System.IO.Compression; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Middlewares; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Primitives; @@ -124,6 +124,7 @@ public class GameServerStartup context.Response.Body = responseBuffer; await next(context); // Handle the request so we can get the server digest hash + responseBuffer.Position = 0; // Compute the server digest hash. if (computeDigests) @@ -139,10 +140,7 @@ public class GameServerStartup context.Response.Headers.Add("X-Digest-A", serverDigest); } - // Set the X-Original-Content-Length header to the length of the response buffer. - context.Response.Headers.Add("X-Original-Content-Length", responseBuffer.Length.ToString()); - - // Copy the buffered response to the actual respose stream. + // Copy the buffered response to the actual response stream. responseBuffer.Position = 0; await responseBuffer.CopyToAsync(oldResponseStream); context.Response.Body = oldResponseStream; diff --git a/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs index b4772da5..d5ce4164 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs @@ -23,6 +23,7 @@ public class RoomVisualizerController : ControllerBase public async Task CreateFakeRoom() { #if !DEBUG + await Task.FromResult(0); return this.NotFound(); #else List users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).Select(u => u.UserId).ToListAsync(); @@ -51,6 +52,7 @@ public class RoomVisualizerController : ControllerBase public async Task CreateRoomsWithDuplicatePlayers() { #if !DEBUG + await Task.FromResult(0); return this.NotFound(); #else List users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(1).Select(u => u.UserId).ToListAsync(); diff --git a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index 414d9a0b..a2843e46 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -2,7 +2,7 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -28,10 +28,10 @@ public class SlotPageController : ControllerBase [HttpGet("rateComment")] public async Task RateComment([FromRoute] int id, [FromQuery] int commentId, [FromQuery] int rating) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); - await this.database.RateComment(user, commentId, rating); + await this.database.RateComment(token.UserId, commentId, rating); return this.Redirect($"~/slot/{id}#{commentId}"); } @@ -39,19 +39,19 @@ public class SlotPageController : ControllerBase [HttpPost("postComment")] public async Task PostComment([FromRoute] int id, [FromForm] string? msg) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); if (msg == null) { - Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); + Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); return this.Redirect("~/slot/" + id); } msg = SanitizationHelper.SanitizeString(msg); - await this.database.PostComment(user, id, CommentType.Level, msg); - Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments); + await this.database.PostComment(token.UserId, id, CommentType.Level, msg); + Logger.Success($"Posted comment from {token.UserId}: \"{msg}\" on user {id}", LogArea.Comments); return this.Redirect("~/slot/" + id); } @@ -61,13 +61,13 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (heartedSlot == null) return this.NotFound(); - await this.database.HeartLevel(user, heartedSlot); + await this.database.HeartLevel(token.UserId, heartedSlot); return this.Redirect(callbackUrl); } @@ -77,13 +77,13 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (heartedSlot == null) return this.NotFound(); - await this.database.UnheartLevel(user, heartedSlot); + await this.database.UnheartLevel(token.UserId, heartedSlot); return this.Redirect(callbackUrl); } @@ -93,13 +93,13 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (queuedSlot == null) return this.NotFound(); - await this.database.QueueLevel(user, queuedSlot); + await this.database.QueueLevel(token.UserId, queuedSlot); return this.Redirect(callbackUrl); } @@ -109,13 +109,13 @@ public class SlotPageController : ControllerBase { if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id; - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); if (queuedSlot == null) return this.NotFound(); - await this.database.UnqueueLevel(user, queuedSlot); + await this.database.UnqueueLevel(token.UserId, queuedSlot); return this.Redirect(callbackUrl); } diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index 16b25782..fe30cfad 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -1,6 +1,7 @@ #nullable enable using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -22,10 +23,10 @@ public class UserPageController : ControllerBase [HttpGet("rateComment")] public async Task RateComment([FromRoute] int id, [FromQuery] int? commentId, [FromQuery] int? rating) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); - await this.database.RateComment(user, commentId.GetValueOrDefault(), rating.GetValueOrDefault()); + await this.database.RateComment(token.UserId, commentId.GetValueOrDefault(), rating.GetValueOrDefault()); return this.Redirect($"~/user/{id}#{commentId}"); } @@ -33,19 +34,19 @@ public class UserPageController : ControllerBase [HttpPost("postComment")] public async Task PostComment([FromRoute] int id, [FromForm] string? msg) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); if (msg == null) { - Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); + Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); return this.Redirect("~/user/" + id); } msg = SanitizationHelper.SanitizeString(msg); - await this.database.PostComment(user, id, CommentType.Profile, msg); - Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments); + await this.database.PostComment(token.UserId, id, CommentType.Profile, msg); + Logger.Success($"Posted comment from {token.UserId}: \"{msg}\" on user {id}", LogArea.Comments); return this.Redirect("~/user/" + id); } @@ -53,13 +54,13 @@ public class UserPageController : ControllerBase [HttpGet("heart")] public async Task HeartUser([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (heartedUser == null) return this.NotFound(); - await this.database.HeartUser(user, heartedUser); + await this.database.HeartUser(token.UserId, heartedUser); return this.Redirect("~/user/" + id); } @@ -67,13 +68,13 @@ public class UserPageController : ControllerBase [HttpGet("unheart")] public async Task UnheartUser([FromRoute] int id) { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null) return this.Redirect("~/login"); + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); if (heartedUser == null) return this.NotFound(); - await this.database.UnheartUser(user, heartedUser); + await this.database.UnheartUser(token.UserId, heartedUser); return this.Redirect("~/user/" + id); } diff --git a/ProjectLighthouse.Servers.Website/Pages/CasePage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/CasePage.cshtml.cs index ba273cf5..4f3fa1ee 100644 --- a/ProjectLighthouse.Servers.Website/Pages/CasePage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/CasePage.cshtml.cs @@ -13,7 +13,7 @@ public class CasePage : BaseLayout public CasePage(Database database) : base(database) {} - public List Cases; + public List Cases = new(); public int CaseCount; public int DismissedCaseCount; diff --git a/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs index bb305761..82d19226 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs @@ -12,16 +12,13 @@ public class FilterTestPage : BaseLayout public string? FilteredText; public string? Text; - + #if DEBUG public IActionResult OnGet(string? text = null) { - #if !DEBUG - return this.NotFound(); - #endif - if (text != null) this.FilteredText = CensorHelper.ScanMessage(text); this.Text = text; return this.Page(); } + #endif } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs index 9d1782f8..db99fd84 100644 --- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs @@ -42,14 +42,14 @@ public class LandingPage : BaseLayout const int maxShownLevels = 5; - this.LatestTeamPicks = await this.Database.Slots.Where(s => s.Type == SlotType.User && s.Type == SlotType.User) + this.LatestTeamPicks = await this.Database.Slots.Where(s => s.Type == SlotType.User && !s.SubLevel) .Where(s => s.TeamPick) .OrderByDescending(s => s.FirstUploaded) .Take(maxShownLevels) .Include(s => s.Creator) .ToListAsync(); - this.NewestLevels = await this.Database.Slots.Where(s => s.Type == SlotType.User && s.Type == SlotType.User) + this.NewestLevels = await this.Database.Slots.Where(s => s.Type == SlotType.User && !s.SubLevel) .OrderByDescending(s => s.FirstUploaded) .Take(maxShownLevels) .Include(s => s.Creator) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml index da1a46b2..a8c0cc46 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml @@ -1,4 +1,5 @@ @using System.Globalization +@using System.Web @using LBPUnion.ProjectLighthouse.Levels @using LBPUnion.ProjectLighthouse.PlayerData @model LBPUnion.ProjectLighthouse.PlayerData.Photo @@ -25,7 +26,7 @@ { case SlotType.User: - in level @Model.Slot.Name + in level @HttpUtility.HtmlDecode(Model.Slot.Name) break; case SlotType.Developer: diff --git a/ProjectLighthouse.Servers.Website/Pages/PirateSignupPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/PirateSignupPage.cshtml.cs index 445aed21..17dff379 100644 --- a/ProjectLighthouse.Servers.Website/Pages/PirateSignupPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/PirateSignupPage.cshtml.cs @@ -11,7 +11,7 @@ public class PirateSignupPage : BaseLayout public PirateSignupPage(Database database) : base(database) {} - public async Task OnGet() + public IActionResult OnGet() { User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.RedirectToPage("/login"); diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml index a4b59b22..6db27b0b 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml @@ -17,7 +17,7 @@ bool isMobile = this.Request.IsMobile(); } -@if (Model.Slot.Hidden) +@if (Model.Slot!.Hidden) {

This level is currently hidden.

diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs index 9d333f89..aaf2e502 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs @@ -54,7 +54,8 @@ public class SlotPage : BaseLayout } } - if (slot.Hidden && (this.User != slot.Creator && !(bool)this.User?.IsModerator)) return this.NotFound(); + if (slot.Hidden || slot.SubLevel && this.User == null && this.User != slot.Creator || !this.User!.IsModerator) + return this.NotFound(); this.Slot = slot; diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs index 3d0b944e..72f5dfab 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs @@ -5,7 +5,6 @@ using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -49,7 +48,7 @@ public class SlotsPage : BaseLayout } else { - finalSearch.Append(part); + finalSearch.Append(part).Append(' '); } } @@ -59,6 +58,7 @@ public class SlotsPage : BaseLayout .Where(p => p.Type == SlotType.User && !p.Hidden) .Where(p => p.Name.Contains(finalSearch.ToString())) .Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower()))) + .Where(p => p.Creator != null && (!p.SubLevel || p.Creator == this.User)) .Where(p => targetGame == null || p.GameVersion == targetGame) .CountAsync(); @@ -71,6 +71,7 @@ public class SlotsPage : BaseLayout .Where(p => p.Type == SlotType.User && !p.Hidden) .Where(p => p.Name.Contains(finalSearch.ToString())) .Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower()))) + .Where(p => p.Creator != null && (!p.SubLevel || p.Creator == this.User)) .Where(p => p.Creator!.LevelVisibility == PrivacyType.All) // TODO: change check for when user is logged in .Where(p => targetGame == null || p.GameVersion == targetGame) .OrderByDescending(p => p.FirstUploaded) diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 7d120102..e345dfeb 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -151,6 +151,7 @@ True True True + True True True True diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedPhotoSubjects.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedPhotoSubjects.cs new file mode 100644 index 00000000..63dc8af3 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedPhotoSubjects.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.PlayerData; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MaintenanceJobs; + +public class CleanupUnusedPhotoSubjects : IMaintenanceJob +{ + private readonly Database database = new(); + public string Name() => "Cleanup Unused PhotoSubjects"; + public string Description() => "Cleanup unused photo subjects in the database."; + + public async Task Run() + { + List subjectCollections = new(); + List usedPhotoSubjectIds = new(); + + subjectCollections.AddRange(this.database.Photos.Select(p => p.PhotoSubjectCollection)); + + foreach (string idCollection in subjectCollections) + { + usedPhotoSubjectIds.AddRange(idCollection.Split(",").Where(x => int.TryParse(x, out _)).Select(int.Parse)); + } + + IQueryable subjectsToRemove = this.database.PhotoSubjects.Where(p => !usedPhotoSubjectIds.Contains(p.PhotoSubjectId)); + + foreach (PhotoSubject subject in subjectsToRemove) + { + Console.WriteLine(@"Removing subject " + subject.PhotoSubjectId); + this.database.PhotoSubjects.Remove(subject); + } + + await this.database.SaveChangesAsync(); + } + +} \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/RepeatingTasks/CleanupRoomsTask.cs b/ProjectLighthouse/Administration/Maintenance/RepeatingTasks/CleanupRoomsTask.cs index 463350b1..e5ec6016 100644 --- a/ProjectLighthouse/Administration/Maintenance/RepeatingTasks/CleanupRoomsTask.cs +++ b/ProjectLighthouse/Administration/Maintenance/RepeatingTasks/CleanupRoomsTask.cs @@ -9,5 +9,5 @@ public class CleanupRoomsTask : IRepeatingTask public string Name => "Cleanup Rooms"; public TimeSpan RepeatInterval => TimeSpan.FromSeconds(10); public DateTime LastRan { get; set; } - public async Task Run(Database database) => RoomHelper.CleanupRooms(); + public Task Run(Database database) => RoomHelper.CleanupRooms(); } \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 5e758490..782e4c72 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -125,20 +125,20 @@ public class Database : DbContext #region Hearts & Queues - public async Task RateComment(User user, int commentId, int rating) + public async Task RateComment(int userId, int commentId, int rating) { Comment? comment = await this.Comments.FirstOrDefaultAsync(c => commentId == c.CommentId); if (comment == null) return false; - if (comment.PosterUserId == user.UserId) return false; + if (comment.PosterUserId == userId) return false; - Reaction? reaction = await this.Reactions.FirstOrDefaultAsync(r => r.UserId == user.UserId && r.TargetId == commentId); + Reaction? reaction = await this.Reactions.FirstOrDefaultAsync(r => r.UserId == userId && r.TargetId == commentId); if (reaction == null) { Reaction newReaction = new() { - UserId = user.UserId, + UserId = userId, TargetId = commentId, Rating = 0, }; @@ -176,7 +176,7 @@ public class Database : DbContext return true; } - public async Task PostComment(User user, int targetId, CommentType type, string message) + public async Task PostComment(int userId, int targetId, CommentType type, string message) { if (message.Length > 100) return false; @@ -195,7 +195,7 @@ public class Database : DbContext ( new Comment { - PosterUserId = user.UserId, + PosterUserId = userId, TargetId = targetId, Type = type, Message = message, @@ -206,9 +206,9 @@ public class Database : DbContext return true; } - public async Task HeartUser(User user, User heartedUser) + public async Task HeartUser(int userId, User heartedUser) { - HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == userId && q.HeartedUserId == heartedUser.UserId); if (heartedProfile != null) return; this.HeartedProfiles.Add @@ -216,24 +216,24 @@ public class Database : DbContext new HeartedProfile { HeartedUserId = heartedUser.UserId, - UserId = user.UserId, + UserId = userId, } ); await this.SaveChangesAsync(); } - public async Task UnheartUser(User user, User heartedUser) + public async Task UnheartUser(int userId, User heartedUser) { - HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == userId && q.HeartedUserId == heartedUser.UserId); if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile); await this.SaveChangesAsync(); } - public async Task HeartLevel(User user, Slot heartedSlot) + public async Task HeartLevel(int userId, Slot heartedSlot) { - HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); + HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == userId && q.SlotId == heartedSlot.SlotId); if (heartedLevel != null) return; this.HeartedLevels.Add @@ -241,24 +241,24 @@ public class Database : DbContext new HeartedLevel { SlotId = heartedSlot.SlotId, - UserId = user.UserId, + UserId = userId, } ); await this.SaveChangesAsync(); } - public async Task UnheartLevel(User user, Slot heartedSlot) + public async Task UnheartLevel(int userId, Slot heartedSlot) { - HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId); + HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == userId && q.SlotId == heartedSlot.SlotId); if (heartedLevel != null) this.HeartedLevels.Remove(heartedLevel); await this.SaveChangesAsync(); } - public async Task QueueLevel(User user, Slot queuedSlot) + public async Task QueueLevel(int userId, Slot queuedSlot) { - QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); + QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == userId && q.SlotId == queuedSlot.SlotId); if (queuedLevel != null) return; this.QueuedLevels.Add @@ -266,16 +266,16 @@ public class Database : DbContext new QueuedLevel { SlotId = queuedSlot.SlotId, - UserId = user.UserId, + UserId = userId, } ); await this.SaveChangesAsync(); } - public async Task UnqueueLevel(User user, Slot queuedSlot) + public async Task UnqueueLevel(int userId, Slot queuedSlot) { - QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId); + QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == userId && q.SlotId == queuedSlot.SlotId); if (queuedLevel != null) this.QueuedLevels.Remove(queuedLevel); await this.SaveChangesAsync(); @@ -285,6 +285,13 @@ public class Database : DbContext #region Game Token Shenanigans + public async Task UsernameFromGameToken(GameToken? token) + { + if (token == null) return ""; + + return await this.Users.Where(u => u.UserId == token.UserId).Select(u => u.Username).FirstAsync(); + } + public async Task UserFromMMAuth(string authToken, bool allowUnapproved = false) { if (ServerStatics.IsUnitTesting) allowUnapproved = true; @@ -300,7 +307,7 @@ public class Database : DbContext return null; } - return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId); + return await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); } public async Task UserFromGameToken @@ -351,7 +358,7 @@ public class Database : DbContext return null; } - User? user = await this.UserFromGameToken(token); + User? user = await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); if (user == null) return null; @@ -362,6 +369,13 @@ public class Database : DbContext #region Web Token Shenanigans + public async Task UsernameFromWebToken(WebToken? token) + { + if (token == null) return ""; + + return await this.Users.Where(u => u.UserId == token.UserId).Select(u => u.Username).FirstAsync(); + } + public User? UserFromLighthouseToken(string lighthouseToken) { WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken); diff --git a/ProjectLighthouse/Levels/Categories/HeartedCategory.cs b/ProjectLighthouse/Levels/Categories/HeartedCategory.cs index 4b8b1a73..556a4a29 100644 --- a/ProjectLighthouse/Levels/Categories/HeartedCategory.cs +++ b/ProjectLighthouse/Levels/Categories/HeartedCategory.cs @@ -22,6 +22,6 @@ public class HeartedCategory : CategoryWithUser .Include(h => h.Slot) .Select(h => h.Slot) .ByGameVersion(GameVersion.LittleBigPlanet3) - .Skip(pageStart) + .Skip(Math.Max(0, pageStart)) .Take(Math.Min(pageSize, 20)); } \ No newline at end of file diff --git a/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs b/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs index 9ba8a23d..198dfe3a 100644 --- a/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs +++ b/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs @@ -18,7 +18,7 @@ public class NewestLevelsCategory : Category (Database database, int pageStart, int pageSize) => database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(s => s.FirstUploaded) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 20)); public override int GetTotalSlots(Database database) => database.Slots.Count(s => s.Type == SlotType.User); } \ No newline at end of file diff --git a/ProjectLighthouse/Levels/Categories/QueueCategory.cs b/ProjectLighthouse/Levels/Categories/QueueCategory.cs index 352d3d69..62941c6e 100644 --- a/ProjectLighthouse/Levels/Categories/QueueCategory.cs +++ b/ProjectLighthouse/Levels/Categories/QueueCategory.cs @@ -25,7 +25,7 @@ public class QueueCategory : CategoryWithUser .Include(q => q.Slot.Location) .Select(q => q.Slot) .ByGameVersion(GameVersion.LittleBigPlanet3) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 20)); public override int GetTotalSlots(Database database, User user) => database.QueuedLevels.Count(q => q.UserId == user.UserId); diff --git a/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs b/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs index 40439cb1..35e992bb 100644 --- a/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs +++ b/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs @@ -20,7 +20,7 @@ public class TeamPicksCategory : Category (GameVersion.LittleBigPlanet3, false, true) .OrderByDescending(s => s.FirstUploaded) .Where(s => s.TeamPick) - .Skip(pageStart - 1) + .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 20)); public override int GetTotalSlots(Database database) => database.Slots.Count(s => s.TeamPick); } \ No newline at end of file diff --git a/ProjectLighthouse/Levels/Slot.cs b/ProjectLighthouse/Levels/Slot.cs index 23a45fb0..e3f5e9b0 100644 --- a/ProjectLighthouse/Levels/Slot.cs +++ b/ProjectLighthouse/Levels/Slot.cs @@ -1,5 +1,4 @@ #nullable enable -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; @@ -155,15 +154,15 @@ public class Slot [XmlIgnore] [NotMapped] - public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita; + public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3; [XmlIgnore] [NotMapped] - public int PlaysUnique => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique + this.PlaysLBPVitaUnique; + public int PlaysUnique => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique; [XmlIgnore] [NotMapped] - public int PlaysComplete => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete + this.PlaysLBPVitaComplete; + public int PlaysComplete => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete; [XmlIgnore] [JsonIgnore] @@ -201,18 +200,6 @@ public class Slot [JsonIgnore] public int PlaysLBP3Unique { get; set; } - [XmlIgnore] - [JsonIgnore] - public int PlaysLBPVita { get; set; } - - [XmlIgnore] - [JsonIgnore] - public int PlaysLBPVitaComplete { get; set; } - - [XmlIgnore] - [JsonIgnore] - public int PlaysLBPVitaUnique { get; set; } - [NotMapped] [JsonIgnore] [XmlElement("thumbsup")] @@ -252,33 +239,18 @@ public class Slot [JsonIgnore] public string HiddenReason { get; set; } = ""; - public string SerializeResources() + public string SerializeDevSlot() { - return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + - LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)); - } - - public string SerializeDevSlot(bool includeExtras = true) - { - - int comments = 0, photos = 0; - if (includeExtras) - { - comments = this.database.Comments.Count(c => c.Type == CommentType.Level && c.TargetId == this.SlotId); - - photos = this.database.Photos.Count(c => c.SlotId == this.SlotId); - } - + int comments = this.Comments; + int photos = this.Photos; int players = RoomHelper.Rooms .Where(r => r.Slot.SlotType == SlotType.Developer && r.Slot.SlotId == this.InternalSlotId) .Sum(r => r.PlayerIds.Count); string slotData = LbpSerializer.StringElement("id", this.InternalSlotId) + - LbpSerializer.StringElement("playerCount", players); - - if(includeExtras) - slotData += LbpSerializer.StringElement("commentCount", comments) + - LbpSerializer.StringElement("photoCount", photos); + LbpSerializer.StringElement("playerCount", players) + + LbpSerializer.StringElement("commentCount", comments) + + LbpSerializer.StringElement("photoCount", photos); return LbpSerializer.TaggedStringElement("slot", slotData, "type", "developer"); } @@ -291,89 +263,76 @@ public class Slot GameVersion gameVersion = GameVersion.LittleBigPlanet1, RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, - Review? yourReview = null + Review? yourReview = null, + bool fullSerialization = false ) { if (this.Type == SlotType.Developer) return this.SerializeDevSlot(); int playerCount = RoomHelper.Rooms.Count(r => r.Slot.SlotType == SlotType.User && r.Slot.SlotId == this.SlotId); - string slotData = LbpSerializer.StringElement("name", this.Name) + - LbpSerializer.StringElement("id", this.SlotId) + - LbpSerializer.StringElement("game", (int)this.GameVersion) + + string slotData = LbpSerializer.StringElement("id", this.SlotId) + LbpSerializer.StringElement("npHandle", this.Creator?.Username) + - LbpSerializer.StringElement("description", this.Description) + - LbpSerializer.StringElement("icon", this.IconHash) + - LbpSerializer.StringElement("rootLevel", this.RootLevel) + - LbpSerializer.StringElement("authorLabels", this.AuthorLabels) + - LbpSerializer.StringElement("labels", this.AuthorLabels) + - this.SerializeResources() + LbpSerializer.StringElement("location", this.Location?.Serialize()) + + LbpSerializer.StringElement("game", (int)this.GameVersion) + + LbpSerializer.StringElement("name", this.Name) + + LbpSerializer.StringElement("description", this.Description) + + LbpSerializer.StringElement("rootLevel", this.RootLevel) + + LbpSerializer.StringElement("icon", this.IconHash) + LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) + LbpSerializer.StringElement("isSubLevel", this.SubLevel) + LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) + - LbpSerializer.StringElement("shareable", this.Shareable) + LbpSerializer.StringElement("background", this.BackgroundHash) + + LbpSerializer.StringElement("shareable", this.Shareable) + + LbpSerializer.StringElement("authorLabels", this.AuthorLabels) + + LbpSerializer.StringElement("leveltype", this.LevelType) + LbpSerializer.StringElement("minPlayers", this.MinimumPlayers) + LbpSerializer.StringElement("maxPlayers", this.MaximumPlayers) + - LbpSerializer.StringElement("moveRequired", this.MoveRequired) + - LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + - LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + - LbpSerializer.StringElement("mmpick", this.TeamPick) + LbpSerializer.StringElement("heartCount", this.Hearts) + - LbpSerializer.StringElement("playCount", this.Plays) + + LbpSerializer.StringElement("thumbsup", this.Thumbsup) + + LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) + + LbpSerializer.StringElement("averageRating", this.RatingLBP1) + + LbpSerializer.StringElement("playerCount", playerCount) + + LbpSerializer.StringElement("mmpick", this.TeamPick) + + (fullSerialization ? LbpSerializer.StringElement("moveRequired", this.MoveRequired) : "") + + (fullSerialization ? LbpSerializer.StringElement("crossControlRequired", this.CrossControllerRequired) : "") + + (yourRatingStats != null ? + LbpSerializer.StringElement("yourRating", yourRatingStats.RatingLBP1) + + LbpSerializer.StringElement("yourDPadRating", yourRatingStats.Rating) + : "") + + (yourVisitedStats != null ? + LbpSerializer.StringElement("yourlbp1PlayCount", yourVisitedStats.PlaysLBP1) + + LbpSerializer.StringElement("yourlbp2PlayCount", yourVisitedStats.PlaysLBP2) + + LbpSerializer.StringElement("yourlbp3PlayCount", yourVisitedStats.PlaysLBP3) + : "") + + LbpSerializer.StringElement("reviewCount", this.ReviewCount) + + LbpSerializer.StringElement("commentCount", this.Comments) + LbpSerializer.StringElement("photoCount", this.Photos) + LbpSerializer.StringElement("authorPhotoCount", this.PhotosWithAuthor) + - LbpSerializer.StringElement("commentCount", this.Comments) + - LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol + (fullSerialization ? LbpSerializer.StringElement("labels", this.AuthorLabels) : "") + + LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + + LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + + (fullSerialization ? + yourReview?.Serialize() + + LbpSerializer.StringElement("reviewsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled) + + LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && this.CommentsEnabled) + : "") + + LbpSerializer.StringElement("playCount", this.Plays) + LbpSerializer.StringElement("completionCount", this.PlaysComplete) + LbpSerializer.StringElement("lbp1PlayCount", this.PlaysLBP1) + LbpSerializer.StringElement("lbp1CompletionCount", this.PlaysLBP1Complete) + LbpSerializer.StringElement("lbp1UniquePlayCount", this.PlaysLBP1Unique) + + LbpSerializer.StringElement("lbp2PlayCount", this.PlaysLBP2) + + LbpSerializer.StringElement("lbp2CompletionCount", this.PlaysLBP2Complete) + + LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol + LbpSerializer.StringElement("lbp3PlayCount", this.PlaysLBP3) + LbpSerializer.StringElement("lbp3CompletionCount", this.PlaysLBP3Complete) + LbpSerializer.StringElement("lbp3UniquePlayCount", this.PlaysLBP3Unique) + - LbpSerializer.StringElement("thumbsup", this.Thumbsup) + - LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) + - LbpSerializer.StringElement("averageRating", this.RatingLBP1) + - LbpSerializer.StringElement("leveltype", this.LevelType) + - LbpSerializer.StringElement("yourRating", yourRatingStats?.RatingLBP1) + - LbpSerializer.StringElement("yourDPadRating", yourRatingStats?.Rating) + - LbpSerializer.StringElement("yourlbpPlayCount", yourVisitedStats?.PlaysLBP1) + - LbpSerializer.StringElement("yourlbp3PlayCount", yourVisitedStats?.PlaysLBP3) + - yourReview?.Serialize("yourReview") + - LbpSerializer.StringElement("reviewsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled) + - LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && CommentsEnabled) + - LbpSerializer.StringElement("playerCount", playerCount) + - LbpSerializer.StringElement("reviewCount", this.ReviewCount); + (gameVersion == GameVersion.LittleBigPlanetVita ? + LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)) + : ""); - int yourPlays; - int plays; - int playsComplete; - int playsUnique; - - if (gameVersion == GameVersion.LittleBigPlanetVita) - { - yourPlays = yourVisitedStats?.PlaysLBPVita ?? 0; - plays = this.PlaysLBPVita; - playsComplete = this.PlaysLBPVitaComplete; - playsUnique = this.PlaysLBPVitaUnique; - } - else - { - yourPlays = yourVisitedStats?.PlaysLBP2 ?? 0; - plays = this.PlaysLBP2; - playsComplete = this.PlaysLBP2Complete; - playsUnique = this.PlaysLBP2Unique; - } - - slotData += LbpSerializer.StringElement("yourlbp2PlayCount", yourPlays) + - LbpSerializer.StringElement("lbp2PlayCount", plays) + - LbpSerializer.StringElement("playCount", plays) + - LbpSerializer.StringElement("lbp2CompletionCount", playsComplete) + - LbpSerializer.StringElement("completionCount", playsComplete) + - LbpSerializer.StringElement("lbp2UniquePlayCount", playsUnique) + // not actually used ingame, as per above comment - LbpSerializer.StringElement("uniquePlayCount", playsUnique); return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } diff --git a/ProjectLighthouse/Levels/VisitedLevel.cs b/ProjectLighthouse/Levels/VisitedLevel.cs index 93a4c4f0..5e09bfbc 100644 --- a/ProjectLighthouse/Levels/VisitedLevel.cs +++ b/ProjectLighthouse/Levels/VisitedLevel.cs @@ -23,5 +23,4 @@ public class VisitedLevel public int PlaysLBP1 { get; set; } public int PlaysLBP2 { get; set; } public int PlaysLBP3 { get; set; } - public int PlaysLBPVita { get; set; } } \ No newline at end of file diff --git a/ProjectLighthouse/Match/Rooms/RoomHelper.cs b/ProjectLighthouse/Match/Rooms/RoomHelper.cs index b68af7f9..d7b8a2de 100644 --- a/ProjectLighthouse/Match/Rooms/RoomHelper.cs +++ b/ProjectLighthouse/Match/Rooms/RoomHelper.cs @@ -184,7 +184,7 @@ public class RoomHelper } [SuppressMessage("ReSharper", "InvertIf")] - public static void CleanupRooms(int? hostId = null, Room? newRoom = null, Database? database = null) + public static Task CleanupRooms(int? hostId = null, Room? newRoom = null, Database? database = null) { #if DEBUG Stopwatch stopwatch = new(); @@ -276,5 +276,7 @@ public class RoomHelper Logger.Info(logText, LogArea.Match); } } + + return Task.FromResult(0); } } \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20220808053519_RemoveVitaPlayCount.cs b/ProjectLighthouse/Migrations/20220808053519_RemoveVitaPlayCount.cs new file mode 100644 index 00000000..cdc0fb2b --- /dev/null +++ b/ProjectLighthouse/Migrations/20220808053519_RemoveVitaPlayCount.cs @@ -0,0 +1,69 @@ +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + + [DbContext(typeof(Database))] + [Migration("20220808053519_RemoveVitaPlayCount")] + public partial class RemoveVitaPlayCount : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("UPDATE Slots SET PlaysLbp2 = PlaysLBP2 + PlaysLBPVita"); + migrationBuilder.Sql("UPDATE Slots SET PlaysLBP2Complete = PlaysLBP2Complete + PlaysLBPVitaComplete"); + migrationBuilder.Sql("UPDATE Slots SET PlaysLBP2Unique = PlaysLBP2Unique + PlaysLBPVitaUnique"); + migrationBuilder.Sql("UPDATE VisitedLevels SET PlaysLBP2 = PlaysLBP2 + PlaysLBPVita"); + + migrationBuilder.DropColumn( + name: "PlaysLBPVita", + table: "Slots"); + + migrationBuilder.DropColumn( + name: "PlaysLBPVitaComplete", + table: "Slots"); + + migrationBuilder.DropColumn( + name: "PlaysLBPVitaUnique", + table: "Slots"); + + migrationBuilder.DropColumn( + name: "PlaysLBPVita", + table: "VisitedLevels"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PlaysLBPVita", + table: "Slots", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "PlaysLBPVitaComplete", + table: "Slots", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "PlaysLBPVitaUnique", + table: "Slots", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "PlaysLBPVita", + table: "VisitedLevels", + type: "int", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/ProjectLighthouse/PlayerData/Profiles/User.cs b/ProjectLighthouse/PlayerData/Profiles/User.cs index 9c024713..0be2e24e 100644 --- a/ProjectLighthouse/PlayerData/Profiles/User.cs +++ b/ProjectLighthouse/PlayerData/Profiles/User.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -29,14 +30,14 @@ public class User } public int UserId { get; set; } - public string Username { get; set; } + public string Username { get; set; } = ""; #nullable enable [JsonIgnore] - public string? EmailAddress { get; set; } = null; + public string? EmailAddress { get; set; } #nullable disable - public bool EmailAddressVerified { get; set; } = false; + public bool EmailAddressVerified { get; set; } [JsonIgnore] public string Password { get; set; } @@ -82,9 +83,19 @@ public class User [JsonIgnore] public int PhotosByMe => this.database.Photos.Count(p => p.CreatorId == this.UserId); - [NotMapped] - [JsonIgnore] - public int PhotosWithMe => Enumerable.Sum(this.database.Photos, photo => photo.Subjects.Count(subject => subject.User.UserId == this.UserId)); + private int PhotosWithMe() + { + List photoSubjectIds = new(); + photoSubjectIds.AddRange(this.database.PhotoSubjects.Where(p => p.UserId == this.UserId).Select(p => p.PhotoSubjectId)); + + var list = this.database.Photos.Select(p => new + { + p.PhotoId, + p.PhotoSubjectCollection, + }).ToList(); + List photoIds = (from v in list where photoSubjectIds.Any(ps => v.PhotoSubjectCollection.Contains(ps.ToString())) select v.PhotoId).ToList(); + return this.database.Photos.Count(p => photoIds.Any(pId => p.PhotoId == pId) && p.CreatorId != this.UserId); + } [JsonIgnore] public int LocationId { get; set; } @@ -156,8 +167,10 @@ public class User [JsonIgnore] public PermissionLevel PermissionLevel { get; set; } = PermissionLevel.Default; + #nullable enable [JsonIgnore] public string? BannedReason { get; set; } + #nullable disable #nullable enable [JsonIgnore] @@ -181,36 +194,36 @@ public class User string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + LbpSerializer.StringElement("game", (int)gameVersion) + this.serializeSlots(gameVersion) + - LbpSerializer.StringElement("lists", this.Lists) + - LbpSerializer.StringElement + LbpSerializer.StringElement("lists", this.Lists, true) + + LbpSerializer.StringElement ( "lists_quota", - ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota + ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota, + true ) + // technically not a part of the user but LBP expects it - LbpSerializer.StringElement("biography", this.Biography) + - LbpSerializer.StringElement("reviewCount", this.Reviews) + - LbpSerializer.StringElement("commentCount", this.Comments) + - LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) + - LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) + - LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.ProfileCommentsEnabled && CommentsEnabled) + - LbpSerializer.StringElement("location", this.Location.Serialize()) + - LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) + - LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) + - LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels) + - LbpSerializer.StringElement("pins", this.Pins) + + LbpSerializer.StringElement("heartCount", this.Hearts, true) + this.serializeEarth(gameVersion) + - LbpSerializer.BlankElement("photos") + - LbpSerializer.StringElement("heartCount", this.Hearts) + - LbpSerializer.StringElement("yay2", this.YayHash) + - LbpSerializer.StringElement("boo2", this.BooHash) + - LbpSerializer.StringElement("meh2", this.MehHash); + LbpSerializer.StringElement("yay2", this.YayHash, true) + + LbpSerializer.StringElement("boo2", this.BooHash, true) + + LbpSerializer.StringElement("meh2", this.MehHash, true) + + LbpSerializer.StringElement("biography", this.Biography, true) + + LbpSerializer.StringElement("reviewCount", this.Reviews, true) + + LbpSerializer.StringElement("commentCount", this.Comments, true) + + LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe, true) + + LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe(), true) + + LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.ProfileCommentsEnabled && this.CommentsEnabled) + + LbpSerializer.StringElement("location", this.Location.Serialize()) + + LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels, true) + + LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers, true) + + LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels, true) + + LbpSerializer.StringElement("pins", this.Pins, true); return LbpSerializer.TaggedStringElement("user", user, "type", "user"); } private string serializeEarth(GameVersion gameVersion) { - return LbpSerializer.StringElement + return LbpSerializer.StringElement ( "planets", gameVersion switch @@ -219,7 +232,8 @@ public class User GameVersion.LittleBigPlanet3 => this.PlanetHashLBP3, GameVersion.LittleBigPlanetVita => this.PlanetHashLBPVita, _ => "", // other versions do not have custom planets - } + }, + true ); } @@ -243,52 +257,45 @@ public class User [XmlIgnore] public int EntitledSlots => ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots + this.AdminGrantedSlots; - /// - /// The number of slots remaining on the earth - /// [JsonIgnore] - public int FreeSlots => this.EntitledSlots - this.UsedSlots; + [XmlIgnore] + private int CrossControlSlots => this.database.Slots.Count(s => s.CreatorId == this.UserId && s.CrossControllerRequired); [JsonIgnore] [XmlIgnore] public int AdminGrantedSlots { get; set; } - private static readonly string[] slotTypes = - { - "lbp2", "lbp3", "crossControl", - }; - private string serializeSlots(GameVersion gameVersion) { string slots = string.Empty; - - string[] slotTypesLocal; + int entitledSlots = this.EntitledSlots; + Dictionary usedSlots = new(); if (gameVersion == GameVersion.LittleBigPlanetVita) { - slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanetVita)); - slotTypesLocal = new[] - { - "lbp2", - }; + usedSlots.Add("lbp2", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanetVita)); } else { - slots += LbpSerializer.StringElement("lbp1UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet1)); - slots += LbpSerializer.StringElement("lbp2UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet2)); - slots += LbpSerializer.StringElement("lbp3UsedSlots", this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet3)); - slotTypesLocal = slotTypes; + int lbp1Used = this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet1); + int lbp2Used = this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet2); + int lbp3Used = this.GetUsedSlotsForGame(GameVersion.LittleBigPlanet3); + int crossControlUsed = this.CrossControlSlots; + usedSlots.Add("crossControl", crossControlUsed); + usedSlots.Add("lbp2", lbp2Used); + usedSlots.Add("lbp3", lbp3Used); + // these 3 actually correspond to lbp1 only despite the name + slots += LbpSerializer.StringElement("lbp1UsedSlots", lbp1Used); + slots += LbpSerializer.StringElement("entitledSlots", entitledSlots); + slots += LbpSerializer.StringElement("freeSlots", entitledSlots - lbp1Used); } - slots += LbpSerializer.StringElement("entitledSlots", this.EntitledSlots); - slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); - - foreach (string slotType in slotTypesLocal) + foreach (KeyValuePair entry in usedSlots) { - slots += LbpSerializer.StringElement(slotType + "EntitledSlots", this.EntitledSlots); - // ReSharper disable once StringLiteralTypo - slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); - slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); + slots += LbpSerializer.StringElement(entry.Key + "UsedSlots", entry.Value); + slots += LbpSerializer.StringElement(entry.Key + "EntitledSlots", entitledSlots); + slots += LbpSerializer.StringElement(entry.Key + (entry.Key == "crossControl" ? "PurchsedSlots" : "PurchasedSlots"), 0); + slots += LbpSerializer.StringElement(entry.Key + "FreeSlots", entitledSlots - entry.Value); } return slots; diff --git a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 543da4f0..92758398 100644 --- a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -323,15 +323,6 @@ namespace ProjectLighthouse.Migrations b.Property("PlaysLBP3Unique") .HasColumnType("int"); - b.Property("PlaysLBPVita") - .HasColumnType("int"); - - b.Property("PlaysLBPVitaComplete") - .HasColumnType("int"); - - b.Property("PlaysLBPVitaUnique") - .HasColumnType("int"); - b.Property("ResourceCollection") .IsRequired() .HasColumnType("longtext"); @@ -376,9 +367,6 @@ namespace ProjectLighthouse.Migrations b.Property("PlaysLBP3") .HasColumnType("int"); - b.Property("PlaysLBPVita") - .HasColumnType("int"); - b.Property("SlotId") .HasColumnType("int"); diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index c6c72de0..484d1306 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using YamlDotNet.Core.Tokens; namespace LBPUnion.ProjectLighthouse.Serialization; @@ -20,6 +22,8 @@ public static class LbpSerializer public static string StringElement(string key, object value) => $"<{key}>{value}"; + public static string StringElement(string key, object value, bool omitIfDefault) => !omitIfDefault || value != null && !Equals(value, default(T)) && (!value.GetType().IsValueType || !Activator.CreateInstance(value.GetType())!.Equals(value)) ? $"<{key}>{value}" : ""; + public static string TaggedStringElement (KeyValuePair pair, KeyValuePair tagPair) => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; diff --git a/README.md b/README.md index a91d5d3d..2ecd7c8c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Project Lighthouse +[![Continuous Integration](https://github.com/LBPUnion/ProjectLighthouse/actions/workflows/ci.yml/badge.svg)](https://github.com/LBPUnion/ProjectLighthouse/actions/workflows/ci.yml) +![GitHub commit activity](https://img.shields.io/github/commit-activity/m/LBPUnion/ProjectLighthouse) +![GitHub contributors](https://img.shields.io/github/contributors/LBPUnion/ProjectLighthouse) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/LBPUnion/ProjectLighthouse) +![Source Lines of Code](https://tokei.ekzhang.com/b1/github/LBPUnion/ProjectLighthouse) + Project Lighthouse is a clean-room, open-source custom server for LittleBigPlanet. This is a project conducted by the [LBP Union Ministry of Technology Research and Development team](https://www.lbpunion.com/technology). diff --git a/scripts-and-tools/example-nginx-lighthouse.conf b/scripts-and-tools/example-nginx-lighthouse.conf index 368556bb..44e89313 100644 --- a/scripts-and-tools/example-nginx-lighthouse.conf +++ b/scripts-and-tools/example-nginx-lighthouse.conf @@ -8,6 +8,12 @@ server { ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers ALL; ssl_prefer_server_ciphers off; + + # Enable gzip Compression + gzip on; + gzip_types *; + gzip_http_version 1.1; + gzip_min_length 100; # Server locations # Technically, the ports dont follow standards, @@ -52,4 +58,4 @@ server { proxy_pass http://127.0.0.1:10061; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } -} \ No newline at end of file +}