From e4f764784e2f6b44b3f0a6da89c57ebc4e3c4a70 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 16:58:53 -0400 Subject: [PATCH 01/36] Properly read username from login data --- ProjectLighthouse/Types/LoginData.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ProjectLighthouse/Types/LoginData.cs b/ProjectLighthouse/Types/LoginData.cs index 7cb1b5ff..256fb2c9 100644 --- a/ProjectLighthouse/Types/LoginData.cs +++ b/ProjectLighthouse/Types/LoginData.cs @@ -11,6 +11,7 @@ namespace LBPUnion.ProjectLighthouse.Types { // - LBP2 digital, with the RPCN username `jvyden` // POST /LITTLEBIGPLANETPS3_XML/login?applicationID=21414&languageID=1&lbp2=1&beta=0&titleID=NPUA80662&country=us // !�0220333||/u||=0� jvydebruUP9000-NPUA80662_008D + // Data is 251 bytes long. /// /// The data sent from POST /LOGIN. /// @@ -20,20 +21,14 @@ namespace LBPUnion.ProjectLighthouse.Types { // public int UnknownNumber { get; set; } // Seems to increment by 1000 every login attempt public static LoginData CreateFromString(string str) { - do { - str = str.Replace("\b", string.Empty); // Trim backspace characters - } while(str.Contains('\b')); + str = str.Replace("\b", ""); // Remove backspace characters using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); using BinaryReader reader = new(ms); LoginData loginData = new(); - - BinaryHelper.ReadUntilByte(reader, 0x20); // Skips to relevant part - -// byte[] endBytes = reader.ReadBytes((int)(ms.Length - reader.BaseStream.Position)); -// string end = Encoding.ASCII.GetString(endBytes); + reader.BaseStream.Position = 80; loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); return loginData; From 24b712cd157a0865c1b7e59682a400501da72995 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 17:27:59 -0400 Subject: [PATCH 02/36] Fix tests --- ProjectLighthouse.Tests/LighthouseTest.cs | 6 +++-- .../Tests/AuthenticationTests.cs | 2 +- ProjectLighthouse.Tests/Tests/MatchTests.cs | 26 +++++++++++++++++-- .../Controllers/MatchController.cs | 4 +-- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseTest.cs index d9027f7f..176989dc 100644 --- a/ProjectLighthouse.Tests/LighthouseTest.cs +++ b/ProjectLighthouse.Tests/LighthouseTest.cs @@ -23,10 +23,12 @@ namespace LBPUnion.ProjectLighthouse.Tests { public async Task AuthenticateResponse(int number = 0) { const char nullChar = (char)0x00; - const char sepChar = (char)0x20; const string username = "unitTestUser"; - string stringContent = $"{nullChar}{sepChar}{username}{number}{nullChar}"; + string nullString = ""; + for(int i = 0; i < 80; i++) nullString += nullChar; + + string stringContent = $"{nullString}{username}{number}{nullChar}"; HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", new StringContent(stringContent)); return response; diff --git a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs index 5ac770ff..ea51884b 100644 --- a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs @@ -47,7 +47,7 @@ namespace LBPUnion.ProjectLighthouse.Tests { string responseContent = await response.Content.ReadAsStringAsync(); Assert.True(response.IsSuccessStatusCode); - Assert.Contains("You are logged in", responseContent); + Assert.Contains("You are now logged in", responseContent); } [DatabaseFact] diff --git a/ProjectLighthouse.Tests/Tests/MatchTests.cs b/ProjectLighthouse.Tests/Tests/MatchTests.cs index e6ba1207..138501cf 100644 --- a/ProjectLighthouse.Tests/Tests/MatchTests.cs +++ b/ProjectLighthouse.Tests/Tests/MatchTests.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types; @@ -10,11 +11,27 @@ namespace LBPUnion.ProjectLighthouse.Tests { private static readonly SemaphoreSlim semaphore = new(1, 1); [DatabaseFact] - public async Task ShouldReturnOk() { + public async Task ShouldRejectEmptyData() { LoginResult loginResult = await this.Authenticate(); await semaphore.WaitAsync(); HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty(), loginResult.AuthTicket); + Assert.False(result.IsSuccessStatusCode); + + semaphore.Release(); + } + + [DatabaseFact] + public async Task ShouldReturnOk() { + LoginResult loginResult = await this.Authenticate(); + await semaphore.WaitAsync(); + + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( + "LITTLEBIGPLANETPS3_XML/match", + Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), + loginResult.AuthTicket + ); + Assert.True(result.IsSuccessStatusCode); semaphore.Release(); @@ -29,7 +46,12 @@ namespace LBPUnion.ProjectLighthouse.Tests { int oldPlayerCount = await this.GetPlayerCount(); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty(), loginResult.AuthTicket); + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( + "LITTLEBIGPLANETPS3_XML/match", + Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), + loginResult.AuthTicket + ); + Assert.True(result.IsSuccessStatusCode); int playerCount = await this.GetPlayerCount(); diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index 6d34b60e..61d63013 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -32,12 +32,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers { #region Parse match data // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + string? bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); if(bodyString.Contains("FindBestRoom")) { return this.Ok("[{\"StatusCode\":200},{\"Players\":[{\"PlayerId\":\"literally1984\",\"matching_res\":0},{\"PlayerId\":\"jvyden\",\"matching_res\":1}],\"Slots\":[[5,0]],\"RoomState\":\"E_ROOM_IN_POD\",\"HostMood\":\"E_MOOD_EVERYONE\",\"LevelCompletionEstimate\":0,\"PassedNoJoinPoint\":0,\"MoveConnected\":false,\"Location\":[\"127.0.0.1\"],\"BuildVersion\":289,\"Language\":1,\"FirstSeenTimestamp\":1427331263756,\"LastSeenTimestamp\":1635112546000,\"GameId\":1,\"NatType\":2,\"Friends\":[],\"Blocked\":[],\"RecentlyLeft\":[],\"FailedJoin\":[]}]"); } - if(bodyString[0] != '[') return this.BadRequest(); + if(string.IsNullOrEmpty(bodyString) || bodyString[0] != '[') return this.BadRequest(); IMatchData? matchData; try { From 33c961d481cf6f754547b2b8092b8b3b517a964f Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 18:04:33 -0400 Subject: [PATCH 03/36] Add compatibility chart to readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index dfa80011..3be3b2bf 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,11 @@ Some modifications may require updates to the database schema. You can automatic 2. Making sure `LIGHTHOUSE_DB_CONNECTION_STRING` is set correctly. See the `Running` section for more details. 3. Making your changes to the database. I won't cover this since if you're making database changes you should know what you're doing. 4. Running `dotnet ef migrations add --project ProjectLighthouse`. + +## Compatibility across games and platforms + +| | PS3 | RPCS3 | +|---------|------------|----------------------------------| +| LBP1 | Somewhat | Crashes on entering controller | +| LBP2 | Yes | Yes (requires patched RPCS3) | +| LBP3 | Connects | Crashes on startup | \ No newline at end of file From 93a452814ca85e43dd37985ef6cba1706e925689 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 18:37:06 -0400 Subject: [PATCH 04/36] Add ability to republish levels --- .../Controllers/PublishController.cs | 29 ++++++++++++++++++- ProjectLighthouse/Types/Levels/Slot.cs | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 8136e10a..508bbdec 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -22,13 +22,23 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } /// - /// Endpoint the game uses to verify that the level is compatible (?) + /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded /// [HttpPost("startPublish")] public async Task StartPublish() { + User user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + Slot slot = await this.GetSlotFromBody(); if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded + // Republish logic + if(slot.SlotId != 0) { + 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(); + } + string resources = slot.Resources .Where(hash => !FileHelper.ResourceExists(hash)) .Aggregate("", (current, hash) => @@ -47,6 +57,23 @@ namespace LBPUnion.ProjectLighthouse.Controllers { Slot slot = await this.GetSlotFromBody(); + // Republish logic + if(slot.SlotId != 0) { + Slot oldSlot = await this.database.Slots + .Include(s => s.Location) + .FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + if(oldSlot == null) return this.NotFound(); + if(oldSlot.CreatorId != user.UserId) return this.BadRequest(); + + slot.CreatorId = oldSlot.CreatorId; + slot.LocationId = oldSlot.LocationId; + slot.SlotId = oldSlot.SlotId; + + this.database.Entry(oldSlot).CurrentValues.SetValues(slot); + await this.database.SaveChangesAsync(); + return this.Ok(oldSlot.Serialize()); + } + //TODO: parse location in body Location l = new() { X = 0, diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index c6fe9f43..92fd9701 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -16,7 +16,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { public string Type { get; set; } [Key] - [XmlIgnore] + [XmlElement("id")] public int SlotId { get; set; } From 14730f754babd0ac60614b984dfa48035ed4ffb2 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 22:02:36 -0400 Subject: [PATCH 05/36] Fix exception on searching slots without a query --- ProjectLighthouse/Controllers/SearchController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ProjectLighthouse/Controllers/SearchController.cs b/ProjectLighthouse/Controllers/SearchController.cs index adaced48..823a89cf 100644 --- a/ProjectLighthouse/Controllers/SearchController.cs +++ b/ProjectLighthouse/Controllers/SearchController.cs @@ -18,6 +18,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { [HttpGet("slots/search")] public async Task SearchSlots([FromQuery] string query) { + if(query == null) return this.BadRequest(); query = query.ToLower(); string[] keywords = query.Split(" "); From 73ad687874d1e730c27203d9340511a27dd79de6 Mon Sep 17 00:00:00 2001 From: jvyden Date: Tue, 26 Oct 2021 22:54:06 -0400 Subject: [PATCH 06/36] Add timestamp to comments --- ProjectLighthouse/Controllers/CommentController.cs | 3 +++ ProjectLighthouse/Helpers/TimeHelper.cs | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 ProjectLighthouse/Helpers/TimeHelper.cs diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index 9f2967f1..f819a5d4 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Profiles; @@ -50,6 +51,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { comment.PosterUserId = poster.UserId; comment.TargetUserId = target.UserId; + comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); + this.database.Comments.Add(comment); await this.database.SaveChangesAsync(); return this.Ok(); diff --git a/ProjectLighthouse/Helpers/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs new file mode 100644 index 00000000..418fff6f --- /dev/null +++ b/ProjectLighthouse/Helpers/TimeHelper.cs @@ -0,0 +1,8 @@ +using System; + +namespace LBPUnion.ProjectLighthouse.Helpers { + public static class TimeHelper { + public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); + public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); + } +} \ No newline at end of file From 367d6795ae49fbe0403af8f969a068335f6bb61a Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 01:31:56 -0400 Subject: [PATCH 07/36] Update to Kettu 1.1.0 --- .../Logging/AspNetToKettuLogger.cs | 12 +----- .../Logging/LighthouseFileLogger.cs | 11 +++-- ProjectLighthouse/Logging/LoggerLevels.cs | 42 ++++--------------- ProjectLighthouse/ProjectLighthouse.csproj | 6 ++- 4 files changed, 21 insertions(+), 50 deletions(-) diff --git a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs index 944fafa5..f1af4bb7 100644 --- a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs +++ b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs @@ -12,17 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Logging { public bool IsEnabled(LogLevel logLevel) => true; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - LoggerLevel loggerLevel = logLevel switch { - - LogLevel.Trace => LoggerLevelAspNetTrace.Instance, - LogLevel.Debug => LoggerLevelAspNetDebug.Instance, - LogLevel.Information => LoggerLevelAspNetInformation.Instance, - LogLevel.Warning => LoggerLevelAspNetWarning.Instance, - LogLevel.Error => LoggerLevelAspNetError.Instance, - LogLevel.Critical => LoggerLevelAspNetCritical.Instance, - LogLevel.None => LoggerLevelAspNetNone.Instance, - _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null), - }; + LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); Logger.Log(state.ToString(), loggerLevel); if(exception == null) return; diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs index 98cb4d60..88f7a4f2 100644 --- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs +++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs @@ -9,9 +9,14 @@ namespace LBPUnion.ProjectLighthouse.Logging { public override void Send(LoggerLine line) { FileHelper.EnsureDirectoryCreated(logsDirectory); - - File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel + ".log"), line.LineData + "\n"); - File.AppendAllText(Path.Combine(logsDirectory, "all.log"), line.LineData + "\n"); + + string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; + + string contentFile = $"{channel}{line.LineData}\n"; + string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n"; + + File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name + ".log"), contentFile); + File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs index 4f8de4e3..72831dd3 100644 --- a/ProjectLighthouse/Logging/LoggerLevels.cs +++ b/ProjectLighthouse/Logging/LoggerLevels.cs @@ -1,4 +1,5 @@ using Kettu; +using Microsoft.Extensions.Logging; namespace LBPUnion.ProjectLighthouse.Logging { public class LoggerLevelStartup : LoggerLevel { @@ -16,40 +17,11 @@ namespace LBPUnion.ProjectLighthouse.Logging { public static readonly LoggerLevelHttp Instance = new(); } - #region ASP.NET - public class LoggerLevelAspNetTrace : LoggerLevel { - public override string Name => "ASP.NET: Trace"; - public static readonly LoggerLevelAspNetTrace Instance = new(); + public class LoggerLevelAspNet : LoggerLevel { + public override string Name => "AspNet"; + + public LoggerLevelAspNet(LogLevel level) { + this.Channel = level.ToString(); + } } - - public class LoggerLevelAspNetDebug : LoggerLevel { - public override string Name => "ASP.NET: Debug"; - public static readonly LoggerLevelAspNetDebug Instance = new(); - } - - public class LoggerLevelAspNetInformation : LoggerLevel { - public override string Name => "ASP.NET: Information"; - public static readonly LoggerLevelAspNetInformation Instance = new(); - } - - public class LoggerLevelAspNetWarning : LoggerLevel { - public override string Name => "ASP.NET: Warning"; - public static readonly LoggerLevelAspNetWarning Instance = new(); - } - - public class LoggerLevelAspNetError : LoggerLevel { - public override string Name => "ASP.NET: Error"; - public static readonly LoggerLevelAspNetError Instance = new(); - } - - public class LoggerLevelAspNetCritical : LoggerLevel { - public override string Name => "ASP.NET: Critical"; - public static readonly LoggerLevelAspNetCritical Instance = new(); - } - - public class LoggerLevelAspNetNone : LoggerLevel { - public override string Name => "ASP.NET: None"; - public static readonly LoggerLevelAspNetNone Instance = new(); - } - #endregion } \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index b7a138f8..35309169 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -9,7 +9,7 @@ - + @@ -23,4 +23,8 @@ + + + + From 7b009a52ad7d30e8810201e3895f861b656e87bd Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 01:33:01 -0400 Subject: [PATCH 08/36] Update Microsoft.NET.Test.Sdk --- ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj index 1b66add2..67903271 100644 --- a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj +++ b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj @@ -14,7 +14,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive From 717209c63ec20c1b9f92659db377fbe7b99f2a47 Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 01:35:22 -0400 Subject: [PATCH 09/36] Cleanup code --- ProjectLighthouse/Controllers/MatchController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index 61d63013..5b14bf76 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -32,7 +32,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { #region Parse match data // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] - string? bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); if(bodyString.Contains("FindBestRoom")) { return this.Ok("[{\"StatusCode\":200},{\"Players\":[{\"PlayerId\":\"literally1984\",\"matching_res\":0},{\"PlayerId\":\"jvyden\",\"matching_res\":1}],\"Slots\":[[5,0]],\"RoomState\":\"E_ROOM_IN_POD\",\"HostMood\":\"E_MOOD_EVERYONE\",\"LevelCompletionEstimate\":0,\"PassedNoJoinPoint\":0,\"MoveConnected\":false,\"Location\":[\"127.0.0.1\"],\"BuildVersion\":289,\"Language\":1,\"FirstSeenTimestamp\":1427331263756,\"LastSeenTimestamp\":1635112546000,\"GameId\":1,\"NatType\":2,\"Friends\":[],\"Blocked\":[],\"RecentlyLeft\":[],\"FailedJoin\":[]}]"); } From a009dda81682208141f095352c7bef0767db461b Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 01:35:41 -0400 Subject: [PATCH 10/36] [skip ci] Don't cancel previous workflows --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cff64812..ccaf4961 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,11 +22,6 @@ jobs: DB_USER: root DB_PASSWORD: lighthouse steps: - - name: Cancel previous runs of this workflow - uses: styfle/cancel-workflow-action@0.6.0 - with: - access_token: ${{ github.token }} - - name: Checkout uses: actions/checkout@v2 From 2647da048923b8f53294bfdcebccf3966102e09a Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 16:07:03 -0400 Subject: [PATCH 11/36] Only host on HTTP 10060 --- ProjectLighthouse/Properties/launchSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse/Properties/launchSettings.json b/ProjectLighthouse/Properties/launchSettings.json index a8f5a37b..5b4d172c 100644 --- a/ProjectLighthouse/Properties/launchSettings.json +++ b/ProjectLighthouse/Properties/launchSettings.json @@ -18,7 +18,7 @@ "ProjectLighthouse": { "commandName": "Project", "dotnetRunMessages": "true", - "applicationUrl": "http://localhost:10060;http://localhost:10061;http://localhost:1062", + "applicationUrl": "http://localhost:10060", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "LIGHTHOUSE_DB_CONNECTION_STRING": "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse" From f36106cc86d4ed6e669561e26218d2e34459d5d1 Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 21:29:22 -0400 Subject: [PATCH 12/36] Log filter requests --- ProjectLighthouse/Controllers/MessageController.cs | 11 ++++++++++- ProjectLighthouse/Logging/LoggerLevels.cs | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index ff6b8112..b060c88f 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -1,5 +1,7 @@ using System.IO; using System.Threading.Tasks; +using Kettu; +using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -32,10 +34,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } /// /// Filters chat messages sent by a user. + /// The reponse sent is the text that will appear in-game. /// [HttpPost("filter")] public async Task Filter() { - return this.Ok(await new StreamReader(this.Request.Body).ReadToEndAsync()); + User user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + + string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync(); + + Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance); + return this.Ok(loggedText); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs index 72831dd3..7ddb690c 100644 --- a/ProjectLighthouse/Logging/LoggerLevels.cs +++ b/ProjectLighthouse/Logging/LoggerLevels.cs @@ -16,6 +16,11 @@ namespace LBPUnion.ProjectLighthouse.Logging { public override string Name => "HTTP"; public static readonly LoggerLevelHttp Instance = new(); } + + public class LoggerLevelFilter : LoggerLevel { + public override string Name => "Filter"; + public static readonly LoggerLevelFilter Instance = new(); + } public class LoggerLevelAspNet : LoggerLevel { public override string Name => "AspNet"; From 4adf88ec6b33540d614d52431330e4a1a944b294 Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 23:13:39 -0400 Subject: [PATCH 13/36] Add slot timestamps --- .../Controllers/PublishController.cs | 2 + ProjectLighthouse/Helpers/TimeHelper.cs | 5 +- ...0211028015915_AddSlotTimestamp.Designer.cs | 421 +++++++++++++++++ .../20211028015915_AddSlotTimestamp.cs | 25 ++ ...lotFirstUploadedAndLastUpdated.Designer.cs | 424 ++++++++++++++++++ ...1513_AddSlotFirstUploadedAndLastUpdated.cs | 35 ++ .../Migrations/DatabaseModelSnapshot.cs | 6 + ProjectLighthouse/Types/Levels/Slot.cs | 10 +- 8 files changed, 926 insertions(+), 2 deletions(-) create mode 100644 ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs create mode 100644 ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.cs create mode 100644 ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs create mode 100644 ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.cs diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 508bbdec..38bdc7e2 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -68,6 +68,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { slot.CreatorId = oldSlot.CreatorId; slot.LocationId = oldSlot.LocationId; slot.SlotId = oldSlot.SlotId; + slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); @@ -83,6 +84,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { await this.database.SaveChangesAsync(); slot.LocationId = l.Id; slot.CreatorId = user.UserId; + slot.FirstUploaded = slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); this.database.Slots.Add(slot); await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse/Helpers/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs index 418fff6f..9f51452a 100644 --- a/ProjectLighthouse/Helpers/TimeHelper.cs +++ b/ProjectLighthouse/Helpers/TimeHelper.cs @@ -5,4 +5,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers { public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); } -} \ No newline at end of file +} + +// 1397109686193 +// 1635389749454 \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs new file mode 100644 index 00000000..48beed7d --- /dev/null +++ b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs @@ -0,0 +1,421 @@ +// +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211028015915_AddSlotTimestamp")] + partial class AddSlotTimestamp + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.Property("HeartedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.Property("QueuedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("QueuedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("QueuedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("ResourceCollection") + .HasColumnType("longtext"); + + b.Property("RootLevel") + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.Property("CommentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PosterUserId") + .HasColumnType("int"); + + b.Property("TargetUserId") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("CommentId"); + + b.HasIndex("PosterUserId"); + + b.HasIndex("TargetUserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("CommentsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("FavouriteSlotCount") + .HasColumnType("int"); + + b.Property("FavouriteUserCount") + .HasColumnType("int"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("HeartCount") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("Lists") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LolCatFtwCount") + .HasColumnType("int"); + + b.Property("PhotosByMeCount") + .HasColumnType("int"); + + b.Property("PhotosWithMeCount") + .HasColumnType("int"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("ReviewCount") + .HasColumnType("int"); + + b.Property("StaffChallengeBronzeCount") + .HasColumnType("int"); + + b.Property("StaffChallengeGoldCount") + .HasColumnType("int"); + + b.Property("StaffChallengeSilverCount") + .HasColumnType("int"); + + b.Property("UsedSlots") + .HasColumnType("int"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.cs b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.cs new file mode 100644 index 00000000..9ff79d40 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.cs @@ -0,0 +1,25 @@ +using LBPUnion.ProjectLighthouse.Helpers; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProjectLighthouse.Migrations +{ + public partial class AddSlotTimestamp : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Timestamp", + table: "Slots", + type: "bigint", + nullable: false, + defaultValue: TimeHelper.UnixTimeMilliseconds()); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Timestamp", + table: "Slots"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs new file mode 100644 index 00000000..d1ba6622 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs @@ -0,0 +1,424 @@ +// +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211028021513_AddSlotFirstUploadedAndLastUpdated")] + partial class AddSlotFirstUploadedAndLastUpdated + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.Property("HeartedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.Property("QueuedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("QueuedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("QueuedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("FirstUploaded") + .HasColumnType("bigint"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUpdated") + .HasColumnType("bigint"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("ResourceCollection") + .HasColumnType("longtext"); + + b.Property("RootLevel") + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.Property("CommentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PosterUserId") + .HasColumnType("int"); + + b.Property("TargetUserId") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("CommentId"); + + b.HasIndex("PosterUserId"); + + b.HasIndex("TargetUserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("CommentsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("FavouriteSlotCount") + .HasColumnType("int"); + + b.Property("FavouriteUserCount") + .HasColumnType("int"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("HeartCount") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("Lists") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LolCatFtwCount") + .HasColumnType("int"); + + b.Property("PhotosByMeCount") + .HasColumnType("int"); + + b.Property("PhotosWithMeCount") + .HasColumnType("int"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("ReviewCount") + .HasColumnType("int"); + + b.Property("StaffChallengeBronzeCount") + .HasColumnType("int"); + + b.Property("StaffChallengeGoldCount") + .HasColumnType("int"); + + b.Property("StaffChallengeSilverCount") + .HasColumnType("int"); + + b.Property("UsedSlots") + .HasColumnType("int"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.cs b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.cs new file mode 100644 index 00000000..58564418 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.cs @@ -0,0 +1,35 @@ +using LBPUnion.ProjectLighthouse.Helpers; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProjectLighthouse.Migrations +{ + public partial class AddSlotFirstUploadedAndLastUpdated : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Timestamp", + table: "Slots", + newName: "LastUpdated"); + + migrationBuilder.AddColumn( + name: "FirstUploaded", + table: "Slots", + type: "bigint", + nullable: false, + defaultValue: TimeHelper.UnixTimeMilliseconds()); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FirstUploaded", + table: "Slots"); + + migrationBuilder.RenameColumn( + name: "LastUpdated", + table: "Slots", + newName: "Timestamp"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 84ad0e5c..ed0ea701 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -97,12 +97,18 @@ namespace ProjectLighthouse.Migrations b.Property("Description") .HasColumnType("longtext"); + b.Property("FirstUploaded") + .HasColumnType("bigint"); + b.Property("IconHash") .HasColumnType("longtext"); b.Property("InitiallyLocked") .HasColumnType("tinyint(1)"); + b.Property("LastUpdated") + .HasColumnType("bigint"); + b.Property("Lbp1Only") .HasColumnType("tinyint(1)"); diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index 92fd9701..4f84db9e 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -83,6 +83,12 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { [XmlElement("moveRequired")] public bool MoveRequired { get; set; } + + [XmlElement("firstPublished")] + public long FirstUploaded { get; set; } + + [XmlElement("lastUpdated")] + public long LastUpdated { get; set; } public string SerializeResources() { return this.Resources @@ -107,7 +113,9 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { LbpSerializer.StringElement("background", this.BackgroundHash) + LbpSerializer.StringElement("minPlayers", this.MinimumPlayers) + LbpSerializer.StringElement("maxPlayers", this.MaximumPlayers) + - LbpSerializer.StringElement("moveRequired", this.MoveRequired); + LbpSerializer.StringElement("moveRequired", this.MoveRequired) + + LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + + LbpSerializer.StringElement("lastUpdated", this.LastUpdated); return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } From 5d9987024ac274e823152900415388745d6502fd Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 27 Oct 2021 23:17:39 -0400 Subject: [PATCH 14/36] Fix /s/user/id endpoint Bruh. --- ProjectLighthouse/Controllers/SlotsController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index d5c0f818..4e8b89fd 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -26,7 +26,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); } - + + [HttpGet("s/user/{id:int}")] public async Task SUser(int id) { Slot slot = await this.database.Slots .Include(s => s.Creator) From 474556b8a696ccac01af2d520dc8f9ac517e7169 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 28 Oct 2021 20:15:51 -0400 Subject: [PATCH 15/36] Get LoginData.Username by looking for it's prefix/header --- ProjectLighthouse/Types/LoginData.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ProjectLighthouse/Types/LoginData.cs b/ProjectLighthouse/Types/LoginData.cs index 256fb2c9..bcd10423 100644 --- a/ProjectLighthouse/Types/LoginData.cs +++ b/ProjectLighthouse/Types/LoginData.cs @@ -1,17 +1,9 @@ +using System; using System.IO; using System.Text; using LBPUnion.ProjectLighthouse.Helpers; namespace LBPUnion.ProjectLighthouse.Types { - // This is all the information I can understand for now. More testing is required. - // Example data: - // - LBP2 digital, with the RPCN username `literally1984` - // POST /LITTLEBIGPLANETPS3_XML/login?applicationID=21414&languageID=1&lbp2=1&beta=0&titleID=NPUA80662&country=us - // !�0256333||x||��Y literally198bruUP9000-NPUA80662_008D - // - LBP2 digital, with the RPCN username `jvyden` - // POST /LITTLEBIGPLANETPS3_XML/login?applicationID=21414&languageID=1&lbp2=1&beta=0&titleID=NPUA80662&country=us - // !�0220333||/u||=0� jvydebruUP9000-NPUA80662_008D - // Data is 251 bytes long. /// /// The data sent from POST /LOGIN. /// @@ -20,15 +12,21 @@ namespace LBPUnion.ProjectLighthouse.Types { // public string GameVersion { get; set; } // public int UnknownNumber { get; set; } // Seems to increment by 1000 every login attempt + /// + /// Converts a X-I-5 Ticket into `LoginData`. + /// https://www.psdevwiki.com/ps3/X-I-5-Ticket + /// public static LoginData CreateFromString(string str) { str = str.Replace("\b", ""); // Remove backspace characters using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); using BinaryReader reader = new(ms); + string usernamePrefix = Encoding.ASCII.GetString(new byte[] { 0x04, 0x00, 0x20 }); + LoginData loginData = new(); - reader.BaseStream.Position = 80; + reader.BaseStream.Position = str.IndexOf(usernamePrefix, StringComparison.Ordinal) + usernamePrefix.Length; loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); return loginData; From 42085139783ebcc120056b4b03938035b16735a7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 28 Oct 2021 20:34:49 -0400 Subject: [PATCH 16/36] Fix tests --- ProjectLighthouse.Tests/LighthouseTest.cs | 6 +----- ProjectLighthouse.Tests/Tests/MatchTests.cs | 4 ++-- ProjectLighthouse/Controllers/LoginController.cs | 6 ++++-- ProjectLighthouse/Types/LoginData.cs | 13 +++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseTest.cs index 176989dc..bf705a2c 100644 --- a/ProjectLighthouse.Tests/LighthouseTest.cs +++ b/ProjectLighthouse.Tests/LighthouseTest.cs @@ -22,13 +22,9 @@ namespace LBPUnion.ProjectLighthouse.Tests { } public async Task AuthenticateResponse(int number = 0) { - const char nullChar = (char)0x00; const string username = "unitTestUser"; - string nullString = ""; - for(int i = 0; i < 80; i++) nullString += nullChar; - - string stringContent = $"{nullString}{username}{number}{nullChar}"; + string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}"; HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", new StringContent(stringContent)); return response; diff --git a/ProjectLighthouse.Tests/Tests/MatchTests.cs b/ProjectLighthouse.Tests/Tests/MatchTests.cs index 138501cf..d14b6250 100644 --- a/ProjectLighthouse.Tests/Tests/MatchTests.cs +++ b/ProjectLighthouse.Tests/Tests/MatchTests.cs @@ -16,9 +16,9 @@ namespace LBPUnion.ProjectLighthouse.Tests { await semaphore.WaitAsync(); HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty(), loginResult.AuthTicket); - Assert.False(result.IsSuccessStatusCode); semaphore.Release(); + Assert.False(result.IsSuccessStatusCode); } [DatabaseFact] @@ -32,9 +32,9 @@ namespace LBPUnion.ProjectLighthouse.Tests { loginResult.AuthTicket ); - Assert.True(result.IsSuccessStatusCode); semaphore.Release(); + Assert.True(result.IsSuccessStatusCode); } public async Task GetPlayerCount() => Convert.ToInt32(await this.Client.GetStringAsync("LITTLEBIGPLANETPS3_XML/totalPlayerCount")); diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs index edf494d0..60f8b741 100644 --- a/ProjectLighthouse/Controllers/LoginController.cs +++ b/ProjectLighthouse/Controllers/LoginController.cs @@ -20,14 +20,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers { public async Task Login() { string body = await new StreamReader(this.Request.Body).ReadToEndAsync(); - LoginData loginData; + LoginData? loginData; try { loginData = LoginData.CreateFromString(body); } catch { - return this.BadRequest(); + loginData = null; } + if(loginData == null) return this.BadRequest(); + Token? token = await this.database.AuthenticateUser(loginData); if(token == null) return this.StatusCode(403, ""); diff --git a/ProjectLighthouse/Types/LoginData.cs b/ProjectLighthouse/Types/LoginData.cs index bcd10423..6a51d2b1 100644 --- a/ProjectLighthouse/Types/LoginData.cs +++ b/ProjectLighthouse/Types/LoginData.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.IO; using System.Text; @@ -8,25 +9,25 @@ namespace LBPUnion.ProjectLighthouse.Types { /// The data sent from POST /LOGIN. /// public class LoginData { - public string Username { get; set; } -// public string GameVersion { get; set; } -// public int UnknownNumber { get; set; } // Seems to increment by 1000 every login attempt + public string Username { get; set; } = null!; + + public static readonly string UsernamePrefix = Encoding.ASCII.GetString(new byte[] { 0x04, 0x00, 0x20 }); /// /// Converts a X-I-5 Ticket into `LoginData`. /// https://www.psdevwiki.com/ps3/X-I-5-Ticket /// - public static LoginData CreateFromString(string str) { + public static LoginData? CreateFromString(string str) { str = str.Replace("\b", ""); // Remove backspace characters using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); using BinaryReader reader = new(ms); - string usernamePrefix = Encoding.ASCII.GetString(new byte[] { 0x04, 0x00, 0x20 }); + if(!str.Contains(UsernamePrefix)) return null; LoginData loginData = new(); - reader.BaseStream.Position = str.IndexOf(usernamePrefix, StringComparison.Ordinal) + usernamePrefix.Length; + reader.BaseStream.Position = str.IndexOf(UsernamePrefix, StringComparison.Ordinal) + UsernamePrefix.Length; loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); return loginData; From 0a43cdbbe1bbe0e1de9326166a914bcc6cb442da Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 28 Oct 2021 22:33:17 -0400 Subject: [PATCH 17/36] Improve Eula --- ProjectLighthouse.sln.DotSettings | 1 + .../Controllers/MessageController.cs | 9 +++++--- ProjectLighthouse/Helpers/EulaHelper.cs | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 ProjectLighthouse/Helpers/EulaHelper.cs diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 2ffd35fb..cb12010b 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -3,6 +3,7 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + True True True True diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index b060c88f..7bb18f05 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -1,6 +1,7 @@ using System.IO; using System.Threading.Tasks; using Kettu; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -18,9 +19,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers { [HttpGet("eula")] public async Task Eula() { User user = await this.database.UserFromRequest(this.Request); - return user == null ? this.StatusCode(403, "") : - this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + - "This 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."); + return user == null + ? this.StatusCode(403, "") + : this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + + (EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") + "\n" + + $"{EulaHelper.License}\n"); } [HttpGet("announce")] diff --git a/ProjectLighthouse/Helpers/EulaHelper.cs b/ProjectLighthouse/Helpers/EulaHelper.cs new file mode 100644 index 00000000..25c531ba --- /dev/null +++ b/ProjectLighthouse/Helpers/EulaHelper.cs @@ -0,0 +1,22 @@ +namespace LBPUnion.ProjectLighthouse.Helpers { + public static class EulaHelper { + public const string License = @" +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see ."; + + 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."; + + public const bool ShowPrivateInstanceNotice = false; + } +} \ No newline at end of file From 84653c9293fa133283d4f5f0188ac27fae8e31a0 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 15:10:34 -0400 Subject: [PATCH 18/36] Fix SUser exception on slot not found --- ProjectLighthouse/Controllers/SlotsController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 4e8b89fd..0d4764db 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -34,6 +34,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { .Include(s => s.Location) .FirstOrDefaultAsync(s => s.SlotId == id); + if(slot == null) return this.NotFound(); + return this.Ok(slot.Serialize()); } } From 5511c12631e9c8c49ddf34b5fae0748229b8c2ca Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 15:38:02 -0400 Subject: [PATCH 19/36] Support paintings being uploaded --- ProjectLighthouse/Helpers/FileHelper.cs | 2 ++ ProjectLighthouse/Types/Files/LbpFileType.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index 8f9e0f44..8bd60898 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -15,6 +15,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers { return file.FileType switch { LbpFileType.FileArchive => false, + LbpFileType.Painting => true, LbpFileType.Unknown => false, LbpFileType.Texture => true, LbpFileType.Script => false, @@ -39,6 +40,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers { byte[] header = reader.ReadBytes(3); return Encoding.ASCII.GetString(header) switch { + "PTG" => LbpFileType.Painting, "TEX" => LbpFileType.Texture, "FSH" => LbpFileType.Script, "VOP" => LbpFileType.Voice, diff --git a/ProjectLighthouse/Types/Files/LbpFileType.cs b/ProjectLighthouse/Types/Files/LbpFileType.cs index fab63a15..9c01d4ed 100644 --- a/ProjectLighthouse/Types/Files/LbpFileType.cs +++ b/ProjectLighthouse/Types/Files/LbpFileType.cs @@ -6,6 +6,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Files { FileArchive, // .farc, (ends with FARC) Plan, // PLN, uploaded with levels Voice, // VOP, voice data + Painting, // PTG, paintings Unknown, } } \ No newline at end of file From b867cf1d8c428d49b759dd960411d3f1fd06b467 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 15:38:21 -0400 Subject: [PATCH 20/36] Ignore Unreachable code warning --- ProjectLighthouse/Controllers/MessageController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index 7bb18f05..a21b15f1 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -22,6 +22,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return user == null ? this.StatusCode(403, "") : this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + + // ReSharper disable once UnreachableCode (EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") + "\n" + $"{EulaHelper.License}\n"); } From b340d4a955ec2a8d9349b7d7530e32aebfdbff05 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 15:42:06 -0400 Subject: [PATCH 21/36] Add newest levels category --- ProjectLighthouse/Controllers/SlotsController.cs | 14 ++++++++++++++ ProjectLighthouse/Controllers/UserController.cs | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 0d4764db..363b43fa 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Serialization; @@ -38,5 +39,18 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok(slot.Serialize()); } + + [HttpGet("slots")] + public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { + string response = Enumerable.Aggregate(this.database.Slots + .Include(s => s.Creator) + .Include(s => s.Location) + .OrderBy(s => s.FirstUploaded) + .Skip(pageStart - 1) + .Take(Math.Min(pageSize, 30)) + , string.Empty, (current, slot) => current + slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); + } } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs index 1c69c039..d3cc5794 100644 --- a/ProjectLighthouse/Controllers/UserController.cs +++ b/ProjectLighthouse/Controllers/UserController.cs @@ -28,6 +28,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok(user.Serialize()); } + [HttpGet("users")] + public async Task GetUserAlt([FromQuery] string u) { + return await GetUser(u); + } + // [HttpPost("user/{username}")] // public async Task CreateUser(string username) { // await new Database().CreateUser(username); From 88e77e1c739bdc1126997bb3d9eeee970ecf6ee7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 16:13:12 -0400 Subject: [PATCH 22/36] Limit max levels and lists to 50 --- ProjectLighthouse/Types/Settings/ServerSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index 1e456c23..b4de8c54 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -8,9 +8,9 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings { /// /// The maximum amount of slots allowed on users' earth /// - public const int EntitledSlots = int.MaxValue; + public const int EntitledSlots = 50; - public const int ListsQuota = 20; + public const int ListsQuota = 50; public const string ServerName = "ProjectLighthouse"; From 741ef15714930bef2dfcaa4e12397ed4ecbe9f12 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 16:13:28 -0400 Subject: [PATCH 23/36] Fix infinite loop for newest slots --- ProjectLighthouse/Controllers/SlotsController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 363b43fa..ba0eb47f 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -41,16 +41,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpGet("slots")] - public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - string response = Enumerable.Aggregate(this.database.Slots + public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { + IQueryable slots = this.database.Slots .Include(s => s.Creator) .Include(s => s.Location) .OrderBy(s => s.FirstUploaded) .Skip(pageStart - 1) - .Take(Math.Min(pageSize, 30)) - , string.Empty, (current, slot) => current + slot.Serialize()); + .Take(Math.Min(pageSize, 30)); + string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30))); } } } \ No newline at end of file From edaf82c25713299d844c247f1173be0d24af631c Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 16:21:23 -0400 Subject: [PATCH 24/36] Show used slots --- ProjectLighthouse/Types/User.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index 2d77f692..4f06deed 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; @@ -50,8 +51,14 @@ namespace LBPUnion.ProjectLighthouse.Types { /// /// The number of used slots on the earth /// - public int UsedSlots { get; set; } - + [NotMapped] + public int UsedSlots { + get { + using Database database = new(); + return database.Slots.Count(s => s.CreatorId == this.UserId); + } + } + /// /// The number of slots remaining on the earth /// From 1a231088209e1ef7aeeb9804cfd9113c806bfbcd Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 16:46:29 -0400 Subject: [PATCH 25/36] Fix north pole bug --- ProjectLighthouse/Controllers/PublishController.cs | 7 +++++-- ProjectLighthouse/Types/Profiles/Location.cs | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 38bdc7e2..0dc6f35c 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -65,6 +65,9 @@ namespace LBPUnion.ProjectLighthouse.Controllers { if(oldSlot == null) return this.NotFound(); if(oldSlot.CreatorId != user.UserId) return this.BadRequest(); + oldSlot.Location.X = slot.Location.X; + oldSlot.Location.Y = slot.Location.Y; + slot.CreatorId = oldSlot.CreatorId; slot.LocationId = oldSlot.LocationId; slot.SlotId = oldSlot.SlotId; @@ -77,8 +80,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { //TODO: parse location in body Location l = new() { - X = 0, - Y = 0, + X = slot.Location.X, + Y = slot.Location.Y, }; this.database.Locations.Add(l); await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse/Types/Profiles/Location.cs b/ProjectLighthouse/Types/Profiles/Location.cs index cfaefa68..a4a9b20d 100644 --- a/ProjectLighthouse/Types/Profiles/Location.cs +++ b/ProjectLighthouse/Types/Profiles/Location.cs @@ -1,12 +1,19 @@ +using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; namespace LBPUnion.ProjectLighthouse.Types.Profiles { /// /// The location of a slot on a planet. /// + [XmlRoot("location"), XmlType("location")] public class Location { + [XmlIgnore] public int Id { get; set; } + + [XmlElement("x")] public int X { get; set; } + + [XmlElement("y")] public int Y { get; set; } public string Serialize() { From 8119a7f030cbe6b4b3b4ef88ae0831ba705ab5f1 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:25:11 -0400 Subject: [PATCH 26/36] Order comments properly --- ProjectLighthouse/Controllers/CommentController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index f819a5d4..52ef1718 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -26,6 +26,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { .Include(c => c.Target) .Include(c => c.Poster) .Where(c => c.Target.Username == username) + .OrderBy(c => c.Timestamp) .ToListAsync(); string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize()); From 12177da47305376b5134fda36ba92e2938f4814a Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:34:14 -0400 Subject: [PATCH 27/36] Remove used slots from DB --- ...29213334_RemoveUsedSlotsFromDb.Designer.cs | 421 ++++++++++++++++++ .../20211029213334_RemoveUsedSlotsFromDb.cs | 24 + .../Migrations/DatabaseModelSnapshot.cs | 3 - 3 files changed, 445 insertions(+), 3 deletions(-) create mode 100644 ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs create mode 100644 ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.cs diff --git a/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs new file mode 100644 index 00000000..59a0dcc7 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs @@ -0,0 +1,421 @@ +// +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211029213334_RemoveUsedSlotsFromDb")] + partial class RemoveUsedSlotsFromDb + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.Property("HeartedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.Property("QueuedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("QueuedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("QueuedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("FirstUploaded") + .HasColumnType("bigint"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUpdated") + .HasColumnType("bigint"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("ResourceCollection") + .HasColumnType("longtext"); + + b.Property("RootLevel") + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.Property("CommentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PosterUserId") + .HasColumnType("int"); + + b.Property("TargetUserId") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("CommentId"); + + b.HasIndex("PosterUserId"); + + b.HasIndex("TargetUserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("CommentsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("FavouriteSlotCount") + .HasColumnType("int"); + + b.Property("FavouriteUserCount") + .HasColumnType("int"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("HeartCount") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("Lists") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LolCatFtwCount") + .HasColumnType("int"); + + b.Property("PhotosByMeCount") + .HasColumnType("int"); + + b.Property("PhotosWithMeCount") + .HasColumnType("int"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("ReviewCount") + .HasColumnType("int"); + + b.Property("StaffChallengeBronzeCount") + .HasColumnType("int"); + + b.Property("StaffChallengeGoldCount") + .HasColumnType("int"); + + b.Property("StaffChallengeSilverCount") + .HasColumnType("int"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.cs b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.cs new file mode 100644 index 00000000..7656c6ad --- /dev/null +++ b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProjectLighthouse.Migrations +{ + public partial class RemoveUsedSlotsFromDb : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UsedSlots", + table: "Users"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UsedSlots", + table: "Users", + type: "int", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index ed0ea701..7ccbdad9 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -295,9 +295,6 @@ namespace ProjectLighthouse.Migrations b.Property("StaffChallengeSilverCount") .HasColumnType("int"); - b.Property("UsedSlots") - .HasColumnType("int"); - b.Property("Username") .HasColumnType("longtext"); From 92c888afafc532d56249c198aca38ba277fd4eb5 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:35:18 -0400 Subject: [PATCH 28/36] [skip ci] Fix NewestSlots warning --- ProjectLighthouse/Controllers/SlotsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index ba0eb47f..3dfa4704 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -41,7 +41,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpGet("slots")] - public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { + public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { IQueryable slots = this.database.Slots .Include(s => s.Creator) .Include(s => s.Location) From 830e9da4eab53e0b5ccb35873427d406e2b56def Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:44:31 -0400 Subject: [PATCH 29/36] Fix timestamps --- ProjectLighthouse/Controllers/PublishController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 0dc6f35c..5a90bbd3 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -71,6 +71,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { slot.CreatorId = oldSlot.CreatorId; slot.LocationId = oldSlot.LocationId; slot.SlotId = oldSlot.SlotId; + slot.FirstUploaded = oldSlot.FirstUploaded; slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); this.database.Entry(oldSlot).CurrentValues.SetValues(slot); @@ -87,7 +88,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { await this.database.SaveChangesAsync(); slot.LocationId = l.Id; slot.CreatorId = user.UserId; - slot.FirstUploaded = slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); + slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); + slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); this.database.Slots.Add(slot); await this.database.SaveChangesAsync(); From 73c68c92feeada1fe91e407e60e7234a54f0d3c7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:49:55 -0400 Subject: [PATCH 30/36] Add fix timestamps step on startup --- ProjectLighthouse/Program.cs | 64 +++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index 8b2a6bf2..a74ebed2 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -1,7 +1,11 @@ using System; using System.Diagnostics; +using System.Linq; using Kettu; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -14,8 +18,8 @@ namespace LBPUnion.ProjectLighthouse { public static class Program { public static void Main(string[] args) { // Log startup time - Stopwatch startupStopwatch = new(); - startupStopwatch.Start(); + Stopwatch stopwatch = new(); + stopwatch.Start(); // Setup logging @@ -29,24 +33,54 @@ namespace LBPUnion.ProjectLighthouse { bool dbConnected = ServerSettings.DbConnected; Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); - if(dbConnected) { - Stopwatch migrationStopwatch = new(); - migrationStopwatch.Start(); - - Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); - using Database database = new(); - database.Database.Migrate(); - - migrationStopwatch.Stop(); - Logger.Log($"Migration took {migrationStopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); - } else Environment.Exit(1); + if(!dbConnected) Environment.Exit(1); + using Database database = new(); + + Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); + MigrateDatabase(database); - startupStopwatch.Stop(); - Logger.Log($"Ready! Startup took {startupStopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); + + Logger.Log("Fixing broken timestamps...", LoggerLevelDatabase.Instance); + FixTimestamps(database); + + stopwatch.Stop(); + Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); CreateHostBuilder(args).Build().Run(); } + public static void MigrateDatabase(Database database) { + Stopwatch stopwatch = new(); + stopwatch.Start(); + + database.Database.Migrate(); + + stopwatch.Stop(); + Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); + } + + public static void FixTimestamps(Database database) { + Stopwatch stopwatch = new(); + stopwatch.Start(); + + foreach(Slot slot in database.Slots.Where(s => s.FirstUploaded == 0)) { + slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); + } + + foreach(Slot slot in database.Slots.Where(s => s.LastUpdated == 0)) { + slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); + } + + foreach(Comment comment in database.Comments.Where(c => c.Timestamp == 0)) { + comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); + } + + database.SaveChanges(); + + stopwatch.Stop(); + Logger.Log($"Fixing timestamps took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); + } + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { From 075887b63ca7ff8eb407c5cee1ca5b121c37add3 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 17:55:21 -0400 Subject: [PATCH 31/36] Fix timestamp order --- ProjectLighthouse/Controllers/CommentController.cs | 2 +- ProjectLighthouse/Controllers/SlotsController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index 52ef1718..5881cc79 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -26,7 +26,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { .Include(c => c.Target) .Include(c => c.Poster) .Where(c => c.Target.Username == username) - .OrderBy(c => c.Timestamp) + .OrderByDescending(c => c.Timestamp) .ToListAsync(); string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize()); diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 3dfa4704..d4733dbd 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -45,7 +45,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers { IQueryable slots = this.database.Slots .Include(s => s.Creator) .Include(s => s.Location) - .OrderBy(s => s.FirstUploaded) + .OrderByDescending(s => s.FirstUploaded) .Skip(pageStart - 1) .Take(Math.Min(pageSize, 30)); string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize()); From 30e5d4e3b57418549a9d6095b60474f1abe484a7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 19:00:50 -0400 Subject: [PATCH 32/36] Add StatisticsController, add /planetStats --- .../Controllers/MatchController.cs | 10 ----- .../Controllers/StatisticsController.cs | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 ProjectLighthouse/Controllers/StatisticsController.cs diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index 5b14bf76..a5aec1cb 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -74,15 +74,5 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok("[{\"StatusCode\":200}]"); } - - [HttpGet("playersInPodCount")] - [HttpGet("totalPlayerCount")] - public async Task TotalPlayerCount() { - int recentMatches = await this.database.LastMatches - .Where(l => TimestampHelper.Timestamp - l.Timestamp < 60) - .CountAsync(); - - return this.Ok(recentMatches.ToString()); - } } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/StatisticsController.cs b/ProjectLighthouse/Controllers/StatisticsController.cs new file mode 100644 index 00000000..d71a9222 --- /dev/null +++ b/ProjectLighthouse/Controllers/StatisticsController.cs @@ -0,0 +1,39 @@ +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Controllers { + [ApiController] + [Route("LITTLEBIGPLANETPS3_XML/")] + [Produces("text/plain")] + public class StatisticsController : ControllerBase { + private readonly Database database; + public StatisticsController(Database database) { + this.database = database; + } + + [HttpGet("playersInPodCount")] + [HttpGet("totalPlayerCount")] + public async Task TotalPlayerCount() { + int recentMatches = await this.database.LastMatches + .Where(l => TimestampHelper.Timestamp - l.Timestamp < 60) + .CountAsync(); + + return this.Ok(recentMatches.ToString()); + } + + [HttpGet("planetStats")] + public async Task PlanetStats() { + int totalSlotCount = await this.database.Slots.CountAsync(); + const int mmPicksCount = 0; + + return this.Ok(LbpSerializer.StringElement("planetStats", + LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + + LbpSerializer.StringElement("mmPicksCount", mmPicksCount) + )); + } + } +} \ No newline at end of file From 5fd10b2bb157ff9308cb1d7c89701a932c49dcef Mon Sep 17 00:00:00 2001 From: jvyden Date: Fri, 29 Oct 2021 19:13:22 -0400 Subject: [PATCH 33/36] Add ability to remove comments. Closes #24. --- .../Controllers/CommentController.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index 5881cc79..524e601b 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -58,5 +58,22 @@ namespace LBPUnion.ProjectLighthouse.Controllers { await this.database.SaveChangesAsync(); return this.Ok(); } + + [HttpPost("deleteUserComment/{username}")] + public async Task DeleteComment([FromQuery] int commentId, string username) { + 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); + + if(comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) + return this.StatusCode(403, ""); + + this.database.Comments.Remove(comment); + await this.database.SaveChangesAsync(); + + return this.Ok(); + } } } \ No newline at end of file From b30c86076e5194e17de2c645fdd25f515306cf67 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 30 Oct 2021 16:39:23 -0400 Subject: [PATCH 34/36] Add MM Pick column to slots --- ProjectLighthouse.sln.DotSettings | 1 + ...20211030203837_AddMMPickToSlot.Designer.cs | 424 ++++++++++++++++++ .../20211030203837_AddMMPickToSlot.cs | 24 + .../Migrations/DatabaseModelSnapshot.cs | 3 + ProjectLighthouse/Types/Levels/Slot.cs | 10 +- 5 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs create mode 100644 ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.cs diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index cb12010b..dd04ad09 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -12,6 +12,7 @@ True True True + True True True True diff --git a/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs new file mode 100644 index 00000000..f9f84485 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs @@ -0,0 +1,424 @@ +// +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211030203837_AddMMPickToSlot")] + partial class AddMMPickToSlot + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.Property("HeartedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.Property("QueuedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("QueuedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("QueuedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("FirstUploaded") + .HasColumnType("bigint"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUpdated") + .HasColumnType("bigint"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MMPick") + .HasColumnType("tinyint(1)"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("ResourceCollection") + .HasColumnType("longtext"); + + b.Property("RootLevel") + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.Property("CommentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PosterUserId") + .HasColumnType("int"); + + b.Property("TargetUserId") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("CommentId"); + + b.HasIndex("PosterUserId"); + + b.HasIndex("TargetUserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("CommentsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("FavouriteSlotCount") + .HasColumnType("int"); + + b.Property("FavouriteUserCount") + .HasColumnType("int"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("HeartCount") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("Lists") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LolCatFtwCount") + .HasColumnType("int"); + + b.Property("PhotosByMeCount") + .HasColumnType("int"); + + b.Property("PhotosWithMeCount") + .HasColumnType("int"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("ReviewCount") + .HasColumnType("int"); + + b.Property("StaffChallengeBronzeCount") + .HasColumnType("int"); + + b.Property("StaffChallengeGoldCount") + .HasColumnType("int"); + + b.Property("StaffChallengeSilverCount") + .HasColumnType("int"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.cs b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.cs new file mode 100644 index 00000000..605b9904 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProjectLighthouse.Migrations +{ + public partial class AddMMPickToSlot : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MMPick", + table: "Slots", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MMPick", + table: "Slots"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 7ccbdad9..4fbf4e28 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -115,6 +115,9 @@ namespace ProjectLighthouse.Migrations b.Property("LocationId") .HasColumnType("int"); + b.Property("MMPick") + .HasColumnType("tinyint(1)"); + b.Property("MaximumPlayers") .HasColumnType("int"); diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index 4f84db9e..c35faa28 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -84,11 +84,14 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { [XmlElement("moveRequired")] public bool MoveRequired { get; set; } - [XmlElement("firstPublished")] + [XmlIgnore] public long FirstUploaded { get; set; } - [XmlElement("lastUpdated")] + [XmlIgnore] public long LastUpdated { get; set; } + + [XmlIgnore] + public bool MMPick { get; set; } public string SerializeResources() { return this.Resources @@ -115,7 +118,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { LbpSerializer.StringElement("maxPlayers", this.MaximumPlayers) + LbpSerializer.StringElement("moveRequired", this.MoveRequired) + LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + - LbpSerializer.StringElement("lastUpdated", this.LastUpdated); + LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + + LbpSerializer.StringElement("mmpick", this.MMPick); return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } From 95a1fd35a5191f9276fe6ee31b855cf76a64ceed Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 30 Oct 2021 17:00:41 -0400 Subject: [PATCH 35/36] Update compatibility chart --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3be3b2bf..479361fd 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,11 @@ Some modifications may require updates to the database schema. You can automatic ## Compatibility across games and platforms -| | PS3 | RPCS3 | -|---------|------------|----------------------------------| -| LBP1 | Somewhat | Crashes on entering controller | -| LBP2 | Yes | Yes (requires patched RPCS3) | -| LBP3 | Connects | Crashes on startup | \ No newline at end of file +| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) | +|----------|------------------------|------------------------------------------------|--------------------| +| LBP1 | Somewhat compatible | Incompatible, crashes on entering pod computer | N/A | +| LBP2 | Compatible | Compatible with patched RPCS3 | N/A | +| LBP3 | Somewhat compatible | Somewhat compatible with workaround | Incompatible | +| LBP Vita | Potentially compatible | N/A | N/A | + +Project Lighthouse is still a heavy work in progress, so this is subject to change at any point. \ No newline at end of file From 173addfd03b31f662fd70bf0e5502b2485299cef Mon Sep 17 00:00:00 2001 From: jvyden Date: Sun, 31 Oct 2021 16:46:56 -0400 Subject: [PATCH 36/36] Re-do formatting --- .github/workflows/ci.yml | 12 +- .run/Development Database.run.xml | 19 +-- ProjectLighthouse.Tests/DatabaseFact.cs | 17 ++- ProjectLighthouse.Tests/LighthouseTest.cs | 51 ++++--- .../ProjectLighthouse.Tests.csproj | 8 +- .../Tests/AuthenticationTests.cs | 25 ++-- .../Tests/DatabaseTests.cs | 9 +- .../Tests/FileTypeTests.cs | 27 ++-- ProjectLighthouse.Tests/Tests/MatchTests.cs | 34 ++--- .../Tests/SerializerTests.cs | 32 ++-- ProjectLighthouse.Tests/Tests/SlotTests.cs | 17 ++- ProjectLighthouse.Tests/Tests/UploadTests.cs | 28 ++-- ProjectLighthouse.sln.DotSettings | 72 +++++++++ .../ClientConfigurationController.cs | 30 ++-- .../Controllers/CommentController.cs | 38 ++--- .../Controllers/EnterLevelController.cs | 10 +- .../Controllers/LevelTagsController.cs | 12 +- .../Controllers/ListController.cs | 137 ++++++++++-------- .../Controllers/LoginController.cs | 36 +++-- .../Controllers/MatchController.cs | 53 ++++--- .../Controllers/MessageController.cs | 45 +++--- .../Controllers/NewsController.cs | 41 ++++-- .../Controllers/PublishController.cs | 75 +++++----- .../Controllers/ResourcesController.cs | 47 +++--- .../Controllers/SearchController.cs | 31 ++-- .../Controllers/SlotsController.cs | 41 +++--- .../Controllers/StatisticsController.cs | 28 ++-- .../Controllers/UserController.cs | 82 ++++++----- ProjectLighthouse/Database.cs | 55 +++---- .../Helpers/AllowSynchronousIOAttribute.cs | 13 +- ProjectLighthouse/Helpers/BinaryHelper.cs | 47 +++--- ProjectLighthouse/Helpers/EulaHelper.cs | 6 +- .../Helpers/Extensions/ExceptionExtensions.cs | 24 +-- ProjectLighthouse/Helpers/FileHelper.cs | 31 ++-- ProjectLighthouse/Helpers/HashHelper.cs | 48 +++--- ProjectLighthouse/Helpers/MatchHelper.cs | 20 ++- ProjectLighthouse/Helpers/TimeHelper.cs | 6 +- ProjectLighthouse/Helpers/TimestampHelper.cs | 6 +- .../Logging/AspNetToKettuLogger.cs | 21 +-- .../Logging/AspNetToKettuLoggerProvider.cs | 15 +- .../Logging/LighthouseFileLogger.cs | 11 +- ProjectLighthouse/Logging/LoggerLevels.cs | 39 +++-- ProjectLighthouse/Logging/NullScope.cs | 14 +- ProjectLighthouse/Program.cs | 70 +++++---- ProjectLighthouse/ProjectLighthouse.csproj | 18 +-- .../Serialization/LbpSerializer.cs | 28 ++-- .../Serialization/XmlOutputFormatter.cs | 9 +- ProjectLighthouse/Startup.cs | 76 ++++++---- ProjectLighthouse/Types/Files/LbpFile.cs | 27 ++-- ProjectLighthouse/Types/Files/LbpFileType.cs | 6 +- ProjectLighthouse/Types/HeartedProfile.cs | 15 +- .../Types/Levels/HeartedLevel.cs | 15 +- ProjectLighthouse/Types/Levels/LevelTags.cs | 8 +- ProjectLighthouse/Types/Levels/QueuedLevel.cs | 13 +- ProjectLighthouse/Types/Levels/Slot.cs | 62 ++++---- ProjectLighthouse/Types/LoginData.cs | 32 ++-- ProjectLighthouse/Types/LoginResult.cs | 20 +-- ProjectLighthouse/Types/Match/IMatchData.cs | 8 +- ProjectLighthouse/Types/Match/RoomState.cs | 6 +- .../Types/Match/UpdateMyPlayerData.cs | 6 +- .../Types/Match/UpdatePlayersInRoom.cs | 6 +- ProjectLighthouse/Types/News/NewsEntry.cs | 25 ++-- ProjectLighthouse/Types/News/NewsImage.cs | 13 +- .../Types/Profiles/ClientsConnected.cs | 16 +- ProjectLighthouse/Types/Profiles/Comment.cs | 41 +++--- ProjectLighthouse/Types/Profiles/LastMatch.cs | 10 +- ProjectLighthouse/Types/Profiles/Location.cs | 16 +- ProjectLighthouse/Types/ResourceList.cs | 11 +- .../Types/Settings/PrivacySettings.cs | 16 +- .../Types/Settings/ServerSettings.cs | 20 ++- ProjectLighthouse/Types/Token.cs | 10 +- ProjectLighthouse/Types/User.cs | 118 ++++++++------- README.md | 55 ++++--- docker-compose.yml | 2 +- 74 files changed, 1286 insertions(+), 905 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccaf4961..0248ba60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -on: [push] +on: [ push ] name: Continuous Integration # Inspired by osu! lazer's CI @@ -31,11 +31,11 @@ jobs: with: mysql-version: '8.0' root-password: ${{ env.DB_PASSWORD }} - + - name: Create Lighthouse Database if: ${{ matrix.os.database }} run: mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -h 127.0.0.1 -e "CREATE DATABASE ${{ env.DB_DATABASE }};"; - + - name: Install .NET 5.0 uses: actions/setup-dotnet@v1 with: @@ -47,10 +47,10 @@ jobs: with: dotnet-version: "6.0.x" include-prerelease: true - + - name: Compile run: dotnet build -c Debug - + - name: Test continue-on-error: true run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}.trx" @@ -79,7 +79,7 @@ jobs: create-status-check: false create-pr-comment: false update-comment-if-one-exists: false - + - name: Check Test Results if: steps.process-trx.outputs.test-outcome == 'Failed' run: | diff --git a/.run/Development Database.run.xml b/.run/Development Database.run.xml index 12a3e5fe..deac13d8 100644 --- a/.run/Development Database.run.xml +++ b/.run/Development Database.run.xml @@ -1,11 +1,12 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/ProjectLighthouse.Tests/DatabaseFact.cs b/ProjectLighthouse.Tests/DatabaseFact.cs index 93e2b07d..7fdc1524 100644 --- a/ProjectLighthouse.Tests/DatabaseFact.cs +++ b/ProjectLighthouse.Tests/DatabaseFact.cs @@ -2,12 +2,19 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.EntityFrameworkCore; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public sealed class DatabaseFact : FactAttribute { - public DatabaseFact() { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public sealed class DatabaseFact : FactAttribute + { + public DatabaseFact() + { ServerSettings.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; - if(!ServerSettings.DbConnected) this.Skip = "Database not available"; - else { + if (!ServerSettings.DbConnected) + { + this.Skip = "Database not available"; + } + else + { using Database database = new(); database.Database.Migrate(); } diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseTest.cs index bf705a2c..e3966fe1 100644 --- a/ProjectLighthouse.Tests/LighthouseTest.cs +++ b/ProjectLighthouse.Tests/LighthouseTest.cs @@ -8,29 +8,33 @@ using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; -namespace LBPUnion.ProjectLighthouse.Tests { +namespace LBPUnion.ProjectLighthouse.Tests +{ [SuppressMessage("ReSharper", "UnusedMember.Global")] - public class LighthouseTest { - public readonly TestServer Server; + public class LighthouseTest + { public readonly HttpClient Client; + public readonly TestServer Server; + + public LighthouseTest() + { + this.Server = new TestServer(new WebHostBuilder().UseStartup()); - public LighthouseTest() { - this.Server = new TestServer(new WebHostBuilder() - .UseStartup()); - this.Client = this.Server.CreateClient(); } - public async Task AuthenticateResponse(int number = 0) { + public async Task AuthenticateResponse(int number = 0) + { const string username = "unitTestUser"; string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}"; - + HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", new StringContent(stringContent)); return response; } - - public async Task Authenticate(int number = 0) { + + public async Task Authenticate(int number = 0) + { HttpResponseMessage response = await this.AuthenticateResponse(number); string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync()); @@ -41,30 +45,31 @@ namespace LBPUnion.ProjectLighthouse.Tests { public Task AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get); - public Task AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method) { - using var requestMessage = new HttpRequestMessage(method, endpoint); + public Task AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method) + { + using HttpRequestMessage? requestMessage = new(method, endpoint); requestMessage.Headers.Add("Cookie", mmAuth); return this.Client.SendAsync(requestMessage); } - public async Task UploadFileRequest(string endpoint, string filePath) { - return await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); - } + public async Task UploadFileRequest(string endpoint, string filePath) + => await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); - public async Task UploadDataRequest(string endpoint, byte[] data) { - return await this.Client.PostAsync(endpoint, new ByteArrayContent(data)); - } + public async Task UploadDataRequest(string endpoint, byte[] data) + => await this.Client.PostAsync(endpoint, new ByteArrayContent(data)); - public async Task AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth) { - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, endpoint); + public async Task AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth) + { + using HttpRequestMessage? requestMessage = new(HttpMethod.Post, endpoint); requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath)); return await this.Client.SendAsync(requestMessage); } - public async Task AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth) { - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, endpoint); + public async Task AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth) + { + using HttpRequestMessage? requestMessage = new(HttpMethod.Post, endpoint); requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Content = new ByteArrayContent(data); return await this.Client.SendAsync(requestMessage); diff --git a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj index 67903271..99d6e9bc 100644 --- a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj +++ b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj @@ -13,9 +13,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -27,7 +27,7 @@ - + PreserveNewest diff --git a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs index ea51884b..87bdf599 100644 --- a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs @@ -5,10 +5,13 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class AuthenticationTests : LighthouseTest { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class AuthenticationTests : LighthouseTest + { [Fact] - public async Task ShouldReturnErrorOnNoPostData() { + public async Task ShouldReturnErrorOnNoPostData() + { HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!); Assert.False(response.IsSuccessStatusCode); #if NET6_0_OR_GREATER @@ -19,7 +22,8 @@ namespace LBPUnion.ProjectLighthouse.Tests { } [DatabaseFact] - public async Task ShouldReturnWithValidData() { + public async Task ShouldReturnWithValidData() + { HttpResponseMessage response = await this.AuthenticateResponse(); Assert.True(response.IsSuccessStatusCode); string responseContent = await response.Content.ReadAsStringAsync(); @@ -28,9 +32,10 @@ namespace LBPUnion.ProjectLighthouse.Tests { } [DatabaseFact] - public async Task CanSerializeBack() { + public async Task CanSerializeBack() + { LoginResult loginResult = await this.Authenticate(); - + Assert.NotNull(loginResult); Assert.NotNull(loginResult.AuthTicket); Assert.NotNull(loginResult.LbpEnvVer); @@ -40,18 +45,20 @@ namespace LBPUnion.ProjectLighthouse.Tests { } [DatabaseFact] - public async Task CanUseToken() { + public async Task CanUseToken() + { LoginResult loginResult = await this.Authenticate(); HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/eula", loginResult.AuthTicket); string responseContent = await response.Content.ReadAsStringAsync(); - + Assert.True(response.IsSuccessStatusCode); Assert.Contains("You are now logged in", responseContent); } [DatabaseFact] - public async Task ShouldReturnForbiddenWhenNotAuthenticated() { + public async Task ShouldReturnForbiddenWhenNotAuthenticated() + { HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/eula"); Assert.False(response.IsSuccessStatusCode); Assert.True(response.StatusCode == HttpStatusCode.Forbidden); diff --git a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs b/ProjectLighthouse.Tests/Tests/DatabaseTests.cs index 1e2cf60e..90551c5d 100644 --- a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs +++ b/ProjectLighthouse.Tests/Tests/DatabaseTests.cs @@ -1,10 +1,13 @@ using System; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Tests { - public class DatabaseTests : LighthouseTest { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class DatabaseTests : LighthouseTest + { [DatabaseFact] - public async Task CanCreateUserTwice() { + public async Task CanCreateUserTwice() + { await using Database database = new(); int rand = new Random().Next(); diff --git a/ProjectLighthouse.Tests/Tests/FileTypeTests.cs b/ProjectLighthouse.Tests/Tests/FileTypeTests.cs index 61960ebe..b7229aa2 100644 --- a/ProjectLighthouse.Tests/Tests/FileTypeTests.cs +++ b/ProjectLighthouse.Tests/Tests/FileTypeTests.cs @@ -4,47 +4,56 @@ using System.Text; using LBPUnion.ProjectLighthouse.Types.Files; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class FileTypeTests { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class FileTypeTests + { [Fact] - public void ShouldRecognizeLevel() { + public void ShouldRecognizeLevel() + { LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl")); Assert.True(file.FileType == LbpFileType.Level); } [Fact] - public void ShouldRecognizeScript() { + public void ShouldRecognizeScript() + { LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff")); Assert.True(file.FileType == LbpFileType.Script); } [Fact] - public void ShouldRecognizeTexture() { + public void ShouldRecognizeTexture() + { LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex")); Assert.True(file.FileType == LbpFileType.Texture); } [Fact] - public void ShouldRecognizeFileArchive() { + public void ShouldRecognizeFileArchive() + { LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); Assert.True(file.FileType == LbpFileType.FileArchive); } [Fact] - public void ShouldNotRecognizeFileArchiveAsScript() { + public void ShouldNotRecognizeFileArchiveAsScript() + { LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); Assert.False(file.FileType == LbpFileType.Script); Assert.True(file.FileType == LbpFileType.FileArchive); } [Fact] - public void ShouldRecognizeNothingAsUnknown() { + public void ShouldRecognizeNothingAsUnknown() + { LbpFile file = new(Array.Empty()); Assert.True(file.FileType == LbpFileType.Unknown); } [Fact] - public void ShouldRecognizeGarbageAsUnknown() { + public void ShouldRecognizeGarbageAsUnknown() + { LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900")); Assert.True(file.FileType == LbpFileType.Unknown); } diff --git a/ProjectLighthouse.Tests/Tests/MatchTests.cs b/ProjectLighthouse.Tests/Tests/MatchTests.cs index d14b6250..5c52f9f5 100644 --- a/ProjectLighthouse.Tests/Tests/MatchTests.cs +++ b/ProjectLighthouse.Tests/Tests/MatchTests.cs @@ -6,12 +6,15 @@ using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class MatchTests : LighthouseTest { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class MatchTests : LighthouseTest + { private static readonly SemaphoreSlim semaphore = new(1, 1); [DatabaseFact] - public async Task ShouldRejectEmptyData() { + public async Task ShouldRejectEmptyData() + { LoginResult loginResult = await this.Authenticate(); await semaphore.WaitAsync(); @@ -22,16 +25,13 @@ namespace LBPUnion.ProjectLighthouse.Tests { } [DatabaseFact] - public async Task ShouldReturnOk() { + public async Task ShouldReturnOk() + { LoginResult loginResult = await this.Authenticate(); await semaphore.WaitAsync(); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( - "LITTLEBIGPLANETPS3_XML/match", - Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), - loginResult.AuthTicket - ); - + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest + ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); semaphore.Release(); Assert.True(result.IsSuccessStatusCode); @@ -39,23 +39,21 @@ namespace LBPUnion.ProjectLighthouse.Tests { public async Task GetPlayerCount() => Convert.ToInt32(await this.Client.GetStringAsync("LITTLEBIGPLANETPS3_XML/totalPlayerCount")); [DatabaseFact] - public async Task ShouldIncrementPlayerCount() { + public async Task ShouldIncrementPlayerCount() + { LoginResult loginResult = await this.Authenticate(new Random().Next()); await semaphore.WaitAsync(); int oldPlayerCount = await this.GetPlayerCount(); - HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( - "LITTLEBIGPLANETPS3_XML/match", - Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), - loginResult.AuthTicket - ); - + HttpResponseMessage result = await this.AuthenticatedUploadDataRequest + ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket); + Assert.True(result.IsSuccessStatusCode); int playerCount = await this.GetPlayerCount(); - + semaphore.Release(); Assert.Equal(oldPlayerCount + 1, playerCount); } diff --git a/ProjectLighthouse.Tests/Tests/SerializerTests.cs b/ProjectLighthouse.Tests/Tests/SerializerTests.cs index 14c1f5c8..2c8af05e 100644 --- a/ProjectLighthouse.Tests/Tests/SerializerTests.cs +++ b/ProjectLighthouse.Tests/Tests/SerializerTests.cs @@ -2,30 +2,42 @@ using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Serialization; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class SerializerTests : LighthouseTest { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class SerializerTests : LighthouseTest + { [Fact] - public void BlankElementWorks() { + public void BlankElementWorks() + { Assert.Equal("", LbpSerializer.BlankElement("test")); } [Fact] - public void StringElementWorks() { + public void StringElementWorks() + { Assert.Equal("asd", LbpSerializer.StringElement("test", "asd")); Assert.Equal("asd", LbpSerializer.StringElement(new KeyValuePair("test", "asd"))); } [Fact] - public void TaggedStringElementWorks() { + public void TaggedStringElementWorks() + { Assert.Equal("asd", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar")); - Assert.Equal("asd", LbpSerializer.TaggedStringElement(new KeyValuePair("test", "asd"), - new KeyValuePair("foo", "bar"))); + Assert.Equal + ( + "asd", + LbpSerializer.TaggedStringElement(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) + ); } [Fact] - public void ElementsWorks() { - Assert.Equal("asdbar", LbpSerializer.Elements(new KeyValuePair("test", "asd"), - new KeyValuePair("foo", "bar"))); + public void ElementsWorks() + { + Assert.Equal + ( + "asdbar", + LbpSerializer.Elements(new KeyValuePair("test", "asd"), new KeyValuePair("foo", "bar")) + ); } } } \ No newline at end of file diff --git a/ProjectLighthouse.Tests/Tests/SlotTests.cs b/ProjectLighthouse.Tests/Tests/SlotTests.cs index 3b136857..ae5a8b9b 100644 --- a/ProjectLighthouse.Tests/Tests/SlotTests.cs +++ b/ProjectLighthouse.Tests/Tests/SlotTests.cs @@ -4,10 +4,13 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Profiles; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class SlotTests : LighthouseTest { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class SlotTests : LighthouseTest + { [DatabaseFact] - public async Task ShouldOnlyShowUsersLevels() { + public async Task ShouldOnlyShowUsersLevels() + { await using Database database = new(); User userA = await database.CreateUser("unitTestUser0"); @@ -17,7 +20,8 @@ namespace LBPUnion.ProjectLighthouse.Tests { database.Locations.Add(l); await database.SaveChangesAsync(); - Slot slotA = new() { + Slot slotA = new() + { Creator = userA, Name = "slotA", Location = l, @@ -25,7 +29,8 @@ namespace LBPUnion.ProjectLighthouse.Tests { ResourceCollection = "", }; - Slot slotB = new() { + Slot slotB = new() + { Creator = userB, Name = "slotB", Location = l, @@ -47,7 +52,7 @@ namespace LBPUnion.ProjectLighthouse.Tests { Assert.NotEqual(respA, respB); Assert.DoesNotContain(respA, "slotB"); Assert.DoesNotContain(respB, "slotA"); - + // Cleanup database.Slots.Remove(slotA); diff --git a/ProjectLighthouse.Tests/Tests/UploadTests.cs b/ProjectLighthouse.Tests/Tests/UploadTests.cs index 30190fc3..41612256 100644 --- a/ProjectLighthouse.Tests/Tests/UploadTests.cs +++ b/ProjectLighthouse.Tests/Tests/UploadTests.cs @@ -4,39 +4,47 @@ using System.Net.Http; using System.Threading.Tasks; using Xunit; -namespace LBPUnion.ProjectLighthouse.Tests { - public class UploadTests : LighthouseTest { - public UploadTests() { +namespace LBPUnion.ProjectLighthouse.Tests +{ + public class UploadTests : LighthouseTest + { + public UploadTests() + { string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r"); - if(Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true); + if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true); } - + [Fact] - public async Task ShouldNotAcceptScript() { + public async Task ShouldNotAcceptScript() + { HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff"); Assert.False(response.IsSuccessStatusCode); } [Fact] - public async Task ShouldNotAcceptFarc() { + public async Task ShouldNotAcceptFarc() + { HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc"); Assert.False(response.IsSuccessStatusCode); } [Fact] - public async Task ShouldNotAcceptGarbage() { + public async Task ShouldNotAcceptGarbage() + { HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin"); Assert.False(response.IsSuccessStatusCode); } [Fact] - public async Task ShouldAcceptTexture() { + public async Task ShouldAcceptTexture() + { HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex"); Assert.True(response.IsSuccessStatusCode); } [Fact] - public async Task ShouldAcceptLevel() { + public async Task ShouldAcceptLevel() + { HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl"); Assert.True(response.IsSuccessStatusCode); } diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index dd04ad09..8b093e41 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -1,8 +1,80 @@  + HINT + HINT + SUGGESTION + WARNING + SUGGESTION + SUGGESTION + WARNING + ERROR + ExpressionBody + ExpressionBody + Field, Property, Event, Method + True + True + NEXT_LINE + NEXT_LINE + TOGETHER + True + USUAL_INDENT + USUAL_INDENT + NEXT_LINE + NEXT_LINE + 1 + 1 + False + False + False + False + False + False + False + 5 + 1 + EXPANDED + NEXT_LINE + NEVER + NEVER + NEVER + False + IF_OWNER_IS_SINGLE_LINE + False + False + True + True + True + True + True + True + True + NEXT_LINE + True + True + CHOP_IF_LONG + CHOP_IF_LONG + True + True + True + True + True + True + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_ALWAYS + CHOP_IF_LONG + CHOP_ALWAYS + UseExplicitType + UseExplicitType + UseExplicitType MM <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + True + True + True + True True True True diff --git a/ProjectLighthouse/Controllers/ClientConfigurationController.cs b/ProjectLighthouse/Controllers/ClientConfigurationController.cs index 7e5aefce..acf08f0f 100644 --- a/ProjectLighthouse/Controllers/ClientConfigurationController.cs +++ b/ProjectLighthouse/Controllers/ClientConfigurationController.cs @@ -2,36 +2,38 @@ using System.Diagnostics.CodeAnalysis; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] - public class ClientConfigurationController : ControllerBase { + public class ClientConfigurationController : ControllerBase + { [HttpGet("network_settings.nws")] [SuppressMessage("ReSharper", "StringLiteralTypo")] - public IActionResult NetworkSettings() { - return this.Ok("ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false"); - } + public IActionResult NetworkSettings() + => this.Ok + ( + "ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false" + ); [HttpGet("t_conf")] [Produces("text/json")] - public IActionResult Conf() { - return this.Ok("[{\"StatusCode\":200}]"); - } + public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]"); [HttpGet("farc_hashes")] - public IActionResult FarcHashes() { - return this.Ok(); - } + public IActionResult FarcHashes() => this.Ok(); [HttpGet("privacySettings")] [Produces("text/xml")] - public IActionResult PrivacySettings() { - PrivacySettings ps = new() { + public IActionResult PrivacySettings() + { + PrivacySettings ps = new() + { LevelVisibility = "all", ProfileVisibility = "all", }; - + return this.Ok(ps.Serialize()); } } diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs index 524e601b..5d673e5e 100644 --- a/ProjectLighthouse/Controllers/CommentController.cs +++ b/ProjectLighthouse/Controllers/CommentController.cs @@ -10,20 +10,24 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class CommentController : ControllerBase { + public class CommentController : ControllerBase + { private readonly Database database; - public CommentController(Database database) { + public CommentController(Database database) + { this.database = database; } [HttpGet("userComments/{username}")] - public async Task GetComments(string username) { - List comments = await this.database.Comments - .Include(c => c.Target) + public async Task GetComments(string username) + { + List comments = await this.database.Comments.Include + (c => c.Target) .Include(c => c.Poster) .Where(c => c.Target.Username == username) .OrderByDescending(c => c.Timestamp) @@ -34,7 +38,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpPost("postUserComment/{username}")] - public async Task PostComment(string username) { + public async Task PostComment(string username) + { this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -43,11 +48,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers { User poster = await this.database.UserFromRequest(this.Request); - if(poster == null) return this.StatusCode(403, ""); - + if (poster == null) return this.StatusCode(403, ""); + User target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); - - if(comment == null || target == null) return this.BadRequest(); + + if (comment == null || target == null) return this.BadRequest(); comment.PosterUserId = poster.UserId; comment.TargetUserId = target.UserId; @@ -60,15 +65,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpPost("deleteUserComment/{username}")] - public async Task DeleteComment([FromQuery] int commentId, string username) { + public async Task DeleteComment([FromQuery] int commentId, string username) + { User user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + 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.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) - return this.StatusCode(403, ""); + if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, ""); this.database.Comments.Remove(comment); await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse/Controllers/EnterLevelController.cs b/ProjectLighthouse/Controllers/EnterLevelController.cs index 88d3933f..4b6c5236 100644 --- a/ProjectLighthouse/Controllers/EnterLevelController.cs +++ b/ProjectLighthouse/Controllers/EnterLevelController.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/enterLevel")] // [Produces("text/plain")] - public class EnterLevelController : ControllerBase { + public class EnterLevelController : ControllerBase + { [HttpGet("enterLevel/{id}")] - public IActionResult EnterLevel(string id) { - return this.Ok(); - } + public IActionResult EnterLevel(string id) => this.Ok(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LevelTagsController.cs b/ProjectLighthouse/Controllers/LevelTagsController.cs index 1ed96dc6..1795a868 100644 --- a/ProjectLighthouse/Controllers/LevelTagsController.cs +++ b/ProjectLighthouse/Controllers/LevelTagsController.cs @@ -2,17 +2,21 @@ using System; using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/tags")] [Produces("text/plain")] - public class LevelTagsController : ControllerBase { + public class LevelTagsController : ControllerBase + { [HttpGet] - public IActionResult Get() { + public IActionResult Get() + { string[] tags = Enum.GetNames(typeof(LevelTags)); int i = 0; - foreach(string tag in tags) { + foreach (string tag in tags) + { tags[i] = $"TAG_{tag.Replace("_", "-")}"; i++; } diff --git a/ProjectLighthouse/Controllers/ListController.cs b/ProjectLighthouse/Controllers/ListController.cs index ede363be..0b1deefb 100644 --- a/ProjectLighthouse/Controllers/ListController.cs +++ b/ProjectLighthouse/Controllers/ListController.cs @@ -8,23 +8,28 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class ListController : ControllerBase { + public class ListController : ControllerBase + { private readonly Database database; - public ListController(Database database) { + public ListController(Database database) + { this.database = database; } #region Levels + #region Level Queue (lolcatftw) [HttpGet("slots/lolcatftw/{username}")] - public IActionResult GetLevelQueue(string username) { - IEnumerable queuedLevels = new Database().QueuedLevels - .Include(q => q.User) + public IActionResult GetLevelQueue(string username) + { + IEnumerable queuedLevels = new Database().QueuedLevels.Include + (q => q.User) .Include(q => q.Slot) .Include(q => q.Slot.Location) .Where(q => q.User.Username == username) @@ -36,30 +41,36 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpPost("lolcatftw/add/user/{id:int}")] - public async Task AddQueuedLevel(int id) { + public async Task AddQueuedLevel(int id) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(queuedLevel != null) return this.Ok(); + if (queuedLevel != null) return this.Ok(); - this.database.QueuedLevels.Add(new QueuedLevel { - SlotId = id, - UserId = user.UserId, - }); + this.database.QueuedLevels.Add + ( + new QueuedLevel + { + SlotId = id, + UserId = user.UserId, + } + ); await this.database.SaveChangesAsync(); return this.Ok(); } - + [HttpPost("lolcatftw/remove/user/{id:int}")] - public async Task RemoveQueuedLevel(int id) { + public async Task RemoveQueuedLevel(int id) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(queuedLevel != null) this.database.QueuedLevels.Remove(queuedLevel); + if (queuedLevel != null) this.database.QueuedLevels.Remove(queuedLevel); await this.database.SaveChangesAsync(); @@ -71,9 +82,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers { #region Hearted Levels [HttpGet("favouriteSlots/{username}")] - public IActionResult GetFavouriteSlots(string username) { - IEnumerable heartedLevels = new Database().HeartedLevels - .Include(q => q.User) + public IActionResult GetFavouriteSlots(string username) + { + IEnumerable heartedLevels = new Database().HeartedLevels.Include + (q => q.User) .Include(q => q.Slot) .Include(q => q.Slot.Location) .Include(q => q.Slot.Creator) @@ -86,17 +98,22 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpPost("favourite/slot/user/{id:int}")] - public async Task AddFavouriteSlot(int id) { + public async Task AddFavouriteSlot(int id) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(heartedLevel != null) return this.Ok(); + if (heartedLevel != null) return this.Ok(); - this.database.HeartedLevels.Add(new HeartedLevel { - SlotId = id, - UserId = user.UserId, - }); + this.database.HeartedLevels.Add + ( + new HeartedLevel + { + SlotId = id, + UserId = user.UserId, + } + ); await this.database.SaveChangesAsync(); @@ -104,12 +121,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } [HttpPost("unfavourite/slot/user/{id:int}")] - public async Task RemoveFavouriteSlot(int id) { + public async Task RemoveFavouriteSlot(int id) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(heartedLevel != null) this.database.HeartedLevels.Remove(heartedLevel); + if (heartedLevel != null) this.database.HeartedLevels.Remove(heartedLevel); await this.database.SaveChangesAsync(); @@ -117,16 +135,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } #endregion + #endregion Levels #region Users - - [HttpGet("favouriteUsers/{username}")] - public IActionResult GetFavouriteUsers(string username) { - IEnumerable heartedProfiles = new Database().HeartedProfiles - .Include(q => q.User) + public IActionResult GetFavouriteUsers(string username) + { + IEnumerable heartedProfiles = new Database().HeartedProfiles.Include + (q => q.User) .Include(q => q.HeartedUser) .Include(q => q.HeartedUser.Location) .Where(q => q.User.Username == username) @@ -136,42 +154,46 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok(LbpSerializer.TaggedStringElement("favouriteUsers", response, "total", 1)); } - + [HttpPost("favourite/user/{username}")] - public async Task AddFavouriteUser(string username) { + public async Task AddFavouriteUser(string username) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); - User? heartedUser = await this.database.Users - .FirstOrDefaultAsync(u => u.Username == username); - if(heartedUser == null) return this.NotFound(); + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (heartedUser == null) return this.NotFound(); - HeartedProfile heartedProfile = await this.database.HeartedProfiles - .FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); - if(heartedProfile != null) return this.Ok(); + HeartedProfile heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync + (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + if (heartedProfile != null) return this.Ok(); - this.database.HeartedProfiles.Add(new HeartedProfile { - HeartedUserId = heartedUser.UserId, - UserId = user.UserId, - }); + this.database.HeartedProfiles.Add + ( + new HeartedProfile + { + HeartedUserId = heartedUser.UserId, + UserId = user.UserId, + } + ); await this.database.SaveChangesAsync(); return this.Ok(); } - + [HttpPost("unfavourite/user/{username}")] - public async Task RemoveFavouriteUser(string username) { + public async Task RemoveFavouriteUser(string username) + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); - User? heartedUser = await this.database.Users - .FirstOrDefaultAsync(u => u.Username == username); - if(heartedUser == null) return this.NotFound(); + User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (heartedUser == null) return this.NotFound(); - HeartedProfile heartedProfile = await this.database.HeartedProfiles - .FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); - if(heartedProfile != null) this.database.HeartedProfiles.Remove(heartedProfile); + HeartedProfile heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync + (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); + if (heartedProfile != null) this.database.HeartedProfiles.Remove(heartedProfile); await this.database.SaveChangesAsync(); @@ -179,5 +201,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers { } #endregion + } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs index 60f8b741..057771c2 100644 --- a/ProjectLighthouse/Controllers/LoginController.cs +++ b/ProjectLighthouse/Controllers/LoginController.cs @@ -5,39 +5,49 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/login")] [Produces("text/xml")] - public class LoginController : ControllerBase { + public class LoginController : ControllerBase + { private readonly Database database; - public LoginController(Database database) { + public LoginController(Database database) + { this.database = database; } - + [HttpPost] - public async Task Login() { + public async Task Login() + { string body = await new StreamReader(this.Request.Body).ReadToEndAsync(); LoginData? loginData; - try { + try + { loginData = LoginData.CreateFromString(body); } - catch { + catch + { loginData = null; } - if(loginData == null) return this.BadRequest(); + if (loginData == null) return this.BadRequest(); Token? token = await this.database.AuthenticateUser(loginData); - if(token == null) return this.StatusCode(403, ""); + if (token == null) return this.StatusCode(403, ""); - return this.Ok(new LoginResult { - AuthTicket = "MM_AUTH=" + token.UserToken, - LbpEnvVer = ServerSettings.ServerName, - }.Serialize()); + return this.Ok + ( + new LoginResult + { + AuthTicket = "MM_AUTH=" + token.UserToken, + LbpEnvVer = ServerSettings.ServerName, + }.Serialize() + ); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index a5aec1cb..a4af9f5d 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -11,57 +11,69 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class MatchController : ControllerBase { + public class MatchController : ControllerBase + { private readonly Database database; - public MatchController(Database database) { + public MatchController(Database database) + { this.database = database; } [HttpPost("match")] [Produces("text/json")] - public async Task Match() { + public async Task Match() + { User? user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); #region Parse match data + // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] - + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - if(bodyString.Contains("FindBestRoom")) { - return this.Ok("[{\"StatusCode\":200},{\"Players\":[{\"PlayerId\":\"literally1984\",\"matching_res\":0},{\"PlayerId\":\"jvyden\",\"matching_res\":1}],\"Slots\":[[5,0]],\"RoomState\":\"E_ROOM_IN_POD\",\"HostMood\":\"E_MOOD_EVERYONE\",\"LevelCompletionEstimate\":0,\"PassedNoJoinPoint\":0,\"MoveConnected\":false,\"Location\":[\"127.0.0.1\"],\"BuildVersion\":289,\"Language\":1,\"FirstSeenTimestamp\":1427331263756,\"LastSeenTimestamp\":1635112546000,\"GameId\":1,\"NatType\":2,\"Friends\":[],\"Blocked\":[],\"RecentlyLeft\":[],\"FailedJoin\":[]}]"); - } - - if(string.IsNullOrEmpty(bodyString) || bodyString[0] != '[') return this.BadRequest(); + if (bodyString.Contains + ("FindBestRoom")) + return this.Ok + ( + "[{\"StatusCode\":200},{\"Players\":[{\"PlayerId\":\"literally1984\",\"matching_res\":0},{\"PlayerId\":\"jvyden\",\"matching_res\":1}],\"Slots\":[[5,0]],\"RoomState\":\"E_ROOM_IN_POD\",\"HostMood\":\"E_MOOD_EVERYONE\",\"LevelCompletionEstimate\":0,\"PassedNoJoinPoint\":0,\"MoveConnected\":false,\"Location\":[\"127.0.0.1\"],\"BuildVersion\":289,\"Language\":1,\"FirstSeenTimestamp\":1427331263756,\"LastSeenTimestamp\":1635112546000,\"GameId\":1,\"NatType\":2,\"Friends\":[],\"Blocked\":[],\"RecentlyLeft\":[],\"FailedJoin\":[]}]" + ); + + if (string.IsNullOrEmpty(bodyString) || bodyString[0] != '[') return this.BadRequest(); IMatchData? matchData; - try { + try + { matchData = MatchHelper.Deserialize(bodyString); } - catch(Exception e) { + catch(Exception e) + { Logger.Log("Exception while parsing MatchData: " + e); Logger.Log("Data: " + bodyString); return this.BadRequest(); } - if(matchData == null) return this.BadRequest(); - + if (matchData == null) return this.BadRequest(); + #endregion #region Update LastMatch - LastMatch? lastMatch = await this.database.LastMatches - .Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); + + LastMatch? lastMatch = await this.database.LastMatches.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); // below makes it not look like trash // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - if(lastMatch == null) { - lastMatch = new LastMatch { + if (lastMatch == null) + { + lastMatch = new LastMatch + { UserId = user.UserId, }; this.database.LastMatches.Add(lastMatch); @@ -70,8 +82,9 @@ namespace LBPUnion.ProjectLighthouse.Controllers { lastMatch.Timestamp = TimestampHelper.Timestamp; await this.database.SaveChangesAsync(); + #endregion - + return this.Ok("[{\"StatusCode\":200}]"); } } diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs index a21b15f1..a96317eb 100644 --- a/ProjectLighthouse/Controllers/MessageController.cs +++ b/ProjectLighthouse/Controllers/MessageController.cs @@ -6,47 +6,52 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] - public class MessageController : ControllerBase { + public class MessageController : ControllerBase + { private readonly Database database; - public MessageController(Database database) { + public MessageController(Database database) + { this.database = database; } [HttpGet("eula")] - public async Task Eula() { + public async Task Eula() + { User user = await this.database.UserFromRequest(this.Request); return user == null ? this.StatusCode(403, "") - : this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + - // ReSharper disable once UnreachableCode - (EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") + "\n" + - $"{EulaHelper.License}\n"); + : this.Ok + ( + $"You are now logged in as user {user.Username} (id {user.UserId}).\n" + + // ReSharper disable once UnreachableCode + (EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") + + "\n" + + $"{EulaHelper.License}\n" + ); } [HttpGet("announce")] - public IActionResult Announce() { - return this.Ok(""); - } + public IActionResult Announce() => this.Ok(""); [HttpGet("notification")] - public IActionResult Notification() { - return this.Ok(); - } + public IActionResult Notification() => this.Ok(); /// - /// Filters chat messages sent by a user. - /// The reponse sent is the text that will appear in-game. + /// Filters chat messages sent by a user. + /// The reponse sent is the text that will appear in-game. /// [HttpPost("filter")] - public async Task Filter() { + public async Task Filter() + { User user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); - + if (user == null) return this.StatusCode(403, ""); + string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync(); - + Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance); return this.Ok(loggedText); } diff --git a/ProjectLighthouse/Controllers/NewsController.cs b/ProjectLighthouse/Controllers/NewsController.cs index 5fa6e8f3..ee0342d2 100644 --- a/ProjectLighthouse/Controllers/NewsController.cs +++ b/ProjectLighthouse/Controllers/NewsController.cs @@ -2,26 +2,35 @@ using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.News; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/news")] [Produces("text/xml")] - public class NewsController : ControllerBase { + public class NewsController : ControllerBase + { [HttpGet] - public IActionResult Get() { - string newsEntry = LbpSerializer.StringElement("item", new NewsEntry { - Category = "no_category", - Summary = "test summary", - Image = new NewsImage { - Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe", - Alignment = "right", - }, - Id = 1, - Title = "Test Title", - Text = "Test Text", - Date = 1348755214000, - }.Serialize()); - + public IActionResult Get() + { + string newsEntry = LbpSerializer.StringElement + ( + "item", + new NewsEntry + { + Category = "no_category", + Summary = "test summary", + Image = new NewsImage + { + Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe", + Alignment = "right", + }, + Id = 1, + Title = "Test Title", + Text = "Test Text", + Date = 1348755214000, + }.Serialize() + ); + return this.Ok(LbpSerializer.StringElement("news", newsEntry)); } } diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 5a90bbd3..4efb4176 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -10,60 +10,64 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class PublishController : ControllerBase { + public class PublishController : ControllerBase + { private readonly Database database; - - public PublishController(Database database) { + + public PublishController(Database database) + { this.database = database; } /// - /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded + /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded /// [HttpPost("startPublish")] - public async Task StartPublish() { + public async Task StartPublish() + { User user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); - + if (user == null) return this.StatusCode(403, ""); + Slot slot = await this.GetSlotFromBody(); - if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded + if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded // Republish logic - if(slot.SlotId != 0) { + if (slot.SlotId != 0) + { 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(); + if (oldSlot == null) return this.NotFound(); + if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); } - string resources = slot.Resources - .Where(hash => !FileHelper.ResourceExists(hash)) - .Aggregate("", (current, hash) => - current + LbpSerializer.StringElement("resource", hash)); + string resources = slot.Resources.Where + (hash => !FileHelper.ResourceExists(hash)) + .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user")); } /// - /// Endpoint actually used to publish a level + /// Endpoint actually used to publish a level /// [HttpPost("publish")] - public async Task Publish() { + public async Task Publish() + { User user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); - + if (user == null) return this.StatusCode(403, ""); + Slot slot = await this.GetSlotFromBody(); // Republish logic - if(slot.SlotId != 0) { - Slot oldSlot = await this.database.Slots - .Include(s => s.Location) - .FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); - if(oldSlot == null) return this.NotFound(); - if(oldSlot.CreatorId != user.UserId) return this.BadRequest(); + if (slot.SlotId != 0) + { + Slot oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); + if (oldSlot == null) return this.NotFound(); + if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); oldSlot.Location.X = slot.Location.X; oldSlot.Location.Y = slot.Location.Y; @@ -73,14 +77,15 @@ namespace LBPUnion.ProjectLighthouse.Controllers { slot.SlotId = oldSlot.SlotId; slot.FirstUploaded = oldSlot.FirstUploaded; slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); - + this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); return this.Ok(oldSlot.Serialize()); } //TODO: parse location in body - Location l = new() { + Location l = new() + { X = slot.Location.X, Y = slot.Location.Y, }; @@ -93,15 +98,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers { this.database.Slots.Add(slot); await this.database.SaveChangesAsync(); - + return this.Ok(slot.Serialize()); } [HttpPost("unpublish/{id:int}")] - public async Task Unpublish(int id) { - Slot slot = await this.database.Slots - .Include(s => s.Location) - .FirstOrDefaultAsync(s => s.SlotId == id); + public async Task Unpublish(int id) + { + Slot slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); this.database.Locations.Remove(slot.Location); this.database.Slots.Remove(slot); @@ -110,8 +114,9 @@ namespace LBPUnion.ProjectLighthouse.Controllers { return this.Ok(); } - - public async Task GetSlotFromBody() { + + public async Task GetSlotFromBody() + { this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); diff --git a/ProjectLighthouse/Controllers/ResourcesController.cs b/ProjectLighthouse/Controllers/ResourcesController.cs index 7b80c591..dcb1a4d8 100644 --- a/ProjectLighthouse/Controllers/ResourcesController.cs +++ b/ProjectLighthouse/Controllers/ResourcesController.cs @@ -10,60 +10,61 @@ using LBPUnion.ProjectLighthouse.Types.Files; using Microsoft.AspNetCore.Mvc; using IOFile = System.IO.File; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class ResourcesController : ControllerBase { + public class ResourcesController : ControllerBase + { [HttpPost("showModerated")] - public IActionResult ShowModerated() { - return this.Ok(LbpSerializer.BlankElement("resources")); - } + public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources")); [HttpPost("filterResources")] [HttpPost("showNotUploaded")] - public async Task FilterResources() { - string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); - + public async Task FilterResources() + { + string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); + XmlSerializer serializer = new(typeof(ResourceList)); ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString)); - if(resourceList == null) return this.BadRequest(); + if (resourceList == null) return this.BadRequest(); - string resources = resourceList.Resources - .Where(s => !FileHelper.ResourceExists(s)) - .Aggregate("", (current, hash) => - current + LbpSerializer.StringElement("resource", hash)); + string resources = resourceList.Resources.Where + (s => !FileHelper.ResourceExists(s)) + .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash)); return this.Ok(LbpSerializer.StringElement("resources", resources)); } [HttpGet("r/{hash}")] - public IActionResult GetResource(string hash) { + public IActionResult GetResource(string hash) + { string path = FileHelper.GetResourcePath(hash); - if(FileHelper.ResourceExists(hash)) { - return this.File(IOFile.OpenRead(path), "application/octet-stream"); - } + if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream"); + return this.NotFound(); } // TODO: check if this is a valid hash [HttpPost("upload/{hash}")] [AllowSynchronousIo] - public async Task UploadResource(string hash) { + public async Task UploadResource(string hash) + { string assetsDirectory = FileHelper.ResourcePath; string path = FileHelper.GetResourcePath(hash); - + FileHelper.EnsureDirectoryCreated(assetsDirectory); - if(FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded + if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded Logger.Log($"Processing resource upload (hash: {hash})"); - LbpFile file = new(await BinaryHelper.ReadFromPipeReader(Request.BodyReader)); + LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader)); + + if (!FileHelper.IsFileSafe(file)) return this.UnprocessableEntity(); - if(!FileHelper.IsFileSafe(file)) return this.UnprocessableEntity(); - await IOFile.WriteAllBytesAsync(path, file.Data); return this.Ok(); } diff --git a/ProjectLighthouse/Controllers/SearchController.cs b/ProjectLighthouse/Controllers/SearchController.cs index 823a89cf..573d3480 100644 --- a/ProjectLighthouse/Controllers/SearchController.cs +++ b/ProjectLighthouse/Controllers/SearchController.cs @@ -6,19 +6,24 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class SearchController : ControllerBase { + public class SearchController : ControllerBase + { private readonly Database database; - public SearchController(Database database) { + public SearchController(Database database) + { this.database = database; } [HttpGet("slots/search")] - public async Task SearchSlots([FromQuery] string query) { - if(query == null) return this.BadRequest(); + public async Task SearchSlots([FromQuery] string query) + { + if (query == null) return this.BadRequest(); + query = query.ToLower(); string[] keywords = query.Split(" "); @@ -27,16 +32,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers { .Include(s => s.Creator) .Include(s => s.Location) .Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable - + // ReSharper disable once LoopCanBeConvertedToQuery - foreach(string keyword in keywords) { - dbQuery = dbQuery.Where(s => - s.Name.ToLower().Contains(keyword) || - s.Description.ToLower().Contains(keyword) || - s.Creator.Username.ToLower().Contains(keyword) || - s.SlotId.ToString().Equals(keyword) + foreach (string keyword in keywords) + dbQuery = dbQuery.Where + ( + s => s.Name.ToLower().Contains(keyword) || + s.Description.ToLower().Contains(keyword) || + s.Creator.Username.ToLower().Contains(keyword) || + s.SlotId.ToString().Equals(keyword) ); - } List slots = await dbQuery.ToListAsync(); string response = slots.Aggregate("", (current, slot) => current + slot.Serialize()); diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index d4733dbd..ede1ae56 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -6,44 +6,47 @@ using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class SlotsController : ControllerBase { + public class SlotsController : ControllerBase + { private readonly Database database; - public SlotsController(Database database) { + public SlotsController(Database database) + { this.database = database; } [HttpGet("slots/by")] - public IActionResult SlotsBy([FromQuery] string u) { - string response = Enumerable.Aggregate( - this.database.Slots - .Include(s => s.Creator) - .Include(s => s.Location) - .Where(s => s.Creator.Username == u) - , string.Empty, (current, slot) => current + slot.Serialize()); + public IActionResult SlotsBy([FromQuery] string u) + { + string response = Enumerable.Aggregate + ( + this.database.Slots.Include(s => s.Creator).Include(s => s.Location).Where(s => s.Creator.Username == u), + string.Empty, + (current, slot) => current + slot.Serialize() + ); return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); } [HttpGet("s/user/{id:int}")] - public async Task SUser(int id) { - Slot slot = await this.database.Slots - .Include(s => s.Creator) - .Include(s => s.Location) - .FirstOrDefaultAsync(s => s.SlotId == id); + public async Task SUser(int id) + { + Slot slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id); - if(slot == null) return this.NotFound(); + if (slot == null) return this.NotFound(); return this.Ok(slot.Serialize()); } [HttpGet("slots")] - public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { - IQueryable slots = this.database.Slots - .Include(s => s.Creator) + public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) + { + IQueryable slots = this.database.Slots.Include + (s => s.Creator) .Include(s => s.Location) .OrderByDescending(s => s.FirstUploaded) .Skip(pageStart - 1) diff --git a/ProjectLighthouse/Controllers/StatisticsController.cs b/ProjectLighthouse/Controllers/StatisticsController.cs index d71a9222..0bacbd4a 100644 --- a/ProjectLighthouse/Controllers/StatisticsController.cs +++ b/ProjectLighthouse/Controllers/StatisticsController.cs @@ -5,35 +5,39 @@ using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] - public class StatisticsController : ControllerBase { + public class StatisticsController : ControllerBase + { private readonly Database database; - public StatisticsController(Database database) { + public StatisticsController(Database database) + { this.database = database; } [HttpGet("playersInPodCount")] [HttpGet("totalPlayerCount")] - public async Task TotalPlayerCount() { - int recentMatches = await this.database.LastMatches - .Where(l => TimestampHelper.Timestamp - l.Timestamp < 60) - .CountAsync(); + public async Task TotalPlayerCount() + { + int recentMatches = await this.database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 60).CountAsync(); return this.Ok(recentMatches.ToString()); } [HttpGet("planetStats")] - public async Task PlanetStats() { + public async Task PlanetStats() + { int totalSlotCount = await this.database.Slots.CountAsync(); const int mmPicksCount = 0; - return this.Ok(LbpSerializer.StringElement("planetStats", - LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + - LbpSerializer.StringElement("mmPicksCount", mmPicksCount) - )); + return this.Ok + ( + LbpSerializer.StringElement + ("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount)) + ); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs index d3cc5794..1938f6cd 100644 --- a/ProjectLighthouse/Controllers/UserController.cs +++ b/ProjectLighthouse/Controllers/UserController.cs @@ -8,44 +8,41 @@ using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers { +namespace LBPUnion.ProjectLighthouse.Controllers +{ [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/xml")] - public class UserController : ControllerBase { + public class UserController : ControllerBase + { private readonly Database database; - public UserController(Database database) { + public UserController(Database database) + { this.database = database; } [HttpGet("user/{username}")] - public async Task GetUser(string username) { - User user = await this.database.Users - .Include(u => u.Location) - .FirstOrDefaultAsync(u => u.Username == username); + public async Task GetUser(string username) + { + User user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username); + + if (user == null) return this.NotFound(); - if(user == null) return this.NotFound(); return this.Ok(user.Serialize()); } [HttpGet("users")] - public async Task GetUserAlt([FromQuery] string u) { - return await GetUser(u); - } - -// [HttpPost("user/{username}")] -// public async Task CreateUser(string username) { -// await new Database().CreateUser(username); -// return await GetUser(username); -// } + public async Task GetUserAlt([FromQuery] string u) => await this.GetUser(u); [HttpPost("updateUser")] - public async Task UpdateUser() { + public async Task UpdateUser() + { User user = await this.database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); + if (user == null) return this.StatusCode(403, ""); - XmlReaderSettings settings = new() { + XmlReaderSettings settings = new() + { Async = true, // this is apparently not default }; @@ -67,35 +64,40 @@ namespace LBPUnion.ProjectLighthouse.Controllers { // // // if you find a way to make it not stupid feel free to replace this - using(XmlReader reader = XmlReader.Create(this.Request.Body, settings)) { + using (XmlReader reader = XmlReader.Create(this.Request.Body, settings)) + { List path = new(); // you can think of this as a file path in the XML, like -> -> - while(await reader.ReadAsync()) { - // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault - switch(reader.NodeType) { + while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault + switch (reader.NodeType) + { case XmlNodeType.Element: path.Add(reader.Name); break; case XmlNodeType.Text: - switch(path[1]) { - case "biography": { + switch (path[1]) + { + case "biography": + { user.Biography = await reader.GetValueAsync(); break; } - case "location": { + case "location": + { locationChanged = true; // if we're here then we're probably about to change the location. // ReSharper disable once ConvertIfStatementToSwitchStatement - if(path[2] == "x") { - user.Location.X = Convert.ToInt32(await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol - } else if(path[2] == "y") { - user.Location.Y = Convert.ToInt32(await reader.GetValueAsync()); - } + if (path[2] == "x") + user.Location.X = Convert.ToInt32 + (await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol + else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync()); break; } - case "icon": { + case "icon": + { user.IconHash = await reader.GetValueAsync(); break; } - case "planets": { + case "planets": + { user.PlanetHash = await reader.GetValueAsync(); break; } @@ -105,21 +107,21 @@ namespace LBPUnion.ProjectLighthouse.Controllers { path.RemoveAt(path.Count - 1); break; } - } } - + // 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 + 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 // set the location in the database to the one we modified above l.X = user.Location.X; l.Y = user.Location.Y; - + // now both are in sync, and will update in the database. } - - if(this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything + + if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything return this.Ok(); } } diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 4dd23287..c80e6c50 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -8,8 +8,10 @@ using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse { - public class Database : DbContext { +namespace LBPUnion.ProjectLighthouse +{ + public class Database : DbContext + { public DbSet Users { get; set; } public DbSet Locations { get; set; } public DbSet Slots { get; set; } @@ -21,21 +23,20 @@ namespace LBPUnion.ProjectLighthouse { public DbSet LastMatches { get; set; } - protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseMySql( - ServerSettings.DbConnectionString, - MySqlServerVersion.LatestSupportedServerVersion - ); + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options.UseMySql(ServerSettings.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); - public async Task CreateUser(string username) { + public async Task CreateUser(string username) + { User user; - if((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) - return user; + if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user; Location l = new(); // store to get id after submitting this.Locations.Add(l); // add to table await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity - user = new User { + user = new User + { Username = username, LocationId = l.Id, Biography = username + " hasn't introduced themselves yet.", @@ -46,14 +47,15 @@ namespace LBPUnion.ProjectLighthouse { return user; } - - #nullable enable - public async Task AuthenticateUser(LoginData loginData) { - // TODO: don't use psn name to authenticate - User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) - ?? await this.CreateUser(loginData.Username); - Token token = new() { + #nullable enable + public async Task AuthenticateUser(LoginData loginData) + { + // TODO: don't use psn name to authenticate + User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) ?? await this.CreateUser(loginData.Username); + + Token token = new() + { UserToken = HashHelper.GenerateAuthToken(), UserId = user.UserId, }; @@ -64,19 +66,18 @@ namespace LBPUnion.ProjectLighthouse { return token; } - public async Task UserFromAuthToken(string authToken) { + public async Task UserFromAuthToken(string authToken) + { Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken); - if(token == null) return null; - return await this.Users - .Include(u => u.Location) - .FirstOrDefaultAsync(u => u.UserId == token.UserId); + if (token == null) return null; + + return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId); } - public async Task UserFromRequest(HttpRequest request) { - if(!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) { - return null; - } - + public async Task UserFromRequest(HttpRequest request) + { + if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null; + return await this.UserFromAuthToken(mmAuth); } #nullable disable diff --git a/ProjectLighthouse/Helpers/AllowSynchronousIOAttribute.cs b/ProjectLighthouse/Helpers/AllowSynchronousIOAttribute.cs index 8cf82612..1054f1a7 100644 --- a/ProjectLighthouse/Helpers/AllowSynchronousIOAttribute.cs +++ b/ProjectLighthouse/Helpers/AllowSynchronousIOAttribute.cs @@ -2,17 +2,20 @@ using System; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.Filters; -namespace LBPUnion.ProjectLighthouse.Helpers { +namespace LBPUnion.ProjectLighthouse.Helpers +{ // Yoinked from https://stackoverflow.com/a/68530667 // Thanks to T-moty! /// - /// Allows synchronous stream operations for this request. + /// Allows synchronous stream operations for this request. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class AllowSynchronousIoAttribute : ActionFilterAttribute { - public override void OnResultExecuting(ResultExecutingContext context) { + public class AllowSynchronousIoAttribute : ActionFilterAttribute + { + public override void OnResultExecuting(ResultExecutingContext context) + { IHttpBodyControlFeature syncIoFeature = context.HttpContext.Features.Get(); - if(syncIoFeature != null) syncIoFeature.AllowSynchronousIO = true; + if (syncIoFeature != null) syncIoFeature.AllowSynchronousIO = true; } } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/BinaryHelper.cs b/ProjectLighthouse/Helpers/BinaryHelper.cs index 80d0b1cf..3765ef46 100644 --- a/ProjectLighthouse/Helpers/BinaryHelper.cs +++ b/ProjectLighthouse/Helpers/BinaryHelper.cs @@ -6,52 +6,57 @@ using System.IO.Pipelines; using System.Text; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class BinaryHelper { - public static string ReadString(BinaryReader reader) { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class BinaryHelper + { + public static string ReadString(BinaryReader reader) + { List readBytes = new(); byte readByte; - do { - readBytes.Add(readByte = reader.ReadByte()); - } while(readByte != 0x00); + do readBytes.Add(readByte = reader.ReadByte()); + while (readByte != 0x00); return Encoding.UTF8.GetString(readBytes.ToArray()); } - public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) { + public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) + { byte readByte; - do { - readByte = reader.ReadByte(); - } while(readByte != byteToReadTo); + do readByte = reader.ReadByte(); + while (readByte != byteToReadTo); } - public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) { + public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) + { long oldPosition = reader.BaseStream.Position; - if(reader.BaseStream.Length < count) return Array.Empty(); - + if (reader.BaseStream.Length < count) return Array.Empty(); + reader.BaseStream.Position = reader.BaseStream.Length - count; byte[] data = reader.ReadBytes(count); - - if(restoreOldPosition) reader.BaseStream.Position = oldPosition; + + if (restoreOldPosition) reader.BaseStream.Position = oldPosition; return data; } // Written with reference from // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 // Surprisingly doesn't take seconds. (67ms for a 100kb file) - public static async Task ReadFromPipeReader(PipeReader reader) { + public static async Task ReadFromPipeReader(PipeReader reader) + { List data = new(); - while(true) { + while (true) + { ReadResult readResult = await reader.ReadAsync(); ReadOnlySequence buffer = readResult.Buffer; - if(readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); - + if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); + reader.AdvanceTo(buffer.Start, buffer.End); - - if(readResult.IsCompleted) break; + + if (readResult.IsCompleted) break; } return data.ToArray(); diff --git a/ProjectLighthouse/Helpers/EulaHelper.cs b/ProjectLighthouse/Helpers/EulaHelper.cs index 25c531ba..cfaa3940 100644 --- a/ProjectLighthouse/Helpers/EulaHelper.cs +++ b/ProjectLighthouse/Helpers/EulaHelper.cs @@ -1,5 +1,7 @@ -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class EulaHelper { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class EulaHelper + { public const string License = @" This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as diff --git a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs b/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs index 0ea759a3..5a119dbc 100644 --- a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs @@ -3,17 +3,23 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions { +namespace LBPUnion.ProjectLighthouse.Helpers.Extensions +{ // https://stackoverflow.com/a/8039737 - public static class ExceptionExtensions { - public static string ToDetailedException(this Exception exception) { + public static class ExceptionExtensions + { + public static string ToDetailedException(this Exception exception) + { PropertyInfo[] properties = exception.GetType().GetProperties(); - - IEnumerable fields = properties - .Select(property => new { - property.Name, - Value = property.GetValue(exception, null), - }) + + IEnumerable fields = properties.Select + ( + property => new + { + property.Name, + Value = property.GetValue(exception, null), + } + ) .Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}"); return string.Join("\n", fields); diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index 8bd60898..fbb23117 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -4,16 +4,20 @@ using System.Linq; using System.Text; using LBPUnion.ProjectLighthouse.Types.Files; -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class FileHelper { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class FileHelper + { public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r"); public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash); - - public static bool IsFileSafe(LbpFile file) { - if(file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); - - return file.FileType switch { + + public static bool IsFileSafe(LbpFile file) + { + if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); + + return file.FileType switch + { LbpFileType.FileArchive => false, LbpFileType.Painting => true, LbpFileType.Unknown => false, @@ -30,16 +34,18 @@ namespace LBPUnion.ProjectLighthouse.Helpers { }; } - public static LbpFileType DetermineFileType(byte[] data) { + public static LbpFileType DetermineFileType(byte[] data) + { using MemoryStream ms = new(data); using BinaryReader reader = new(ms); string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4)); - if(footer == "FARC") return LbpFileType.FileArchive; + if (footer == "FARC") return LbpFileType.FileArchive; byte[] header = reader.ReadBytes(3); - return Encoding.ASCII.GetString(header) switch { + return Encoding.ASCII.GetString(header) switch + { "PTG" => LbpFileType.Painting, "TEX" => LbpFileType.Texture, "FSH" => LbpFileType.Script, @@ -52,8 +58,9 @@ namespace LBPUnion.ProjectLighthouse.Helpers { public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); - public static void EnsureDirectoryCreated(string path) { - if(!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path))); + public static void EnsureDirectoryCreated(string path) + { + if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path))); } public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray(); diff --git a/ProjectLighthouse/Helpers/HashHelper.cs b/ProjectLighthouse/Helpers/HashHelper.cs index b2ea531e..6cf17969 100644 --- a/ProjectLighthouse/Helpers/HashHelper.cs +++ b/ProjectLighthouse/Helpers/HashHelper.cs @@ -4,32 +4,22 @@ using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using System.Text; -namespace LBPUnion.ProjectLighthouse.Helpers { +namespace LBPUnion.ProjectLighthouse.Helpers +{ [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static class HashHelper { + public static class HashHelper + { // private static readonly SHA1 sha1 = SHA1.Create(); private static readonly SHA256 sha256 = SHA256.Create(); private static readonly Random random = new(); - #region Hash Functions - public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); - - public static string Sha256Hash(byte[] bytes) { - byte[] hash = sha256.ComputeHash(bytes); - return Encoding.UTF8.GetString(hash, 0, hash.Length); - } - - public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); - - public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); - #endregion - /// - /// Generates a specified amount of random bytes in an array. + /// Generates a specified amount of random bytes in an array. /// /// The amount of bytes to generate. /// The bytes generated - public static IEnumerable GenerateRandomBytes(int count) { + public static IEnumerable GenerateRandomBytes(int count) + { byte[] b = new byte[count]; random.NextBytes(b); @@ -37,13 +27,31 @@ namespace LBPUnion.ProjectLighthouse.Helpers { } /// - /// Generates a random SHA256 & BCrypted token + /// Generates a random SHA256 & BCrypted token /// /// The token as a string. - public static string GenerateAuthToken() { - byte[] bytes = (byte[]) GenerateRandomBytes(256); + public static string GenerateAuthToken() + { + byte[] bytes = (byte[])GenerateRandomBytes(256); return BCryptHash(Sha256Hash(bytes)); } + + #region Hash Functions + + public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); + + public static string Sha256Hash(byte[] bytes) + { + byte[] hash = sha256.ComputeHash(bytes); + return Encoding.UTF8.GetString(hash, 0, hash.Length); + } + + public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); + + public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); + + #endregion + } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/MatchHelper.cs b/ProjectLighthouse/Helpers/MatchHelper.cs index 22d34ad0..e7e66895 100644 --- a/ProjectLighthouse/Helpers/MatchHelper.cs +++ b/ProjectLighthouse/Helpers/MatchHelper.cs @@ -3,14 +3,18 @@ using System.Linq; using System.Text.Json; using LBPUnion.ProjectLighthouse.Types.Match; -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class MatchHelper { - public static IMatchData? Deserialize(string data) { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class MatchHelper + { + public static IMatchData? Deserialize(string data) + { string matchType = ""; int i = 1; - while(true) { - if(data[i] == ',') break; + while (true) + { + if (data[i] == ',') break; matchType += data[i]; i++; @@ -21,8 +25,10 @@ namespace LBPUnion.ProjectLighthouse.Helpers { return Deserialize(matchType, matchData); } - public static IMatchData? Deserialize(string matchType, string matchData) { - return matchType switch { + public static IMatchData? Deserialize(string matchType, string matchData) + { + return matchType switch + { "UpdateMyPlayerData" => JsonSerializer.Deserialize(matchData), "UpdatePlayersInRoom" => JsonSerializer.Deserialize(matchData), _ => null, diff --git a/ProjectLighthouse/Helpers/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs index 9f51452a..77d8757d 100644 --- a/ProjectLighthouse/Helpers/TimeHelper.cs +++ b/ProjectLighthouse/Helpers/TimeHelper.cs @@ -1,7 +1,9 @@ using System; -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class TimeHelper { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class TimeHelper + { public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); } diff --git a/ProjectLighthouse/Helpers/TimestampHelper.cs b/ProjectLighthouse/Helpers/TimestampHelper.cs index 62ac5d60..3a3e9d48 100644 --- a/ProjectLighthouse/Helpers/TimestampHelper.cs +++ b/ProjectLighthouse/Helpers/TimestampHelper.cs @@ -1,7 +1,9 @@ using System; -namespace LBPUnion.ProjectLighthouse.Helpers { - public static class TimestampHelper { +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class TimestampHelper + { public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs index f1af4bb7..b20ce5a7 100644 --- a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs +++ b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs @@ -3,22 +3,23 @@ using Kettu; using LBPUnion.ProjectLighthouse.Helpers.Extensions; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging { - public class AspNetToKettuLogger : ILogger { +namespace LBPUnion.ProjectLighthouse.Logging +{ + public class AspNetToKettuLogger : ILogger + { - public IDisposable BeginScope(TState state) { - return NullScope.Instance; - } + public IDisposable BeginScope(TState state) => NullScope.Instance; public bool IsEnabled(LogLevel logLevel) => true; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); - + Logger.Log(state.ToString(), loggerLevel); - if(exception == null) return; + if (exception == null) return; string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n"); - foreach(string line in lines) Logger.Log(line, loggerLevel); + foreach (string line in lines) Logger.Log(line, loggerLevel); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs b/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs index 557b4462..84039bfe 100644 --- a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs +++ b/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs @@ -1,15 +1,16 @@ using System; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging { +namespace LBPUnion.ProjectLighthouse.Logging +{ [ProviderAlias("Kettu")] - public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable { - public void Dispose() { + public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable + { + public void Dispose() + { GC.SuppressFinalize(this); } - - public ILogger CreateLogger(string categoryName) { - return new AspNetToKettuLogger(); - } + + public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs index 88f7a4f2..1075dd0f 100644 --- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs +++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs @@ -3,11 +3,14 @@ using System.IO; using Kettu; using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Logging { - public class LighthouseFileLogger : LoggerBase { +namespace LBPUnion.ProjectLighthouse.Logging +{ + public class LighthouseFileLogger : LoggerBase + { private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); - - public override void Send(LoggerLine line) { + + public override void Send(LoggerLine line) + { FileHelper.EnsureDirectoryCreated(logsDirectory); string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs index 7ddb690c..b033f58b 100644 --- a/ProjectLighthouse/Logging/LoggerLevels.cs +++ b/ProjectLighthouse/Logging/LoggerLevels.cs @@ -1,32 +1,39 @@ using Kettu; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse.Logging { - public class LoggerLevelStartup : LoggerLevel { - public override string Name => "Startup"; +namespace LBPUnion.ProjectLighthouse.Logging +{ + public class LoggerLevelStartup : LoggerLevel + { public static readonly LoggerLevelStartup Instance = new(); + public override string Name => "Startup"; } - public class LoggerLevelDatabase : LoggerLevel { - public override string Name => "Database"; + public class LoggerLevelDatabase : LoggerLevel + { public static readonly LoggerLevelDatabase Instance = new(); + public override string Name => "Database"; } - public class LoggerLevelHttp : LoggerLevel { - public override string Name => "HTTP"; + public class LoggerLevelHttp : LoggerLevel + { public static readonly LoggerLevelHttp Instance = new(); + public override string Name => "HTTP"; } - - public class LoggerLevelFilter : LoggerLevel { - public override string Name => "Filter"; - public static readonly LoggerLevelFilter Instance = new(); - } - public class LoggerLevelAspNet : LoggerLevel { - public override string Name => "AspNet"; - - public LoggerLevelAspNet(LogLevel level) { + public class LoggerLevelFilter : LoggerLevel + { + public static readonly LoggerLevelFilter Instance = new(); + public override string Name => "Filter"; + } + + public class LoggerLevelAspNet : LoggerLevel + { + + public LoggerLevelAspNet(LogLevel level) + { this.Channel = level.ToString(); } + public override string Name => "AspNet"; } } \ No newline at end of file diff --git a/ProjectLighthouse/Logging/NullScope.cs b/ProjectLighthouse/Logging/NullScope.cs index 3152fea5..5d790f78 100644 --- a/ProjectLighthouse/Logging/NullScope.cs +++ b/ProjectLighthouse/Logging/NullScope.cs @@ -1,12 +1,16 @@ using System; -namespace LBPUnion.ProjectLighthouse.Logging { - public class NullScope : IDisposable{ +namespace LBPUnion.ProjectLighthouse.Logging +{ + public class NullScope : IDisposable + { + + private NullScope() + {} public static NullScope Instance { get; } = new(); - private NullScope() {} - - public void Dispose() { + public void Dispose() + { GC.SuppressFinalize(this); } } diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index a74ebed2..804f9c94 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -14,31 +14,33 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace LBPUnion.ProjectLighthouse { - public static class Program { - public static void Main(string[] args) { +namespace LBPUnion.ProjectLighthouse +{ + public static class Program + { + public static void Main(string[] args) + { // Log startup time Stopwatch stopwatch = new(); stopwatch.Start(); - + // Setup logging - + Logger.StartLogging(); LoggerLine.LogFormat = "[{0}] {1}"; Logger.AddLogger(new ConsoleLogger()); Logger.AddLogger(new LighthouseFileLogger()); - + Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance); Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance); bool dbConnected = ServerSettings.DbConnected; Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); - if(!dbConnected) Environment.Exit(1); + if (!dbConnected) Environment.Exit(1); using Database database = new(); Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); MigrateDatabase(database); - Logger.Log("Fixing broken timestamps...", LoggerLevelDatabase.Instance); FixTimestamps(database); @@ -49,46 +51,50 @@ namespace LBPUnion.ProjectLighthouse { CreateHostBuilder(args).Build().Run(); } - public static void MigrateDatabase(Database database) { + public static void MigrateDatabase(Database database) + { Stopwatch stopwatch = new(); stopwatch.Start(); - + database.Database.Migrate(); - + stopwatch.Stop(); Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); } - public static void FixTimestamps(Database database) { + public static void FixTimestamps(Database database) + { Stopwatch stopwatch = new(); stopwatch.Start(); - - foreach(Slot slot in database.Slots.Where(s => s.FirstUploaded == 0)) { - slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); - } - foreach(Slot slot in database.Slots.Where(s => s.LastUpdated == 0)) { - slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); - } + foreach (Slot slot in database.Slots.Where(s => s.FirstUploaded == 0)) slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); - foreach(Comment comment in database.Comments.Where(c => c.Timestamp == 0)) { - comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); - } + foreach (Slot slot in database.Slots.Where(s => s.LastUpdated == 0)) slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); + + foreach (Comment comment in database.Comments.Where(c => c.Timestamp == 0)) comment.Timestamp = TimeHelper.UnixTimeMilliseconds(); database.SaveChanges(); - + stopwatch.Stop(); Logger.Log($"Fixing timestamps took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); } - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { - webBuilder.UseStartup(); - }) - .ConfigureLogging(logging => { - logging.ClearProviders(); - logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - }); + public static IHostBuilder CreateHostBuilder(string[] args) + => Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults + ( + webBuilder => + { + webBuilder.UseStartup(); + } + ) + .ConfigureLogging + ( + logging => + { + logging.ClearProviders(); + logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index 35309169..3a7ade23 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -8,23 +8,23 @@ - - - - + + + + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - + - + - + diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index e7946c02..1d2cfdaf 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -2,26 +2,30 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace LBPUnion.ProjectLighthouse.Serialization { +namespace LBPUnion.ProjectLighthouse.Serialization +{ /// - /// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root elements), - /// so I wrote my own crappy one. + /// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root + /// elements), + /// so I wrote my own crappy one. /// [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static class LbpSerializer { + public static class LbpSerializer + { public static string BlankElement(string key) => $"<{key}>"; public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value}"; public static string StringElement(string key, object value) => $"<{key}>{value}"; - - public static string TaggedStringElement(KeyValuePair pair, KeyValuePair tagPair) => - $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; - - public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => - $"<{key} {tagKey}=\"{tagValue}\">{value}"; - public static string Elements(params KeyValuePair[] pairs) => - pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); + public static string TaggedStringElement + (KeyValuePair pair, KeyValuePair tagPair) + => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; + + public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; + + public static string Elements + (params KeyValuePair[] pairs) + => pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/XmlOutputFormatter.cs b/ProjectLighthouse/Serialization/XmlOutputFormatter.cs index da7c25ae..6103f8cd 100644 --- a/ProjectLighthouse/Serialization/XmlOutputFormatter.cs +++ b/ProjectLighthouse/Serialization/XmlOutputFormatter.cs @@ -1,8 +1,11 @@ using Microsoft.AspNetCore.Mvc.Formatters; -namespace LBPUnion.ProjectLighthouse.Serialization { - public class XmlOutputFormatter : StringOutputFormatter { - public XmlOutputFormatter() { +namespace LBPUnion.ProjectLighthouse.Serialization +{ + public class XmlOutputFormatter : StringOutputFormatter + { + public XmlOutputFormatter() + { this.SupportedMediaTypes.Add("text/xml"); this.SupportedMediaTypes.Add("application/xml"); } diff --git a/ProjectLighthouse/Startup.cs b/ProjectLighthouse/Startup.cs index d83ff302..bd3b2640 100644 --- a/ProjectLighthouse/Startup.cs +++ b/ProjectLighthouse/Startup.cs @@ -10,57 +10,69 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace LBPUnion.ProjectLighthouse { - public class Startup { - public Startup(IConfiguration configuration) { +namespace LBPUnion.ProjectLighthouse +{ + public class Startup + { + public Startup(IConfiguration configuration) + { this.Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) { + public void ConfigureServices(IServiceCollection services) + { services.AddControllers(); - services.AddMvc(options => - options.OutputFormatters.Add(new XmlOutputFormatter())); - + services.AddMvc(options => options.OutputFormatters.Add(new XmlOutputFormatter())); + services.AddDbContext(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if(env.IsDevelopment()) { - app.UseDeveloperExceptionPage(); - } + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); // Logs every request and the response to it // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" - app.Use(async (context, next) => { - Stopwatch requestStopwatch = new(); - requestStopwatch.Start(); - - context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging - await next(); // Handle the request so we can get the status code from it - - requestStopwatch.Stop(); - - Logger.Log( - $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", - LoggerLevelHttp.Instance - ); - - if(context.Request.Method == "POST") { - context.Request.Body.Position = 0; - Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); + app.Use + ( + async (context, next) => + { + Stopwatch requestStopwatch = new(); + requestStopwatch.Start(); + + context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging + await next(); // Handle the request so we can get the status code from it + + requestStopwatch.Stop(); + + Logger.Log + ( + $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", + LoggerLevelHttp.Instance + ); + + if (context.Request.Method == "POST") + { + context.Request.Body.Position = 0; + Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); + } } - }); + ); app.UseRouting(); - app.UseEndpoints(endpoints => { - endpoints.MapControllers(); - }); + app.UseEndpoints + ( + endpoints => + { + endpoints.MapControllers(); + } + ); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFile.cs b/ProjectLighthouse/Types/Files/LbpFile.cs index 70ad51da..b226276c 100644 --- a/ProjectLighthouse/Types/Files/LbpFile.cs +++ b/ProjectLighthouse/Types/Files/LbpFile.cs @@ -1,21 +1,24 @@ using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Types.Files { - public class LbpFile { - public LbpFile(byte[] data) { - this.Data = data; - this.FileType = FileHelper.DetermineFileType(this.Data); - } - - /// - /// The type of file. - /// - public LbpFileType FileType; +namespace LBPUnion.ProjectLighthouse.Types.Files +{ + public class LbpFile + { /// - /// A buffer of the file's data. + /// A buffer of the file's data. /// public readonly byte[] Data; + /// + /// The type of file. + /// + public LbpFileType FileType; + + public LbpFile(byte[] data) + { + this.Data = data; + this.FileType = FileHelper.DetermineFileType(this.Data); + } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFileType.cs b/ProjectLighthouse/Types/Files/LbpFileType.cs index 9c01d4ed..871aac91 100644 --- a/ProjectLighthouse/Types/Files/LbpFileType.cs +++ b/ProjectLighthouse/Types/Files/LbpFileType.cs @@ -1,5 +1,7 @@ -namespace LBPUnion.ProjectLighthouse.Types.Files { - public enum LbpFileType { +namespace LBPUnion.ProjectLighthouse.Types.Files +{ + public enum LbpFileType + { Script, // .ff, FSH Texture, // TEX Level, // LVL diff --git a/ProjectLighthouse/Types/HeartedProfile.cs b/ProjectLighthouse/Types/HeartedProfile.cs index 6d2908f0..bf4ebea5 100644 --- a/ProjectLighthouse/Types/HeartedProfile.cs +++ b/ProjectLighthouse/Types/HeartedProfile.cs @@ -2,22 +2,27 @@ using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types { - public class HeartedProfile { +namespace LBPUnion.ProjectLighthouse.Types +{ + public class HeartedProfile + { // ReSharper disable once UnusedMember.Global #if NET6_0_OR_GREATER [Obsolete($"Use {nameof(HeartedUserId)} instead, this is a key which you should never need to use.")] #else [Obsolete("Use HeartedUserId instead, this is a key which you should never need to use.")] #endif - [Key] public int HeartedProfileId { get; set; } + [Key] + public int HeartedProfileId { get; set; } public int UserId { get; set; } - [ForeignKey(nameof(UserId))] public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } public int HeartedUserId { get; set; } - [ForeignKey(nameof(HeartedUserId))] public User HeartedUser { get; set; } + [ForeignKey(nameof(HeartedUserId))] + public User HeartedUser { get; set; } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/HeartedLevel.cs b/ProjectLighthouse/Types/Levels/HeartedLevel.cs index 40c55dea..72b9855b 100644 --- a/ProjectLighthouse/Types/Levels/HeartedLevel.cs +++ b/ProjectLighthouse/Types/Levels/HeartedLevel.cs @@ -1,17 +1,22 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels { - public class HeartedLevel { +namespace LBPUnion.ProjectLighthouse.Types.Levels +{ + public class HeartedLevel + { // ReSharper disable once UnusedMember.Global - [Key] public int HeartedLevelId { get; set; } + [Key] + public int HeartedLevelId { get; set; } public int UserId { get; set; } - [ForeignKey(nameof(UserId))] public User User { get; set; } + [ForeignKey(nameof(UserId))] + public User User { get; set; } public int SlotId { get; set; } - [ForeignKey(nameof(SlotId))] public Slot Slot { get; set; } + [ForeignKey(nameof(SlotId))] + public Slot Slot { get; set; } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/LevelTags.cs b/ProjectLighthouse/Types/Levels/LevelTags.cs index 3bb2a212..784985e4 100644 --- a/ProjectLighthouse/Types/Levels/LevelTags.cs +++ b/ProjectLighthouse/Types/Levels/LevelTags.cs @@ -1,12 +1,14 @@ using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Levels { +namespace LBPUnion.ProjectLighthouse.Types.Levels +{ /// - /// A series of tags that can be applied to a level + /// A series of tags that can be applied to a level /// [SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "UnusedMember.Global")] - public enum LevelTags { + public enum LevelTags + { Brilliant, Beautiful, Funky, diff --git a/ProjectLighthouse/Types/Levels/QueuedLevel.cs b/ProjectLighthouse/Types/Levels/QueuedLevel.cs index 484f0c36..e238627e 100644 --- a/ProjectLighthouse/Types/Levels/QueuedLevel.cs +++ b/ProjectLighthouse/Types/Levels/QueuedLevel.cs @@ -1,16 +1,19 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Levels { - public class QueuedLevel { +namespace LBPUnion.ProjectLighthouse.Types.Levels +{ + public class QueuedLevel + { // ReSharper disable once UnusedMember.Global - [Key] public int QueuedLevelId { get; set; } - + [Key] + public int QueuedLevelId { get; set; } + public int UserId { get; set; } [ForeignKey(nameof(UserId))] public User User { get; set; } - + public int SlotId { get; set; } [ForeignKey(nameof(SlotId))] diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index c35faa28..79b9d2fe 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -5,12 +5,15 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Levels { +namespace LBPUnion.ProjectLighthouse.Types.Levels +{ /// - /// A LittleBigPlanet level. + /// A LittleBigPlanet level. /// - [XmlRoot("slot"), XmlType("slot")] - public class Slot { + [XmlRoot("slot")] + [XmlType("slot")] + public class Slot + { [XmlAttribute("type")] [NotMapped] public string Type { get; set; } @@ -19,87 +22,86 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { [XmlElement("id")] public int SlotId { get; set; } - [XmlElement("name")] public string Name { get; set; } - + [XmlElement("description")] public string Description { get; set; } - + [XmlElement("icon")] public string IconHash { get; set; } - + [XmlElement("rootLevel")] public string RootLevel { get; set; } public string ResourceCollection { get; set; } - + [NotMapped] [XmlElement("resource")] public string[] Resources { get => this.ResourceCollection.Split(","); set => this.ResourceCollection = string.Join(',', value); } - + [XmlIgnore] public int LocationId { get; set; } - [XmlIgnore] + [XmlIgnore] public int CreatorId { get; set; } [ForeignKey(nameof(CreatorId))] public User Creator { get; set; } /// - /// The location of the level on the creator's earth + /// The location of the level on the creator's earth /// [XmlElement("location")] [ForeignKey(nameof(LocationId))] public Location Location { get; set; } - + [XmlElement("initiallyLocked")] public bool InitiallyLocked { get; set; } - + [XmlElement("isSubLevel")] public bool SubLevel { get; set; } - + [XmlElement("isLBP1Only")] public bool Lbp1Only { get; set; } - + [XmlElement("shareable")] public int Shareable { get; set; } - + [XmlElement("authorLabels")] public string AuthorLabels { get; set; } - [XmlElement("background")] + [XmlElement("background")] public string BackgroundHash { get; set; } = ""; - + [XmlElement("minPlayers")] public int MinimumPlayers { get; set; } - + [XmlElement("maxPlayers")] public int MaximumPlayers { get; set; } - + [XmlElement("moveRequired")] public bool MoveRequired { get; set; } - + [XmlIgnore] public long FirstUploaded { get; set; } - + [XmlIgnore] public long LastUpdated { get; set; } - + [XmlIgnore] public bool MMPick { get; set; } - public string SerializeResources() { - return this.Resources - .Aggregate("", (current, resource) => - current + LbpSerializer.StringElement("resource", resource)); + public string SerializeResources() + { + return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)); } - public string Serialize() { + public string Serialize() + { string slotData = LbpSerializer.StringElement("name", this.Name) + LbpSerializer.StringElement("id", this.SlotId) + LbpSerializer.StringElement("game", 1) + @@ -120,7 +122,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels { LbpSerializer.StringElement("firstPublished", this.FirstUploaded) + LbpSerializer.StringElement("lastUpdated", this.LastUpdated) + LbpSerializer.StringElement("mmpick", this.MMPick); - + return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } } diff --git a/ProjectLighthouse/Types/LoginData.cs b/ProjectLighthouse/Types/LoginData.cs index 6a51d2b1..5fcde0f1 100644 --- a/ProjectLighthouse/Types/LoginData.cs +++ b/ProjectLighthouse/Types/LoginData.cs @@ -4,29 +4,39 @@ using System.IO; using System.Text; using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Types { +namespace LBPUnion.ProjectLighthouse.Types +{ /// - /// The data sent from POST /LOGIN. + /// The data sent from POST /LOGIN. /// - public class LoginData { + public class LoginData + { + + public static readonly string UsernamePrefix = Encoding.ASCII.GetString + ( + new byte[] + { + 0x04, 0x00, 0x20, + } + ); + public string Username { get; set; } = null!; - public static readonly string UsernamePrefix = Encoding.ASCII.GetString(new byte[] { 0x04, 0x00, 0x20 }); - /// - /// Converts a X-I-5 Ticket into `LoginData`. - /// https://www.psdevwiki.com/ps3/X-I-5-Ticket - /// - public static LoginData? CreateFromString(string str) { + /// Converts a X-I-5 Ticket into `LoginData`. + /// https://www.psdevwiki.com/ps3/X-I-5-Ticket + /// + public static LoginData? CreateFromString(string str) + { str = str.Replace("\b", ""); // Remove backspace characters using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); using BinaryReader reader = new(ms); - if(!str.Contains(UsernamePrefix)) return null; + if (!str.Contains(UsernamePrefix)) return null; LoginData loginData = new(); - + reader.BaseStream.Position = str.IndexOf(UsernamePrefix, StringComparison.Ordinal) + UsernamePrefix.Length; loginData.Username = BinaryHelper.ReadString(reader).Replace("\0", string.Empty); diff --git a/ProjectLighthouse/Types/LoginResult.cs b/ProjectLighthouse/Types/LoginResult.cs index c0ed0997..bafb2416 100644 --- a/ProjectLighthouse/Types/LoginResult.cs +++ b/ProjectLighthouse/Types/LoginResult.cs @@ -2,23 +2,23 @@ using System.Collections.Generic; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types { +namespace LBPUnion.ProjectLighthouse.Types +{ /// - /// Response to POST /login + /// Response to POST /login /// - [XmlRoot("loginResult"), XmlType("loginResult")] - public class LoginResult { + [XmlRoot("loginResult")] + [XmlType("loginResult")] + public class LoginResult + { [XmlElement("authTicket")] public string AuthTicket { get; set; } [XmlElement("lbpEnvVer")] public string LbpEnvVer { get; set; } - public string Serialize() { - return LbpSerializer.Elements( - new KeyValuePair("authTicket", this.AuthTicket), - new KeyValuePair("lbpEnvVer", this.LbpEnvVer) - ); - } + public string Serialize() + => LbpSerializer.Elements + (new KeyValuePair("authTicket", this.AuthTicket), new KeyValuePair("lbpEnvVer", this.LbpEnvVer)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/IMatchData.cs b/ProjectLighthouse/Types/Match/IMatchData.cs index 435b08ef..b785b433 100644 --- a/ProjectLighthouse/Types/Match/IMatchData.cs +++ b/ProjectLighthouse/Types/Match/IMatchData.cs @@ -1,5 +1,5 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match { - public interface IMatchData { - - } +namespace LBPUnion.ProjectLighthouse.Types.Match +{ + public interface IMatchData + {} } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomState.cs b/ProjectLighthouse/Types/Match/RoomState.cs index 1cb53079..2cfc28e2 100644 --- a/ProjectLighthouse/Types/Match/RoomState.cs +++ b/ProjectLighthouse/Types/Match/RoomState.cs @@ -1,5 +1,7 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match { - public enum RoomState { +namespace LBPUnion.ProjectLighthouse.Types.Match +{ + public enum RoomState + { Idle = 0, LookingForPlayersForLevel = 1, Unknown = 2, diff --git a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs b/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs index fade3952..ff93dd2c 100644 --- a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs +++ b/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs @@ -1,5 +1,7 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match { - public class UpdateMyPlayerData : IMatchData { +namespace LBPUnion.ProjectLighthouse.Types.Match +{ + public class UpdateMyPlayerData : IMatchData + { public string Player; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs b/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs index a689bcc2..a7752ec2 100644 --- a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs +++ b/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; -namespace LBPUnion.ProjectLighthouse.Types.Match { - public class UpdatePlayersInRoom : IMatchData { +namespace LBPUnion.ProjectLighthouse.Types.Match +{ + public class UpdatePlayersInRoom : IMatchData + { public List Players; public List Reservations; } diff --git a/ProjectLighthouse/Types/News/NewsEntry.cs b/ProjectLighthouse/Types/News/NewsEntry.cs index aecf5b88..2ef65ecb 100644 --- a/ProjectLighthouse/Types/News/NewsEntry.cs +++ b/ProjectLighthouse/Types/News/NewsEntry.cs @@ -1,10 +1,12 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.News { +namespace LBPUnion.ProjectLighthouse.Types.News +{ /// - /// Used on the info moon on LBP1. Broken for unknown reasons + /// Used on the info moon on LBP1. Broken for unknown reasons /// - public class NewsEntry { + public class NewsEntry + { public int Id { get; set; } public string Title { get; set; } public string Summary { get; set; } @@ -13,14 +15,13 @@ namespace LBPUnion.ProjectLighthouse.Types.News { public string Category { get; set; } public long Date { get; set; } - public string Serialize() { - return LbpSerializer.StringElement("id", this.Id) + - LbpSerializer.StringElement("title", this.Title) + - LbpSerializer.StringElement("summary", this.Summary) + - LbpSerializer.StringElement("text", this.Text) + - LbpSerializer.StringElement("date", this.Date) + - this.Image.Serialize() + - LbpSerializer.StringElement("category", this.Category); - } + public string Serialize() + => LbpSerializer.StringElement("id", this.Id) + + LbpSerializer.StringElement("title", this.Title) + + LbpSerializer.StringElement("summary", this.Summary) + + LbpSerializer.StringElement("text", this.Text) + + LbpSerializer.StringElement("date", this.Date) + + this.Image.Serialize() + + LbpSerializer.StringElement("category", this.Category); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/News/NewsImage.cs b/ProjectLighthouse/Types/News/NewsImage.cs index 4e134ae0..f174afff 100644 --- a/ProjectLighthouse/Types/News/NewsImage.cs +++ b/ProjectLighthouse/Types/News/NewsImage.cs @@ -1,14 +1,13 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.News { - public class NewsImage { +namespace LBPUnion.ProjectLighthouse.Types.News +{ + public class NewsImage + { public string Hash { get; set; } public string Alignment { get; set; } - public string Serialize() { - return LbpSerializer.StringElement("image", - LbpSerializer.StringElement("hash", this.Hash) + - LbpSerializer.StringElement("alignment", this.Alignment)); - } + public string Serialize() + => LbpSerializer.StringElement("image", LbpSerializer.StringElement("hash", this.Hash) + LbpSerializer.StringElement("alignment", this.Alignment)); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs b/ProjectLighthouse/Types/Profiles/ClientsConnected.cs index 2241ca4e..be6c77fe 100644 --- a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs +++ b/ProjectLighthouse/Types/Profiles/ClientsConnected.cs @@ -1,22 +1,26 @@ using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Profiles { +namespace LBPUnion.ProjectLighthouse.Types.Profiles +{ [Keyless] - public class ClientsConnected { + public class ClientsConnected + { public bool Lbp1 { get; set; } public bool Lbp2 { get; set; } public bool LbpMe { get; set; } public bool Lbp3Ps3 { get; set; } public bool Lbp3Ps4 { get; set; } - public string Serialize() { - return LbpSerializer.StringElement("clientsConnected", + public string Serialize() + => LbpSerializer.StringElement + ( + "clientsConnected", LbpSerializer.StringElement("lbp1", this.Lbp1) + LbpSerializer.StringElement("lbp2", this.Lbp2) + LbpSerializer.StringElement("lbpme", this.LbpMe) + LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) + - LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4)); - } + LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4) + ); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/Comment.cs b/ProjectLighthouse/Types/Profiles/Comment.cs index a10f2d16..f2260fe2 100644 --- a/ProjectLighthouse/Types/Profiles/Comment.cs +++ b/ProjectLighthouse/Types/Profiles/Comment.cs @@ -3,20 +3,23 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles { - [XmlRoot("comment"), XmlType("comment")] - public class Comment { +namespace LBPUnion.ProjectLighthouse.Types.Profiles +{ + [XmlRoot("comment")] + [XmlType("comment")] + public class Comment + { [Key] [XmlAttribute("id")] public int CommentId { get; set; } - + public int PosterUserId { get; set; } - + public int TargetUserId { get; set; } [ForeignKey(nameof(PosterUserId))] public User Poster { get; set; } - + [ForeignKey(nameof(TargetUserId))] public User Target { get; set; } @@ -24,24 +27,22 @@ namespace LBPUnion.ProjectLighthouse.Types.Profiles { [XmlElement("message")] public string Message { get; set; } + public int ThumbsUp { get; set; } public int ThumbsDown { get; set; } - private string serialize() { - return LbpSerializer.StringElement("id", this.CommentId) + - LbpSerializer.StringElement("npHandle", this.Poster.Username) + - LbpSerializer.StringElement("timestamp", this.Timestamp) + - LbpSerializer.StringElement("message", this.Message) + - LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + - LbpSerializer.StringElement("thumbsdown", this.ThumbsDown); - } + private string serialize() + => LbpSerializer.StringElement("id", this.CommentId) + + LbpSerializer.StringElement("npHandle", this.Poster.Username) + + LbpSerializer.StringElement("timestamp", this.Timestamp) + + LbpSerializer.StringElement("message", this.Message) + + LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + + LbpSerializer.StringElement("thumbsdown", this.ThumbsDown); - public string Serialize(int yourThumb) { - return LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb)); - } + public string Serialize + (int yourThumb) + => LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb)); - public string Serialize() { - return LbpSerializer.StringElement("comment", this.serialize()); - } + public string Serialize() => LbpSerializer.StringElement("comment", this.serialize()); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/LastMatch.cs b/ProjectLighthouse/Types/Profiles/LastMatch.cs index 0513ca78..ec637c14 100644 --- a/ProjectLighthouse/Types/Profiles/LastMatch.cs +++ b/ProjectLighthouse/Types/Profiles/LastMatch.cs @@ -1,8 +1,12 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types.Profiles { - public class LastMatch { - [Key] public int UserId { get; set; } +namespace LBPUnion.ProjectLighthouse.Types.Profiles +{ + public class LastMatch + { + [Key] + public int UserId { get; set; } + public long Timestamp { get; set; } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/Location.cs b/ProjectLighthouse/Types/Profiles/Location.cs index a4a9b20d..139565e6 100644 --- a/ProjectLighthouse/Types/Profiles/Location.cs +++ b/ProjectLighthouse/Types/Profiles/Location.cs @@ -1,12 +1,15 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles { +namespace LBPUnion.ProjectLighthouse.Types.Profiles +{ /// - /// The location of a slot on a planet. + /// The location of a slot on a planet. /// - [XmlRoot("location"), XmlType("location")] - public class Location { + [XmlRoot("location")] + [XmlType("location")] + public class Location + { [XmlIgnore] public int Id { get; set; } @@ -16,9 +19,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Profiles { [XmlElement("y")] public int Y { get; set; } - public string Serialize() { - return LbpSerializer.StringElement("x", this.X) + - LbpSerializer.StringElement("y", this.Y); - } + public string Serialize() => LbpSerializer.StringElement("x", this.X) + LbpSerializer.StringElement("y", this.Y); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/ResourceList.cs b/ProjectLighthouse/Types/ResourceList.cs index 7e293cd8..5af4425a 100644 --- a/ProjectLighthouse/Types/ResourceList.cs +++ b/ProjectLighthouse/Types/ResourceList.cs @@ -1,9 +1,12 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types { - [XmlRoot("resources"), XmlType("resources")] - public class ResourceList { - [XmlElement("resource")] +namespace LBPUnion.ProjectLighthouse.Types +{ + [XmlRoot("resources")] + [XmlType("resources")] + public class ResourceList + { + [XmlElement("resource")] public string[] Resources; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/PrivacySettings.cs b/ProjectLighthouse/Types/Settings/PrivacySettings.cs index c381a66d..ac636c5d 100644 --- a/ProjectLighthouse/Types/Settings/PrivacySettings.cs +++ b/ProjectLighthouse/Types/Settings/PrivacySettings.cs @@ -1,15 +1,17 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Settings { - public class PrivacySettings { +namespace LBPUnion.ProjectLighthouse.Types.Settings +{ + public class PrivacySettings + { public string LevelVisibility { get; set; } public string ProfileVisibility { get; set; } - public string Serialize() { - return LbpSerializer.StringElement("privacySettings", - LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + - LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility) + public string Serialize() + => LbpSerializer.StringElement + ( + "privacySettings", + LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility) ); - } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index b4de8c54..daaa3e95 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -3,10 +3,12 @@ using System; using Kettu; using LBPUnion.ProjectLighthouse.Logging; -namespace LBPUnion.ProjectLighthouse.Types.Settings { - public static class ServerSettings { +namespace LBPUnion.ProjectLighthouse.Types.Settings +{ + public static class ServerSettings + { /// - /// The maximum amount of slots allowed on users' earth + /// The maximum amount of slots allowed on users' earth /// public const int EntitledSlots = 50; @@ -15,11 +17,11 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings { public const string ServerName = "ProjectLighthouse"; private static string? dbConnectionString; + public static string DbConnectionString { get { - if(dbConnectionString == null) { - return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? ""; - } + if (dbConnectionString == null) return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? ""; + return dbConnectionString; } set => dbConnectionString = value; @@ -27,10 +29,12 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings { public static bool DbConnected { get { - try { + try + { return new Database().Database.CanConnect(); } - catch(Exception e) { + catch(Exception e) + { Logger.Log(e.ToString(), LoggerLevelDatabase.Instance); return false; } diff --git a/ProjectLighthouse/Types/Token.cs b/ProjectLighthouse/Types/Token.cs index 9232de0c..979459f1 100644 --- a/ProjectLighthouse/Types/Token.cs +++ b/ProjectLighthouse/Types/Token.cs @@ -1,9 +1,13 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types { - public class Token { +namespace LBPUnion.ProjectLighthouse.Types +{ + public class Token + { // ReSharper disable once UnusedMember.Global - [Key] public int TokenId { get; set; } + [Key] + public int TokenId { get; set; } + public int UserId { get; set; } public string UserToken { get; set; } } diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index 4f06deed..8d16af5d 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -4,8 +4,13 @@ using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; -namespace LBPUnion.ProjectLighthouse.Types { - public class User { +namespace LBPUnion.ProjectLighthouse.Types +{ + public class User + { + +// [NotMapped] + public readonly ClientsConnected ClientsConnected = new(); public int UserId { get; set; } public string Username { get; set; } public string IconHash { get; set; } @@ -14,21 +19,22 @@ namespace LBPUnion.ProjectLighthouse.Types { public int HeartCount { get; set; } public string YayHash { get; set; } public string BooHash { get; set; } - + /// - /// A user-customizable biography shown on the profile card + /// A user-customizable biography shown on the profile card /// public string Biography { get; set; } + public int ReviewCount { get; set; } public int CommentCount { get; set; } public int PhotosByMeCount { get; set; } public int PhotosWithMeCount { get; set; } public bool CommentsEnabled { get; set; } - + public int LocationId { get; set; } /// - /// The location of the profile card on the user's earth + /// The location of the profile card on the user's earth /// [ForeignKey("LocationId")] public Location Location { get; set; } @@ -42,56 +48,9 @@ namespace LBPUnion.ProjectLighthouse.Types { public int StaffChallengeBronzeCount { get; set; } public string PlanetHash { get; set; } = ""; - -// [NotMapped] - public readonly ClientsConnected ClientsConnected = new(); - - #region Slots - /// - /// The number of used slots on the earth - /// - [NotMapped] - public int UsedSlots { - get { - using Database database = new(); - return database.Slots.Count(s => s.CreatorId == this.UserId); - } - } - - /// - /// The number of slots remaining on the earth - /// - public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots; - - private static readonly string[] slotTypes = { -// "lbp1", - "lbp2", - "lbp3", - "crossControl", - }; - - private string SerializeSlots() { - string slots = string.Empty; - - slots += LbpSerializer.StringElement("lbp1UsedSlots", this.UsedSlots); - slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots); - slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); - - foreach(string slotType in slotTypes) { - slots += LbpSerializer.StringElement(slotType + "UsedSlots", this.UsedSlots); - slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots); - // ReSharper disable once StringLiteralTypo - slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); - slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); - } - return slots; - - } - - #endregion Slots - - public string Serialize() { + public string Serialize() + { string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + LbpSerializer.StringElement("game", this.Game) + this.SerializeSlots() + @@ -117,8 +76,55 @@ namespace LBPUnion.ProjectLighthouse.Types { LbpSerializer.StringElement("planets", this.PlanetHash) + LbpSerializer.BlankElement("photos") + this.ClientsConnected.Serialize(); - + return LbpSerializer.TaggedStringElement("user", user, "type", "user"); } + + #region Slots + + /// + /// The number of used slots on the earth + /// + [NotMapped] + public int UsedSlots { + get { + using Database database = new(); + return database.Slots.Count(s => s.CreatorId == this.UserId); + } + } + + /// + /// The number of slots remaining on the earth + /// + public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots; + + private static readonly string[] slotTypes = + { +// "lbp1", + "lbp2", "lbp3", "crossControl", + }; + + private string SerializeSlots() + { + string slots = string.Empty; + + slots += LbpSerializer.StringElement("lbp1UsedSlots", this.UsedSlots); + slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots); + slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); + + foreach (string slotType in slotTypes) + { + slots += LbpSerializer.StringElement(slotType + "UsedSlots", this.UsedSlots); + slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots); + // ReSharper disable once StringLiteralTypo + slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); + slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); + } + return slots; + + } + + #endregion Slots + } } \ No newline at end of file diff --git a/README.md b/README.md index 479361fd..8867d5e9 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,80 @@ # Project Lighthouse + Project Lighthouse is an umbrella project for all work to investigate and develop private servers for LittleBigPlanet. This project is the main server component that LittleBigPlanet games connect to. ## WARNING! -This is beta software, and thus is not ready for public use yet. -We're not responsible if someone connects and hacks your entire machine and deletes all your files. + +This is beta software, and thus is not ready for public use yet. We're not responsible if someone connects and hacks +your entire machine and deletes all your files. That said, feel free to develop privately! ## Building + This will be written when we're out of beta. Consider this your barrier to entry ;). ## Running -Lighthouse requires a MySQL database at this time. -For Linux users running docker, one can be set up using the `docker-compose.yml` file in the root of the project folder. -Next, make sure the `LIGHTHOUSE_DB_CONNECTION_STRING` environment variable is set correctly. -By default, it is `server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse`. If you are running the database via -the above `docker-compose.yml` you shouldn't need to change this. For other development/especially production environments +Lighthouse requires a MySQL database at this time. For Linux users running docker, one can be set up using +the `docker-compose.yml` file in the root of the project folder. + +Next, make sure the `LIGHTHOUSE_DB_CONNECTION_STRING` environment variable is set correctly. By default, it +is `server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse`. If you are running the database via the +above `docker-compose.yml` you shouldn't need to change this. For other development/especially production environments you will need to change this. Once you've gotten MySQL running you can run Lighthouse. It will take care of the rest. ## Connecting -PS3 is difficult to set up, so I will be going over how to set up RPCS3 instead. A guide will be coming for PS3 closer to release. -You can also follow this guide if you want to learn how to modify your EBOOT. -*Note: This requires a modified copy of RPCS3. You can find a working patch [here](https://gist.github.com/jvyden/0d9619f7dd3dbc49f7583486bdacad75).* +PS3 is difficult to set up, so I will be going over how to set up RPCS3 instead. A guide will be coming for PS3 closer +to release. You can also follow this guide if you want to learn how to modify your EBOOT. -Start by getting a copy of LittleBigPlanet 2 installed. It can be digital (NPUA80662) or disc (BCUS98245). -I won't get into how because if you got this far you should already know what you're doing. For those that don't, the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it. +*Note: This requires a modified copy of RPCS3. You can find a working +patch [here](https://gist.github.com/jvyden/0d9619f7dd3dbc49f7583486bdacad75).* -Next, download [UnionPatcher](https://github.com/LBPUnion/UnionPatcher/). Binaries can be found by reading the README.md file. +Start by getting a copy of LittleBigPlanet 2 installed. It can be digital (NPUA80662) or disc (BCUS98245). I won't get +into how because if you got this far you should already know what you're doing. For those that don't, +the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it. -You should have everything you need now, so open up RPCS3 and go to Utilities -> Decrypt PS3 Binaries. Point this to `rpcs3/dev_hdd0/game/(title id)/USRDIR/EBOOT.BIN`. +Next, download [UnionPatcher](https://github.com/LBPUnion/UnionPatcher/). Binaries can be found by reading the README.md +file. -This should give you a file named `EBOOT.elf` in the same folder. Next, fire up UnionPatcher (making sure to select the correct project to start, e.g. on Mac launch `UnionPatcher.Gui.MacOS`.) +You should have everything you need now, so open up RPCS3 and go to Utilities -> Decrypt PS3 Binaries. Point this +to `rpcs3/dev_hdd0/game/(title id)/USRDIR/EBOOT.BIN`. -Now that you have your decrypted eboot, open UnionPatcher and select the `EBOOT.elf` you got earlier in the top box, enter `http://localhost:10060/LITTLEBIGPLANETPS3_XML` in the second, and the output filename in the third. -For this guide I'll use `EBOOTlocalhost.elf`. +This should give you a file named `EBOOT.elf` in the same folder. Next, fire up UnionPatcher (making sure to select the +correct project to start, e.g. on Mac launch `UnionPatcher.Gui.MacOS`.) + +Now that you have your decrypted eboot, open UnionPatcher and select the `EBOOT.elf` you got earlier in the top box, +enter `http://localhost:10060/LITTLEBIGPLANETPS3_XML` in the second, and the output filename in the third. For this +guide I'll use `EBOOTlocalhost.elf`. Now, copy the `EBOOTlocalhost.elf` file to where you got your `EBOOT.elf` file from, and you're now good to go. To launch the game with the patched EBOOT, open up RPCS3, go to File, Boot SELF/ELF, and open up `EBOOTlocalhost.elf`. -Assuming you are running the patched version of RPCS3, you patched the file correctly, the database is migrated, and Lighthouse is running, the game should now connect. +Assuming you are running the patched version of RPCS3, you patched the file correctly, the database is migrated, and +Lighthouse is running, the game should now connect. Finally, take a break. Chances are that took a while. ## Contributing Tips + ### Database + Some modifications may require updates to the database schema. You can automatically create a migration file by: 1. Making sure the tools are installed. You can do this by running `dotnet tool restore`. 2. Making sure `LIGHTHOUSE_DB_CONNECTION_STRING` is set correctly. See the `Running` section for more details. -3. Making your changes to the database. I won't cover this since if you're making database changes you should know what you're doing. +3. Making your changes to the database. I won't cover this since if you're making database changes you should know what + you're doing. 4. Running `dotnet ef migrations add --project ProjectLighthouse`. ## Compatibility across games and platforms -| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) | +| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) | |----------|------------------------|------------------------------------------------|--------------------| | LBP1 | Somewhat compatible | Incompatible, crashes on entering pod computer | N/A | | LBP2 | Compatible | Compatible with patched RPCS3 | N/A | diff --git a/docker-compose.yml b/docker-compose.yml index 973902f2..0f507cfe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,6 @@ services: - '3306' # Expose port to localhost:3306 volumes: - lighthouse-db:/var/lib/mysql - + volumes: lighthouse-db: \ No newline at end of file