mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-14 05:42:27 +00:00
Implement online story features and photos taken in levels (#389)
* Initial commit to support developer slots * Remove hearting story levels, prevent race condition in adding dev slots, and remove LastContactHelper local db object. * Fix photos taken in pod showing wrong level. * Add support for pod and create mode photos * Add time display to photos and added photo display to level page * Add pagination to in game photos * Update in pod description * Fix migration * Adjust wording of photos taken on local slots * Set slot default type to User Fixes old slots being set to developer slots * Apply suggestions * Add player count to developer slots Co-authored-by: Jayden <jvyden@jvyden.xyz>
This commit is contained in:
parent
395412eecb
commit
fdf1988a34
25 changed files with 483 additions and 47 deletions
|
@ -23,32 +23,40 @@ public class CommentController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("rateUserComment/{username}")]
|
[HttpPost("rateUserComment/{username}")]
|
||||||
[HttpPost("rateComment/user/{slotId:int}")]
|
[HttpPost("rateComment/{slotType}/{slotId:int}")]
|
||||||
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, int? slotId)
|
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);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||||
|
|
||||||
bool success = await this.database.RateComment(user, commentId, rating);
|
bool success = await this.database.RateComment(user, commentId, rating);
|
||||||
if (!success) return this.BadRequest();
|
if (!success) return this.BadRequest();
|
||||||
|
|
||||||
return this.Ok();
|
return this.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("comments/user/{slotId:int}")]
|
[HttpGet("comments/{slotType}/{slotId:int}")]
|
||||||
[HttpGet("userComments/{username}")]
|
[HttpGet("userComments/{username}")]
|
||||||
public async Task<IActionResult> GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, int? slotId)
|
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);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
int targetId = slotId.GetValueOrDefault();
|
int targetId = slotId;
|
||||||
CommentType type = CommentType.Level;
|
CommentType type = CommentType.Level;
|
||||||
if (!string.IsNullOrWhiteSpace(username))
|
if (!string.IsNullOrWhiteSpace(username))
|
||||||
{
|
{
|
||||||
targetId = this.database.Users.First(u => u.Username.Equals(username)).UserId;
|
targetId = this.database.Users.First(u => u.Username.Equals(username)).UserId;
|
||||||
type = CommentType.Profile;
|
type = CommentType.Profile;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (SlotHelper.IsTypeInvalid(slotType) || slotId == 0) return this.BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == CommentType.Level && slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||||
|
|
||||||
List<Comment> comments = await this.database.Comments.Include
|
List<Comment> comments = await this.database.Comments.Include
|
||||||
(c => c.Poster)
|
(c => c.Poster)
|
||||||
|
@ -72,8 +80,8 @@ public class CommentController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("postUserComment/{username}")]
|
[HttpPost("postUserComment/{username}")]
|
||||||
[HttpPost("postComment/user/{slotId:int}")]
|
[HttpPost("postComment/{slotType}/{slotId:int}")]
|
||||||
public async Task<IActionResult> PostComment(string? username, int? slotId)
|
public async Task<IActionResult> PostComment(string? username, string? slotType, int slotId)
|
||||||
{
|
{
|
||||||
User? poster = await this.database.UserFromGameRequest(this.Request);
|
User? poster = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (poster == null) return this.StatusCode(403, "");
|
if (poster == null) return this.StatusCode(403, "");
|
||||||
|
@ -86,14 +94,18 @@ public class CommentController : ControllerBase
|
||||||
|
|
||||||
SanitizationHelper.SanitizeStringsInClass(comment);
|
SanitizationHelper.SanitizeStringsInClass(comment);
|
||||||
|
|
||||||
CommentType type = (slotId.GetValueOrDefault() == 0 ? CommentType.Profile : CommentType.Level);
|
CommentType type = (slotId == 0 ? CommentType.Profile : CommentType.Level);
|
||||||
|
|
||||||
|
if (type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest();
|
||||||
|
|
||||||
if (comment == null) return this.BadRequest();
|
if (comment == null) return this.BadRequest();
|
||||||
|
|
||||||
int targetId = slotId.GetValueOrDefault();
|
int targetId = slotId;
|
||||||
|
|
||||||
if (type == CommentType.Profile) targetId = this.database.Users.First(u => u.Username == username).UserId;
|
if (type == CommentType.Profile) targetId = this.database.Users.First(u => u.Username == username).UserId;
|
||||||
|
|
||||||
|
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(poster, targetId, type, comment.Message);
|
||||||
if (success) return this.Ok();
|
if (success) return this.Ok();
|
||||||
|
|
||||||
|
@ -101,8 +113,8 @@ public class CommentController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("deleteUserComment/{username}")]
|
[HttpPost("deleteUserComment/{username}")]
|
||||||
[HttpPost("deleteComment/user/{slotId:int}")]
|
[HttpPost("deleteComment/{slotType}/{slotId:int}")]
|
||||||
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, int? slotId)
|
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
@ -110,6 +122,10 @@ public class CommentController : ControllerBase
|
||||||
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||||
if (comment == null) return this.NotFound();
|
if (comment == null) return this.NotFound();
|
||||||
|
|
||||||
|
if (comment.Type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest();
|
||||||
|
|
||||||
|
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||||
|
|
||||||
// if you are not the poster
|
// if you are not the poster
|
||||||
if (comment.PosterUserId != user.UserId)
|
if (comment.PosterUserId != user.UserId)
|
||||||
{
|
{
|
||||||
|
@ -125,7 +141,7 @@ public class CommentController : ControllerBase
|
||||||
{
|
{
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId);
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId);
|
||||||
// if you aren't the creator of the level
|
// if you aren't the creator of the level
|
||||||
if (slot == null || slot.CreatorId != user.UserId || slotId.GetValueOrDefault() != slot.SlotId)
|
if (slot == null || slot.CreatorId != user.UserId || slotId != slot.SlotId)
|
||||||
{
|
{
|
||||||
return this.StatusCode(403, "");
|
return this.StatusCode(403, "");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ using LBPUnion.ProjectLighthouse.Match.MatchCommands;
|
||||||
using LBPUnion.ProjectLighthouse.Match.Rooms;
|
using LBPUnion.ProjectLighthouse.Match.Rooms;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ public class MatchController : ControllerBase
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
await LastContactHelper.SetLastContact(user, gameToken.GameVersion, gameToken.Platform);
|
await LastContactHelper.SetLastContact(this.database, user, gameToken.GameVersion, gameToken.Platform);
|
||||||
|
|
||||||
#region Process match data
|
#region Process match data
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ using System.Xml.Serialization;
|
||||||
using Discord;
|
using Discord;
|
||||||
using LBPUnion.ProjectLighthouse.Configuration;
|
using LBPUnion.ProjectLighthouse.Configuration;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.Logging;
|
using LBPUnion.ProjectLighthouse.Logging;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
@ -53,6 +53,38 @@ public class PhotosController : ControllerBase
|
||||||
photo.CreatorId = user.UserId;
|
photo.CreatorId = user.UserId;
|
||||||
photo.Creator = user;
|
photo.Creator = user;
|
||||||
|
|
||||||
|
if (photo.XmlLevelInfo != null)
|
||||||
|
{
|
||||||
|
bool validLevel = false;
|
||||||
|
PhotoSlot photoSlot = photo.XmlLevelInfo;
|
||||||
|
if (photoSlot.SlotType is SlotType.Pod or SlotType.Local) photoSlot.SlotId = 0;
|
||||||
|
switch (photoSlot.SlotType)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SlotType.Pod:
|
||||||
|
case SlotType.Local:
|
||||||
|
case SlotType.Developer:
|
||||||
|
{
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId);
|
||||||
|
if (slot != null)
|
||||||
|
photoSlot.SlotId = slot.SlotId;
|
||||||
|
else
|
||||||
|
photoSlot.SlotId = await SlotHelper.GetPlaceholderSlotId(this.database, photoSlot.SlotId, photoSlot.SlotType);
|
||||||
|
validLevel = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: Logger.Warn($"Invalid photo level type: {photoSlot.SlotType}", LogArea.Photos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validLevel) photo.SlotId = photo.XmlLevelInfo.SlotId;
|
||||||
|
}
|
||||||
|
|
||||||
if (photo.Subjects.Count > 4) return this.BadRequest();
|
if (photo.Subjects.Count > 4) return this.BadRequest();
|
||||||
|
|
||||||
if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp;
|
if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp;
|
||||||
|
@ -104,11 +136,23 @@ public class PhotosController : ControllerBase
|
||||||
return this.Ok();
|
return this.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("photos/user/{id:int}")]
|
[HttpGet("photos/{slotType}/{id:int}")]
|
||||||
public async Task<IActionResult> SlotPhotos(int id)
|
public async Task<IActionResult> SlotPhotos([FromQuery] int pageStart, [FromQuery] int pageSize, string slotType, int id)
|
||||||
{
|
{
|
||||||
List<Photo> photos = await this.database.Photos.Include(p => p.Creator).Take(10).ToListAsync();
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id));
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||||
|
|
||||||
|
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||||
|
|
||||||
|
List<Photo> photos = await this.database.Photos.Include(p => p.Creator)
|
||||||
|
.Where(p => p.SlotId == id)
|
||||||
|
.OrderByDescending(s => s.Timestamp)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.ToListAsync();
|
||||||
|
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id, SlotHelper.ParseType(slotType)));
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +170,7 @@ public class PhotosController : ControllerBase
|
||||||
.Skip(pageStart - 1)
|
.Skip(pageStart - 1)
|
||||||
.Take(Math.Min(pageSize, 30))
|
.Take(Math.Min(pageSize, 30))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize());
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +189,7 @@ public class PhotosController : ControllerBase
|
||||||
(s => s.Timestamp)
|
(s => s.Timestamp)
|
||||||
.Skip(pageStart - 1)
|
.Skip(pageStart - 1)
|
||||||
.Take(Math.Min(pageSize, 30))
|
.Take(Math.Min(pageSize, 30))
|
||||||
.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
.Aggregate(string.Empty, (s, photo) => s + photo.Serialize());
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Extensions;
|
using LBPUnion.ProjectLighthouse.Extensions;
|
||||||
using LBPUnion.ProjectLighthouse.Levels;
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
using LBPUnion.ProjectLighthouse.Extensions;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Levels;
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
|
@ -23,13 +24,15 @@ public class ScoreController : ControllerBase
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("scoreboard/user/{id:int}")]
|
[HttpPost("scoreboard/{slotType}/{id:int}")]
|
||||||
public async Task<IActionResult> SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
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);
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
User user = userAndToken.Value.Item1;
|
User user = userAndToken.Value.Item1;
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
@ -43,6 +46,8 @@ public class ScoreController : ControllerBase
|
||||||
|
|
||||||
SanitizationHelper.SanitizeStringsInClass(score);
|
SanitizationHelper.SanitizeStringsInClass(score);
|
||||||
|
|
||||||
|
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||||
|
|
||||||
score.SlotId = id;
|
score.SlotId = id;
|
||||||
|
|
||||||
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
||||||
|
@ -92,15 +97,19 @@ public class ScoreController : ControllerBase
|
||||||
//=> await TopScores(slotId, type);
|
//=> await TopScores(slotId, type);
|
||||||
=> this.Ok(LbpSerializer.BlankElement("scores"));
|
=> this.Ok(LbpSerializer.BlankElement("scores"));
|
||||||
|
|
||||||
[HttpGet("topscores/user/{slotId:int}/{type:int}")]
|
[HttpGet("topscores/{slotType}/{slotId:int}/{type:int}")]
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
public async Task<IActionResult> TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
public async Task<IActionResult> TopScores(string slotType, int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||||
{
|
{
|
||||||
// Get username
|
// Get username
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
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, user, pageStart, pageSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
@ -65,6 +64,47 @@ public class SlotsController : ControllerBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("slotList")]
|
||||||
|
public async Task<IActionResult> GetSlotListAlt([FromQuery] int[] s)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
List<string?> serializedSlots = new();
|
||||||
|
foreach (int slotId in s)
|
||||||
|
{
|
||||||
|
Slot? slot = await this.database.Slots.Include(t => t.Creator).Include(t => t.Location).Where(t => t.SlotId == slotId && t.Type == SlotType.User).FirstOrDefaultAsync();
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
slot = await this.database.Slots.Where(t => t.InternalSlotId == slotId && t.Type == SlotType.Developer).FirstOrDefaultAsync();
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
serializedSlots.Add($"<slot type=\"developer\"><id>{slotId}</id></slot>");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serializedSlots.Add(slot.Serialize());
|
||||||
|
}
|
||||||
|
string serialized = serializedSlots.Aggregate(string.Empty, (current, slot) => slot == null ? current : current + slot);
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.TaggedStringElement("slots", serialized, "total", serializedSlots.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
[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, "");
|
||||||
|
|
||||||
|
int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||||
|
Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId);
|
||||||
|
|
||||||
|
return this.Ok(slot.SerializeDevSlot());
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("s/user/{id:int}")]
|
[HttpGet("s/user/{id:int}")]
|
||||||
public async Task<IActionResult> SUser(int id)
|
public async Task<IActionResult> SUser(int id)
|
||||||
{
|
{
|
||||||
|
@ -458,10 +498,10 @@ public class SlotsController : ControllerBase
|
||||||
if (gameFilterType == "both")
|
if (gameFilterType == "both")
|
||||||
// Get game versions less than the current version
|
// Get game versions less than the current version
|
||||||
// Needs support for LBP3 ("both" = LBP1+2)
|
// Needs support for LBP3 ("both" = LBP1+2)
|
||||||
whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
whereSlots = this.database.Slots.Where(s => s.Type == SlotType.User && s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
||||||
else
|
else
|
||||||
// Get game versions exactly equal to gamefiltertype
|
// Get game versions exactly equal to gamefiltertype
|
||||||
whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
whereSlots = this.database.Slots.Where(s => s.Type == SlotType.User && s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
||||||
|
|
||||||
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
|
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ public class GameServerStartup
|
||||||
if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1)
|
if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1)
|
||||||
// Ignore UserFromGameToken null because user must exist for a token to exist
|
// Ignore UserFromGameToken null because user must exist for a token to exist
|
||||||
await LastContactHelper.SetLastContact
|
await LastContactHelper.SetLastContact
|
||||||
((await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1, gameToken.Platform);
|
(database, (await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1, gameToken.Platform);
|
||||||
}
|
}
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,14 @@ public class LandingPage : BaseLayout
|
||||||
const int maxShownLevels = 5;
|
const int maxShownLevels = 5;
|
||||||
|
|
||||||
this.LatestTeamPicks = await this.Database.Slots.Where
|
this.LatestTeamPicks = await this.Database.Slots.Where
|
||||||
(s => s.TeamPick)
|
(s => s.Type == SlotType.User)
|
||||||
|
.Where(s => s.TeamPick)
|
||||||
.OrderByDescending(s => s.FirstUploaded)
|
.OrderByDescending(s => s.FirstUploaded)
|
||||||
.Take(maxShownLevels)
|
.Take(maxShownLevels)
|
||||||
.Include(s => s.Creator)
|
.Include(s => s.Creator)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
this.NewestLevels = await this.Database.Slots.OrderByDescending(s => s.FirstUploaded).Take(maxShownLevels).Include(s => s.Creator).ToListAsync();
|
this.NewestLevels = await this.Database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).Take(maxShownLevels).Include(s => s.Creator).ToListAsync();
|
||||||
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
@using System.Globalization
|
||||||
|
@using LBPUnion.ProjectLighthouse.Levels
|
||||||
@using LBPUnion.ProjectLighthouse.PlayerData
|
@using LBPUnion.ProjectLighthouse.PlayerData
|
||||||
@using LBPUnion.ProjectLighthouse.Types
|
|
||||||
@model LBPUnion.ProjectLighthouse.PlayerData.Photo
|
@model LBPUnion.ProjectLighthouse.PlayerData.Photo
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +19,27 @@
|
||||||
<b>
|
<b>
|
||||||
<a href="/user/@Model.Creator?.UserId">@Model.Creator?.Username</a>
|
<a href="/user/@Model.Creator?.UserId">@Model.Creator?.Username</a>
|
||||||
</b>
|
</b>
|
||||||
|
@if (Model.Slot != null)
|
||||||
|
{
|
||||||
|
switch (Model.Slot.Type)
|
||||||
|
{
|
||||||
|
case SlotType.User:
|
||||||
|
<span>
|
||||||
|
in level <b><a href="/slot/@Model.SlotId">@Model.Slot.Name</a></b>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case SlotType.Developer:
|
||||||
|
<span>in a story mode level</span>
|
||||||
|
break;
|
||||||
|
case SlotType.Pod:
|
||||||
|
<span>in the pod</span>
|
||||||
|
break;
|
||||||
|
case SlotType.Local:
|
||||||
|
<span>in a level on the moon</span>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
at @DateTime.UnixEpoch.AddSeconds(Model.Timestamp).ToString(CultureInfo.CurrentCulture)
|
||||||
</i>
|
</i>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -124,4 +146,4 @@
|
||||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -40,6 +40,7 @@ public class PhotosPage : BaseLayout
|
||||||
|
|
||||||
this.Photos = await this.Database.Photos.Include
|
this.Photos = await this.Database.Photos.Include
|
||||||
(p => p.Creator)
|
(p => p.Creator)
|
||||||
|
.Include(p => p.Slot)
|
||||||
.Where(p => p.Creator!.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue))
|
.Where(p => p.Creator!.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue))
|
||||||
.OrderByDescending(p => p.Timestamp)
|
.OrderByDescending(p => p.Timestamp)
|
||||||
.Skip(pageNumber * ServerStatics.PageSize)
|
.Skip(pageNumber * ServerStatics.PageSize)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
@using LBPUnion.ProjectLighthouse.Administration
|
@using LBPUnion.ProjectLighthouse.Administration
|
||||||
@using LBPUnion.ProjectLighthouse.Configuration
|
@using LBPUnion.ProjectLighthouse.Configuration
|
||||||
@using LBPUnion.ProjectLighthouse.Extensions
|
@using LBPUnion.ProjectLighthouse.Extensions
|
||||||
|
@using LBPUnion.ProjectLighthouse.PlayerData
|
||||||
@using LBPUnion.ProjectLighthouse.PlayerData.Reviews
|
@using LBPUnion.ProjectLighthouse.PlayerData.Reviews
|
||||||
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SlotPage
|
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SlotPage
|
||||||
|
|
||||||
|
@ -162,7 +163,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (Model.Photos.Count != 0)
|
||||||
|
{
|
||||||
|
<div class="ui purple segment">
|
||||||
|
<h2>Most recent photos</h2>
|
||||||
|
|
||||||
|
<div class="ui center aligned grid">
|
||||||
|
@foreach (Photo photo in Model.Photos)
|
||||||
|
{
|
||||||
|
<div class="eight wide column">
|
||||||
|
@await Html.PartialAsync("Partials/PhotoPartial", photo)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (Model.User != null && Model.User.IsAdmin)
|
@if (Model.User != null && Model.User.IsAdmin)
|
||||||
{
|
{
|
||||||
<div class="ui yellow segment">
|
<div class="ui yellow segment">
|
||||||
|
|
|
@ -15,6 +15,7 @@ public class SlotPage : BaseLayout
|
||||||
{
|
{
|
||||||
public List<Comment> Comments = new();
|
public List<Comment> Comments = new();
|
||||||
public List<Review> Reviews = new();
|
public List<Review> Reviews = new();
|
||||||
|
public List<Photo> Photos = new();
|
||||||
|
|
||||||
public readonly bool CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled;
|
public readonly bool CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled;
|
||||||
public readonly bool ReviewsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled;
|
public readonly bool ReviewsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled;
|
||||||
|
@ -25,7 +26,10 @@ public class SlotPage : BaseLayout
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int id)
|
public async Task<IActionResult> OnGet([FromRoute] int id)
|
||||||
{
|
{
|
||||||
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
Slot? slot = await this.Database.Slots.Include
|
||||||
|
(s => s.Creator)
|
||||||
|
.Where(s => s.Type == SlotType.User)
|
||||||
|
.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (slot == null) return this.NotFound();
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
this.Slot = slot;
|
this.Slot = slot;
|
||||||
|
@ -57,6 +61,12 @@ public class SlotPage : BaseLayout
|
||||||
this.Reviews = new List<Review>();
|
this.Reviews = new List<Review>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Photos = await this.Database.Photos.Include(p => p.Creator)
|
||||||
|
.OrderByDescending(p => p.Timestamp)
|
||||||
|
.Where(r => r.SlotId == id)
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
if (this.User == null) return this.Page();
|
if (this.User == null) return this.Page();
|
||||||
|
|
||||||
foreach (Comment c in this.Comments)
|
foreach (Comment c in this.Comments)
|
||||||
|
|
|
@ -55,6 +55,7 @@ public class SlotsPage : BaseLayout
|
||||||
this.SearchValue = name.Trim();
|
this.SearchValue = name.Trim();
|
||||||
|
|
||||||
this.SlotCount = await this.Database.Slots.Include(p => p.Creator)
|
this.SlotCount = await this.Database.Slots.Include(p => p.Creator)
|
||||||
|
.Where(p => p.Type == SlotType.User)
|
||||||
.Where(p => p.Name.Contains(finalSearch.ToString()))
|
.Where(p => p.Name.Contains(finalSearch.ToString()))
|
||||||
.Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower())))
|
.Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower())))
|
||||||
.Where(p => targetGame == null || p.GameVersion == targetGame)
|
.Where(p => targetGame == null || p.GameVersion == targetGame)
|
||||||
|
@ -66,6 +67,7 @@ public class SlotsPage : BaseLayout
|
||||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||||
|
|
||||||
this.Slots = await this.Database.Slots.Include(p => p.Creator)
|
this.Slots = await this.Database.Slots.Include(p => p.Creator)
|
||||||
|
.Where(p => p.Type == SlotType.User)
|
||||||
.Where(p => p.Name.Contains(finalSearch.ToString()))
|
.Where(p => p.Name.Contains(finalSearch.ToString()))
|
||||||
.Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower())))
|
.Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower())))
|
||||||
.Where(p => targetGame == null || p.GameVersion == targetGame)
|
.Where(p => targetGame == null || p.GameVersion == targetGame)
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class UserPage : BaseLayout
|
||||||
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
||||||
if (this.ProfileUser == null) return this.NotFound();
|
if (this.ProfileUser == null) return this.NotFound();
|
||||||
|
|
||||||
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(6).ToListAsync();
|
this.Photos = await this.Database.Photos.Include(p => p.Slot).OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(6).ToListAsync();
|
||||||
if (this.CommentsEnabled)
|
if (this.CommentsEnabled)
|
||||||
{
|
{
|
||||||
this.Comments = await this.Database.Comments.Include(p => p.Poster)
|
this.Comments = await this.Database.Comments.Include(p => p.Poster)
|
||||||
|
|
|
@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Levels;
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Extensions;
|
namespace LBPUnion.ProjectLighthouse.Extensions;
|
||||||
|
@ -19,6 +18,8 @@ public static class DatabaseExtensions
|
||||||
public static IQueryable<Slot> ByGameVersion
|
public static IQueryable<Slot> ByGameVersion
|
||||||
(this IQueryable<Slot> query, GameVersion gameVersion, bool includeSublevels = false, bool includeCreatorAndLocation = false)
|
(this IQueryable<Slot> query, GameVersion gameVersion, bool includeSublevels = false, bool includeCreatorAndLocation = false)
|
||||||
{
|
{
|
||||||
|
query = query.Where(s => s.Type == SlotType.User);
|
||||||
|
|
||||||
if (includeCreatorAndLocation)
|
if (includeCreatorAndLocation)
|
||||||
{
|
{
|
||||||
query = query.Include(s => s.Creator).Include(s => s.Location);
|
query = query.Include(s => s.Creator).Include(s => s.Location);
|
||||||
|
|
106
ProjectLighthouse/Helpers/SlotHelper.cs
Normal file
106
ProjectLighthouse/Helpers/SlotHelper.cs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class SlotHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static SlotType ParseType(string? slotType)
|
||||||
|
{
|
||||||
|
if (slotType == null) return SlotType.Unknown;
|
||||||
|
return slotType switch
|
||||||
|
{
|
||||||
|
"developer" => SlotType.Developer,
|
||||||
|
"user" => SlotType.User,
|
||||||
|
"moon" => SlotType.Moon,
|
||||||
|
"pod" => SlotType.Pod,
|
||||||
|
"local" => SlotType.Local,
|
||||||
|
_ => SlotType.Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTypeInvalid(string? slotType)
|
||||||
|
{
|
||||||
|
if (slotType == null) return true;
|
||||||
|
return slotType switch
|
||||||
|
{
|
||||||
|
"developer" => false,
|
||||||
|
"user" => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly SemaphoreSlim semaphore = new(1, 1);
|
||||||
|
|
||||||
|
public static async Task<int> GetPlaceholderSlotId(Database database, int guid, SlotType slotType)
|
||||||
|
{
|
||||||
|
int slotId = await database.Slots.Where(s => s.Type == slotType && s.InternalSlotId == guid).Select(s => s.SlotId).FirstOrDefaultAsync();
|
||||||
|
if (slotId != 0) return slotId;
|
||||||
|
|
||||||
|
await semaphore.WaitAsync(TimeSpan.FromSeconds(5));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// if two requests come in at the same time for the same story level which hasn't been generated
|
||||||
|
// one will wait for the lock to be released and the second will be caught by this second check
|
||||||
|
slotId = await database.Slots
|
||||||
|
.Where(s => s.Type == slotType && s.InternalSlotId == guid)
|
||||||
|
.Select(s => s.SlotId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (slotId != 0) return slotId;
|
||||||
|
|
||||||
|
Location? devLocation = await database.Locations.FirstOrDefaultAsync(l => l.Id == 1);
|
||||||
|
if (devLocation == null)
|
||||||
|
{
|
||||||
|
devLocation = new Location
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
};
|
||||||
|
database.Locations.Add(devLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
int devCreatorId = await database.Users.Where(u => u.Username.Length == 0).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||||
|
if (devCreatorId == 0)
|
||||||
|
{
|
||||||
|
User devCreator = new()
|
||||||
|
{
|
||||||
|
Username = "",
|
||||||
|
Banned = true,
|
||||||
|
Biography = "Placeholder author of story levels",
|
||||||
|
BannedReason = "Banned to not show in users list",
|
||||||
|
LocationId = devLocation.Id,
|
||||||
|
};
|
||||||
|
database.Users.Add(devCreator);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
devCreatorId = devCreator.UserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Slot slot = new()
|
||||||
|
{
|
||||||
|
Name = $"{slotType} slot {guid}",
|
||||||
|
Description = $"Placeholder for {slotType} type level",
|
||||||
|
CreatorId = devCreatorId,
|
||||||
|
InternalSlotId = guid,
|
||||||
|
LocationId = devLocation.Id,
|
||||||
|
Type = slotType,
|
||||||
|
};
|
||||||
|
|
||||||
|
database.Slots.Add(slot);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
return slot.SlotId;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -16,7 +17,7 @@ public static class StatisticsHelper
|
||||||
(GameVersion gameVersion)
|
(GameVersion gameVersion)
|
||||||
=> await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300 && l.GameVersion == gameVersion).CountAsync();
|
=> await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300 && l.GameVersion == gameVersion).CountAsync();
|
||||||
|
|
||||||
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
|
public static async Task<int> SlotCount() => await database.Slots.Where(s => s.Type == SlotType.User).CountAsync();
|
||||||
|
|
||||||
public static async Task<int> UserCount() => await database.Users.CountAsync(u => !u.Banned);
|
public static async Task<int> UserCount() => await database.Users.CountAsync(u => !u.Banned);
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,12 @@ public class NewestLevelsCategory : Category
|
||||||
public override string Description { get; set; } = "Levels recently published";
|
public override string Description { get; set; } = "Levels recently published";
|
||||||
public override string IconHash { get; set; } = "g820623";
|
public override string IconHash { get; set; } = "g820623";
|
||||||
public override string Endpoint { get; set; } = "newest";
|
public override string Endpoint { get; set; } = "newest";
|
||||||
public override Slot? GetPreviewSlot(Database database) => database.Slots.OrderByDescending(s => s.FirstUploaded).FirstOrDefault();
|
public override Slot? GetPreviewSlot(Database database) => database.Slots.Where(s => s.Type == SlotType.User).OrderByDescending(s => s.FirstUploaded).FirstOrDefault();
|
||||||
public override IEnumerable<Slot> GetSlots
|
public override IEnumerable<Slot> GetSlots
|
||||||
(Database database, int pageStart, int pageSize)
|
(Database database, int pageStart, int pageSize)
|
||||||
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
=> database.Slots.ByGameVersion(GameVersion.LittleBigPlanet3, false, true)
|
||||||
.OrderByDescending(s => s.FirstUploaded)
|
.OrderByDescending(s => s.FirstUploaded)
|
||||||
.Skip(pageStart - 1)
|
.Skip(pageStart - 1)
|
||||||
.Take(Math.Min(pageSize, 20));
|
.Take(Math.Min(pageSize, 20));
|
||||||
public override int GetTotalSlots(Database database) => database.Slots.Count();
|
public override int GetTotalSlots(Database database) => database.Slots.Count(s => s.Type == SlotType.User);
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -40,14 +41,15 @@ public class Slot
|
||||||
}
|
}
|
||||||
|
|
||||||
[XmlAttribute("type")]
|
[XmlAttribute("type")]
|
||||||
[NotMapped]
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string Type { get; set; } = "user";
|
public SlotType Type { get; set; } = SlotType.User;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[XmlElement("id")]
|
[XmlElement("id")]
|
||||||
public int SlotId { get; set; }
|
public int SlotId { get; set; }
|
||||||
|
|
||||||
|
public int InternalSlotId { get; set; }
|
||||||
|
|
||||||
[XmlElement("name")]
|
[XmlElement("name")]
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
|
|
||||||
|
@ -240,6 +242,24 @@ public class Slot
|
||||||
LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize));
|
LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string SerializeDevSlot()
|
||||||
|
{
|
||||||
|
int comments = this.database.Comments.Count(c => c.Type == CommentType.Level && c.TargetId == this.SlotId);
|
||||||
|
|
||||||
|
int photos = this.database.Photos.Count(c => c.SlotId == this.SlotId);
|
||||||
|
|
||||||
|
int players = RoomHelper.Rooms
|
||||||
|
.Where(r => r.Slot.SlotType == SlotType.Developer && r.Slot.SlotId == this.InternalSlotId)
|
||||||
|
.Sum(r => r.PlayerIds.Count);
|
||||||
|
|
||||||
|
string slotData = LbpSerializer.StringElement("id", this.InternalSlotId) +
|
||||||
|
LbpSerializer.StringElement("playerCount", players) +
|
||||||
|
LbpSerializer.StringElement("commentCount", comments) +
|
||||||
|
LbpSerializer.StringElement("photoCount", photos);
|
||||||
|
|
||||||
|
return LbpSerializer.TaggedStringElement("slot", slotData, "type", "developer");
|
||||||
|
}
|
||||||
|
|
||||||
public string Serialize
|
public string Serialize
|
||||||
(
|
(
|
||||||
GameVersion gameVersion = GameVersion.LittleBigPlanet1,
|
GameVersion gameVersion = GameVersion.LittleBigPlanet1,
|
||||||
|
@ -248,6 +268,8 @@ public class Slot
|
||||||
Review? yourReview = null
|
Review? yourReview = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (this.Type == SlotType.Developer) return this.SerializeDevSlot();
|
||||||
|
|
||||||
int playerCount = RoomHelper.Rooms.Count(r => r.Slot.SlotType == SlotType.User && r.Slot.SlotId == this.SlotId);
|
int playerCount = RoomHelper.Rooms.Count(r => r.Slot.SlotType == SlotType.User && r.Slot.SlotId == this.SlotId);
|
||||||
|
|
||||||
string slotData = LbpSerializer.StringElement("name", this.Name) +
|
string slotData = LbpSerializer.StringElement("name", this.Name) +
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Levels;
|
namespace LBPUnion.ProjectLighthouse.Levels;
|
||||||
|
|
||||||
public enum SlotType
|
public enum SlotType
|
||||||
{
|
{
|
||||||
|
[XmlEnum("developer")]
|
||||||
Developer = 0,
|
Developer = 0,
|
||||||
|
[XmlEnum("user")]
|
||||||
User = 1,
|
User = 1,
|
||||||
|
[XmlEnum("moon")]
|
||||||
Moon = 2,
|
Moon = 2,
|
||||||
Unknown = 3,
|
Unknown = 3,
|
||||||
Unknown2 = 4,
|
Unknown2 = 4,
|
||||||
|
[XmlEnum("pod")]
|
||||||
Pod = 5,
|
Pod = 5,
|
||||||
|
[XmlEnum("local")]
|
||||||
|
Local = 6,
|
||||||
DLC = 8,
|
DLC = 8,
|
||||||
}
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
using LBPUnion.ProjectLighthouse;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ProjectLighthouse.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(Database))]
|
||||||
|
[Migration("20220729002704_DeveloperSlots")]
|
||||||
|
public partial class DeveloperSlots : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "InternalSlotId",
|
||||||
|
table: "Slots",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Type",
|
||||||
|
table: "Slots",
|
||||||
|
type: "int",
|
||||||
|
defaultValue: 1,
|
||||||
|
nullable: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "SlotId",
|
||||||
|
table: "Photos",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Photos_SlotId",
|
||||||
|
table: "Photos",
|
||||||
|
column: "SlotId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Photos_Slots_SlotId",
|
||||||
|
table: "Photos",
|
||||||
|
column: "SlotId",
|
||||||
|
principalTable: "Slots",
|
||||||
|
principalColumn: "SlotId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Photos_Slots_SlotId",
|
||||||
|
table: "Photos");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Photos_SlotId",
|
||||||
|
table: "Photos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "InternalSlotId",
|
||||||
|
table: "Slots");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Type",
|
||||||
|
table: "Slots");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SlotId",
|
||||||
|
table: "Photos");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -210,6 +210,9 @@ namespace ProjectLighthouse.Migrations
|
||||||
b.Property<bool>("InitiallyLocked")
|
b.Property<bool>("InitiallyLocked")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("InternalSlotId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<long>("LastUpdated")
|
b.Property<long>("LastUpdated")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
@ -289,6 +292,10 @@ namespace ProjectLighthouse.Migrations
|
||||||
b.Property<bool>("TeamPick")
|
b.Property<bool>("TeamPick")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.HasKey("SlotId");
|
b.HasKey("SlotId");
|
||||||
|
|
||||||
b.HasIndex("CreatorId");
|
b.HasIndex("CreatorId");
|
||||||
|
@ -461,6 +468,9 @@ namespace ProjectLighthouse.Migrations
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int?>("SlotId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("SmallHash")
|
b.Property<string>("SmallHash")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
@ -472,6 +482,8 @@ namespace ProjectLighthouse.Migrations
|
||||||
|
|
||||||
b.HasIndex("CreatorId");
|
b.HasIndex("CreatorId");
|
||||||
|
|
||||||
|
b.HasIndex("SlotId");
|
||||||
|
|
||||||
b.ToTable("Photos");
|
b.ToTable("Photos");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1006,7 +1018,13 @@ namespace ProjectLighthouse.Migrations
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SlotId");
|
||||||
|
|
||||||
b.Navigation("Creator");
|
b.Navigation("Creator");
|
||||||
|
|
||||||
|
b.Navigation("Slot");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b =>
|
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b =>
|
||||||
|
|
|
@ -9,9 +9,8 @@ namespace LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
|
|
||||||
public static class LastContactHelper
|
public static class LastContactHelper
|
||||||
{
|
{
|
||||||
private static readonly Database database = new();
|
|
||||||
|
|
||||||
public static async Task SetLastContact(User user, GameVersion gameVersion, Platform platform)
|
public static async Task SetLastContact(Database database, User user, GameVersion gameVersion, Platform platform)
|
||||||
{
|
{
|
||||||
LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
|
LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -19,6 +20,10 @@ public class Photo
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
private List<PhotoSubject>? _subjects;
|
private List<PhotoSubject>? _subjects;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
[XmlElement("slot")]
|
||||||
|
public PhotoSlot? XmlLevelInfo;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
[XmlArray("subjects")]
|
[XmlArray("subjects")]
|
||||||
[XmlArrayItem("subject")]
|
[XmlArrayItem("subject")]
|
||||||
|
@ -81,9 +86,34 @@ public class Photo
|
||||||
[ForeignKey(nameof(CreatorId))]
|
[ForeignKey(nameof(CreatorId))]
|
||||||
public User? Creator { get; set; }
|
public User? Creator { get; set; }
|
||||||
|
|
||||||
public string Serialize(int slotId)
|
public int? SlotId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(SlotId))]
|
||||||
|
public Slot? Slot { get; set; }
|
||||||
|
|
||||||
|
public string Serialize()
|
||||||
{
|
{
|
||||||
string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", "user");
|
using Database database = new();
|
||||||
|
var partialSlot = database.Slots.Where(s => s.SlotId == this.SlotId.GetValueOrDefault())
|
||||||
|
.Select(s => new
|
||||||
|
{
|
||||||
|
s.InternalSlotId,
|
||||||
|
s.Type,
|
||||||
|
})
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (partialSlot == null) return this.Serialize(0, SlotType.User);
|
||||||
|
|
||||||
|
int serializedSlotId = partialSlot.InternalSlotId;
|
||||||
|
if (serializedSlotId == 0) serializedSlotId = this.SlotId.GetValueOrDefault();
|
||||||
|
|
||||||
|
return this.Serialize(serializedSlotId, partialSlot.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Serialize(int slotId, SlotType slotType)
|
||||||
|
{
|
||||||
|
|
||||||
|
string slot = LbpSerializer.TaggedStringElement("slot", LbpSerializer.StringElement("id", slotId), "type", slotType.ToString().ToLower());
|
||||||
|
if (slotId == 0) slot = "";
|
||||||
|
|
||||||
string subjectsAggregate = this.Subjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize());
|
string subjectsAggregate = this.Subjects.Aggregate(string.Empty, (s, subject) => s + subject.Serialize());
|
||||||
|
|
||||||
|
|
20
ProjectLighthouse/PlayerData/PhotoSlot.cs
Normal file
20
ProjectLighthouse/PlayerData/PhotoSlot.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using LBPUnion.ProjectLighthouse.Levels;
|
||||||
|
|
||||||
|
namespace LBPUnion.ProjectLighthouse.PlayerData;
|
||||||
|
|
||||||
|
[XmlRoot("slot")]
|
||||||
|
public class PhotoSlot
|
||||||
|
{
|
||||||
|
[XmlAttribute("type")]
|
||||||
|
public SlotType SlotType { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("id")]
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("rootLevel")]
|
||||||
|
public string RootLevel { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("name")]
|
||||||
|
public string LevelName { get; set; }
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue