Merge branch 'LBPUnion-main'

This commit is contained in:
LumaLivy 2021-11-18 15:16:39 -05:00
commit 93fe204563
28 changed files with 193 additions and 100 deletions

View file

@ -19,7 +19,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
public LighthouseTest()
{
this.Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
this.Server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
this.Client = this.Server.CreateClient();
}

View file

@ -13,6 +13,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>

View file

@ -1,3 +1,4 @@
using System.Net.Http;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
@ -48,10 +49,17 @@ namespace LBPUnion.ProjectLighthouse.Tests
LoginResult loginResult = await this.Authenticate();
string respA = await (await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0", loginResult.AuthTicket)).Content
.ReadAsStringAsync();
string respB = await (await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1", loginResult.AuthTicket)).Content
.ReadAsStringAsync();
HttpResponseMessage respMessageA = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0", loginResult.AuthTicket);
HttpResponseMessage respMessageB = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1", loginResult.AuthTicket);
Assert.True(respMessageA.IsSuccessStatusCode);
Assert.True(respMessageB.IsSuccessStatusCode);
string respA = await respMessageA.Content.ReadAsStringAsync();
string respB = await respMessageB.Content.ReadAsStringAsync();
Assert.False(string.IsNullOrEmpty(respA));
Assert.False(string.IsNullOrEmpty(respB));
Assert.NotEqual(respA, respB);
Assert.DoesNotContain(respA, "slotB");

View file

@ -73,6 +73,7 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DLC/@EntryIndexedValue">DLC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LBP/@EntryIndexedValue">LBP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MM/@EntryIndexedValue">MM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NAT/@EntryIndexedValue">NAT</s:String>
@ -93,6 +94,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=BCJS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Braaains/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=brun/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=dpadrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ezoiar/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=farc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=friendscores/@EntryIndexedValue">True</s:Boolean>

View file

@ -1,3 +1,4 @@
#nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -44,14 +45,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Comment));
Comment comment = (Comment)serializer.Deserialize(new StringReader(bodyString));
User poster = await this.database.UserFromRequest(this.Request);
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
User? poster = await this.database.UserFromRequest(this.Request);
if (poster == null) return this.StatusCode(403, "");
User target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (comment == null || target == null) return this.BadRequest();
comment.PosterUserId = poster.UserId;
@ -67,10 +66,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("deleteUserComment/{username}")]
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username)
{
User user = await this.database.UserFromRequest(this.Request);
User? user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
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.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, "");

View file

@ -1,4 +1,5 @@
#nullable enable
using System;
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types;
@ -52,7 +53,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
default: return this.BadRequest();
}
v = new();
v = new VisitedLevel();
v.SlotId = slotId;
v.UserId = user.UserId;
this.database.VisitedLevels.Add(v);
@ -63,6 +64,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
if (v == null) return this.StatusCode(403, "");
if (v == null)
{
return this.NotFound();
}
switch (gameVersion)
{
case GameVersion.LittleBigPlanet2:
@ -77,7 +83,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
slot.PlaysLBPVita++;
v.PlaysLBPVita++;
break;
default: return this.BadRequest();
case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException();
case GameVersion.Unknown:
default:
return this.BadRequest();
}
await this.database.SaveChangesAsync();
@ -101,7 +110,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
slot.PlaysLBP1Unique++;
v = new();
v = new VisitedLevel();
v.SlotId = id;
v.UserId = user.UserId;
this.database.VisitedLevels.Add(v);
@ -112,6 +121,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
if (v == null) return StatusCode(403, "");
if (v == null)
{
return this.NotFound();
}
slot.PlaysLBP1++;
v.PlaysLBP1++;

View file

