mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-09-21 08:49:05 +00:00
Initial implementation of recent activity
DB migrations intentionally left out since they aren't finalized
This commit is contained in:
parent
605f9e68c5
commit
23cb1bef1c
86 changed files with 1542 additions and 93 deletions
|
@ -0,0 +1,286 @@
|
|||
using System.Linq.Expressions;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/stream")]
|
||||
[Produces("text/xml")]
|
||||
public class ActivityController : ControllerBase
|
||||
{
|
||||
private readonly DatabaseContext database;
|
||||
|
||||
public ActivityController(DatabaseContext database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
public class ActivityDto
|
||||
{
|
||||
public required ActivityEntity Activity { get; set; }
|
||||
public int? TargetSlotId { get; set; }
|
||||
public int? TargetUserId { get; set; }
|
||||
public int? TargetPlaylistId { get; set; }
|
||||
public int? SlotCreatorId { get; set; }
|
||||
}
|
||||
//TODO refactor this mess into a separate db file or something
|
||||
|
||||
private static Expression<Func<ActivityEntity, ActivityDto>> ActivityToDto()
|
||||
{
|
||||
return a => new ActivityDto
|
||||
{
|
||||
Activity = a,
|
||||
TargetSlotId = a is LevelActivityEntity
|
||||
? ((LevelActivityEntity)a).SlotId
|
||||
: a is PhotoActivityEntity && ((PhotoActivityEntity)a).Photo.PhotoId != 0
|
||||
? ((PhotoActivityEntity)a).Photo.SlotId
|
||||
: a is CommentActivityEntity && ((CommentActivityEntity)a).Comment.Type == CommentType.Level
|
||||
? ((CommentActivityEntity)a).Comment.TargetId
|
||||
: a is ScoreActivityEntity
|
||||
? ((ScoreActivityEntity)a).Score.SlotId
|
||||
: 0,
|
||||
|
||||
TargetUserId = a is UserActivityEntity
|
||||
? ((UserActivityEntity)a).TargetUserId
|
||||
: a is CommentActivityEntity && ((CommentActivityEntity)a).Comment.Type == CommentType.Profile
|
||||
? ((CommentActivityEntity)a).Comment.TargetId
|
||||
: a is PhotoActivityEntity && ((PhotoActivityEntity)a).Photo.SlotId != 0
|
||||
? ((PhotoActivityEntity)a).Photo.CreatorId
|
||||
: 0,
|
||||
TargetPlaylistId = a is PlaylistActivityEntity ? ((PlaylistActivityEntity)a).PlaylistId : 0,
|
||||
};
|
||||
}
|
||||
|
||||
private static IQueryable<IGrouping<ActivityGroup, ActivityEntity>> GroupActivities
|
||||
(IQueryable<ActivityEntity> activityQuery)
|
||||
{
|
||||
return activityQuery.Select(ActivityToDto())
|
||||
.GroupBy(dto => new ActivityGroup
|
||||
{
|
||||
Timestamp = dto.Activity.Timestamp.Date,
|
||||
UserId = dto.Activity.UserId,
|
||||
TargetUserId = dto.TargetUserId,
|
||||
TargetSlotId = dto.TargetSlotId,
|
||||
TargetPlaylistId = dto.TargetPlaylistId,
|
||||
},
|
||||
dto => dto.Activity);
|
||||
}
|
||||
|
||||
private static IQueryable<IGrouping<ActivityGroup, ActivityEntity>> GroupActivities
|
||||
(IQueryable<ActivityDto> activityQuery)
|
||||
{
|
||||
return activityQuery.GroupBy(dto => new ActivityGroup
|
||||
{
|
||||
Timestamp = dto.Activity.Timestamp.Date,
|
||||
UserId = dto.Activity.UserId,
|
||||
TargetUserId = dto.TargetUserId,
|
||||
TargetSlotId = dto.TargetSlotId,
|
||||
TargetPlaylistId = dto.TargetPlaylistId,
|
||||
},
|
||||
dto => dto.Activity);
|
||||
}
|
||||
|
||||
// TODO this is kinda ass, can maybe improve once comment migration is merged
|
||||
private async Task<IQueryable<ActivityEntity>> GetFilters
|
||||
(
|
||||
GameTokenEntity token,
|
||||
bool excludeNews,
|
||||
bool excludeMyLevels,
|
||||
bool excludeFriends,
|
||||
bool excludeFavouriteUsers,
|
||||
bool excludeMyself
|
||||
)
|
||||
{
|
||||
IQueryable<ActivityEntity> query = this.database.Activities.AsQueryable();
|
||||
if (excludeNews) query = query.Where(a => a.Type != EventType.NewsPost);
|
||||
|
||||
IQueryable<ActivityDto> dtoQuery = query.Select(a => new ActivityDto
|
||||
{
|
||||
Activity = a,
|
||||
SlotCreatorId = a is LevelActivityEntity
|
||||
? ((LevelActivityEntity)a).Slot.CreatorId
|
||||
: a is PhotoActivityEntity && ((PhotoActivityEntity)a).Photo.SlotId != 0
|
||||
? ((PhotoActivityEntity)a).Photo.Slot!.CreatorId
|
||||
: a is CommentActivityEntity && ((CommentActivityEntity)a).Comment.Type == CommentType.Level
|
||||
? ((CommentActivityEntity)a).Comment.TargetId
|
||||
: a is ScoreActivityEntity
|
||||
? ((ScoreActivityEntity)a).Score.Slot.CreatorId
|
||||
: 0,
|
||||
});
|
||||
|
||||
Expression<Func<ActivityDto, bool>> predicate = PredicateExtensions.False<ActivityDto>();
|
||||
|
||||
predicate = predicate.Or(a => a.SlotCreatorId == 0 || excludeMyLevels
|
||||
? a.SlotCreatorId != token.UserId
|
||||
: a.SlotCreatorId == token.UserId);
|
||||
|
||||
List<int>? friendIds = UserFriendStore.GetUserFriendData(token.UserId)?.FriendIds;
|
||||
if (friendIds != null)
|
||||
{
|
||||
predicate = excludeFriends
|
||||
? predicate.Or(a => !friendIds.Contains(a.Activity.UserId))
|
||||
: predicate.Or(a => friendIds.Contains(a.Activity.UserId));
|
||||
}
|
||||
|
||||
List<int> favouriteUsers = await this.database.HeartedProfiles.Where(hp => hp.UserId == token.UserId)
|
||||
.Select(hp => hp.HeartedUserId)
|
||||
.ToListAsync();
|
||||
|
||||
predicate = excludeFavouriteUsers
|
||||
? predicate.Or(a => !favouriteUsers.Contains(a.Activity.UserId))
|
||||
: predicate.Or(a => favouriteUsers.Contains(a.Activity.UserId));
|
||||
|
||||
predicate = excludeMyself
|
||||
? predicate.Or(a => a.Activity.UserId != token.UserId)
|
||||
: predicate.Or(a => a.Activity.UserId == token.UserId);
|
||||
|
||||
query = dtoQuery.Where(predicate).Select(dto => dto.Activity);
|
||||
|
||||
return query.OrderByDescending(a => a.Timestamp);
|
||||
}
|
||||
|
||||
public Task<DateTime> GetMostRecentEventTime(GameTokenEntity token, DateTime upperBound)
|
||||
{
|
||||
return this.database.Activities.Where(a => a.UserId == token.UserId)
|
||||
.Where(a => a.Timestamp < upperBound)
|
||||
.OrderByDescending(a => a.Timestamp)
|
||||
.Select(a => a.Timestamp)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GlobalActivity
|
||||
(
|
||||
long timestamp,
|
||||
bool excludeNews,
|
||||
bool excludeMyLevels,
|
||||
bool excludeFriends,
|
||||
bool excludeFavouriteUsers,
|
||||
bool excludeMyself
|
||||
)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (token.GameVersion == GameVersion.LittleBigPlanet1) return this.BadRequest();
|
||||
|
||||
if (timestamp > TimeHelper.TimestampMillis || timestamp <= 0) timestamp = TimeHelper.TimestampMillis;
|
||||
|
||||
DateTime start = DateTimeExtensions.FromUnixTimeMilliseconds(timestamp);
|
||||
|
||||
DateTime soonestTime = await this.GetMostRecentEventTime(token, start);
|
||||
Console.WriteLine(@"Most recent event occurred at " + soonestTime);
|
||||
soonestTime = soonestTime.Subtract(TimeSpan.FromDays(1));
|
||||
|
||||
long soonestTimestamp = soonestTime.ToUnixTimeMilliseconds();
|
||||
|
||||
long endTimestamp = soonestTimestamp - 86_400_000;
|
||||
|
||||
Console.WriteLine(@$"soonestTime: {soonestTimestamp}, endTime: {endTimestamp}");
|
||||
|
||||
IQueryable<ActivityEntity> activityEvents = await this.GetFilters(token,
|
||||
excludeNews,
|
||||
excludeMyLevels,
|
||||
excludeFriends,
|
||||
excludeFavouriteUsers,
|
||||
excludeMyself);
|
||||
|
||||
DateTime end = DateTimeExtensions.FromUnixTimeMilliseconds(endTimestamp);
|
||||
|
||||
activityEvents = activityEvents.Where(a => a.Timestamp < start && a.Timestamp > end);
|
||||
|
||||
Console.WriteLine($@"start: {start}, end: {end}");
|
||||
|
||||
List<IGrouping<ActivityGroup, ActivityEntity>> groups = await GroupActivities(activityEvents).ToListAsync();
|
||||
|
||||
foreach (IGrouping<ActivityGroup, ActivityEntity> group in groups)
|
||||
{
|
||||
ActivityGroup key = group.Key;
|
||||
Console.WriteLine(
|
||||
$@"{key.GroupType}: Timestamp: {key.Timestamp}, UserId: {key.UserId}, TargetSlotId: {key.TargetSlotId}, " +
|
||||
@$"TargetUserId: {key.TargetUserId}, TargetPlaylistId: {key.TargetPlaylistId}");
|
||||
foreach (ActivityEntity activity in group)
|
||||
{
|
||||
Console.WriteLine($@" {activity.Type}: Timestamp: {activity.Timestamp}");
|
||||
}
|
||||
}
|
||||
|
||||
DateTime oldestTime = groups.Any() ? groups.Min(g => g.Any() ? g.Min(a => a.Timestamp) : end) : end;
|
||||
long oldestTimestamp = oldestTime.ToUnixTimeMilliseconds();
|
||||
|
||||
return this.Ok(await GameStream.CreateFromEntityResult(this.database, token, groups, timestamp, oldestTimestamp));
|
||||
}
|
||||
|
||||
[HttpGet("slot/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> SlotActivity(string slotType, int slotId, long timestamp)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (token.GameVersion == GameVersion.LittleBigPlanet1) return this.BadRequest();
|
||||
|
||||
if (timestamp > TimeHelper.TimestampMillis || timestamp <= 0) timestamp = TimeHelper.TimestampMillis;
|
||||
|
||||
long endTimestamp = timestamp - 864_000;
|
||||
|
||||
if (slotType is not ("developer" or "user")) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer")
|
||||
slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
IQueryable<ActivityDto> slotActivity = this.database.Activities.Select(ActivityToDto())
|
||||
.Where(a => a.TargetSlotId == slotId);
|
||||
|
||||
DateTime start = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime;
|
||||
DateTime end = DateTimeOffset.FromUnixTimeMilliseconds(endTimestamp).DateTime;
|
||||
|
||||
slotActivity = slotActivity.Where(a => a.Activity.Timestamp < start && a.Activity.Timestamp > end);
|
||||
|
||||
List<IGrouping<ActivityGroup, ActivityEntity>> groups = await GroupActivities(slotActivity).ToListAsync();
|
||||
|
||||
DateTime oldestTime = groups.Max(g => g.Max(a => a.Timestamp));
|
||||
long oldestTimestamp = new DateTimeOffset(oldestTime).ToUnixTimeMilliseconds();
|
||||
|
||||
return this.Ok(await GameStream.CreateFromEntityResult(this.database, token, groups, timestamp, oldestTimestamp));
|
||||
}
|
||||
|
||||
[HttpGet("user2/{userId:int}/")]
|
||||
public async Task<IActionResult> UserActivity(int userId, long timestamp)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (token.GameVersion == GameVersion.LittleBigPlanet1) return this.BadRequest();
|
||||
|
||||
if (timestamp > TimeHelper.TimestampMillis || timestamp <= 0) timestamp = TimeHelper.TimestampMillis;
|
||||
|
||||
long endTimestamp = timestamp - 864_000;
|
||||
|
||||
IQueryable<ActivityDto> userActivity = this.database.Activities.Select(ActivityToDto())
|
||||
.Where(a => a.TargetUserId == userId);
|
||||
|
||||
DateTime start = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime;
|
||||
DateTime end = DateTimeOffset.FromUnixTimeMilliseconds(endTimestamp).DateTime;
|
||||
|
||||
userActivity = userActivity.Where(a => a.Activity.Timestamp < start && a.Activity.Timestamp > end);
|
||||
|
||||
List<IGrouping<ActivityGroup, ActivityEntity>> groups = await GroupActivities(userActivity).ToListAsync();
|
||||
|
||||
DateTime oldestTime = groups.Max(g => g.Max(a => a.Timestamp));
|
||||
long oldestTimestamp = new DateTimeOffset(oldestTime).ToUnixTimeMilliseconds();
|
||||
|
||||
return this.Ok(
|
||||
await GameStream.CreateFromEntityResult(this.database, token, groups, timestamp, oldestTimestamp));
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Comment;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -44,6 +44,20 @@ public class CommentController : ControllerBase
|
|||
return this.Ok();
|
||||
}
|
||||
|
||||
[HttpGet("userComment/{username}")]
|
||||
[HttpGet("comment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> GetSingleComment(string? username, string? slotType, int? slotId, int commentId)
|
||||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
if (username == null == (SlotHelper.IsTypeInvalid(slotType) || slotId == null)) return this.BadRequest();
|
||||
|
||||
CommentEntity? comment = await this.database.Comments.FindAsync(commentId);
|
||||
if (comment == null) return this.NotFound();
|
||||
|
||||
return this.Ok(GameComment.CreateFromEntity(comment, token.UserId));
|
||||
}
|
||||
|
||||
[HttpGet("comments/{slotType}/{slotId:int}")]
|
||||
[HttpGet("userComments/{username}")]
|
||||
public async Task<IActionResult> GetComments(string? username, string? slotType, int slotId)
|
||||
|
|
|
@ -5,7 +5,7 @@ using LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Users;
|
|||
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -12,7 +12,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Photo;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -13,6 +13,9 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
|||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -9,7 +9,9 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -4,7 +4,8 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -11,7 +11,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Resources;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -6,7 +6,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -8,7 +8,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Score;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -8,7 +8,7 @@ using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -13,7 +13,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -6,7 +6,7 @@ using LBPUnion.ProjectLighthouse.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -7,7 +7,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter.Sorts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Extensions;
|
||||
|
|
|
@ -5,6 +5,8 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
|
|
@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
|
|
@ -5,6 +5,8 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Types.Categories;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions
|
||||
@using LBPUnion.ProjectLighthouse.Types.Entities.Profile
|
||||
@using LBPUnion.ProjectLighthouse.Types.Levels
|
||||
@using LBPUnion.ProjectLighthouse.Types.Serialization
|
||||
@using LBPUnion.ProjectLighthouse.Types.Serialization.Photo
|
||||
@model LBPUnion.ProjectLighthouse.Types.Entities.Profile.PhotoEntity
|
||||
|
||||
@{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@using LBPUnion.ProjectLighthouse.Files
|
||||
@using LBPUnion.ProjectLighthouse.Helpers
|
||||
@using LBPUnion.ProjectLighthouse.Types.Entities.Level
|
||||
@using LBPUnion.ProjectLighthouse.Types.Serialization
|
||||
@using LBPUnion.ProjectLighthouse.Types.Serialization.Review
|
||||
|
||||
@{
|
||||
bool isMobile = (bool?)ViewData["IsMobile"] ?? false;
|
||||
|
|
|
@ -11,7 +11,7 @@ using LBPUnion.ProjectLighthouse.Tests.Helpers;
|
|||
using LBPUnion.ProjectLighthouse.Tests.Integration;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
|
|
|
@ -4,7 +4,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
using LBPUnion.ProjectLighthouse.Tests.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
|
|
|
@ -5,7 +5,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
using LBPUnion.ProjectLighthouse.Tests.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Xml.Serialization;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using Xunit;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Tests.Unit;
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
|||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
|
140
ProjectLighthouse/Database/ActivityInterceptor.cs
Normal file
140
ProjectLighthouse/Database/ActivityInterceptor.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Database;
|
||||
|
||||
public class ActivityInterceptor : SaveChangesInterceptor
|
||||
{
|
||||
private class CustomTrackedEntity
|
||||
{
|
||||
public required EntityState State { get; init; }
|
||||
public required object Entity { get; init; }
|
||||
public required object OldEntity { get; init; }
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<(Type Type, int HashCode), CustomTrackedEntity> unsavedEntities;
|
||||
private readonly IEntityEventHandler eventHandler;
|
||||
|
||||
public ActivityInterceptor(IEntityEventHandler eventHandler)
|
||||
{
|
||||
this.eventHandler = eventHandler;
|
||||
this.unsavedEntities = new ConcurrentDictionary<(Type Type, int HashCode), CustomTrackedEntity>();
|
||||
}
|
||||
|
||||
#region Hooking stuff
|
||||
|
||||
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
|
||||
{
|
||||
this.SaveNewEntities(eventData);
|
||||
return base.SavingChanges(eventData, result);
|
||||
}
|
||||
|
||||
public override ValueTask<InterceptionResult<int>> SavingChangesAsync
|
||||
(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = new())
|
||||
{
|
||||
this.SaveNewEntities(eventData);
|
||||
return base.SavingChangesAsync(eventData, result, cancellationToken);
|
||||
}
|
||||
|
||||
public override int SavedChanges(SaveChangesCompletedEventData eventData, int result)
|
||||
{
|
||||
this.ParseInsertedEntities(eventData);
|
||||
return base.SavedChanges(eventData, result);
|
||||
}
|
||||
|
||||
public override ValueTask<int> SavedChangesAsync
|
||||
(SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = new())
|
||||
{
|
||||
this.ParseInsertedEntities(eventData);
|
||||
return base.SavedChangesAsync(eventData, result, cancellationToken);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SaveNewEntities(DbContextEventData eventData)
|
||||
{
|
||||
if (eventData.Context == null) return;
|
||||
|
||||
DbContext context = eventData.Context;
|
||||
|
||||
this.unsavedEntities.Clear();
|
||||
|
||||
foreach (EntityEntry entry in context.ChangeTracker.Entries())
|
||||
{
|
||||
// Ignore activities
|
||||
if (entry.Metadata.BaseType?.ClrType == typeof(ActivityEntity) || entry.Metadata.ClrType == typeof(LastContactEntity)) continue;
|
||||
|
||||
// Ignore tokens
|
||||
if (entry.Metadata.Name.Contains("Token")) continue;
|
||||
|
||||
if (entry.State is not (EntityState.Added or EntityState.Deleted or EntityState.Modified)) continue;
|
||||
|
||||
this.unsavedEntities.TryAdd((entry.Entity.GetType(), entry.Entity.GetHashCode()),
|
||||
new CustomTrackedEntity
|
||||
{
|
||||
State = entry.State,
|
||||
Entity = entry.Entity,
|
||||
OldEntity = entry.OriginalValues.ToObject(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseInsertedEntities(DbContextEventData eventData)
|
||||
{
|
||||
if (eventData.Context is not DatabaseContext context) return;
|
||||
|
||||
HashSet<CustomTrackedEntity> entities = new();
|
||||
|
||||
List<EntityEntry> entries = context.ChangeTracker.Entries().ToList();
|
||||
|
||||
foreach (KeyValuePair<(Type Type, int HashCode), CustomTrackedEntity> kvp in this.unsavedEntities)
|
||||
{
|
||||
EntityEntry entry = entries.FirstOrDefault(e =>
|
||||
e.Metadata.ClrType == kvp.Key.Type && e.Entity.GetHashCode() == kvp.Key.HashCode);
|
||||
switch (kvp.Value.State)
|
||||
{
|
||||
case EntityState.Added:
|
||||
case EntityState.Modified:
|
||||
if (entry != null) entities.Add(kvp.Value);
|
||||
break;
|
||||
case EntityState.Deleted:
|
||||
if (entry == null) entities.Add(kvp.Value);
|
||||
break;
|
||||
case EntityState.Detached:
|
||||
case EntityState.Unchanged:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CustomTrackedEntity entity in entities)
|
||||
{
|
||||
switch (entity.State)
|
||||
{
|
||||
case EntityState.Added:
|
||||
this.eventHandler.OnEntityInserted(context, entity.Entity);
|
||||
break;
|
||||
case EntityState.Deleted:
|
||||
this.eventHandler.OnEntityDeleted(context, entity.Entity);
|
||||
break;
|
||||
case EntityState.Modified:
|
||||
this.eventHandler.OnEntityChanged(context, entity.OldEntity, entity.Entity);
|
||||
break;
|
||||
case EntityState.Detached:
|
||||
case EntityState.Unchanged:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
|
@ -86,16 +87,6 @@ public partial class DatabaseContext : DbContext
|
|||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
|
||||
{ }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<LevelActivityEntity>().UseTpcMappingStrategy();
|
||||
modelBuilder.Entity<PhotoActivityEntity>().UseTpcMappingStrategy();
|
||||
modelBuilder.Entity<PlaylistActivityEntity>().UseTpcMappingStrategy();
|
||||
modelBuilder.Entity<ScoreActivityEntity>().UseTpcMappingStrategy();
|
||||
modelBuilder.Entity<UserActivityEntity>().UseTpcMappingStrategy();
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
public static DatabaseContext CreateNewInstance()
|
||||
{
|
||||
DbContextOptionsBuilder<DatabaseContext> builder = new();
|
||||
|
@ -103,4 +94,26 @@ public partial class DatabaseContext : DbContext
|
|||
MySqlServerVersion.LatestSupportedServerVersion);
|
||||
return new DatabaseContext(builder.Options);
|
||||
}
|
||||
|
||||
#region Activity
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
//TODO implement reviews
|
||||
modelBuilder.Entity<LevelActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<PhotoActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<PlaylistActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<ScoreActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<UserActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<NewsActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<CommentActivityEntity>().UseTphMappingStrategy();
|
||||
modelBuilder.Entity<UserActivityEntity>().UseTphMappingStrategy();
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.AddInterceptors(new ActivityInterceptor(new ActivityEntityEventHandler()));
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
#endregion
|
||||
}
|
12
ProjectLighthouse/Extensions/DateTimeExtensions.cs
Normal file
12
ProjectLighthouse/Extensions/DateTimeExtensions.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Extensions;
|
||||
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
public static long ToUnixTimeMilliseconds(this DateTime dateTime) =>
|
||||
new DateTimeOffset(dateTime).ToUniversalTime().ToUnixTimeMilliseconds();
|
||||
|
||||
public static DateTime FromUnixTimeMilliseconds(long timestamp) =>
|
||||
DateTimeOffset.FromUnixTimeMilliseconds(timestamp).ToUniversalTime().DateTime;
|
||||
}
|
179
ProjectLighthouse/Types/Activity/ActivityEntityEventHandler.cs
Normal file
179
ProjectLighthouse/Types/Activity/ActivityEntityEventHandler.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
|
||||
//TODO implement missing event triggers
|
||||
public class ActivityEntityEventHandler : IEntityEventHandler
|
||||
{
|
||||
public void OnEntityInserted<T>(DatabaseContext database, T entity) where T : class
|
||||
{
|
||||
Console.WriteLine($@"OnEntityInserted: {entity.GetType().Name}");
|
||||
ActivityEntity? activity = entity switch
|
||||
{
|
||||
SlotEntity slot => new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.PublishLevel,
|
||||
SlotId = slot.SlotId,
|
||||
UserId = slot.CreatorId,
|
||||
},
|
||||
CommentEntity comment => new CommentActivityEntity
|
||||
{
|
||||
Type = comment.Type == CommentType.Level ? EventType.CommentOnLevel : EventType.CommentOnUser,
|
||||
CommentId = comment.CommentId,
|
||||
UserId = comment.PosterUserId,
|
||||
},
|
||||
PhotoEntity photo => new PhotoActivityEntity
|
||||
{
|
||||
Type = EventType.UploadPhoto,
|
||||
PhotoId = photo.PhotoId,
|
||||
UserId = photo.CreatorId,
|
||||
},
|
||||
ScoreEntity score => new ScoreActivityEntity
|
||||
{
|
||||
Type = EventType.Score,
|
||||
ScoreId = score.ScoreId,
|
||||
//TODO merge score migration
|
||||
// UserId = int.Parse(score.PlayerIds[0]),
|
||||
},
|
||||
HeartedLevelEntity heartedLevel => new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.HeartLevel,
|
||||
SlotId = heartedLevel.SlotId,
|
||||
UserId = heartedLevel.UserId,
|
||||
},
|
||||
HeartedProfileEntity heartedProfile => new UserActivityEntity
|
||||
{
|
||||
Type = EventType.HeartUser,
|
||||
TargetUserId = heartedProfile.HeartedUserId,
|
||||
UserId = heartedProfile.UserId,
|
||||
},
|
||||
VisitedLevelEntity visitedLevel => new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.PlayLevel,
|
||||
SlotId = visitedLevel.SlotId,
|
||||
UserId = visitedLevel.UserId,
|
||||
},
|
||||
_ => null,
|
||||
};
|
||||
InsertActivity(database, activity);
|
||||
}
|
||||
|
||||
private static void InsertActivity(DatabaseContext database, ActivityEntity? activity)
|
||||
{
|
||||
if (activity == null) return;
|
||||
|
||||
Console.WriteLine("Inserting activity: " + activity.GetType().Name);
|
||||
|
||||
activity.Timestamp = DateTime.UtcNow;
|
||||
database.Activities.Add(activity);
|
||||
database.SaveChanges();
|
||||
}
|
||||
|
||||
public void OnEntityChanged<T>(DatabaseContext database, T origEntity, T currentEntity) where T : class
|
||||
{
|
||||
foreach (PropertyInfo propInfo in currentEntity.GetType().GetProperties())
|
||||
{
|
||||
if (!propInfo.CanRead || !propInfo.CanWrite) continue;
|
||||
|
||||
if (propInfo.CustomAttributes.Any(c => c.AttributeType == typeof(NotMappedAttribute))) continue;
|
||||
|
||||
object? origVal = propInfo.GetValue(origEntity);
|
||||
object? newVal = propInfo.GetValue(currentEntity);
|
||||
if ((origVal == null && newVal == null) || (origVal != null && newVal != null && origVal.Equals(newVal)))
|
||||
continue;
|
||||
|
||||
Console.WriteLine($@"Value for {propInfo.Name} changed");
|
||||
Console.WriteLine($@"Orig val: {origVal?.ToString() ?? "null"}");
|
||||
Console.WriteLine($@"New val: {newVal?.ToString() ?? "null"}");
|
||||
}
|
||||
|
||||
Console.WriteLine($@"OnEntityChanged: {currentEntity.GetType().Name}");
|
||||
ActivityEntity? activity = null;
|
||||
switch (currentEntity)
|
||||
{
|
||||
case VisitedLevelEntity visitedLevel:
|
||||
{
|
||||
if (origEntity is not VisitedLevelEntity) break;
|
||||
|
||||
activity = new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.PlayLevel,
|
||||
SlotId = visitedLevel.SlotId,
|
||||
UserId = visitedLevel.UserId,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case SlotEntity slotEntity:
|
||||
{
|
||||
if (origEntity is not SlotEntity oldSlotEntity) break;
|
||||
|
||||
if (!oldSlotEntity.TeamPick && slotEntity.TeamPick)
|
||||
{
|
||||
activity = new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.MMPickLevel,
|
||||
SlotId = slotEntity.SlotId,
|
||||
UserId = SlotHelper.GetPlaceholderUserId(database).Result,
|
||||
};
|
||||
}
|
||||
else if (oldSlotEntity.SlotId == slotEntity.SlotId && slotEntity.Type == SlotType.User)
|
||||
{
|
||||
activity = new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.PublishLevel,
|
||||
SlotId = slotEntity.SlotId,
|
||||
UserId = slotEntity.CreatorId,
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InsertActivity(database, activity);
|
||||
}
|
||||
|
||||
public void OnEntityDeleted<T>(DatabaseContext database, T entity) where T : class
|
||||
{
|
||||
Console.WriteLine($@"OnEntityDeleted: {entity.GetType().Name}");
|
||||
ActivityEntity? activity = entity switch
|
||||
{
|
||||
//TODO move this to EntityModified and use CommentEntity.Deleted
|
||||
CommentEntity comment => comment.Type switch
|
||||
{
|
||||
CommentType.Level => new CommentActivityEntity
|
||||
{
|
||||
Type = EventType.DeleteLevelComment,
|
||||
CommentId = comment.CommentId,
|
||||
UserId = comment.PosterUserId,
|
||||
},
|
||||
_ => null,
|
||||
},
|
||||
HeartedLevelEntity heartedLevel => new LevelActivityEntity
|
||||
{
|
||||
Type = EventType.UnheartLevel,
|
||||
SlotId = heartedLevel.SlotId,
|
||||
UserId = heartedLevel.UserId,
|
||||
},
|
||||
HeartedProfileEntity heartedProfile => new UserActivityEntity
|
||||
{
|
||||
Type = EventType.UnheartUser,
|
||||
TargetUserId = heartedProfile.HeartedUserId,
|
||||
UserId = heartedProfile.UserId,
|
||||
},
|
||||
_ => null,
|
||||
};
|
||||
InsertActivity(database, activity);
|
||||
}
|
||||
}
|
41
ProjectLighthouse/Types/Activity/ActivityGroup.cs
Normal file
41
ProjectLighthouse/Types/Activity/ActivityGroup.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
|
||||
public class ActivityGroup
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public int? TargetSlotId { get; set; }
|
||||
public int? TargetUserId { get; set; }
|
||||
public int? TargetPlaylistId { get; set; }
|
||||
|
||||
public int TargetId =>
|
||||
this.GroupType switch
|
||||
{
|
||||
ActivityGroupType.User => this.TargetUserId ?? 0,
|
||||
ActivityGroupType.Level => this.TargetSlotId ?? 0,
|
||||
ActivityGroupType.Playlist => this.TargetPlaylistId ?? 0,
|
||||
_ => this.UserId,
|
||||
};
|
||||
|
||||
public ActivityGroupType GroupType =>
|
||||
this.TargetSlotId != 0
|
||||
? ActivityGroupType.Level
|
||||
: this.TargetUserId != 0
|
||||
? ActivityGroupType.User
|
||||
: ActivityGroupType.Playlist;
|
||||
}
|
||||
|
||||
public enum ActivityGroupType
|
||||
{
|
||||
[XmlEnum("user")]
|
||||
User,
|
||||
|
||||
[XmlEnum("slot")]
|
||||
Level,
|
||||
|
||||
[XmlEnum("playlist")]
|
||||
Playlist,
|
||||
}
|
|
@ -1,26 +1,69 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
|
||||
public enum EventType
|
||||
{
|
||||
[XmlEnum("heart_level")]
|
||||
HeartLevel,
|
||||
|
||||
[XmlEnum("unheart_level")]
|
||||
UnheartLevel,
|
||||
|
||||
[XmlEnum("heart_user")]
|
||||
HeartUser,
|
||||
|
||||
[XmlEnum("unheart_user")]
|
||||
UnheartUser,
|
||||
|
||||
[XmlEnum("play_level")]
|
||||
PlayLevel,
|
||||
|
||||
[XmlEnum("rate_level")]
|
||||
RateLevel,
|
||||
|
||||
[XmlEnum("tag_level")]
|
||||
TagLevel,
|
||||
|
||||
[XmlEnum("comment_on_level")]
|
||||
CommentOnLevel,
|
||||
|
||||
[XmlEnum("delete_level_comment")]
|
||||
DeleteLevelComment,
|
||||
|
||||
[XmlEnum("upload_photo")]
|
||||
UploadPhoto,
|
||||
|
||||
[XmlEnum("publish_level")]
|
||||
PublishLevel,
|
||||
|
||||
[XmlEnum("unpublish_level")]
|
||||
UnpublishLevel,
|
||||
|
||||
[XmlEnum("score")]
|
||||
Score,
|
||||
|
||||
[XmlEnum("news_post")]
|
||||
NewsPost,
|
||||
|
||||
[XmlEnum("mm_pick_level")]
|
||||
MMPickLevel,
|
||||
|
||||
[XmlEnum("dpad_rate_level")]
|
||||
DpadRateLevel,
|
||||
|
||||
[XmlEnum("review_level")]
|
||||
ReviewLevel,
|
||||
|
||||
[XmlEnum("comment_on_user")]
|
||||
CommentOnUser,
|
||||
|
||||
[XmlEnum("create_playlist")]
|
||||
CreatePlaylist,
|
||||
|
||||
[XmlEnum("heart_playlist")]
|
||||
HeartPlaylist,
|
||||
|
||||
[XmlEnum("add_level_to_playlist")]
|
||||
AddLevelToPlaylist,
|
||||
}
|
10
ProjectLighthouse/Types/Activity/IEntityEventHandler.cs
Normal file
10
ProjectLighthouse/Types/Activity/IEntityEventHandler.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
|
||||
public interface IEntityEventHandler
|
||||
{
|
||||
public void OnEntityInserted<T>(DatabaseContext database, T entity) where T : class;
|
||||
public void OnEntityChanged<T>(DatabaseContext database, T origEntity, T currentEntity) where T : class;
|
||||
public void OnEntityDeleted<T>(DatabaseContext database, T entity) where T : class;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
@ -10,7 +11,7 @@ public class ActivityEntity
|
|||
[Key]
|
||||
public int ActivityId { get; set; }
|
||||
|
||||
public long Timestamp { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// Supported event types: CommentOnUser, CommentOnLevel, DeleteLevelComment
|
||||
/// </summary>
|
||||
public class CommentActivityEntity : ActivityEntity
|
||||
{
|
||||
public int CommentId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(CommentId))]
|
||||
public CommentEntity Comment { get; set; }
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// Supported event types: CommentOnUser, CommentOnLevel, DeleteLevelComment
|
||||
/// </summary>
|
||||
public class CommentActivityEntry
|
||||
{
|
||||
|
||||
}
|
|
@ -3,6 +3,9 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
|||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// Supported event types: CreatePlaylist, HeartPlaylist, AddLevelToPlaylist
|
||||
/// </summary>
|
||||
public class PlaylistActivityEntity : ActivityEntity
|
||||
{
|
||||
public int PlaylistId { get; set; }
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
|
||||
public class ReviewActivityEntity : ActivityEntity
|
||||
{
|
||||
public int ReviewId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ReviewId))]
|
||||
public ReviewEntity Review { get; set; }
|
||||
|
||||
// TODO review_modified?
|
||||
}
|
|
@ -1,9 +1,15 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// Supported event types: HeartUser, UnheartUser
|
||||
/// </summary>
|
||||
public class UserActivityEntity : ActivityEntity
|
||||
{
|
||||
|
||||
public int TargetUserId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(TargetUserId))]
|
||||
public UserEntity TargetUser { get; set; }
|
||||
}
|
|
@ -3,7 +3,7 @@ using System;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Filter;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
[XmlInclude(typeof(GameUserCommentEvent))]
|
||||
[XmlInclude(typeof(GameSlotCommentEvent))]
|
||||
public class GameCommentEvent : GameEvent
|
||||
{
|
||||
[XmlElement("comment_id")]
|
||||
public int CommentId { get; set; }
|
||||
}
|
||||
|
||||
public class GameUserCommentEvent : GameCommentEvent
|
||||
{
|
||||
[XmlElement("object_user")]
|
||||
public string TargetUsername { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
CommentEntity comment = await database.Comments.FindAsync(this.CommentId);
|
||||
if (comment == null) return;
|
||||
|
||||
UserEntity user = await database.Users.FindAsync(comment.TargetId);
|
||||
if (user == null) return;
|
||||
|
||||
this.TargetUsername = user.Username;
|
||||
}
|
||||
}
|
||||
|
||||
public class GameSlotCommentEvent : GameCommentEvent
|
||||
{
|
||||
[XmlElement("object_slot_id")]
|
||||
public ReviewSlot TargetSlot { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
CommentEntity comment = await database.Comments.FindAsync(this.CommentId);
|
||||
if (comment == null) return;
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(comment.TargetId);
|
||||
|
||||
if (slot == null) return;
|
||||
|
||||
this.TargetSlot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
[XmlInclude(typeof(GameCommentEvent))]
|
||||
[XmlInclude(typeof(GamePhotoUploadEvent))]
|
||||
[XmlInclude(typeof(GamePlayLevelEvent))]
|
||||
[XmlInclude(typeof(GameReviewEvent))]
|
||||
[XmlInclude(typeof(GameScoreEvent))]
|
||||
[XmlInclude(typeof(GameHeartLevelEvent))]
|
||||
[XmlInclude(typeof(GameHeartUserEvent))]
|
||||
public class GameEvent : ILbpSerializable, INeedsPreparationForSerialization
|
||||
{
|
||||
[XmlIgnore]
|
||||
private int UserId { get; set; }
|
||||
|
||||
[XmlAttribute("type")]
|
||||
public EventType Type { get; set; }
|
||||
|
||||
[XmlElement("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[XmlElement("actor")]
|
||||
public string Username { get; set; }
|
||||
|
||||
protected async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
Console.WriteLine($@"SERIALIZATION!! {this.UserId} - {this.GetHashCode()}");
|
||||
UserEntity user = await database.Users.FindAsync(this.UserId);
|
||||
if (user == null) return;
|
||||
this.Username = user.Username;
|
||||
}
|
||||
|
||||
public static IEnumerable<GameEvent> CreateFromActivityGroups(IGrouping<EventType, ActivityEntity> group)
|
||||
{
|
||||
List<GameEvent> events = new();
|
||||
|
||||
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
|
||||
// Events with Count need special treatment
|
||||
switch (group.Key)
|
||||
{
|
||||
case EventType.PlayLevel:
|
||||
{
|
||||
if (group.First() is not LevelActivityEntity levelActivity) break;
|
||||
|
||||
events.Add(new GamePlayLevelEvent
|
||||
{
|
||||
Slot = new ReviewSlot
|
||||
{
|
||||
SlotId = levelActivity.SlotId,
|
||||
},
|
||||
Count = group.Count(),
|
||||
UserId = levelActivity.UserId,
|
||||
Timestamp = levelActivity.Timestamp.ToUnixTimeMilliseconds(),
|
||||
Type = levelActivity.Type,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case EventType.PublishLevel:
|
||||
{
|
||||
if (group.First() is not LevelActivityEntity levelActivity) break;
|
||||
|
||||
events.Add(new GamePublishLevelEvent
|
||||
{
|
||||
Slot = new ReviewSlot
|
||||
{
|
||||
SlotId = levelActivity.SlotId,
|
||||
},
|
||||
Count = group.Count(),
|
||||
UserId = levelActivity.UserId,
|
||||
Timestamp = levelActivity.Timestamp.ToUnixTimeMilliseconds(),
|
||||
Type = levelActivity.Type,
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Everything else can be handled as normal
|
||||
default: events.AddRange(group.Select(CreateFromActivity));
|
||||
break;
|
||||
}
|
||||
return events.AsEnumerable();
|
||||
}
|
||||
|
||||
private static GameEvent CreateFromActivity(ActivityEntity activity)
|
||||
{
|
||||
GameEvent gameEvent = activity.Type switch
|
||||
{
|
||||
EventType.PlayLevel => new GamePlayLevelEvent
|
||||
{
|
||||
Slot = new ReviewSlot
|
||||
{
|
||||
SlotId = ((LevelActivityEntity)activity).SlotId,
|
||||
},
|
||||
},
|
||||
EventType.CommentOnLevel => new GameSlotCommentEvent
|
||||
{
|
||||
CommentId = ((CommentActivityEntity)activity).CommentId,
|
||||
},
|
||||
EventType.CommentOnUser => new GameUserCommentEvent
|
||||
{
|
||||
CommentId = ((CommentActivityEntity)activity).CommentId,
|
||||
},
|
||||
EventType.HeartUser or EventType.UnheartUser => new GameHeartUserEvent
|
||||
{
|
||||
TargetUserId = ((UserActivityEntity)activity).TargetUserId,
|
||||
},
|
||||
EventType.HeartLevel or EventType.UnheartLevel => new GameHeartLevelEvent
|
||||
{
|
||||
TargetSlot = new ReviewSlot
|
||||
{
|
||||
SlotId = ((LevelActivityEntity)activity).SlotId,
|
||||
},
|
||||
},
|
||||
_ => new GameEvent(),
|
||||
};
|
||||
gameEvent.UserId = activity.UserId;
|
||||
gameEvent.Type = activity.Type;
|
||||
gameEvent.Timestamp = activity.Timestamp.ToUnixTimeMilliseconds();
|
||||
return gameEvent;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GameHeartUserEvent : GameEvent
|
||||
{
|
||||
[XmlIgnore]
|
||||
public int TargetUserId { get; set; }
|
||||
|
||||
[XmlElement("object_user")]
|
||||
public string TargetUsername { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
UserEntity targetUser = await database.Users.FindAsync(this.TargetUserId);
|
||||
if (targetUser == null) return;
|
||||
|
||||
this.TargetUsername = targetUser.Username;
|
||||
}
|
||||
}
|
||||
|
||||
public class GameHeartLevelEvent : GameEvent
|
||||
{
|
||||
[XmlElement("object_slot_id")]
|
||||
public ReviewSlot TargetSlot { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(this.TargetSlot.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.TargetSlot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GamePhotoUploadEvent : GameEvent
|
||||
{
|
||||
[XmlElement("photo_id")]
|
||||
public int PhotoId { get; set; }
|
||||
|
||||
[XmlElement("object_slot_id")]
|
||||
[DefaultValue(null)]
|
||||
public ReviewSlot SlotId { get; set; }
|
||||
|
||||
[XmlElement("user_in_photo")]
|
||||
public List<string> PhotoParticipants { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
PhotoEntity photo = await database.Photos.Where(p => p.PhotoId == this.PhotoId)
|
||||
.Include(p => p.PhotoSubjects)
|
||||
.ThenInclude(ps => ps.User)
|
||||
.FirstOrDefaultAsync();
|
||||
if (photo == null) return;
|
||||
|
||||
this.PhotoParticipants = photo.PhotoSubjects.Select(ps => ps.User.Username).ToList();
|
||||
|
||||
if (photo.SlotId == null) return;
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(photo.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.SlotId = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GamePlayLevelEvent : GameEvent
|
||||
{
|
||||
[XmlElement("object_slot_id")]
|
||||
public ReviewSlot Slot { get; set; }
|
||||
|
||||
[XmlElement("count")]
|
||||
public int Count { get; set; } = 1;
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(this.Slot.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.Slot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GamePublishLevelEvent : GameEvent
|
||||
{
|
||||
[XmlElement("object_slot_id")]
|
||||
public ReviewSlot Slot { get; set; }
|
||||
|
||||
[XmlElement("republish")]
|
||||
public bool IsRepublish { get; set; }
|
||||
|
||||
[XmlElement("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(this.Slot.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.Slot = ReviewSlot.CreateFromEntity(slot);
|
||||
// TODO does this work?
|
||||
this.IsRepublish = slot.LastUpdated == slot.FirstUploaded;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GameReviewEvent : GameEvent
|
||||
{
|
||||
[XmlElement("slot_id")]
|
||||
public ReviewSlot Slot { get; set; }
|
||||
|
||||
[XmlElement("review_id")]
|
||||
public int ReviewId { get; set; }
|
||||
|
||||
[XmlElement("review_modified")]
|
||||
[DefaultValue(0)]
|
||||
public long ReviewTimestamp { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
ReviewEntity review = await database.Reviews.FindAsync(this.ReviewId);
|
||||
if (review == null) return;
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(review.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.Slot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
|
||||
public class GameScoreEvent : GameEvent
|
||||
{
|
||||
[XmlIgnore]
|
||||
public int ScoreId { get; set; }
|
||||
|
||||
[XmlElement("object_slot_id")]
|
||||
public ReviewSlot Slot { get; set; }
|
||||
|
||||
[XmlElement("score")]
|
||||
public int Score { get; set; }
|
||||
|
||||
[XmlElement("user_count")]
|
||||
public int UserCount { get; set; }
|
||||
|
||||
public new async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
await base.PrepareSerialization(database);
|
||||
|
||||
ScoreEntity score = await database.Scores.FindAsync(this.ScoreId);
|
||||
if (score == null) return;
|
||||
|
||||
SlotEntity slot = await database.Slots.FindAsync(score.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.Score = score.Points;
|
||||
//TODO is this correct?
|
||||
this.UserCount = score.Type;
|
||||
|
||||
this.Slot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity;
|
||||
|
||||
public class GameSlotStreamGroup : GameStreamGroup, INeedsPreparationForSerialization
|
||||
{
|
||||
[XmlElement("slot_id")]
|
||||
public ReviewSlot Slot { get; set; }
|
||||
|
||||
public async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
SlotEntity slot = await database.Slots.FindAsync(this.Slot.SlotId);
|
||||
if (slot == null) return;
|
||||
|
||||
this.Slot = ReviewSlot.CreateFromEntity(slot);
|
||||
}
|
||||
}
|
127
ProjectLighthouse/Types/Serialization/Activity/GameStream.cs
Normal file
127
ProjectLighthouse/Types/Serialization/Activity/GameStream.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// The global stream object, contains all
|
||||
/// </summary>
|
||||
[XmlRoot("stream")]
|
||||
public class GameStream : ILbpSerializable, INeedsPreparationForSerialization
|
||||
{
|
||||
[XmlIgnore]
|
||||
private List<int> SlotIds { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
private List<int> UserIds { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
private int TargetUserId { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
private GameVersion TargetGame { get; set; }
|
||||
|
||||
[XmlElement("start_timestamp")]
|
||||
public long StartTimestamp { get; set; }
|
||||
|
||||
[XmlElement("end_timestamp")]
|
||||
public long EndTimestamp { get; set; }
|
||||
|
||||
[XmlArray("groups")]
|
||||
[XmlArrayItem("group")]
|
||||
public List<GameStreamGroup> Groups { get; set; }
|
||||
|
||||
[XmlArray("slots")]
|
||||
[XmlArrayItem("slot")]
|
||||
public List<SlotBase> Slots { get; set; }
|
||||
|
||||
[XmlArray("users")]
|
||||
[XmlArrayItem("user")]
|
||||
public List<GameUser> Users { get; set; }
|
||||
|
||||
[XmlArray("news")]
|
||||
[XmlArrayItem("item")]
|
||||
public List<object> News { get; set; }
|
||||
//TODO implement lbp1 and lbp2 news objects
|
||||
|
||||
public async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
if (this.SlotIds.Count > 0)
|
||||
{
|
||||
this.Slots = new List<SlotBase>();
|
||||
foreach (int slotId in this.SlotIds)
|
||||
{
|
||||
SlotEntity slot = await database.Slots.FindAsync(slotId);
|
||||
if (slot == null) continue;
|
||||
|
||||
this.Slots.Add(SlotBase.CreateFromEntity(slot, this.TargetGame, this.TargetUserId));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.UserIds.Count > 0)
|
||||
{
|
||||
this.Users = new List<GameUser>();
|
||||
foreach (int userId in this.UserIds)
|
||||
{
|
||||
UserEntity user = await database.Users.FindAsync(userId);
|
||||
if (user == null) continue;
|
||||
|
||||
this.Users.Add(GameUser.CreateFromEntity(user, this.TargetGame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<GameStream> CreateFromEntityResult
|
||||
(
|
||||
DatabaseContext database,
|
||||
GameTokenEntity token,
|
||||
List<IGrouping<ActivityGroup, ActivityEntity>> results,
|
||||
long startTimestamp,
|
||||
long endTimestamp
|
||||
)
|
||||
{
|
||||
List<int> slotIds = results.Where(g => g.Key.TargetSlotId != null && g.Key.TargetSlotId.Value != 0)
|
||||
.Select(g => g.Key.TargetSlotId.Value)
|
||||
.ToList();
|
||||
Console.WriteLine($@"slotIds: {string.Join(",", slotIds)}");
|
||||
List<int> userIds = results.Where(g => g.Key.TargetUserId != null && g.Key.TargetUserId.Value != 0)
|
||||
.Select(g => g.Key.TargetUserId.Value)
|
||||
.Distinct()
|
||||
.Union(results.Select(g => g.Key.UserId))
|
||||
.ToList();
|
||||
// Cache target levels and users within DbContext
|
||||
await database.Slots.Where(s => slotIds.Contains(s.SlotId)).LoadAsync();
|
||||
await database.Users.Where(u => userIds.Contains(u.UserId)).LoadAsync();
|
||||
Console.WriteLine($@"userIds: {string.Join(",", userIds)}");
|
||||
Console.WriteLine($@"Stream contains {slotIds.Count} slots and {userIds.Count} users");
|
||||
GameStream gameStream = new()
|
||||
{
|
||||
TargetUserId = token.UserId,
|
||||
TargetGame = token.GameVersion,
|
||||
StartTimestamp = startTimestamp,
|
||||
EndTimestamp = endTimestamp,
|
||||
SlotIds = slotIds,
|
||||
UserIds = userIds,
|
||||
Groups = new List<GameStreamGroup>(),
|
||||
};
|
||||
foreach (IGrouping<ActivityGroup, ActivityEntity> group in results)
|
||||
{
|
||||
gameStream.Groups.Add(GameStreamGroup.CreateFromGrouping(group));
|
||||
}
|
||||
|
||||
return gameStream;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Activity;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Activity.Events;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity;
|
||||
|
||||
/// <summary>
|
||||
/// Top level groups generally contain all events for a given level or user
|
||||
/// <para>
|
||||
/// The sub-groups are always <see cref="GameUserStreamGroup"/> and contain all activities from a single user
|
||||
/// for the top level group entity
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[XmlInclude(typeof(GameUserStreamGroup))]
|
||||
[XmlInclude(typeof(GameSlotStreamGroup))]
|
||||
public class GameStreamGroup : ILbpSerializable
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public ActivityGroupType Type { get; set; }
|
||||
|
||||
[XmlElement("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[XmlArray("subgroups")]
|
||||
[XmlArrayItem("group")]
|
||||
[DefaultValue(null)]
|
||||
public List<GameStreamGroup> Groups { get; set; }
|
||||
|
||||
[XmlArray("events")]
|
||||
[XmlArrayItem("event")]
|
||||
[DefaultValue(null)]
|
||||
public List<GameEvent> Events { get; set; }
|
||||
|
||||
public static GameStreamGroup CreateFromGrouping(IGrouping<ActivityGroup, ActivityEntity> group)
|
||||
{
|
||||
ActivityGroupType type = group.Key.GroupType;
|
||||
GameStreamGroup gameGroup = type switch
|
||||
{
|
||||
ActivityGroupType.Level => new GameSlotStreamGroup
|
||||
{
|
||||
Slot = new ReviewSlot
|
||||
{
|
||||
SlotId = group.Key.TargetId,
|
||||
},
|
||||
},
|
||||
ActivityGroupType.User => new GameUserStreamGroup
|
||||
{
|
||||
UserId = group.Key.TargetId,
|
||||
},
|
||||
_ => new GameStreamGroup(),
|
||||
};
|
||||
gameGroup.Timestamp = new DateTimeOffset(group.Select(a => a.Timestamp).MaxBy(a => a)).ToUnixTimeMilliseconds();
|
||||
gameGroup.Type = type;
|
||||
|
||||
List<IGrouping<EventType, ActivityEntity>> eventGroups = group.OrderByDescending(a => a.Timestamp).GroupBy(g => g.Type).ToList();
|
||||
//TODO removeme debug
|
||||
foreach (IGrouping<EventType, ActivityEntity> bruh in eventGroups)
|
||||
{
|
||||
Console.WriteLine($@"group key: {bruh.Key}, count={bruh.Count()}");
|
||||
}
|
||||
gameGroup.Groups = new List<GameStreamGroup>
|
||||
{
|
||||
new GameUserStreamGroup
|
||||
{
|
||||
UserId = group.Key.UserId,
|
||||
Type = ActivityGroupType.User,
|
||||
Timestamp = gameGroup.Timestamp,
|
||||
Events = eventGroups.SelectMany(GameEvent.CreateFromActivityGroups).ToList(),
|
||||
},
|
||||
};
|
||||
|
||||
return gameGroup;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Activity;
|
||||
|
||||
public class GameUserStreamGroup : GameStreamGroup, INeedsPreparationForSerialization
|
||||
{
|
||||
[XmlIgnore]
|
||||
public int UserId { get; set; }
|
||||
|
||||
[XmlElement("user_id")]
|
||||
public string Username { get; set; }
|
||||
|
||||
public async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
UserEntity user = await database.Users.FindAsync(this.UserId);
|
||||
if (user == null) return;
|
||||
|
||||
this.Username = user.Username;
|
||||
}
|
||||
|
||||
public static GameUserStreamGroup Create(int userId) =>
|
||||
new()
|
||||
{
|
||||
UserId = userId,
|
||||
};
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Comment;
|
||||
|
||||
[XmlRoot("comments")]
|
||||
public struct CommentListResponse : ILbpSerializable
|
|
@ -6,7 +6,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Comment;
|
||||
|
||||
[XmlRoot("comment")]
|
||||
[XmlType("comment")]
|
|
@ -9,7 +9,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Photo;
|
||||
|
||||
[XmlRoot("photo")]
|
||||
[XmlType("photo")]
|
|
@ -1,7 +1,7 @@
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Photo;
|
||||
|
||||
[XmlType("subject")]
|
||||
[XmlRoot("subject")]
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Photo;
|
||||
|
||||
[XmlRoot("photos")]
|
||||
public struct PhotoListResponse : ILbpSerializable
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Photo;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public class PhotoSlot : ILbpSerializable
|
|
@ -1,6 +1,6 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
|
||||
[XmlRoot("author")]
|
||||
public struct Author : ILbpSerializable
|
|
@ -10,7 +10,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
|
||||
[XmlRoot("playlist")]
|
||||
public class GamePlaylist : ILbpSerializable, INeedsPreparationForSerialization
|
|
@ -2,7 +2,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
|
||||
public struct GenericPlaylistResponse<T> : ILbpSerializable, IHasCustomRoot where T : ILbpSerializable
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
|
||||
public struct IconList : ILbpSerializable
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Playlist;
|
||||
|
||||
[XmlRoot("playlists")]
|
||||
public struct PlaylistResponse : ILbpSerializable
|
|
@ -8,7 +8,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
|||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
[XmlRoot("deleted_by")]
|
||||
public enum DeletedBy
|
|
@ -2,7 +2,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
[XmlRoot("reviews")]
|
||||
public struct ReviewResponse : ILbpSerializable
|
22
ProjectLighthouse/Types/Serialization/Review/ReviewSlot.cs
Normal file
22
ProjectLighthouse/Types/Serialization/Review/ReviewSlot.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public class ReviewSlot : ILbpSerializable
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public SlotType SlotType { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public int SlotId { get; set; }
|
||||
|
||||
public static ReviewSlot CreateFromEntity(SlotEntity slot) =>
|
||||
new()
|
||||
{
|
||||
SlotType = slot.Type,
|
||||
SlotId = slot.Type == SlotType.User ? slot.SlotId : slot.InternalSlotId,
|
||||
};
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public class ReviewSlot : ILbpSerializable
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public SlotType SlotType { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public int SlotId { get; set; }
|
||||
}
|
|
@ -6,7 +6,7 @@ using LBPUnion.ProjectLighthouse.Database;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Score;
|
||||
|
||||
[XmlRoot("playRecord")]
|
||||
[XmlType("playRecord")]
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Score;
|
||||
|
||||
[XmlRoot("scoreboards")]
|
||||
public class MultiScoreboardResponse : ILbpSerializable
|
|
@ -2,7 +2,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Score;
|
||||
|
||||
public struct ScoreboardResponse: ILbpSerializable, IHasCustomRoot
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("categories")]
|
||||
public class CategoryListResponse : ILbpSerializable
|
|
@ -2,7 +2,7 @@
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("category")]
|
||||
public class GameCategory : ILbpSerializable
|
|
@ -7,7 +7,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
|||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public class GameDeveloperSlot : SlotBase, INeedsPreparationForSerialization
|
|
@ -13,10 +13,12 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Misc;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.Review;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public class GameUserSlot : SlotBase, INeedsPreparationForSerialization
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
public struct GenericSlotResponse : ILbpSerializable, IHasCustomRoot
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("planetStats")]
|
||||
public class PlanetStatsResponse : ILbpSerializable
|
|
@ -3,9 +3,10 @@ using System.Xml.Serialization;
|
|||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlInclude(typeof(GameUserSlot))]
|
||||
[XmlInclude(typeof(GameDeveloperSlot))]
|
||||
|
@ -49,7 +50,7 @@ public abstract class SlotBase : ILbpSerializable
|
|||
public static SlotBase CreateFromEntity(SlotEntity slot, GameTokenEntity token)
|
||||
=> CreateFromEntity(slot, token.GameVersion, token.UserId);
|
||||
|
||||
private static SlotBase CreateFromEntity(SlotEntity slot, GameVersion targetGame, int targetUserId)
|
||||
public static SlotBase CreateFromEntity(SlotEntity slot, GameVersion targetGame, int targetUserId)
|
||||
{
|
||||
if (slot == null)
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.Slot;
|
||||
|
||||
[XmlRoot("slot")]
|
||||
public struct SlotResourceResponse : ILbpSerializable
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
[XmlRoot("npdata")]
|
||||
public struct FriendResponse : ILbpSerializable
|
|
@ -11,7 +11,7 @@ using LBPUnion.ProjectLighthouse.Types.Misc;
|
|||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
[XmlRoot("user")]
|
||||
public class GameUser : ILbpSerializable, INeedsPreparationForSerialization
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Filter;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
public struct GenericUserResponse<T> : ILbpSerializable, IHasCustomRoot where T : ILbpSerializable
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
[XmlRoot("users")]
|
||||
public struct MinimalUserListResponse : ILbpSerializable
|
|
@ -1,7 +1,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
public class NpHandle : ILbpSerializable
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Serialization.User;
|
||||
|
||||
public struct UserListResponse : ILbpSerializable, IHasCustomRoot
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ public struct UserListResponse : ILbpSerializable, IHasCustomRoot
|
|||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public string RootTag { get; set; }
|
||||
private string RootTag { get; set; }
|
||||
|
||||
[XmlElement("user")]
|
||||
public List<GameUser> Users { get; set; }
|
Loading…
Add table
Add a link
Reference in a new issue