Lots of bug fixes and performance improvements (#410)

* Many bug fixes and performance enhancements

* Fix warnings and speed up photos with me

* Finish refactoring user serialization

* Finish refactoring user serialization
Use GameTokens instead of User when possible
Prevent negative page sizes

* Fix debug compilation

* Add gzip compression to example nginx config

* Remove deflate changes

* Add UsernameFromWebToken

Co-authored-by: Jayden <jvyden@jvyden.xyz>
This commit is contained in:
Josh 2022-08-12 19:56:17 -05:00 committed by GitHub
parent 8dbd0e63ff
commit d23a264b8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 625 additions and 505 deletions

View file

@ -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;

View file

@ -26,12 +26,12 @@ public class CommentController : ControllerBase
[HttpPost("rateComment/{slotType}/{slotId:int}")]
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId)
{
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<IActionResult> 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<int> 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<IActionResult> 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<IActionResult> 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();

View file

@ -26,8 +26,8 @@ public class FriendsController : ControllerBase
[HttpPost("npdata")]
public async Task<IActionResult> 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;

View file

@ -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<IActionResult> 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<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId);
IQueryable<VisitedLevel> 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<IActionResult> 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<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId);
IQueryable<VisitedLevel> 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

View file

@ -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<int> 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);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.";
[HttpGet("eula")]
public async Task<IActionResult> 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 <https://www.gnu.org/licenses/>.";
[HttpGet("announce")]
public async Task<IActionResult> 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 <https://www.gnu.org/licenses/>.";
[HttpPost("filter")]
public async Task<IActionResult> 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);
}

View file

@ -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<string> 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<int> 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<IActionResult> 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<Photo> 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<IActionResult> 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<Photo> 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<IActionResult> 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<Photo> 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<int> 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<int> 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<IActionResult> 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();

View file

@ -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<IActionResult> 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<IActionResult> 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<IActionResult> 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);

View file

@ -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<Slot> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<Slot> 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<Slot> 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<string, object>
{
{ "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<IActionResult> 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<IActionResult> 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<HeartedProfile> 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<User> 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<string, object>
{
{ "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<IActionResult> 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<IActionResult> 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();
}

View file

@ -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<IActionResult> 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);

View file

@ -30,19 +30,19 @@ public class ReviewController : ControllerBase
[HttpPost("rate/user/{slotId}")]
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
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<IActionResult> 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<IActionResult> 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<IActionResult> 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<Review?> 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<IActionResult> 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<Review?> 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<Review?> 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<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
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<RatedReview> reactions = await this.database.RatedReviews.Where(r => r.ReviewId == review.ReviewId).ToListAsync();
List<int> 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<IActionResult> 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;

View file

@ -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<IActionResult> 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<Score> 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<IActionResult> 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));

View file

@ -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<Slot> slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync();
List<Slot> 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));

View file

@ -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<IActionResult> 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<IActionResult> 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<IActionResult> SUser(int id)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
@ -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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<int, int> playersBySlotId = new();
foreach (Room room in RoomHelper.Rooms)
@ -451,7 +458,7 @@ public class SlotsController : ControllerBase
}
IEnumerable<int> orderedPlayersBySlotId = playersBySlotId
.Skip(pageStart - 1)
.Skip(Math.Max(0, pageStart - 1))
.Take(Math.Min(pageSize, 30))
.OrderByDescending(kvp => kvp.Value)
.Select(kvp => kvp.Key);