@ -46,7 +46,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Where(q => q.User.Username == username).Count()));
return this.Ok
(
LbpSerializer.TaggedStringElement
("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("lolcatftw/add/user/{id:int}")]
@ -110,7 +114,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
return this.Ok(LbpSerializer.TaggedStringElement("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Where(q => q.User.Username == username).Count()));
return this.Ok
(
LbpSerializer.TaggedStringElement
("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("favourite/slot/user/{id:int}")]
@ -173,7 +181,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion));
return this.Ok(LbpSerializer.TaggedStringElement("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Where(q => q.User.Username == username).Count()));
return this.Ok
(
LbpSerializer.TaggedStringElement
("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username))
);
}
[HttpPost("favourite/user/{username}")]
@ -224,4 +236,4 @@ namespace LBPUnion.ProjectLighthouse.Controllers
#endregion
}
}
}

View file

@ -82,7 +82,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (user == null || userFromQuery == null) return this.NotFound();
if (userFromQuery == null) return this.NotFound();
List<Photo> photos = await this.database.Photos.Include
(p => p.Creator)
@ -100,7 +100,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (user == null || userFromQuery == null) return this.NotFound();
if (userFromQuery == null) return this.NotFound();
List<Photo> photos = new();
foreach (Photo photo in this.database.Photos.Include(p => p.Creator))

View file

@ -1,3 +1,5 @@
#nullable enable
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -30,10 +32,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("startPublish")]
public async Task<IActionResult> StartPublish()
{
User user = await this.database.UserFromRequest(this.Request);
User? user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot slot = await this.GetSlotFromBody();
Slot? slot = await this.GetSlotFromBody();
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest();
@ -43,7 +45,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
// Republish logic
if (slot.SlotId != 0)
{
Slot oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
if (oldSlot == null) return this.NotFound();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
}
@ -72,13 +74,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers
User user = userAndToken.Value.Item1;
Token token = userAndToken.Value.Item2;
Slot slot = await this.GetSlotFromBody();
Slot? slot = await this.GetSlotFromBody();
if (slot == null || slot.Location == null) return this.BadRequest();
// Republish logic
if (slot.SlotId != 0)
{
Slot oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
if (oldSlot == null) return this.NotFound();
if (oldSlot.Location == null) throw new ArgumentNullException();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
oldSlot.Location.X = slot.Location.X;
@ -125,10 +131,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unpublish/{id:int}")]
public async Task<IActionResult> Unpublish(int id)
{
User user = await this.database.UserFromRequest(this.Request);
User? user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
Slot slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
if (slot.Location == null) throw new ArgumentNullException();
if (slot.CreatorId != user.UserId) return this.StatusCode(403, "");
@ -140,13 +149,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.Ok();
}
public async Task<Slot> GetSlotFromBody()
public async Task<Slot?> GetSlotFromBody()
{
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(Slot));
Slot slot = (Slot)serializer.Deserialize(new StringReader(bodyString));
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
return slot;
}

View file

@ -33,13 +33,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
if (ratedLevel == null)
{
ratedLevel = new();
ratedLevel = new RatedLevel();
ratedLevel.SlotId = slotId;
ratedLevel.UserId = user.UserId;
ratedLevel.Rating = 0;
this.database.RatedLevels.Add(ratedLevel);
}
ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0);
await this.database.SaveChangesAsync();
@ -48,7 +48,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
// LBP2 and beyond rating
[HttpPost("dpadrate/user/{slotId}")]
[HttpPost("dpadrate/user/{slotId:int}")]
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
{
User? user = await this.database.UserFromRequest(this.Request);
@ -60,19 +60,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
if (ratedLevel == null)
{
ratedLevel = new();
ratedLevel = new RatedLevel();
ratedLevel.SlotId = slotId;
ratedLevel.UserId = user.UserId;
ratedLevel.RatingLBP1 = 0;
this.database.RatedLevels.Add(ratedLevel);
}
ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1);
await this.database.SaveChangesAsync();
return this.Ok();
}
}
}

View file

