diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cff64812..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
@@ -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
@@ -36,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:
@@ -52,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"
@@ -84,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 d9027f7f..e3966fe1 100644
--- a/ProjectLighthouse.Tests/LighthouseTest.cs
+++ b/ProjectLighthouse.Tests/LighthouseTest.cs
@@ -8,31 +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) {
- const char nullChar = (char)0x00;
- const char sepChar = (char)0x20;
+ public async Task AuthenticateResponse(int number = 0)
+ {
const string username = "unitTestUser";
- string stringContent = $"{nullChar}{sepChar}{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;
}
-
- 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());
@@ -43,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 1b66add2..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 5ac770ff..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 logged in", responseContent);
+ 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 e6ba1207..5c52f9f5 100644
--- a/ProjectLighthouse.Tests/Tests/MatchTests.cs
+++ b/ProjectLighthouse.Tests/Tests/MatchTests.cs
@@ -1,39 +1,59 @@
using System;
using System.Net.Http;
+using System.Text;
using System.Threading;
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 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.True(result.IsSuccessStatusCode);
semaphore.Release();
+ Assert.False(result.IsSuccessStatusCode);
+ }
+
+ [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);
+
+ semaphore.Release();
+ Assert.True(result.IsSuccessStatusCode);
}
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", 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();
-
+
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 2ffd35fb..8b093e41 100644
--- a/ProjectLighthouse.sln.DotSettings
+++ b/ProjectLighthouse.sln.DotSettings
@@ -1,8 +1,81 @@
+ 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
True
@@ -11,6 +84,7 @@
True
True
True
+ True
True
True
True
diff --git a/ProjectLighthouse/Controllers/ClientConfigurationController.cs b/ProjectLighthouse/Controllers/ClientConfigurationController.cs
index dc3d90a7..fc12c2e7 100644
--- a/ProjectLighthouse/Controllers/ClientConfigurationController.cs
+++ b/ProjectLighthouse/Controllers/ClientConfigurationController.cs
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Types.Settings;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers
@@ -13,24 +14,20 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[SuppressMessage("ReSharper", "StringLiteralTypo")]
public IActionResult NetworkSettings()
{
- var hostname = this.Request.Host;
- 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\n"
- + $"TelemetryServer {hostname}\nCDNHostName {hostname}");
+ HostString hostname = this.Request.Host;
+ 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\n" +
+ $"TelemetryServer {hostname}\nCDNHostName {hostname}"
+ );
}
[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")]
diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs
index 9f2967f1..5d673e5e 100644
--- a/ProjectLighthouse/Controllers/CommentController.cs
+++ b/ProjectLighthouse/Controllers/CommentController.cs
@@ -3,28 +3,34 @@ 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;
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)
.ToListAsync();
string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize());
@@ -32,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();
@@ -41,18 +48,36 @@ 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;
+ comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
+
this.database.Comments.Add(comment);
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
diff --git a/ProjectLighthouse/Controllers/DeveloperController.cs b/ProjectLighthouse/Controllers/DeveloperController.cs
index 223ad66a..0ec94875 100644
--- a/ProjectLighthouse/Controllers/DeveloperController.cs
+++ b/ProjectLighthouse/Controllers/DeveloperController.cs
@@ -1,5 +1,4 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers
{
@@ -8,9 +7,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public class DeveloperController : Controller
{
[HttpGet("/developer_videos")]
- public IActionResult DeveloperVideos()
- {
- return this.Ok();
- }
+ public IActionResult DeveloperVideos() => this.Ok();
}
}
\ No newline at end of file
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 491c1bfb..057771c2 100644
--- a/ProjectLighthouse/Controllers/LoginController.cs
+++ b/ProjectLighthouse/Controllers/LoginController.cs
@@ -24,25 +24,30 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
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, "");
- 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 e4ccb354..81fe97e0 100644
--- a/ProjectLighthouse/Controllers/MatchController.cs
+++ b/ProjectLighthouse/Controllers/MatchController.cs
@@ -40,10 +40,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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\":[]}]");
- }
+ 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();
@@ -52,7 +52,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
matchData = MatchHelper.Deserialize(bodyString);
}
- catch (Exception e)
+ catch(Exception e)
{
Logger.Log("Exception while parsing MatchData: " + e);
Logger.Log("Data: " + bodyString);
@@ -66,8 +66,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
#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
@@ -88,16 +87,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/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs
index 41c3c77f..193443c5 100644
--- a/ProjectLighthouse/Controllers/MessageController.cs
+++ b/ProjectLighthouse/Controllers/MessageController.cs
@@ -1,5 +1,8 @@
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;
@@ -22,34 +25,36 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
User user = await this.database.UserFromRequest(this.Request);
return user == null
- ? this.Forbid()
- : 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.");
+ ? 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"
+ );
}
[HttpGet("announce")]
- public async Task Announce()
- {
- User user = await this.database.UserFromRequest(this.Request);
- return user == null
- ? this.Forbid()
- : 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.");
- }
+ 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.
+ /// 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/Controllers/NewsController.cs b/ProjectLighthouse/Controllers/NewsController.cs
index a3f118a3..ee0342d2 100644
--- a/ProjectLighthouse/Controllers/NewsController.cs
+++ b/ProjectLighthouse/Controllers/NewsController.cs
@@ -1,4 +1,3 @@
-using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.News;
using Microsoft.AspNetCore.Mvc;
@@ -13,20 +12,24 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet]
public IActionResult Get()
{
- string newsEntry = LbpSerializer.StringElement("item", new NewsEntry
- {
- Category = "no_category",
- Summary = "test summary",
- Image = new NewsImage
+ string newsEntry = LbpSerializer.StringElement
+ (
+ "item",
+ new NewsEntry
{
- Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe",
- Alignment = "right",
- },
- Id = 1,
- Title = "Test Title",
- Text = "Test Text",
- Date = 1348755214000,
- }.Serialize());
+ 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 8136e10a..4efb4176 100644
--- a/ProjectLighthouse/Controllers/PublishController.cs
+++ b/ProjectLighthouse/Controllers/PublishController.cs
@@ -10,64 +10,102 @@ 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 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() {
- Slot slot = await this.GetSlotFromBody();
- if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
+ public async Task StartPublish()
+ {
+ User user = await this.database.UserFromRequest(this.Request);
+ if (user == null) return this.StatusCode(403, "");
- string resources = slot.Resources
- .Where(hash => !FileHelper.ResourceExists(hash))
- .Aggregate("", (current, hash) =>
- current + LbpSerializer.StringElement("resource", hash));
+ 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) => 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();
+
+ oldSlot.Location.X = slot.Location.X;
+ oldSlot.Location.Y = slot.Location.Y;
+
+ 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);
+ await this.database.SaveChangesAsync();
+ return this.Ok(oldSlot.Serialize());
+ }
+
//TODO: parse location in body
- Location l = new() {
- X = 0,
- Y = 0,
+ Location l = new()
+ {
+ X = slot.Location.X,
+ Y = slot.Location.Y,
};
this.database.Locations.Add(l);
await this.database.SaveChangesAsync();
slot.LocationId = l.Id;
slot.CreatorId = user.UserId;
+ slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
+ slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
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);
@@ -76,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 c7726935..dcb1a4d8 100644
--- a/ProjectLighthouse/Controllers/ResourcesController.cs
+++ b/ProjectLighthouse/Controllers/ResourcesController.cs
@@ -18,10 +18,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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")]
@@ -30,14 +27,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(ResourceList));
- ResourceList resourceList = (ResourceList) serializer.Deserialize(new StringReader(bodyString));
+ ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString));
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));
}
@@ -47,10 +43,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
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();
}
@@ -68,7 +61,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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();
diff --git a/ProjectLighthouse/Controllers/SearchController.cs b/ProjectLighthouse/Controllers/SearchController.cs
index adaced48..573d3480 100644
--- a/ProjectLighthouse/Controllers/SearchController.cs
+++ b/ProjectLighthouse/Controllers/SearchController.cs
@@ -6,18 +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) {
+ public async Task SearchSlots([FromQuery] string query)
+ {
+ if (query == null) return this.BadRequest();
+
query = query.ToLower();
string[] keywords = query.Split(" ");
@@ -26,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 d5c0f818..ede1ae56 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;
@@ -5,35 +6,54 @@ 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));
}
-
- 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);
+
+ [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);
+
+ 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)
+ .Include(s => s.Location)
+ .OrderByDescending(s => s.FirstUploaded)
+ .Skip(pageStart - 1)
+ .Take(Math.Min(pageSize, 30));
+ string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
+
+ return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
+ }
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/StatisticsController.cs b/ProjectLighthouse/Controllers/StatisticsController.cs
new file mode 100644
index 00000000..0bacbd4a
--- /dev/null
+++ b/ProjectLighthouse/Controllers/StatisticsController.cs
@@ -0,0 +1,43 @@
+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
diff --git a/ProjectLighthouse/Controllers/StoreController.cs b/ProjectLighthouse/Controllers/StoreController.cs
index 1fe7862b..7c68be02 100644
--- a/ProjectLighthouse/Controllers/StoreController.cs
+++ b/ProjectLighthouse/Controllers/StoreController.cs
@@ -1,5 +1,4 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers
{
@@ -9,9 +8,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public class StoreController : Controller
{
[HttpGet("promotions")]
- public IActionResult Promotions()
- {
- return Ok();
- }
+ public IActionResult Promotions() => this.Ok();
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs
index 666f7d87..265d9126 100644
--- a/ProjectLighthouse/Controllers/UserController.cs
+++ b/ProjectLighthouse/Controllers/UserController.cs
@@ -25,25 +25,18 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("user/{username}")]
public async Task GetUser(string username)
{
- User user = await this.database.Users
- .Include(u => u.Location)
- .FirstOrDefaultAsync(u => u.Username == username);
+ User user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return this.NotFound();
+
return this.Ok(user.Serialize());
}
- [HttpGet("user/{username}/playlists")]
- public IActionResult GetUserPlaylists(string username)
- {
- return this.Ok();
- }
+ [HttpGet("users")]
+ public async Task GetUserAlt([FromQuery] string u) => await this.GetUser(u);
-// [HttpPost("user/{username}")]
-// public async Task CreateUser(string username) {
-// await new Database().CreateUser(username);
-// return await GetUser(username);
-// }
+ [HttpGet("user/{username}/playlists")]
+ public IActionResult GetUserPlaylists(string username) => this.Ok();
[HttpPost("updateUser")]
public async Task UpdateUser()
@@ -77,11 +70,8 @@ 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))
{
- List
- path = new(); // you can think of this as a file path in the XML, like -> ->
- while (await reader.ReadAsync())
- {
- // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
+ 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)
{
case XmlNodeType.Element:
@@ -97,21 +87,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
case "location":
{
- locationChanged =
- true; // if we're here then we're probably about to change the 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());
- }
-
+ 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":
@@ -131,14 +112,12 @@ 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
{
- Location l = await this.database.Locations.Where(l => l.Id == user.LocationId)
- .FirstOrDefaultAsync(); // find the location in the database again
+ 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;
@@ -147,8 +126,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
// 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 2bb4714c..c80e6c50 100644
--- a/ProjectLighthouse/Database.cs
+++ b/ProjectLighthouse/Database.cs
@@ -23,16 +23,13 @@ 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)
{
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
@@ -51,12 +48,11 @@ namespace LBPUnion.ProjectLighthouse
return user;
}
-#nullable enable
+ #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);
+ User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) ?? await this.CreateUser(loginData.Username);
Token token = new()
{
@@ -74,20 +70,16 @@ namespace LBPUnion.ProjectLighthouse
{
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);
+
+ 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;
- }
+ if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
return await this.UserFromAuthToken(mmAuth);
}
-#nullable disable
+ #nullable disable
}
}
\ No newline at end of file
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
new file mode 100644
index 00000000..cfaa3940
--- /dev/null
+++ b/ProjectLighthouse/Helpers/EulaHelper.cs
@@ -0,0 +1,24 @@
+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
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 8f9e0f44..fbb23117 100644
--- a/ProjectLighthouse/Helpers/FileHelper.cs
+++ b/ProjectLighthouse/Helpers/FileHelper.cs
@@ -4,17 +4,22 @@ 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,
LbpFileType.Texture => true,
LbpFileType.Script => false,
@@ -29,16 +34,19 @@ 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,
"VOP" => LbpFileType.Voice,
@@ -50,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 865a04f3..ffda6e17 100644
--- a/ProjectLighthouse/Helpers/HashHelper.cs
+++ b/ProjectLighthouse/Helpers/HashHelper.cs
@@ -6,32 +6,22 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
-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);
@@ -39,34 +29,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));
}
-
- public static async Task ComputeDigest(string path, string authCookie, Stream body,
- string digestKey)
+
+ public static async Task ComputeDigest(string path, string authCookie, Stream body, string digestKey)
{
- MemoryStream memoryStream = new MemoryStream();
+ MemoryStream memoryStream = new();
byte[] pathBytes = Encoding.UTF8.GetBytes(path);
- byte[] cookieBytes = string.IsNullOrEmpty(authCookie)
- ? Array.Empty()
- : Encoding.UTF8.GetBytes(authCookie);
+ byte[] cookieBytes = string.IsNullOrEmpty(authCookie) ? Array.Empty() : Encoding.UTF8.GetBytes(authCookie);
byte[] keyBytes = Encoding.UTF8.GetBytes(digestKey);
-
+
await body.CopyToAsync(memoryStream);
byte[] bodyBytes = memoryStream.ToArray();
using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
sha1.AppendData(bodyBytes);
- if (cookieBytes.Length > 0)
- sha1.AppendData(cookieBytes);
+ if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes);
sha1.AppendData(pathBytes);
sha1.AppendData(keyBytes);
@@ -75,5 +62,22 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
return digestString;
}
+
+ #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/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs
new file mode 100644
index 00000000..77d8757d
--- /dev/null
+++ b/ProjectLighthouse/Helpers/TimeHelper.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace LBPUnion.ProjectLighthouse.Helpers
+{
+ public static class TimeHelper
+ {
+ public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds();
+ public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds();
+ }
+}
+
+// 1397109686193
+// 1635389749454
\ No newline at end of file
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 944fafa5..b20ce5a7 100644
--- a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs
+++ b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs
@@ -3,32 +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) {
- 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),
- };
-
+ 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 82863a72..1075dd0f 100644
--- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs
+++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs
@@ -3,16 +3,23 @@ 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)
{
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..b033f58b 100644
--- a/ProjectLighthouse/Logging/LoggerLevels.cs
+++ b/ProjectLighthouse/Logging/LoggerLevels.cs
@@ -1,55 +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";
}
- #region ASP.NET
- public class LoggerLevelAspNetTrace : LoggerLevel {
- public override string Name => "ASP.NET: Trace";
- public static readonly LoggerLevelAspNetTrace Instance = new();
+ public class LoggerLevelFilter : LoggerLevel
+ {
+ public static readonly LoggerLevelFilter Instance = new();
+ public override string Name => "Filter";
}
-
- public class LoggerLevelAspNetDebug : LoggerLevel {
- public override string Name => "ASP.NET: Debug";
- public static readonly LoggerLevelAspNetDebug Instance = new();
+
+ public class LoggerLevelAspNet : LoggerLevel
+ {
+
+ public LoggerLevelAspNet(LogLevel level)
+ {
+ this.Channel = level.ToString();
+ }
+ public override string Name => "AspNet";
}
-
- 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/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/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/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