diff --git a/.idea/.idea.ProjectLighthouse/.idea/discord.xml b/.idea/.idea.ProjectLighthouse/.idea/discord.xml
index d8e95616..62733844 100644
--- a/.idea/.idea.ProjectLighthouse/.idea/discord.xml
+++ b/.idea/.idea.ProjectLighthouse/.idea/discord.xml
@@ -2,6 +2,8 @@
+
+
\ No newline at end of file
diff --git a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs
index 5aa82220..33da6dcb 100644
--- a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs
+++ b/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs
@@ -16,8 +16,8 @@ public class DatabaseTests : LighthouseServerTest
await using Database database = new();
int rand = new Random().Next();
- User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
- User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
+ User userA = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken());
+ User userB = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken());
Assert.NotNull(userA);
Assert.NotNull(userB);
diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings
index b64de555..5d18923a 100644
--- a/ProjectLighthouse.sln.DotSettings
+++ b/ProjectLighthouse.sln.DotSettings
@@ -133,6 +133,7 @@
True
True
True
+ True
True
True
True
@@ -144,4 +145,5 @@
True
True
True
+ True
True
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/Api/UserEndpoints.cs b/ProjectLighthouse/Controllers/Api/UserEndpoints.cs
index 7fc505eb..63510326 100644
--- a/ProjectLighthouse/Controllers/Api/UserEndpoints.cs
+++ b/ProjectLighthouse/Controllers/Api/UserEndpoints.cs
@@ -1,10 +1,13 @@
#nullable enable
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
+// ReSharper disable RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute
+
namespace LBPUnion.ProjectLighthouse.Controllers.Api;
///
@@ -36,4 +39,21 @@ public class UserEndpoints : ApiEndpointController
return this.Ok(user);
}
+
+ ///
+ /// Gets a user and their information from the database.
+ ///
+ /// The ID of the user
+ /// The user's status
+ /// The user's status, if successful.
+ /// The user could not be found.
+ [HttpGet("user/{id:int}/status")]
+ [ProducesResponseType(typeof(UserStatus), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task GetUserStatus(int id)
+ {
+ UserStatus userStatus = new(this.database, id);
+
+ return this.Ok(userStatus);
+ }
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/GameApi/CommentController.cs b/ProjectLighthouse/Controllers/GameApi/CommentController.cs
index cfca4f09..e2ca484c 100644
--- a/ProjectLighthouse/Controllers/GameApi/CommentController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/CommentController.cs
@@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
@@ -39,14 +38,13 @@ public class CommentController : ControllerBase
return this.Ok();
}
-
[HttpGet("comments/user/{slotId:int}")]
[HttpGet("userComments/{username}")]
public async Task GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, int? slotId)
{
User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
-
+
int targetId = slotId.GetValueOrDefault();
CommentType type = CommentType.Level;
if (!string.IsNullOrWhiteSpace(username))
@@ -55,24 +53,24 @@ public class CommentController : ControllerBase
type = CommentType.Profile;
}
- List comments = await this.database.Comments
- .Include(c => c.Poster)
+ List comments = await this.database.Comments.Include
+ (c => c.Poster)
.Where(c => c.TargetId == targetId && c.Type == type)
.OrderByDescending(c => c.Timestamp)
.Skip(pageStart - 1)
- .Take(Math.Min(pageSize,
- 30))
+ .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 outputXml = comments.Aggregate
+ (string.Empty, (current, comment) => current + comment.Serialize(this.getReaction(user.UserId, comment.CommentId).Result));
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
}
- public async Task getReaction(int userId, int commentId)
+ 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;
}
@@ -80,11 +78,11 @@ public class CommentController : ControllerBase
[HttpPost("postComment/user/{slotId:int}")]
public async Task PostComment(string? username, int? slotId)
{
- this.Request.Body.Position = 0;
+ this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Comment));
- Comment? comment = (Comment?) serializer.Deserialize(new StringReader(bodyString));
+ Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
CommentType type = (slotId.GetValueOrDefault() == 0 ? CommentType.Profile : CommentType.Level);
@@ -112,6 +110,7 @@ public class CommentController : ControllerBase
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
if (comment == null) return this.NotFound();
+
// if you are not the poster
if (comment.PosterUserId != user.UserId)
{
diff --git a/ProjectLighthouse/Controllers/GameApi/LoginController.cs b/ProjectLighthouse/Controllers/GameApi/LoginController.cs
index 739e5322..20ec33ae 100644
--- a/ProjectLighthouse/Controllers/GameApi/LoginController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/LoginController.cs
@@ -34,10 +34,6 @@ public class LoginController : ControllerBase
await this.Request.Body.CopyToAsync(ms);
byte[] loginData = ms.ToArray();
- #if DEBUG
- await IOFile.WriteAllBytesAsync($"npTicket-{TimestampHelper.TimestampMillis}.txt", loginData);
- #endif
-
NPTicket? npTicket;
try
{
@@ -145,7 +141,7 @@ public class LoginController : ControllerBase
await this.database.SaveChangesAsync();
// Create a new room on LBP2/3/Vita
- if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion);
+ if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion, token.Platform);
return this.Ok
(
diff --git a/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs b/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs
index 7ba10605..d250fb44 100644
--- a/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs
@@ -74,14 +74,14 @@ public class MatchController : ControllerBase
#endregion
- await LastContactHelper.SetLastContact(user, gameToken.GameVersion);
+ await LastContactHelper.SetLastContact(user, gameToken.GameVersion, gameToken.Platform);
#region Process match data
if (matchData is UpdateMyPlayerData playerData)
{
MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
- Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, true);
+ Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, gameToken.Platform, true);
if (playerData.RoomState != null)
if (room != null && Equals(room.Host, user))
@@ -90,7 +90,7 @@ public class MatchController : ControllerBase
if (matchData is FindBestRoom && MatchHelper.UserLocations.Count > 1)
{
- FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.UserLocation);
+ FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.Platform, gameToken.UserLocation);
if (response == null) return this.NotFound();
@@ -112,7 +112,7 @@ public class MatchController : ControllerBase
}
// Create a new one as requested
- RoomHelper.CreateRoom(users, gameToken.GameVersion, createRoom.RoomSlot);
+ RoomHelper.CreateRoom(users, gameToken.GameVersion, gameToken.Platform, createRoom.RoomSlot);
}
if (matchData is UpdatePlayersInRoom updatePlayersInRoom)
diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs
index b7128d8d..bcfdd34a 100644
--- a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs
@@ -33,10 +33,13 @@ public class PublishController : ControllerBase
[HttpPost("startPublish")]
public async Task StartPublish()
{
- User? user = await this.database.UserFromGameRequest(this.Request);
- if (user == null) return this.StatusCode(403, "");
+ (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
- if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
+ if (userAndToken == null) return this.StatusCode(403, "");
+
+ // ReSharper disable once PossibleInvalidOperationException
+ User user = userAndToken.Value.Item1;
+ GameToken gameToken = userAndToken.Value.Item2;
Slot? slot = await this.getSlotFromBody();
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
@@ -52,6 +55,10 @@ public class PublishController : ControllerBase
if (oldSlot == null) return this.NotFound();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
}
+ else if (user.GetUsedSlotsForGame(gameToken.GameVersion, database) > ServerSettings.Instance.EntitledSlots)
+ {
+ return this.StatusCode(403, "");
+ }
slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here
@@ -76,9 +83,6 @@ public class PublishController : ControllerBase
// ReSharper disable once PossibleInvalidOperationException
User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2;
-
- if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
-
Slot? slot = await this.getSlotFromBody();
if (slot?.Location == null) return this.BadRequest();
@@ -133,6 +137,11 @@ public class PublishController : ControllerBase
return this.Ok(oldSlot.Serialize(gameToken.GameVersion));
}
+ if (user.GetUsedSlotsForGame(gameToken.GameVersion, database) > ServerSettings.Instance.EntitledSlots)
+ {
+ return this.StatusCode(403, "");
+ }
+
//TODO: parse location in body
Location l = new()
{
diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs
index e7ad7d7f..71bbc3f4 100644
--- a/ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs
@@ -17,7 +17,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots;
[ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")]
-[Produces("text/plain")]
+[Produces("text/xml")]
public class ReviewController : ControllerBase
{
private readonly Database database;
@@ -141,51 +141,19 @@ public class ReviewController : ControllerBase
GameVersion gameVersion = gameToken.GameVersion;
- Random rand = new();
-
- Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync
- (r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
-
- VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync
- (v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion);
-
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
if (slot == null) return this.BadRequest();
- bool canNowReviewLevel = slot.CreatorId != user.UserId && visitedLevel != null && yourReview == null;
- if (canNowReviewLevel)
- {
- RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync
- (r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
-
- yourReview = new Review();
- yourReview.ReviewerId = user.UserId;
- yourReview.Reviewer = user;
- yourReview.Thumb = ratedLevel?.Rating ?? 0;
- yourReview.Slot = slot;
- yourReview.SlotId = slotId;
- yourReview.Deleted = false;
- yourReview.DeletedBy = DeletedBy.None;
- yourReview.Text = "You haven't reviewed this level yet. Edit this to write one!";
- yourReview.LabelCollection = "";
- yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds();
- }
-
IQueryable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion)
.Include(r => r.Reviewer)
.Include(r => r.Slot)
.OrderByDescending(r => r.ThumbsUp)
- .ThenByDescending(_ => EF.Functions.Random())
+ .ThenByDescending(r => r.Timestamp)
.Skip(pageStart - 1)
.Take(pageSize);
- IEnumerable prependedReviews;
- if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level
- // prepend the fake review to the top of the list to be easily edited
- prependedReviews = reviews.ToList().Prepend(yourReview);
- else prependedReviews = reviews.ToList();
- string inner = prependedReviews.Aggregate
+ string inner = reviews.ToList().Aggregate
(
string.Empty,
(current, review) =>
diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs
index e92785ba..95df5843 100644
--- a/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs
@@ -64,7 +64,9 @@ public class ScoreController : ControllerBase
break;
}
- IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection);
+ IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId)
+ .Where(s => s.PlayerIdCollection == score.PlayerIdCollection)
+ .Where(s => s.Type == score.Type);
if (existingScore.Any())
{
@@ -80,7 +82,7 @@ public class ScoreController : ControllerBase
await this.database.SaveChangesAsync();
- string myRanking = this.getScores(score.SlotId, score.Type, user);
+ string myRanking = this.getScores(score.SlotId, score.Type, user, -1, 5, "scoreboardSegment");
return this.Ok(myRanking);
}
@@ -103,7 +105,7 @@ public class ScoreController : ControllerBase
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
- private string getScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5)
+ private string getScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5, string rootName = "scores")
{
// This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT
@@ -136,11 +138,11 @@ public class ScoreController : ControllerBase
);
string res;
- if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores);
+ if (myScore == null) res = LbpSerializer.StringElement(rootName, serializedScores);
else
res = LbpSerializer.TaggedStringElement
(
- "scores",
+ rootName,
serializedScores,
new Dictionary
{
diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs
index 04d94441..9d89570a 100644
--- a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs
+++ b/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs
@@ -4,9 +4,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
+using LBPUnion.ProjectLighthouse.Types.Reviews;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -24,18 +26,6 @@ public class SlotsController : ControllerBase
this.database = database;
}
- private IQueryable getSlots(GameVersion gameVersion)
- {
- IQueryable query = this.database.Slots.Include(s => s.Creator).Include(s => s.Location);
-
- if (gameVersion == GameVersion.LittleBigPlanetVita || gameVersion == GameVersion.LittleBigPlanetPSP || gameVersion == GameVersion.Unknown)
- {
- return query.Where(s => s.GameVersion == gameVersion && !s.SubLevel);
- }
-
- return query.Where(s => s.GameVersion <= gameVersion && !s.SubLevel);
- }
-
[HttpGet("slots/by")]
public async Task SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
{
@@ -49,8 +39,7 @@ public class SlotsController : ControllerBase
string response = Enumerable.Aggregate
(
- this.getSlots
- (gameVersion)
+ this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId)
.Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)),
@@ -88,13 +77,14 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion;
- Slot? slot = await this.getSlots(gameVersion).FirstOrDefaultAsync(s => s.SlotId == id);
+ Slot? slot = await this.database.Slots.ByGameVersion(gameVersion, true).FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
- return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel));
+ Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == user.UserId);
+ return this.Ok(slot.Serialize(gameVersion, ratedLevel, visitedLevel, review));
}
[HttpGet("slots/cool")]
@@ -129,7 +119,11 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion;
- IQueryable slots = this.getSlots(gameVersion).OrderByDescending(s => s.FirstUploaded).Skip(pageStart - 1).Take(Math.Min(pageSize, 30));
+ IQueryable slots = this.database.Slots.ByGameVersion
+ (gameVersion)
+ .OrderByDescending(s => s.FirstUploaded)
+ .Skip(pageStart - 1)
+ .Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
@@ -160,7 +154,7 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion;
- IQueryable slots = this.getSlots(gameVersion)
+ IQueryable slots = this.database.Slots.ByGameVersion(gameVersion)
.Where(s => s.TeamPick)
.OrderByDescending(s => s.LastUpdated)
.Skip(pageStart - 1)
@@ -194,7 +188,7 @@ public class SlotsController : ControllerBase
GameVersion gameVersion = token.GameVersion;
- IEnumerable slots = this.getSlots(gameVersion).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30));
+ IEnumerable slots = this.database.Slots.ByGameVersion(gameVersion).OrderBy(_ => EF.Functions.Random()).Take(Math.Min(pageSize, 30));
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameVersion));
@@ -383,7 +377,7 @@ public class SlotsController : ControllerBase
{
if (version == GameVersion.LittleBigPlanetVita || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown)
{
- return this.getSlots(version);
+ return this.database.Slots.ByGameVersion(version);
}
string _dateFilterType = dateFilterType ?? "";
diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs
index 3f84078f..ad99952e 100644
--- a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs
+++ b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs
@@ -62,10 +62,7 @@ public class AdminSlotController : ControllerBase
if (slot.Location == null) throw new ArgumentNullException();
- this.database.Locations.Remove(slot.Location);
- this.database.Slots.Remove(slot);
-
- await this.database.SaveChangesAsync();
+ await this.database.RemoveSlot(slot);
return this.Ok();
}
diff --git a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs b/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs
index 854f05c9..cc690c93 100644
--- a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs
+++ b/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs
@@ -26,7 +26,7 @@ public class RoomVisualizerController : ControllerBase
return this.NotFound();
#else
List users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).ToListAsync();
- RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2);
+ RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2, Platform.PS3);
foreach (User user in users)
{
diff --git a/ProjectLighthouse/Controllers/Website/SlotPageController.cs b/ProjectLighthouse/Controllers/Website/SlotPageController.cs
index 92edbb3e..24968595 100644
--- a/ProjectLighthouse/Controllers/Website/SlotPageController.cs
+++ b/ProjectLighthouse/Controllers/Website/SlotPageController.cs
@@ -1,5 +1,7 @@
#nullable enable
using System.Threading.Tasks;
+using Kettu;
+using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
@@ -34,15 +36,20 @@ public class SlotPageController : ControllerBase
return this.Redirect($"~/slot/{id}#{commentId}");
}
- [HttpGet("postComment")]
- public async Task PostComment([FromRoute] int id, [FromQuery] string? msg)
+ [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");
- if (msg == null) return this.Redirect("~/slot/" + id);
+ if (msg == null)
+ {
+ Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance);
+ return this.Redirect("~/slot/" + id);
+ }
await this.database.PostComment(user, id, CommentType.Level, msg);
+ Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance);
return this.Redirect("~/slot/" + id);
}
diff --git a/ProjectLighthouse/Controllers/Website/UserPageController.cs b/ProjectLighthouse/Controllers/Website/UserPageController.cs
index c22e1623..627ff511 100644
--- a/ProjectLighthouse/Controllers/Website/UserPageController.cs
+++ b/ProjectLighthouse/Controllers/Website/UserPageController.cs
@@ -1,10 +1,9 @@
#nullable enable
-using System;
using System.Threading.Tasks;
+using Kettu;
+using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
-using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website;
@@ -20,20 +19,6 @@ public class UserPageController : ControllerBase
this.database = database;
}
- [HttpGet("heart")]
- public async Task HeartUser([FromRoute] int id)
- {
- User? user = this.database.UserFromWebRequest(this.Request);
- if (user == 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);
-
- return this.Redirect("~/user/" + id);
- }
-
[HttpGet("rateComment")]
public async Task RateComment([FromRoute] int id, [FromQuery] int? commentId, [FromQuery] int? rating)
{
@@ -45,15 +30,34 @@ public class UserPageController : ControllerBase
return this.Redirect($"~/user/{id}#{commentId}");
}
- [HttpGet("postComment")]
- public async Task PostComment([FromRoute] int id, [FromQuery] string? msg)
+ [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");
- if (msg == null) return this.Redirect("~/user/" + id);
+ if (msg == null)
+ {
+ Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance);
+ return this.Redirect("~/user/" + id);
+ }
await this.database.PostComment(user, id, CommentType.Profile, msg);
+ Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance);
+
+ return this.Redirect("~/user/" + id);
+ }
+
+ [HttpGet("heart")]
+ public async Task HeartUser([FromRoute] int id)
+ {
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == 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);
return this.Redirect("~/user/" + id);
}
diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs
index 6ae7f4e2..0ae12fde 100644
--- a/ProjectLighthouse/Database.cs
+++ b/ProjectLighthouse/Database.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types;
@@ -46,7 +47,17 @@ public class Database : DbContext
public async Task CreateUser(string username, string password)
{
- if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash");
+ if (!password.StartsWith('$')) throw new ArgumentException(nameof(password) + " is not a BCrypt hash");
+
+ // 16 is PSN max, 3 is PSN minimum
+ if (!ServerStatics.IsUnitTesting || !username.StartsWith("unitTestUser"))
+ {
+ if (username.Length > 16 || username.Length < 3) throw new ArgumentException(nameof(username) + " is either too long or too short");
+
+ Regex regex = new("^[a-zA-Z0-9_.-]*$");
+
+ if (!regex.IsMatch(username)) throw new ArgumentException(nameof(username) + " does not match the username regex");
+ }
User user;
if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user;
@@ -82,6 +93,7 @@ public class Database : DbContext
UserId = user.UserId,
UserLocation = userLocation,
GameVersion = npTicket.GameVersion,
+ Platform = npTicket.Platform,
};
this.GameTokens.Add(gameToken);
@@ -143,6 +155,8 @@ public class Database : DbContext
public async Task PostComment(User user, int targetId, CommentType type, string message)
{
+ if (message.Length > 100) return false;
+
if (type == CommentType.Profile)
{
User? targetUser = await this.Users.FirstOrDefaultAsync(u => u.UserId == targetId);
@@ -151,7 +165,7 @@ public class Database : DbContext
else
{
Slot? targetSlot = await this.Slots.FirstOrDefaultAsync(u => u.SlotId == targetId);
- if(targetSlot == null) return false;
+ if (targetSlot == null) return false;
}
this.Comments.Add
diff --git a/ProjectLighthouse/Helpers/Extensions/SlotsExtensions.cs b/ProjectLighthouse/Helpers/Extensions/SlotsExtensions.cs
new file mode 100644
index 00000000..457ef413
--- /dev/null
+++ b/ProjectLighthouse/Helpers/Extensions/SlotsExtensions.cs
@@ -0,0 +1,31 @@
+using System.Linq;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Levels;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Helpers.Extensions;
+
+public static class SlotsExtensions
+{
+ public static IQueryable ByGameVersion
+ (this DbSet set, GameVersion gameVersion, bool includeSublevels = false)
+ => set.AsQueryable().ByGameVersion(gameVersion, includeSublevels);
+
+ public static IQueryable ByGameVersion(this IQueryable queryable, GameVersion gameVersion, bool includeSublevels = false)
+ {
+ IQueryable query = queryable.Include(s => s.Creator).Include(s => s.Location);
+
+ if (gameVersion == GameVersion.LittleBigPlanetVita || gameVersion == GameVersion.LittleBigPlanetPSP || gameVersion == GameVersion.Unknown)
+ {
+ query = query.Where(s => s.GameVersion == gameVersion);
+ }
+ else
+ {
+ query = query.Where(s => s.GameVersion <= gameVersion);
+ }
+
+ if (!includeSublevels) query = query.Where(s => !s.SubLevel);
+
+ return query;
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs
index 9d8c4f20..e3e39d17 100644
--- a/ProjectLighthouse/Helpers/FileHelper.cs
+++ b/ProjectLighthouse/Helpers/FileHelper.cs
@@ -1,7 +1,13 @@
+#nullable enable
using System;
+using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Kettu;
+using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types.Files;
using LBPUnion.ProjectLighthouse.Types.Settings;
@@ -101,4 +107,44 @@ public static class FileHelper
}
public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray();
+
+ public static void ConvertAllTexturesToPng()
+ {
+ EnsureDirectoryCreated(Path.Combine(Environment.CurrentDirectory, "png"));
+ if (Directory.Exists("r"))
+ {
+ Logger.Log
+ ("Converting all textures to PNG. This may take a while if this is the first time running this operation...", LoggerLevelStartup.Instance);
+
+ ConcurrentQueue fileQueue = new();
+
+ foreach (string filename in Directory.GetFiles("r")) fileQueue.Enqueue(filename);
+
+ for(int i = 0; i < Environment.ProcessorCount; i++)
+ {
+ Task.Factory.StartNew
+ (
+ () =>
+ {
+ while (fileQueue.TryDequeue(out string? filename))
+ {
+ LbpFile? file = LbpFile.FromHash(filename.Replace("r" + Path.DirectorySeparatorChar, ""));
+ if (file == null) continue;
+
+ if (file.FileType == LbpFileType.Jpeg || file.FileType == LbpFileType.Png || file.FileType == LbpFileType.Texture)
+ {
+ ImageHelper.LbpFileToPNG(file);
+ }
+ }
+ }
+ );
+ }
+
+ while (!fileQueue.IsEmpty)
+ {
+ Thread.Sleep(100);
+ }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/LastContactHelper.cs b/ProjectLighthouse/Helpers/LastContactHelper.cs
index cdc7a06d..8f76bf22 100644
--- a/ProjectLighthouse/Helpers/LastContactHelper.cs
+++ b/ProjectLighthouse/Helpers/LastContactHelper.cs
@@ -11,7 +11,7 @@ public static class LastContactHelper
{
private static readonly Database database = new();
- public static async Task SetLastContact(User user, GameVersion gameVersion)
+ public static async Task SetLastContact(User user, GameVersion gameVersion, Platform platform)
{
LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
@@ -28,6 +28,7 @@ public static class LastContactHelper
lastContact.Timestamp = TimestampHelper.Timestamp;
lastContact.GameVersion = gameVersion;
+ lastContact.Platform = platform;
await database.SaveChangesAsync();
}
diff --git a/ProjectLighthouse/Helpers/RoomHelper.cs b/ProjectLighthouse/Helpers/RoomHelper.cs
index 7cca021b..81db373a 100644
--- a/ProjectLighthouse/Helpers/RoomHelper.cs
+++ b/ProjectLighthouse/Helpers/RoomHelper.cs
@@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Threading.Tasks;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Match;
+using LBPUnion.ProjectLighthouse.Types.Profiles;
namespace LBPUnion.ProjectLighthouse.Helpers;
@@ -22,9 +24,25 @@ public class RoomHelper
private static int roomIdIncrement;
+ public static void StartCleanupThread()
+ {
+ // ReSharper disable once FunctionNeverReturns
+ Task.Factory.StartNew
+ (
+ async () =>
+ {
+ while (true)
+ {
+ CleanupRooms();
+ await Task.Delay(10000);
+ }
+ }
+ );
+ }
+
internal static int RoomIdIncrement => roomIdIncrement++;
- public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, string? location)
+ public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, Platform? platform, string? location)
{
if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP)
{
@@ -42,6 +60,7 @@ public class RoomHelper
}
rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList();
+ if (platform != null) rooms = rooms.Where(r => r.RoomPlatform == platform).ToList();
foreach (Room room in rooms)
// Look for rooms looking for players before moving on to rooms that are idle.
@@ -115,7 +134,7 @@ public class RoomHelper
return null;
}
- public static Room CreateRoom(User user, GameVersion roomVersion, RoomSlot? slot = null)
+ public static Room CreateRoom(User user, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null)
=> CreateRoom
(
new List
@@ -123,9 +142,10 @@ public class RoomHelper
user,
},
roomVersion,
+ roomPlatform,
slot
);
- public static Room CreateRoom(List users, GameVersion roomVersion, RoomSlot? slot = null)
+ public static Room CreateRoom(List users, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null)
{
Room room = new()
{
@@ -134,6 +154,7 @@ public class RoomHelper
State = RoomState.Idle,
Slot = slot ?? PodSlot,
RoomVersion = roomVersion,
+ RoomPlatform = roomPlatform,
};
CleanupRooms(room.Host, room);
@@ -143,13 +164,22 @@ public class RoomHelper
return room;
}
- public static Room? FindRoomByUser(User user, GameVersion roomVersion, bool createIfDoesNotExist = false)
+ public static Room? FindRoomByUser(User user, GameVersion roomVersion, Platform roomPlatform, bool createIfDoesNotExist = false)
{
lock(Rooms)
foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player)))
return room;
- return createIfDoesNotExist ? CreateRoom(user, roomVersion) : null;
+ return createIfDoesNotExist ? CreateRoom(user, roomVersion, roomPlatform) : null;
+ }
+
+ public static Room? FindRoomByUserId(int userId)
+ {
+ lock(Rooms)
+ foreach (Room room in Rooms.Where(room => room.Players.Any(player => player.UserId == userId)))
+ return room;
+
+ return null;
}
[SuppressMessage("ReSharper", "InvertIf")]
@@ -157,6 +187,16 @@ public class RoomHelper
{
lock(Rooms)
{
+ int roomCountBeforeCleanup = Rooms.Count;
+
+ // Remove offline players from rooms
+ foreach (Room room in Rooms)
+ {
+ // do not shorten, this prevents collection modified errors
+ List playersToRemove = room.Players.Where(player => player.Status.StatusType == StatusType.Offline).ToList();
+ foreach (User user in playersToRemove) room.Players.Remove(user);
+ }
+
// Delete old rooms based on host
if (host != null)
try
@@ -179,6 +219,13 @@ public class RoomHelper
Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms
Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms
+
+ int roomCountAfterCleanup = Rooms.Count;
+
+ if (roomCountBeforeCleanup != roomCountAfterCleanup)
+ {
+ Logger.Log($"Cleaned up {roomCountBeforeCleanup - roomCountAfterCleanup} rooms.", LoggerLevelMatch.Instance);
+ }
}
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs
index e7175fc3..303dda97 100644
--- a/ProjectLighthouse/Logging/LoggerLevels.cs
+++ b/ProjectLighthouse/Logging/LoggerLevels.cs
@@ -63,6 +63,12 @@ public class LoggerLevelInflux : LoggerLevel
public override string Name => "Influx";
}
+public class LoggerLevelComments : LoggerLevel
+{
+ public static readonly LoggerLevelComments Instance = new();
+ public override string Name => "Comments";
+}
+
public class LoggerLevelAspNet : LoggerLevel
{
diff --git a/ProjectLighthouse/Migrations/20220217045519_AddPlatformForLastContactsAndGameTokens.cs b/ProjectLighthouse/Migrations/20220217045519_AddPlatformForLastContactsAndGameTokens.cs
new file mode 100644
index 00000000..78342cfe
--- /dev/null
+++ b/ProjectLighthouse/Migrations/20220217045519_AddPlatformForLastContactsAndGameTokens.cs
@@ -0,0 +1,41 @@
+using LBPUnion.ProjectLighthouse;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ProjectLighthouse.Migrations
+{
+ [DbContext(typeof(Database))]
+ [Migration("20220217045519_AddPlatformForLastContactsAndGameTokens")]
+ public partial class AddPlatformForLastContactsAndGameTokens : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Platform",
+ table: "LastContacts",
+ type: "int",
+ nullable: false,
+ defaultValue: -1);
+
+ migrationBuilder.AddColumn(
+ name: "Platform",
+ table: "GameTokens",
+ type: "int",
+ nullable: false,
+ defaultValue: -1);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Platform",
+ table: "LastContacts");
+
+ migrationBuilder.DropColumn(
+ name: "Platform",
+ table: "GameTokens");
+ }
+ }
+}
diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
index 21676d01..23e9069d 100644
--- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
+++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
@@ -81,6 +81,9 @@ namespace ProjectLighthouse.Migrations
b.Property("GameVersion")
.HasColumnType("int");
+ b.Property("Platform")
+ .HasColumnType("int");
+
b.Property("Used")
.HasColumnType("tinyint(1)");
@@ -458,6 +461,9 @@ namespace ProjectLighthouse.Migrations
b.Property("GameVersion")
.HasColumnType("int");
+ b.Property("Platform")
+ .HasColumnType("int");
+
b.Property("Timestamp")
.HasColumnType("bigint");
diff --git a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml b/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml
index 0316b86c..265b4951 100644
--- a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml
+++ b/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml
@@ -52,7 +52,7 @@
{
if (version == GameVersion.LittleBigPlanet1 || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) continue;
- FindBestRoomResponse? response = RoomHelper.FindBestRoom(null, version, null);
+ FindBestRoomResponse? response = RoomHelper.FindBestRoom(null, version, null, null);
string text = response == null ? "No room found." : "Room " + response.RoomId;
Best room for @version.ToPrettyString(): @text
@@ -72,7 +72,7 @@
You are currently in this room.
}
- @room.Players.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString()
+ @room.Players.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString()on paltform @room.RoomPlatform
Slot type: @room.Slot.SlotType, slot id: @room.Slot.SlotId
@foreach (User player in room.Players)
{
diff --git a/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml b/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml
index 7d038558..c6114822 100644
--- a/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml
+++ b/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml
@@ -1,7 +1,6 @@
@using System.IO
@using System.Web
@using LBPUnion.ProjectLighthouse.Types.Profiles
-