@ -102,6 +102,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.Ok(GetScores(slotId, type, user, pageStart, pageSize));
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5)
{
// This is hella ugly but it technically assigns the proper rank to a score

View file

@ -32,14 +32,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
GameVersion gameVersion = token.GameVersion;
User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u);
if (user == null) return StatusCode(403, "");
if (user == null) return this.NotFound();
string response = Enumerable.Aggregate
(
this.database.Slots.Where(s => s.GameVersion <= gameVersion)
.Include(s => s.Creator)
.Include(s => s.Location)
.Where(s => s.Creator.Username == user.Username)
.Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, ServerSettings.EntitledSlots)),
string.Empty,

View file

@ -26,9 +26,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
this.database = database;
}
public async Task<string> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
public async Task<string?> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return "";
return user.Serialize(gameVersion);
@ -52,14 +51,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
Token? token = await this.database.TokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
List<string> serializedUsers = new();
List<string?> serializedUsers = new();
foreach (string userId in u)
{
string? serializedUser = await this.GetSerializedUser(userId, token.GameVersion);
if (serializedUser != "") serializedUsers.Add(serializedUser);
}
string serialized = serializedUsers.Aggregate(string.Empty, (current, u) => u == null ? current : current + u);
string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user);
return this.Ok(LbpSerializer.StringElement("users", serialized));
}
@ -145,8 +144,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
// the way location on a user card works is stupid and will not save with the way below as-is, so we do the following:
if (locationChanged) // only modify the database if we modify here
{
Location? l = await this.database.Locations.Where(l => l.Id == user.LocationId).FirstOrDefaultAsync(); // find the location in the database again
if (l == null) return this.StatusCode(403, "");
Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again
if (l == null)
{
throw new Exception("this shouldn't happen ever but we handle this");
}
// set the location in the database to the one we modified above
l.X = user.Location.X;
l.Y = user.Location.Y;
@ -166,7 +170,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync();
Pins? pinJson = JsonSerializer.Deserialize<Pins>(pinsString);
if (pinJson == null) return this.BadRequest();
// Sometimes the update gets called periodically as pin progress updates via playing,

View file

@ -0,0 +1,24 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace LBPUnion.ProjectLighthouse
{
public class FakeRemoteIPAddressMiddleware
{
private readonly RequestDelegate next;
private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1");
public FakeRemoteIPAddressMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Connection.RemoteIpAddress = this.fakeIpAddress;
await this.next(httpContext);
}
}
}

View file

@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class EulaHelper
@ -19,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
public const string PrivateInstanceNotice = @"This server is a private testing instance.
Please do not make anything public for now, and keep in mind security isn't as tight as a full release would.";
// ReSharper disable once UnreachableCode
[SuppressMessage("ReSharper", "HeuristicUnreachableCode")]
public const string PrivateInstanceNoticeOrBlank = ShowPrivateInstanceNotice ? PrivateInstanceNotice : "";
public const bool ShowPrivateInstanceNotice = false;

View file

@ -1,17 +1,10 @@
using System.IO;
using System.Linq;
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
{
public static class StringExtensions
{
public static string ToFileName(this string text)
{
char[] invalidPathChars = Path.GetInvalidFileNameChars();
string path = text;
foreach (char c in invalidPathChars) path = path.Replace(c.ToString(), "");
return path;
}
public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), ""));
}
}

View file

@ -1,12 +1,14 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
namespace LBPUnion.ProjectLighthouse.Helpers
{
[NotMapped]
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
public static class FriendHelper
{
public static Dictionary<int, int[]> FriendIdsByUserId = new();
public static Dictionary<int, int[]> BlockedIdsByUserId = new();
public static readonly Dictionary<int, int[]> FriendIdsByUserId = new();
public static readonly Dictionary<int, int[]> BlockedIdsByUserId = new();
}
}

View file

@ -20,7 +20,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
SlotId = 0,
};
private static int roomIdIncrement = 0;
private static int roomIdIncrement;
internal static int RoomIdIncrement => roomIdIncrement++;
@ -128,13 +128,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static Room? FindRoomByUser(User user, bool createIfDoesNotExist = false)
{
foreach (Room room in Rooms)
{
foreach (User player in room.Players)
{
if (user == player) return room;
}
}
foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player))) return room;
return createIfDoesNotExist ? CreateRoom(user) : null;
}

View file

@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
<PackageReference Include="Kettu" Version="1.2.1"/>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0"/>

View file

@ -51,7 +51,7 @@ namespace LBPUnion.ProjectLighthouse
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
bool computeDigests = true;
string serverDigestKey = Environment.GetEnvironmentVariable("SERVER_DIGEST_KEY");

View file

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace LBPUnion.ProjectLighthouse
{
public class TestStartup : Startup
{
public TestStartup(IConfiguration configuration) : base(configuration)
{}
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<FakeRemoteIPAddressMiddleware>();
base.Configure(app, env);
}
}
}

View file

@ -17,25 +17,25 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
{
[XmlAttribute("type")]
[NotMapped]
public string Type { get; set; }
public string Type { get; set; } = "user";
[Key]
[XmlElement("id")]
public int SlotId { get; set; }
[XmlElement("name")]
public string Name { get; set; }
public string Name { get; set; } = "";
[XmlElement("description")]
public string Description { get; set; }
public string Description { get; set; } = "";
[XmlElement("icon")]
public string IconHash { get; set; }
public string IconHash { get; set; } = "";
[XmlElement("rootLevel")]
public string RootLevel { get; set; }
public string RootLevel { get; set; } = "";
public string ResourceCollection { get; set; }
public string ResourceCollection { get; set; } = "";
[NotMapped]
[XmlElement("resource")]
@ -51,14 +51,14 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
public int CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public User Creator { get; set; }
public User? Creator { get; set; }
/// <summary>
/// The location of the level on the creator's earth
/// </summary>
[XmlElement("location")]
[ForeignKey(nameof(LocationId))]
public Location Location { get; set; }
public Location? Location { get; set; }
[XmlElement("initiallyLocked")]
public bool InitiallyLocked { get; set; }
@ -111,21 +111,15 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
[XmlIgnore]
[NotMapped]
public int Plays {
get => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita;
}
public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita;
[XmlIgnore]
[NotMapped]
public int PlaysUnique {
get => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique + this.PlaysLBPVitaUnique;
}
public int PlaysUnique => this.PlaysLBP1Unique + this.PlaysLBP2Unique + this.PlaysLBP3Unique + this.PlaysLBPVitaUnique;
[XmlIgnore]
[NotMapped]
public int PlaysComplete {
get => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete + this.PlaysLBPVitaComplete;
}
public int PlaysComplete => this.PlaysLBP1Complete + this.PlaysLBP2Complete + this.PlaysLBP3Complete + this.PlaysLBPVitaComplete;
[XmlIgnore]
public int PlaysLBP1 { get; set; }
@ -193,8 +187,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
if (!ratedLevels.Any()) return 3.0;
return Enumerable.Average(ratedLevels, r => r.RatingLBP1);
;
}
}
@ -217,7 +209,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
LbpSerializer.StringElement("icon", this.IconHash) +
LbpSerializer.StringElement("rootLevel", this.RootLevel) +
this.SerializeResources() +
LbpSerializer.StringElement("location", this.Location.Serialize()) +
LbpSerializer.StringElement("location", this.Location?.Serialize()) +
LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) +
LbpSerializer.StringElement("isSubLevel", this.SubLevel) +
LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) +

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.Match
@ -13,6 +14,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match
public IEnumerable<int> FirstSlot => this.Slots[0];
[JsonPropertyName("Location")]
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
public List<string> Locations { get; set; }
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Types.Levels;
namespace LBPUnion.ProjectLighthouse.Types.Match
@ -17,6 +18,13 @@ namespace LBPUnion.ProjectLighthouse.Types.Match
public User Host => this.Players[0];
#nullable enable
public override bool Equals(object? obj)
{
if (obj is Room room) return room.RoomId == RoomId;
return false;
}
public static bool operator ==(Room? room1, Room? room2)
{
if (ReferenceEquals(room1, room2)) return true;
@ -26,6 +34,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match
}
public static bool operator !=(Room? room1, Room? room2) => !(room1 == room2);
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode() => this.RoomId;
#nullable disable
}

View file

@ -1,5 +1,8 @@
using System.Diagnostics.CodeAnalysis;
namespace LBPUnion.ProjectLighthouse.Types.Match
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public enum RoomState
{
/// <summary>

View file

@ -3,7 +3,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match
{
public class UpdateMyPlayerData : IMatchData
{
public string Player { get; set; }
public string Player { get; set; } = null!;
public RoomState? RoomState { get; set; }
}

View file

@ -22,19 +22,19 @@ namespace LBPUnion.ProjectLighthouse.Types
public long Timestamp { get; set; }
[XmlElement("small")]
public string SmallHash { get; set; }
public string SmallHash { get; set; } = "";
[XmlElement("medium")]
public string MediumHash { get; set; }
public string MediumHash { get; set; } = "";
[XmlElement("large")]
public string LargeHash { get; set; }
public string LargeHash { get; set; } = "";
[XmlElement("plan")]
public string PlanHash { get; set; }
public string PlanHash { get; set; } = "";
[NotMapped]
private List<PhotoSubject>? subjects;
private List<PhotoSubject>? _subjects;
[NotMapped]
[XmlArray("subjects")]
@ -45,15 +45,13 @@ namespace LBPUnion.ProjectLighthouse.Types
public List<PhotoSubject> Subjects {
get {
if (this.SubjectsXmlDontUseLiterallyEver != null) return this.SubjectsXmlDontUseLiterallyEver;
if (this.subjects != null) return this.subjects;
if (this._subjects != null) return this._subjects;
List<PhotoSubject> response = new();
using Database database = new();
foreach (string idStr in this.PhotoSubjectIds)
foreach (string idStr in this.PhotoSubjectIds.Where(idStr => !string.IsNullOrEmpty(idStr)))
{
if (string.IsNullOrEmpty(idStr)) continue;
if (!int.TryParse(idStr, out int id)) throw new InvalidCastException(idStr + " is not a valid number.");
PhotoSubject? photoSubject = database.PhotoSubjects.Include(p => p.User).FirstOrDefault(p => p.PhotoSubjectId == id);
@ -64,7 +62,7 @@ namespace LBPUnion.ProjectLighthouse.Types
return response;
}
set => this.subjects = value;
set => this._subjects = value;
}
[NotMapped]
@ -74,12 +72,12 @@ namespace LBPUnion.ProjectLighthouse.Types
set => this.PhotoSubjectCollection = string.Join(',', value);
}
public string PhotoSubjectCollection { get; set; }
public string PhotoSubjectCollection { get; set; } = "";
public int CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public User Creator { get; set; }
public User? Creator { get; set; }
public string Serialize(int slotId)
{
@ -92,7 +90,7 @@ namespace LBPUnion.ProjectLighthouse.Types
LbpSerializer.StringElement("medium", this.MediumHash) +
LbpSerializer.StringElement("large", this.LargeHash) +
LbpSerializer.StringElement("plan", this.PlanHash) +
LbpSerializer.StringElement("author", this.Creator.Username) +
LbpSerializer.StringElement("author", this.Creator?.Username) +
LbpSerializer.StringElement("subjects", subjectsAggregate) +
slot;

View file

@ -206,6 +206,7 @@ namespace LBPUnion.ProjectLighthouse.Types
}
public static bool operator !=(User? user1, User? user2) => !(user1 == user2);
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode() => this.UserId;
#nullable disable
}