Re-do formatting

This commit is contained in:
jvyden 2021-10-31 16:46:56 -04:00
parent 95a1fd35a5
commit 173addfd03
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
74 changed files with 1286 additions and 905 deletions

View file

@ -1,4 +1,4 @@
on: [push] on: [ push ]
name: Continuous Integration name: Continuous Integration
# Inspired by osu! lazer's CI # Inspired by osu! lazer's CI

View file

@ -1,11 +1,12 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Development Database" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker"> <configuration default="false" name="Development Database" type="docker-deploy" factoryName="docker-compose.yml"
server-name="Docker">
<deployment type="docker-compose.yml"> <deployment type="docker-compose.yml">
<settings> <settings>
<option name="envFilePath" value="" /> <option name="envFilePath" value=""/>
<option name="sourceFilePath" value="docker-compose.yml" /> <option name="sourceFilePath" value="docker-compose.yml"/>
</settings> </settings>
</deployment> </deployment>
<method v="2" /> <method v="2"/>
</configuration> </configuration>
</component> </component>

View file

@ -2,12 +2,19 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public sealed class DatabaseFact : FactAttribute { {
public DatabaseFact() { public sealed class DatabaseFact : FactAttribute
{
public DatabaseFact()
{
ServerSettings.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; ServerSettings.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
if(!ServerSettings.DbConnected) this.Skip = "Database not available"; if (!ServerSettings.DbConnected)
else { {
this.Skip = "Database not available";
}
else
{
using Database database = new(); using Database database = new();
database.Database.Migrate(); database.Database.Migrate();
} }

View file

@ -8,20 +8,23 @@ using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
{
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public class LighthouseTest { public class LighthouseTest
public readonly TestServer Server; {
public readonly HttpClient Client; public readonly HttpClient Client;
public readonly TestServer Server;
public LighthouseTest() { public LighthouseTest()
this.Server = new TestServer(new WebHostBuilder() {
.UseStartup<Startup>()); this.Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
this.Client = this.Server.CreateClient(); this.Client = this.Server.CreateClient();
} }
public async Task<HttpResponseMessage> AuthenticateResponse(int number = 0) { public async Task<HttpResponseMessage> AuthenticateResponse(int number = 0)
{
const string username = "unitTestUser"; const string username = "unitTestUser";
string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}"; string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}";
@ -30,7 +33,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
return response; return response;
} }
public async Task<LoginResult> Authenticate(int number = 0) { public async Task<LoginResult> Authenticate(int number = 0)
{
HttpResponseMessage response = await this.AuthenticateResponse(number); HttpResponseMessage response = await this.AuthenticateResponse(number);
string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync()); string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync());
@ -41,30 +45,31 @@ namespace LBPUnion.ProjectLighthouse.Tests {
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get); public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get);
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method) { public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method)
using var requestMessage = new HttpRequestMessage(method, endpoint); {
using HttpRequestMessage? requestMessage = new(method, endpoint);
requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Headers.Add("Cookie", mmAuth);
return this.Client.SendAsync(requestMessage); return this.Client.SendAsync(requestMessage);
} }
public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath) { public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath)
return await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); => await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath)));
}
public async Task<HttpResponseMessage> UploadDataRequest(string endpoint, byte[] data) { public async Task<HttpResponseMessage> UploadDataRequest(string endpoint, byte[] data)
return await this.Client.PostAsync(endpoint, new ByteArrayContent(data)); => await this.Client.PostAsync(endpoint, new ByteArrayContent(data));
}
public async Task<HttpResponseMessage> AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth) { public async Task<HttpResponseMessage> AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth)
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, endpoint); {
using HttpRequestMessage? requestMessage = new(HttpMethod.Post, endpoint);
requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Headers.Add("Cookie", mmAuth);
requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath)); requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath));
return await this.Client.SendAsync(requestMessage); return await this.Client.SendAsync(requestMessage);
} }
public async Task<HttpResponseMessage> AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth) { public async Task<HttpResponseMessage> AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth)
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, endpoint); {
using HttpRequestMessage? requestMessage = new(HttpMethod.Post, endpoint);
requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Headers.Add("Cookie", mmAuth);
requestMessage.Content = new ByteArrayContent(data); requestMessage.Content = new ByteArrayContent(data);
return await this.Client.SendAsync(requestMessage); return await this.Client.SendAsync(requestMessage);

View file

@ -13,9 +13,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.11" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.11"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@ -27,7 +27,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj" /> <ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
<Content Include="ExampleFiles\**"> <Content Include="ExampleFiles\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View file

@ -5,10 +5,13 @@ using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings; using LBPUnion.ProjectLighthouse.Types.Settings;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class AuthenticationTests : LighthouseTest { {
public class AuthenticationTests : LighthouseTest
{
[Fact] [Fact]
public async Task ShouldReturnErrorOnNoPostData() { public async Task ShouldReturnErrorOnNoPostData()
{
HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!); HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!);
Assert.False(response.IsSuccessStatusCode); Assert.False(response.IsSuccessStatusCode);
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
@ -19,7 +22,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
} }
[DatabaseFact] [DatabaseFact]
public async Task ShouldReturnWithValidData() { public async Task ShouldReturnWithValidData()
{
HttpResponseMessage response = await this.AuthenticateResponse(); HttpResponseMessage response = await this.AuthenticateResponse();
Assert.True(response.IsSuccessStatusCode); Assert.True(response.IsSuccessStatusCode);
string responseContent = await response.Content.ReadAsStringAsync(); string responseContent = await response.Content.ReadAsStringAsync();
@ -28,7 +32,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
} }
[DatabaseFact] [DatabaseFact]
public async Task CanSerializeBack() { public async Task CanSerializeBack()
{
LoginResult loginResult = await this.Authenticate(); LoginResult loginResult = await this.Authenticate();
Assert.NotNull(loginResult); Assert.NotNull(loginResult);
@ -40,7 +45,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
} }
[DatabaseFact] [DatabaseFact]
public async Task CanUseToken() { public async Task CanUseToken()
{
LoginResult loginResult = await this.Authenticate(); LoginResult loginResult = await this.Authenticate();
HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/eula", loginResult.AuthTicket); HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/eula", loginResult.AuthTicket);
@ -51,7 +57,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
} }
[DatabaseFact] [DatabaseFact]
public async Task ShouldReturnForbiddenWhenNotAuthenticated() { public async Task ShouldReturnForbiddenWhenNotAuthenticated()
{
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/eula"); HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/eula");
Assert.False(response.IsSuccessStatusCode); Assert.False(response.IsSuccessStatusCode);
Assert.True(response.StatusCode == HttpStatusCode.Forbidden); Assert.True(response.StatusCode == HttpStatusCode.Forbidden);

View file

@ -1,10 +1,13 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class DatabaseTests : LighthouseTest { {
public class DatabaseTests : LighthouseTest
{
[DatabaseFact] [DatabaseFact]
public async Task CanCreateUserTwice() { public async Task CanCreateUserTwice()
{
await using Database database = new(); await using Database database = new();
int rand = new Random().Next(); int rand = new Random().Next();

View file

@ -4,47 +4,56 @@ using System.Text;
using LBPUnion.ProjectLighthouse.Types.Files; using LBPUnion.ProjectLighthouse.Types.Files;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class FileTypeTests { {
public class FileTypeTests
{
[Fact] [Fact]
public void ShouldRecognizeLevel() { public void ShouldRecognizeLevel()
{
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl")); LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl"));
Assert.True(file.FileType == LbpFileType.Level); Assert.True(file.FileType == LbpFileType.Level);
} }
[Fact] [Fact]
public void ShouldRecognizeScript() { public void ShouldRecognizeScript()
{
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff")); LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff"));
Assert.True(file.FileType == LbpFileType.Script); Assert.True(file.FileType == LbpFileType.Script);
} }
[Fact] [Fact]
public void ShouldRecognizeTexture() { public void ShouldRecognizeTexture()
{
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex")); LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex"));
Assert.True(file.FileType == LbpFileType.Texture); Assert.True(file.FileType == LbpFileType.Texture);
} }
[Fact] [Fact]
public void ShouldRecognizeFileArchive() { public void ShouldRecognizeFileArchive()
{
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
Assert.True(file.FileType == LbpFileType.FileArchive); Assert.True(file.FileType == LbpFileType.FileArchive);
} }
[Fact] [Fact]
public void ShouldNotRecognizeFileArchiveAsScript() { public void ShouldNotRecognizeFileArchiveAsScript()
{
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc")); LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
Assert.False(file.FileType == LbpFileType.Script); Assert.False(file.FileType == LbpFileType.Script);
Assert.True(file.FileType == LbpFileType.FileArchive); Assert.True(file.FileType == LbpFileType.FileArchive);
} }
[Fact] [Fact]
public void ShouldRecognizeNothingAsUnknown() { public void ShouldRecognizeNothingAsUnknown()
{
LbpFile file = new(Array.Empty<byte>()); LbpFile file = new(Array.Empty<byte>());
Assert.True(file.FileType == LbpFileType.Unknown); Assert.True(file.FileType == LbpFileType.Unknown);
} }
[Fact] [Fact]
public void ShouldRecognizeGarbageAsUnknown() { public void ShouldRecognizeGarbageAsUnknown()
{
LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900")); LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900"));
Assert.True(file.FileType == LbpFileType.Unknown); Assert.True(file.FileType == LbpFileType.Unknown);
} }

View file

@ -6,12 +6,15 @@ using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class MatchTests : LighthouseTest { {
public class MatchTests : LighthouseTest
{
private static readonly SemaphoreSlim semaphore = new(1, 1); private static readonly SemaphoreSlim semaphore = new(1, 1);
[DatabaseFact] [DatabaseFact]
public async Task ShouldRejectEmptyData() { public async Task ShouldRejectEmptyData()
{
LoginResult loginResult = await this.Authenticate(); LoginResult loginResult = await this.Authenticate();
await semaphore.WaitAsync(); await semaphore.WaitAsync();
@ -22,16 +25,13 @@ namespace LBPUnion.ProjectLighthouse.Tests {
} }
[DatabaseFact] [DatabaseFact]
public async Task ShouldReturnOk() { public async Task ShouldReturnOk()
{
LoginResult loginResult = await this.Authenticate(); LoginResult loginResult = await this.Authenticate();
await semaphore.WaitAsync(); await semaphore.WaitAsync();
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
"LITTLEBIGPLANETPS3_XML/match", ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"),
loginResult.AuthTicket
);
semaphore.Release(); semaphore.Release();
Assert.True(result.IsSuccessStatusCode); Assert.True(result.IsSuccessStatusCode);
@ -39,18 +39,16 @@ namespace LBPUnion.ProjectLighthouse.Tests {
public async Task<int> GetPlayerCount() => Convert.ToInt32(await this.Client.GetStringAsync("LITTLEBIGPLANETPS3_XML/totalPlayerCount")); public async Task<int> GetPlayerCount() => Convert.ToInt32(await this.Client.GetStringAsync("LITTLEBIGPLANETPS3_XML/totalPlayerCount"));
[DatabaseFact] [DatabaseFact]
public async Task ShouldIncrementPlayerCount() { public async Task ShouldIncrementPlayerCount()
{
LoginResult loginResult = await this.Authenticate(new Random().Next()); LoginResult loginResult = await this.Authenticate(new Random().Next());
await semaphore.WaitAsync(); await semaphore.WaitAsync();
int oldPlayerCount = await this.GetPlayerCount(); int oldPlayerCount = await this.GetPlayerCount();
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest( HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
"LITTLEBIGPLANETPS3_XML/match", ("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"),
loginResult.AuthTicket
);
Assert.True(result.IsSuccessStatusCode); Assert.True(result.IsSuccessStatusCode);

View file

@ -2,30 +2,42 @@ using System.Collections.Generic;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class SerializerTests : LighthouseTest { {
public class SerializerTests : LighthouseTest
{
[Fact] [Fact]
public void BlankElementWorks() { public void BlankElementWorks()
{
Assert.Equal("<test></test>", LbpSerializer.BlankElement("test")); Assert.Equal("<test></test>", LbpSerializer.BlankElement("test"));
} }
[Fact] [Fact]
public void StringElementWorks() { public void StringElementWorks()
{
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement("test", "asd")); Assert.Equal("<test>asd</test>", LbpSerializer.StringElement("test", "asd"));
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement(new KeyValuePair<string, object>("test", "asd"))); Assert.Equal("<test>asd</test>", LbpSerializer.StringElement(new KeyValuePair<string, object>("test", "asd")));
} }
[Fact] [Fact]
public void TaggedStringElementWorks() { public void TaggedStringElementWorks()
{
Assert.Equal("<test foo=\"bar\">asd</test>", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar")); Assert.Equal("<test foo=\"bar\">asd</test>", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar"));
Assert.Equal("<test foo=\"bar\">asd</test>", LbpSerializer.TaggedStringElement(new KeyValuePair<string, object>("test", "asd"), Assert.Equal
new KeyValuePair<string, object>("foo", "bar"))); (
"<test foo=\"bar\">asd</test>",
LbpSerializer.TaggedStringElement(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
);
} }
[Fact] [Fact]
public void ElementsWorks() { public void ElementsWorks()
Assert.Equal("<test>asd</test><foo>bar</foo>", LbpSerializer.Elements(new KeyValuePair<string, object>("test", "asd"), {
new KeyValuePair<string, object>("foo", "bar"))); Assert.Equal
(
"<test>asd</test><foo>bar</foo>",
LbpSerializer.Elements(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
);
} }
} }
} }

View file

@ -4,10 +4,13 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Profiles;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class SlotTests : LighthouseTest { {
public class SlotTests : LighthouseTest
{
[DatabaseFact] [DatabaseFact]
public async Task ShouldOnlyShowUsersLevels() { public async Task ShouldOnlyShowUsersLevels()
{
await using Database database = new(); await using Database database = new();
User userA = await database.CreateUser("unitTestUser0"); User userA = await database.CreateUser("unitTestUser0");
@ -17,7 +20,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
database.Locations.Add(l); database.Locations.Add(l);
await database.SaveChangesAsync(); await database.SaveChangesAsync();
Slot slotA = new() { Slot slotA = new()
{
Creator = userA, Creator = userA,
Name = "slotA", Name = "slotA",
Location = l, Location = l,
@ -25,7 +29,8 @@ namespace LBPUnion.ProjectLighthouse.Tests {
ResourceCollection = "", ResourceCollection = "",
}; };
Slot slotB = new() { Slot slotB = new()
{
Creator = userB, Creator = userB,
Name = "slotB", Name = "slotB",
Location = l, Location = l,

View file

@ -4,39 +4,47 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
namespace LBPUnion.ProjectLighthouse.Tests { namespace LBPUnion.ProjectLighthouse.Tests
public class UploadTests : LighthouseTest { {
public UploadTests() { public class UploadTests : LighthouseTest
{
public UploadTests()
{
string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r"); string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r");
if(Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true); if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true);
} }
[Fact] [Fact]
public async Task ShouldNotAcceptScript() { public async Task ShouldNotAcceptScript()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff"); HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff");
Assert.False(response.IsSuccessStatusCode); Assert.False(response.IsSuccessStatusCode);
} }
[Fact] [Fact]
public async Task ShouldNotAcceptFarc() { public async Task ShouldNotAcceptFarc()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc"); HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc");
Assert.False(response.IsSuccessStatusCode); Assert.False(response.IsSuccessStatusCode);
} }
[Fact] [Fact]
public async Task ShouldNotAcceptGarbage() { public async Task ShouldNotAcceptGarbage()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin"); HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin");
Assert.False(response.IsSuccessStatusCode); Assert.False(response.IsSuccessStatusCode);
} }
[Fact] [Fact]
public async Task ShouldAcceptTexture() { public async Task ShouldAcceptTexture()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex"); HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex");
Assert.True(response.IsSuccessStatusCode); Assert.True(response.IsSuccessStatusCode);
} }
[Fact] [Fact]
public async Task ShouldAcceptLevel() { public async Task ShouldAcceptLevel()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl"); HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl");
Assert.True(response.IsSuccessStatusCode); Assert.True(response.IsSuccessStatusCode);
} }

View file

@ -1,8 +1,80 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeConstructorOrDestructorBody/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeLocalFunctionBody/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeMethodOrOperatorBody/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeObjectCreationWhenTypeNotEvident/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTrailingCommaInMultilineLists/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTrailingCommaInSinglelineLists/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/LOCAL_FUNCTION_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">Field, Property, Event, Method</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/TRAILING_COMMA_IN_MULTILINE_LISTS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/TRAILING_COMMA_IN_SINGLELINE_LISTS/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_INSIDE_NAMESPACE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_PREPROCESSOR_IF/@EntryValue">USUAL_INDENT</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_PREPROCESSOR_OTHER/@EntryValue">USUAL_INDENT</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INVOCABLE_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_DECLARATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EXPR_MEMBER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_PROPERTY_PATTERNS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_SWITCH_EXPRESSION_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">False</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_FORMAL_PARAMETERS_ON_LINE/@EntryValue">5</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_INITIALIZER_ELEMENTS_ON_LINE/@EntryValue">1</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/NESTED_TERNARY_STYLE/@EntryValue">EXPANDED</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OTHER_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_CASE_STATEMENT_ON_SAME_LINE/@EntryValue">IF_OWNER_IS_SINGLE_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_PROPERTY_PATTERN_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_ARROW_OP/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_FOREACH_PARENTHESES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_IF_PARENTHESES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_SWITCH_PARENTHESES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_USING_PARENTHESES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_WHILE_PARENTHESES/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/TYPE_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_FIRST_TYPE_PARAMETER_CONSTRAINT/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_LINQ_EXPRESSION/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_BINARY_EXPRESSIONS/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_OBJECT_AND_COLLECTION_INITIALIZER_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PROPERTY_PATTERN/@EntryValue">CHOP_ALWAYS</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MM/@EntryIndexedValue">MM</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MM/@EntryIndexedValue">MM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Braaains/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Braaains/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=brun/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=brun/@EntryIndexedValue">True</s:Boolean>

View file

@ -2,32 +2,34 @@ using System.Diagnostics.CodeAnalysis;
using LBPUnion.ProjectLighthouse.Types.Settings; using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/plain")] [Produces("text/plain")]
public class ClientConfigurationController : ControllerBase { public class ClientConfigurationController : ControllerBase
{
[HttpGet("network_settings.nws")] [HttpGet("network_settings.nws")]
[SuppressMessage("ReSharper", "StringLiteralTypo")] [SuppressMessage("ReSharper", "StringLiteralTypo")]
public IActionResult NetworkSettings() { public IActionResult NetworkSettings()
return this.Ok("ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false"); => this.Ok
} (
"ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels true\nAllowModeratedPoppetItems true\nShowLevelBoos true\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 120.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 30.0\nTIMEOUT_DIVE_IN_TOTAL 1000000.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nCDNHostName localhost\nTelemetryServer localhost\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false"
);
[HttpGet("t_conf")] [HttpGet("t_conf")]
[Produces("text/json")] [Produces("text/json")]
public IActionResult Conf() { public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]");
return this.Ok("[{\"StatusCode\":200}]");
}
[HttpGet("farc_hashes")] [HttpGet("farc_hashes")]
public IActionResult FarcHashes() { public IActionResult FarcHashes() => this.Ok();
return this.Ok();
}
[HttpGet("privacySettings")] [HttpGet("privacySettings")]
[Produces("text/xml")] [Produces("text/xml")]
public IActionResult PrivacySettings() { public IActionResult PrivacySettings()
PrivacySettings ps = new() { {
PrivacySettings ps = new()
{
LevelVisibility = "all", LevelVisibility = "all",
ProfileVisibility = "all", ProfileVisibility = "all",
}; };

View file

@ -10,20 +10,24 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class CommentController : ControllerBase { public class CommentController : ControllerBase
{
private readonly Database database; private readonly Database database;
public CommentController(Database database) { public CommentController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("userComments/{username}")] [HttpGet("userComments/{username}")]
public async Task<IActionResult> GetComments(string username) { public async Task<IActionResult> GetComments(string username)
List<Comment> comments = await this.database.Comments {
.Include(c => c.Target) List<Comment> comments = await this.database.Comments.Include
(c => c.Target)
.Include(c => c.Poster) .Include(c => c.Poster)
.Where(c => c.Target.Username == username) .Where(c => c.Target.Username == username)
.OrderByDescending(c => c.Timestamp) .OrderByDescending(c => c.Timestamp)
@ -34,7 +38,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("postUserComment/{username}")] [HttpPost("postUserComment/{username}")]
public async Task<IActionResult> PostComment(string username) { public async Task<IActionResult> PostComment(string username)
{
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
@ -43,11 +48,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
User poster = await this.database.UserFromRequest(this.Request); 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); 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.PosterUserId = poster.UserId;
comment.TargetUserId = target.UserId; comment.TargetUserId = target.UserId;
@ -60,15 +65,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("deleteUserComment/{username}")] [HttpPost("deleteUserComment/{username}")]
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username) { public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username)
{
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
Comment comment = await this.database.Comments Comment comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
.FirstOrDefaultAsync(c => c.CommentId == commentId);
if(comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, "");
return this.StatusCode(403, "");
this.database.Comments.Remove(comment); this.database.Comments.Remove(comment);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();

View file

@ -1,13 +1,13 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/enterLevel")] [Route("LITTLEBIGPLANETPS3_XML/enterLevel")]
// [Produces("text/plain")] // [Produces("text/plain")]
public class EnterLevelController : ControllerBase { public class EnterLevelController : ControllerBase
{
[HttpGet("enterLevel/{id}")] [HttpGet("enterLevel/{id}")]
public IActionResult EnterLevel(string id) { public IActionResult EnterLevel(string id) => this.Ok();
return this.Ok();
}
} }
} }

View file

@ -2,17 +2,21 @@ using System;
using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/tags")] [Route("LITTLEBIGPLANETPS3_XML/tags")]
[Produces("text/plain")] [Produces("text/plain")]
public class LevelTagsController : ControllerBase { public class LevelTagsController : ControllerBase
{
[HttpGet] [HttpGet]
public IActionResult Get() { public IActionResult Get()
{
string[] tags = Enum.GetNames(typeof(LevelTags)); string[] tags = Enum.GetNames(typeof(LevelTags));
int i = 0; int i = 0;
foreach(string tag in tags) { foreach (string tag in tags)
{
tags[i] = $"TAG_{tag.Replace("_", "-")}"; tags[i] = $"TAG_{tag.Replace("_", "-")}";
i++; i++;
} }

View file

@ -8,23 +8,28 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class ListController : ControllerBase { public class ListController : ControllerBase
{
private readonly Database database; private readonly Database database;
public ListController(Database database) { public ListController(Database database)
{
this.database = database; this.database = database;
} }
#region Levels #region Levels
#region Level Queue (lolcatftw) #region Level Queue (lolcatftw)
[HttpGet("slots/lolcatftw/{username}")] [HttpGet("slots/lolcatftw/{username}")]
public IActionResult GetLevelQueue(string username) { public IActionResult GetLevelQueue(string username)
IEnumerable<QueuedLevel> queuedLevels = new Database().QueuedLevels {
.Include(q => q.User) IEnumerable<QueuedLevel> queuedLevels = new Database().QueuedLevels.Include
(q => q.User)
.Include(q => q.Slot) .Include(q => q.Slot)
.Include(q => q.Slot.Location) .Include(q => q.Slot.Location)
.Where(q => q.User.Username == username) .Where(q => q.User.Username == username)
@ -36,17 +41,22 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("lolcatftw/add/user/{id:int}")] [HttpPost("lolcatftw/add/user/{id:int}")]
public async Task<IActionResult> AddQueuedLevel(int id) { public async Task<IActionResult> AddQueuedLevel(int id)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); 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 { this.database.QueuedLevels.Add
(
new QueuedLevel
{
SlotId = id, SlotId = id,
UserId = user.UserId, UserId = user.UserId,
}); }
);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
@ -54,12 +64,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("lolcatftw/remove/user/{id:int}")] [HttpPost("lolcatftw/remove/user/{id:int}")]
public async Task<IActionResult> RemoveQueuedLevel(int id) { public async Task<IActionResult> RemoveQueuedLevel(int id)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); 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(); await this.database.SaveChangesAsync();
@ -71,9 +82,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
#region Hearted Levels #region Hearted Levels
[HttpGet("favouriteSlots/{username}")] [HttpGet("favouriteSlots/{username}")]
public IActionResult GetFavouriteSlots(string username) { public IActionResult GetFavouriteSlots(string username)
IEnumerable<HeartedLevel> heartedLevels = new Database().HeartedLevels {
.Include(q => q.User) IEnumerable<HeartedLevel> heartedLevels = new Database().HeartedLevels.Include
(q => q.User)
.Include(q => q.Slot) .Include(q => q.Slot)
.Include(q => q.Slot.Location) .Include(q => q.Slot.Location)
.Include(q => q.Slot.Creator) .Include(q => q.Slot.Creator)
@ -86,17 +98,22 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("favourite/slot/user/{id:int}")] [HttpPost("favourite/slot/user/{id:int}")]
public async Task<IActionResult> AddFavouriteSlot(int id) { public async Task<IActionResult> AddFavouriteSlot(int id)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); 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 { this.database.HeartedLevels.Add
(
new HeartedLevel
{
SlotId = id, SlotId = id,
UserId = user.UserId, UserId = user.UserId,
}); }
);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
@ -104,12 +121,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("unfavourite/slot/user/{id:int}")] [HttpPost("unfavourite/slot/user/{id:int}")]
public async Task<IActionResult> RemoveFavouriteSlot(int id) { public async Task<IActionResult> RemoveFavouriteSlot(int id)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); 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(); await this.database.SaveChangesAsync();
@ -117,16 +135,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
#endregion #endregion
#endregion Levels #endregion Levels
#region Users #region Users
[HttpGet("favouriteUsers/{username}")] [HttpGet("favouriteUsers/{username}")]
public IActionResult GetFavouriteUsers(string username) { public IActionResult GetFavouriteUsers(string username)
IEnumerable<HeartedProfile> heartedProfiles = new Database().HeartedProfiles {
.Include(q => q.User) IEnumerable<HeartedProfile> heartedProfiles = new Database().HeartedProfiles.Include
(q => q.User)
.Include(q => q.HeartedUser) .Include(q => q.HeartedUser)
.Include(q => q.HeartedUser.Location) .Include(q => q.HeartedUser.Location)
.Where(q => q.User.Username == username) .Where(q => q.User.Username == username)
@ -138,22 +156,26 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("favourite/user/{username}")] [HttpPost("favourite/user/{username}")]
public async Task<IActionResult> AddFavouriteUser(string username) { public async Task<IActionResult> AddFavouriteUser(string username)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
User? heartedUser = await this.database.Users User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound();
if(heartedUser == null) return this.NotFound();
HeartedProfile heartedProfile = await this.database.HeartedProfiles HeartedProfile heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync
.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
if(heartedProfile != null) return this.Ok(); if (heartedProfile != null) return this.Ok();
this.database.HeartedProfiles.Add(new HeartedProfile { this.database.HeartedProfiles.Add
(
new HeartedProfile
{
HeartedUserId = heartedUser.UserId, HeartedUserId = heartedUser.UserId,
UserId = user.UserId, UserId = user.UserId,
}); }
);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
@ -161,17 +183,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("unfavourite/user/{username}")] [HttpPost("unfavourite/user/{username}")]
public async Task<IActionResult> RemoveFavouriteUser(string username) { public async Task<IActionResult> RemoveFavouriteUser(string username)
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
User? heartedUser = await this.database.Users User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
.FirstOrDefaultAsync(u => u.Username == username); if (heartedUser == null) return this.NotFound();
if(heartedUser == null) return this.NotFound();
HeartedProfile heartedProfile = await this.database.HeartedProfiles HeartedProfile heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync
.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId); (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
if(heartedProfile != null) this.database.HeartedProfiles.Remove(heartedProfile); if (heartedProfile != null) this.database.HeartedProfiles.Remove(heartedProfile);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
@ -179,5 +201,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
#endregion #endregion
} }
} }

View file

@ -5,39 +5,49 @@ using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings; using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/login")] [Route("LITTLEBIGPLANETPS3_XML/login")]
[Produces("text/xml")] [Produces("text/xml")]
public class LoginController : ControllerBase { public class LoginController : ControllerBase
{
private readonly Database database; private readonly Database database;
public LoginController(Database database) { public LoginController(Database database)
{
this.database = database; this.database = database;
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> Login() { public async Task<IActionResult> Login()
{
string body = await new StreamReader(this.Request.Body).ReadToEndAsync(); string body = await new StreamReader(this.Request.Body).ReadToEndAsync();
LoginData? loginData; LoginData? loginData;
try { try
{
loginData = LoginData.CreateFromString(body); loginData = LoginData.CreateFromString(body);
} }
catch { catch
{
loginData = null; loginData = null;
} }
if(loginData == null) return this.BadRequest(); if (loginData == null) return this.BadRequest();
Token? token = await this.database.AuthenticateUser(loginData); Token? token = await this.database.AuthenticateUser(loginData);
if(token == null) return this.StatusCode(403, ""); if (token == null) return this.StatusCode(403, "");
return this.Ok(new LoginResult { return this.Ok
(
new LoginResult
{
AuthTicket = "MM_AUTH=" + token.UserToken, AuthTicket = "MM_AUTH=" + token.UserToken,
LbpEnvVer = ServerSettings.ServerName, LbpEnvVer = ServerSettings.ServerName,
}.Serialize()); }.Serialize()
);
} }
} }
} }

View file

@ -11,57 +11,69 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class MatchController : ControllerBase { public class MatchController : ControllerBase
{
private readonly Database database; private readonly Database database;
public MatchController(Database database) { public MatchController(Database database)
{
this.database = database; this.database = database;
} }
[HttpPost("match")] [HttpPost("match")]
[Produces("text/json")] [Produces("text/json")]
public async Task<IActionResult> Match() { public async Task<IActionResult> Match()
{
User? user = await this.database.UserFromRequest(this.Request); User? user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
#region Parse match data #region Parse match data
// Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]] // Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]]
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
if(bodyString.Contains("FindBestRoom")) { if (bodyString.Contains
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\":[]}]"); ("FindBestRoom"))
} return this.Ok
(
"[{\"StatusCode\":200},{\"Players\":[{\"PlayerId\":\"literally1984\",\"matching_res\":0},{\"PlayerId\":\"jvyden\",\"matching_res\":1}],\"Slots\":[[5,0]],\"RoomState\":\"E_ROOM_IN_POD\",\"HostMood\":\"E_MOOD_EVERYONE\",\"LevelCompletionEstimate\":0,\"PassedNoJoinPoint\":0,\"MoveConnected\":false,\"Location\":[\"127.0.0.1\"],\"BuildVersion\":289,\"Language\":1,\"FirstSeenTimestamp\":1427331263756,\"LastSeenTimestamp\":1635112546000,\"GameId\":1,\"NatType\":2,\"Friends\":[],\"Blocked\":[],\"RecentlyLeft\":[],\"FailedJoin\":[]}]"
);
if(string.IsNullOrEmpty(bodyString) || bodyString[0] != '[') return this.BadRequest(); if (string.IsNullOrEmpty(bodyString) || bodyString[0] != '[') return this.BadRequest();
IMatchData? matchData; IMatchData? matchData;
try { try
{
matchData = MatchHelper.Deserialize(bodyString); matchData = MatchHelper.Deserialize(bodyString);
} }
catch(Exception e) { catch(Exception e)
{
Logger.Log("Exception while parsing MatchData: " + e); Logger.Log("Exception while parsing MatchData: " + e);
Logger.Log("Data: " + bodyString); Logger.Log("Data: " + bodyString);
return this.BadRequest(); return this.BadRequest();
} }
if(matchData == null) return this.BadRequest(); if (matchData == null) return this.BadRequest();
#endregion #endregion
#region Update LastMatch #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 // below makes it not look like trash
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression // ReSharper disable once ConvertIfStatementToNullCoalescingExpression
if(lastMatch == null) { if (lastMatch == null)
lastMatch = new LastMatch { {
lastMatch = new LastMatch
{
UserId = user.UserId, UserId = user.UserId,
}; };
this.database.LastMatches.Add(lastMatch); this.database.LastMatches.Add(lastMatch);
@ -70,6 +82,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
lastMatch.Timestamp = TimestampHelper.Timestamp; lastMatch.Timestamp = TimestampHelper.Timestamp;
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
#endregion #endregion
return this.Ok("[{\"StatusCode\":200}]"); return this.Ok("[{\"StatusCode\":200}]");

View file

@ -6,44 +6,49 @@ using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/plain")] [Produces("text/plain")]
public class MessageController : ControllerBase { public class MessageController : ControllerBase
{
private readonly Database database; private readonly Database database;
public MessageController(Database database) { public MessageController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("eula")] [HttpGet("eula")]
public async Task<IActionResult> Eula() { public async Task<IActionResult> Eula()
{
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
return user == null return user == null
? this.StatusCode(403, "") ? this.StatusCode(403, "")
: this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n" + : this.Ok
(
$"You are now logged in as user {user.Username} (id {user.UserId}).\n" +
// ReSharper disable once UnreachableCode // ReSharper disable once UnreachableCode
(EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") + "\n" + (EulaHelper.ShowPrivateInstanceNotice ? "\n" + EulaHelper.PrivateInstanceNotice : "") +
$"{EulaHelper.License}\n"); "\n" +
$"{EulaHelper.License}\n"
);
} }
[HttpGet("announce")] [HttpGet("announce")]
public IActionResult Announce() { public IActionResult Announce() => this.Ok("");
return this.Ok("");
}
[HttpGet("notification")] [HttpGet("notification")]
public IActionResult Notification() { public IActionResult Notification() => this.Ok();
return this.Ok();
}
/// <summary> /// <summary>
/// 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. /// The reponse sent is the text that will appear in-game.
/// </summary> /// </summary>
[HttpPost("filter")] [HttpPost("filter")]
public async Task<IActionResult> Filter() { public async Task<IActionResult> Filter()
{
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync(); string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync();

View file

@ -2,17 +2,25 @@ using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.News; using LBPUnion.ProjectLighthouse.Types.News;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/news")] [Route("LITTLEBIGPLANETPS3_XML/news")]
[Produces("text/xml")] [Produces("text/xml")]
public class NewsController : ControllerBase { public class NewsController : ControllerBase
{
[HttpGet] [HttpGet]
public IActionResult Get() { public IActionResult Get()
string newsEntry = LbpSerializer.StringElement("item", new NewsEntry { {
string newsEntry = LbpSerializer.StringElement
(
"item",
new NewsEntry
{
Category = "no_category", Category = "no_category",
Summary = "test summary", Summary = "test summary",
Image = new NewsImage { Image = new NewsImage
{
Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe", Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe",
Alignment = "right", Alignment = "right",
}, },
@ -20,7 +28,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
Title = "Test Title", Title = "Test Title",
Text = "Test Text", Text = "Test Text",
Date = 1348755214000, Date = 1348755214000,
}.Serialize()); }.Serialize()
);
return this.Ok(LbpSerializer.StringElement("news", newsEntry)); return this.Ok(LbpSerializer.StringElement("news", newsEntry));
} }

View file

@ -10,14 +10,17 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class PublishController : ControllerBase { public class PublishController : ControllerBase
{
private readonly Database database; private readonly Database database;
public PublishController(Database database) { public PublishController(Database database)
{
this.database = database; this.database = database;
} }
@ -25,24 +28,25 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
/// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded /// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded
/// </summary> /// </summary>
[HttpPost("startPublish")] [HttpPost("startPublish")]
public async Task<IActionResult> StartPublish() { public async Task<IActionResult> StartPublish()
{
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
Slot slot = await this.GetSlotFromBody(); Slot slot = await this.GetSlotFromBody();
if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
// Republish logic // Republish logic
if(slot.SlotId != 0) { if (slot.SlotId != 0)
{
Slot oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); Slot oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
if(oldSlot == null) return this.NotFound(); if (oldSlot == null) return this.NotFound();
if(oldSlot.CreatorId != user.UserId) return this.BadRequest(); if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
} }
string resources = slot.Resources string resources = slot.Resources.Where
.Where(hash => !FileHelper.ResourceExists(hash)) (hash => !FileHelper.ResourceExists(hash))
.Aggregate("", (current, hash) => .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
current + LbpSerializer.StringElement("resource", hash));
return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user")); return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user"));
} }
@ -51,19 +55,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
/// Endpoint actually used to publish a level /// Endpoint actually used to publish a level
/// </summary> /// </summary>
[HttpPost("publish")] [HttpPost("publish")]
public async Task<IActionResult> Publish() { public async Task<IActionResult> Publish()
{
User user = await this.database.UserFromRequest(this.Request); 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(); Slot slot = await this.GetSlotFromBody();
// Republish logic // Republish logic
if(slot.SlotId != 0) { if (slot.SlotId != 0)
Slot oldSlot = await this.database.Slots {
.Include(s => s.Location) Slot oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId); if (oldSlot == null) return this.NotFound();
if(oldSlot == null) return this.NotFound(); if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
if(oldSlot.CreatorId != user.UserId) return this.BadRequest();
oldSlot.Location.X = slot.Location.X; oldSlot.Location.X = slot.Location.X;
oldSlot.Location.Y = slot.Location.Y; oldSlot.Location.Y = slot.Location.Y;
@ -80,7 +84,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
//TODO: parse location in body //TODO: parse location in body
Location l = new() { Location l = new()
{
X = slot.Location.X, X = slot.Location.X,
Y = slot.Location.Y, Y = slot.Location.Y,
}; };
@ -98,10 +103,9 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
} }
[HttpPost("unpublish/{id:int}")] [HttpPost("unpublish/{id:int}")]
public async Task<IActionResult> Unpublish(int id) { public async Task<IActionResult> Unpublish(int id)
Slot slot = await this.database.Slots {
.Include(s => s.Location) Slot slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
.FirstOrDefaultAsync(s => s.SlotId == id);
this.database.Locations.Remove(slot.Location); this.database.Locations.Remove(slot.Location);
this.database.Slots.Remove(slot); this.database.Slots.Remove(slot);
@ -111,7 +115,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
return this.Ok(); return this.Ok();
} }
public async Task<Slot> GetSlotFromBody() { public async Task<Slot> GetSlotFromBody()
{
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();

View file

@ -10,59 +10,60 @@ using LBPUnion.ProjectLighthouse.Types.Files;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using IOFile = System.IO.File; using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class ResourcesController : ControllerBase { public class ResourcesController : ControllerBase
{
[HttpPost("showModerated")] [HttpPost("showModerated")]
public IActionResult ShowModerated() { public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources"));
return this.Ok(LbpSerializer.BlankElement("resources"));
}
[HttpPost("filterResources")] [HttpPost("filterResources")]
[HttpPost("showNotUploaded")] [HttpPost("showNotUploaded")]
public async Task<IActionResult> FilterResources() { public async Task<IActionResult> FilterResources()
{
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(ResourceList)); 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(); if (resourceList == null) return this.BadRequest();
string resources = resourceList.Resources string resources = resourceList.Resources.Where
.Where(s => !FileHelper.ResourceExists(s)) (s => !FileHelper.ResourceExists(s))
.Aggregate("", (current, hash) => .Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
current + LbpSerializer.StringElement("resource", hash));
return this.Ok(LbpSerializer.StringElement("resources", resources)); return this.Ok(LbpSerializer.StringElement("resources", resources));
} }
[HttpGet("r/{hash}")] [HttpGet("r/{hash}")]
public IActionResult GetResource(string hash) { public IActionResult GetResource(string hash)
{
string path = FileHelper.GetResourcePath(hash); string path = FileHelper.GetResourcePath(hash);
if(FileHelper.ResourceExists(hash)) { if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream");
return this.File(IOFile.OpenRead(path), "application/octet-stream");
}
return this.NotFound(); return this.NotFound();
} }
// TODO: check if this is a valid hash // TODO: check if this is a valid hash
[HttpPost("upload/{hash}")] [HttpPost("upload/{hash}")]
[AllowSynchronousIo] [AllowSynchronousIo]
public async Task<IActionResult> UploadResource(string hash) { public async Task<IActionResult> UploadResource(string hash)
{
string assetsDirectory = FileHelper.ResourcePath; string assetsDirectory = FileHelper.ResourcePath;
string path = FileHelper.GetResourcePath(hash); string path = FileHelper.GetResourcePath(hash);
FileHelper.EnsureDirectoryCreated(assetsDirectory); FileHelper.EnsureDirectoryCreated(assetsDirectory);
if(FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded
Logger.Log($"Processing resource upload (hash: {hash})"); Logger.Log($"Processing resource upload (hash: {hash})");
LbpFile file = new(await BinaryHelper.ReadFromPipeReader(Request.BodyReader)); LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader));
if(!FileHelper.IsFileSafe(file)) return this.UnprocessableEntity(); if (!FileHelper.IsFileSafe(file)) return this.UnprocessableEntity();
await IOFile.WriteAllBytesAsync(path, file.Data); await IOFile.WriteAllBytesAsync(path, file.Data);
return this.Ok(); return this.Ok();

View file

@ -6,19 +6,24 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class SearchController : ControllerBase { public class SearchController : ControllerBase
{
private readonly Database database; private readonly Database database;
public SearchController(Database database) { public SearchController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("slots/search")] [HttpGet("slots/search")]
public async Task<IActionResult> SearchSlots([FromQuery] string query) { public async Task<IActionResult> SearchSlots([FromQuery] string query)
if(query == null) return this.BadRequest(); {
if (query == null) return this.BadRequest();
query = query.ToLower(); query = query.ToLower();
string[] keywords = query.Split(" "); string[] keywords = query.Split(" ");
@ -29,14 +34,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable .Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
// ReSharper disable once LoopCanBeConvertedToQuery // ReSharper disable once LoopCanBeConvertedToQuery
foreach(string keyword in keywords) { foreach (string keyword in keywords)
dbQuery = dbQuery.Where(s => dbQuery = dbQuery.Where
s.Name.ToLower().Contains(keyword) || (
s => s.Name.ToLower().Contains(keyword) ||
s.Description.ToLower().Contains(keyword) || s.Description.ToLower().Contains(keyword) ||
s.Creator.Username.ToLower().Contains(keyword) || s.Creator.Username.ToLower().Contains(keyword) ||
s.SlotId.ToString().Equals(keyword) s.SlotId.ToString().Equals(keyword)
); );
}
List<Slot> slots = await dbQuery.ToListAsync(); List<Slot> slots = await dbQuery.ToListAsync();
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize()); string response = slots.Aggregate("", (current, slot) => current + slot.Serialize());

View file

@ -6,44 +6,47 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class SlotsController : ControllerBase { public class SlotsController : ControllerBase
{
private readonly Database database; private readonly Database database;
public SlotsController(Database database) { public SlotsController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("slots/by")] [HttpGet("slots/by")]
public IActionResult SlotsBy([FromQuery] string u) { public IActionResult SlotsBy([FromQuery] string u)
string response = Enumerable.Aggregate( {
this.database.Slots string response = Enumerable.Aggregate
.Include(s => s.Creator) (
.Include(s => s.Location) this.database.Slots.Include(s => s.Creator).Include(s => s.Location).Where(s => s.Creator.Username == u),
.Where(s => s.Creator.Username == u) string.Empty,
, string.Empty, (current, slot) => current + slot.Serialize()); (current, slot) => current + slot.Serialize()
);
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1));
} }
[HttpGet("s/user/{id:int}")] [HttpGet("s/user/{id:int}")]
public async Task<IActionResult> SUser(int id) { public async Task<IActionResult> SUser(int id)
Slot slot = await this.database.Slots {
.Include(s => s.Creator) Slot slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
.Include(s => s.Location)
.FirstOrDefaultAsync(s => s.SlotId == id);
if(slot == null) return this.NotFound(); if (slot == null) return this.NotFound();
return this.Ok(slot.Serialize()); return this.Ok(slot.Serialize());
} }
[HttpGet("slots")] [HttpGet("slots")]
public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize) { public IActionResult NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
IQueryable<Slot> slots = this.database.Slots {
.Include(s => s.Creator) IQueryable<Slot> slots = this.database.Slots.Include
(s => s.Creator)
.Include(s => s.Location) .Include(s => s.Location)
.OrderByDescending(s => s.FirstUploaded) .OrderByDescending(s => s.FirstUploaded)
.Skip(pageStart - 1) .Skip(pageStart - 1)

View file

@ -5,35 +5,39 @@ using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/plain")] [Produces("text/plain")]
public class StatisticsController : ControllerBase { public class StatisticsController : ControllerBase
{
private readonly Database database; private readonly Database database;
public StatisticsController(Database database) { public StatisticsController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("playersInPodCount")] [HttpGet("playersInPodCount")]
[HttpGet("totalPlayerCount")] [HttpGet("totalPlayerCount")]
public async Task<IActionResult> TotalPlayerCount() { public async Task<IActionResult> TotalPlayerCount()
int recentMatches = await this.database.LastMatches {
.Where(l => TimestampHelper.Timestamp - l.Timestamp < 60) int recentMatches = await this.database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 60).CountAsync();
.CountAsync();
return this.Ok(recentMatches.ToString()); return this.Ok(recentMatches.ToString());
} }
[HttpGet("planetStats")] [HttpGet("planetStats")]
public async Task<IActionResult> PlanetStats() { public async Task<IActionResult> PlanetStats()
{
int totalSlotCount = await this.database.Slots.CountAsync(); int totalSlotCount = await this.database.Slots.CountAsync();
const int mmPicksCount = 0; const int mmPicksCount = 0;
return this.Ok(LbpSerializer.StringElement("planetStats", return this.Ok
LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + (
LbpSerializer.StringElement("mmPicksCount", mmPicksCount) LbpSerializer.StringElement
)); ("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount))
);
} }
} }
} }

View file

@ -8,44 +8,41 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers { namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")] [Produces("text/xml")]
public class UserController : ControllerBase { public class UserController : ControllerBase
{
private readonly Database database; private readonly Database database;
public UserController(Database database) { public UserController(Database database)
{
this.database = database; this.database = database;
} }
[HttpGet("user/{username}")] [HttpGet("user/{username}")]
public async Task<IActionResult> GetUser(string username) { public async Task<IActionResult> GetUser(string username)
User user = await this.database.Users {
.Include(u => u.Location) User user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return this.NotFound();
if(user == null) return this.NotFound();
return this.Ok(user.Serialize()); return this.Ok(user.Serialize());
} }
[HttpGet("users")] [HttpGet("users")]
public async Task<IActionResult> GetUserAlt([FromQuery] string u) { public async Task<IActionResult> GetUserAlt([FromQuery] string u) => await this.GetUser(u);
return await GetUser(u);
}
// [HttpPost("user/{username}")]
// public async Task<IActionResult> CreateUser(string username) {
// await new Database().CreateUser(username);
// return await GetUser(username);
// }
[HttpPost("updateUser")] [HttpPost("updateUser")]
public async Task<IActionResult> UpdateUser() { public async Task<IActionResult> UpdateUser()
{
User user = await this.database.UserFromRequest(this.Request); User user = await this.database.UserFromRequest(this.Request);
if(user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
XmlReaderSettings settings = new() { XmlReaderSettings settings = new()
{
Async = true, // this is apparently not default Async = true, // this is apparently not default
}; };
@ -67,35 +64,40 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
// </updateUser> // </updateUser>
// //
// if you find a way to make it not stupid feel free to replace this // if you find a way to make it not stupid feel free to replace this
using(XmlReader reader = XmlReader.Create(this.Request.Body, settings)) { using (XmlReader reader = XmlReader.Create(this.Request.Body, settings))
{
List<string> path = new(); // you can think of this as a file path in the XML, like <updateUser> -> <location> -> <x> List<string> path = new(); // you can think of this as a file path in the XML, like <updateUser> -> <location> -> <x>
while(await reader.ReadAsync()) { while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (reader.NodeType)
switch(reader.NodeType) { {
case XmlNodeType.Element: case XmlNodeType.Element:
path.Add(reader.Name); path.Add(reader.Name);
break; break;
case XmlNodeType.Text: case XmlNodeType.Text:
switch(path[1]) { switch (path[1])
case "biography": { {
case "biography":
{
user.Biography = await reader.GetValueAsync(); user.Biography = await reader.GetValueAsync();
break; break;
} }
case "location": { 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 // ReSharper disable once ConvertIfStatementToSwitchStatement
if(path[2] == "x") { 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 user.Location.X = Convert.ToInt32
} else if(path[2] == "y") { (await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol
user.Location.Y = Convert.ToInt32(await reader.GetValueAsync()); else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync());
}
break; break;
} }
case "icon": { case "icon":
{
user.IconHash = await reader.GetValueAsync(); user.IconHash = await reader.GetValueAsync();
break; break;
} }
case "planets": { case "planets":
{
user.PlanetHash = await reader.GetValueAsync(); user.PlanetHash = await reader.GetValueAsync();
break; break;
} }
@ -106,10 +108,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
break; 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: // the way location on a user card works is stupid and will not save with the way below as-is, so we do the following:
if(locationChanged) { // only modify the database if we modify here if (locationChanged)
{ // only modify the database if we modify here
Location l = await this.database.Locations.Where(l => l.Id == user.LocationId).FirstOrDefaultAsync(); // find the location in the database again 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 // set the location in the database to the one we modified above
@ -119,7 +121,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers {
// now both are in sync, and will update in the database. // 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(); return this.Ok();
} }
} }

View file

@ -8,8 +8,10 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse { namespace LBPUnion.ProjectLighthouse
public class Database : DbContext { {
public class Database : DbContext
{
public DbSet<User> Users { get; set; } public DbSet<User> Users { get; set; }
public DbSet<Location> Locations { get; set; } public DbSet<Location> Locations { get; set; }
public DbSet<Slot> Slots { get; set; } public DbSet<Slot> Slots { get; set; }
@ -21,21 +23,20 @@ namespace LBPUnion.ProjectLighthouse {
public DbSet<LastMatch> LastMatches { get; set; } public DbSet<LastMatch> LastMatches { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseMySql( protected override void OnConfiguring(DbContextOptionsBuilder options)
ServerSettings.DbConnectionString, => options.UseMySql(ServerSettings.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
MySqlServerVersion.LatestSupportedServerVersion
);
public async Task<User> CreateUser(string username) { public async Task<User> CreateUser(string username)
{
User user; User user;
if((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user;
return user;
Location l = new(); // store to get id after submitting Location l = new(); // store to get id after submitting
this.Locations.Add(l); // add to table this.Locations.Add(l); // add to table
await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity
user = new User { user = new User
{
Username = username, Username = username,
LocationId = l.Id, LocationId = l.Id,
Biography = username + " hasn't introduced themselves yet.", Biography = username + " hasn't introduced themselves yet.",
@ -48,12 +49,13 @@ namespace LBPUnion.ProjectLighthouse {
} }
#nullable enable #nullable enable
public async Task<Token?> AuthenticateUser(LoginData loginData) { public async Task<Token?> AuthenticateUser(LoginData loginData)
{
// TODO: don't use psn name to authenticate // TODO: don't use psn name to authenticate
User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) ?? await this.CreateUser(loginData.Username);
?? await this.CreateUser(loginData.Username);
Token token = new() { Token token = new()
{
UserToken = HashHelper.GenerateAuthToken(), UserToken = HashHelper.GenerateAuthToken(),
UserId = user.UserId, UserId = user.UserId,
}; };
@ -64,18 +66,17 @@ namespace LBPUnion.ProjectLighthouse {
return token; return token;
} }
public async Task<User?> UserFromAuthToken(string authToken) { public async Task<User?> UserFromAuthToken(string authToken)
{
Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken); Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
if(token == null) return null; if (token == null) return null;
return await this.Users
.Include(u => u.Location) return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId);
.FirstOrDefaultAsync(u => u.UserId == token.UserId);
} }
public async Task<User?> UserFromRequest(HttpRequest request) { public async Task<User?> 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); return await this.UserFromAuthToken(mmAuth);
} }

View file

@ -2,17 +2,20 @@ using System;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
{
// Yoinked from https://stackoverflow.com/a/68530667 // Yoinked from https://stackoverflow.com/a/68530667
// Thanks to T-moty! // Thanks to T-moty!
/// <summary> /// <summary>
/// Allows synchronous stream operations for this request. /// Allows synchronous stream operations for this request.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AllowSynchronousIoAttribute : ActionFilterAttribute { public class AllowSynchronousIoAttribute : ActionFilterAttribute
public override void OnResultExecuting(ResultExecutingContext context) { {
public override void OnResultExecuting(ResultExecutingContext context)
{
IHttpBodyControlFeature syncIoFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>(); IHttpBodyControlFeature syncIoFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
if(syncIoFeature != null) syncIoFeature.AllowSynchronousIO = true; if (syncIoFeature != null) syncIoFeature.AllowSynchronousIO = true;
} }
} }
} }

View file

@ -6,52 +6,57 @@ using System.IO.Pipelines;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class BinaryHelper { {
public static string ReadString(BinaryReader reader) { public static class BinaryHelper
{
public static string ReadString(BinaryReader reader)
{
List<byte> readBytes = new(); List<byte> readBytes = new();
byte readByte; byte readByte;
do { do readBytes.Add(readByte = reader.ReadByte());
readBytes.Add(readByte = reader.ReadByte()); while (readByte != 0x00);
} while(readByte != 0x00);
return Encoding.UTF8.GetString(readBytes.ToArray()); return Encoding.UTF8.GetString(readBytes.ToArray());
} }
public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) { public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo)
{
byte readByte; byte readByte;
do { do readByte = reader.ReadByte();
readByte = reader.ReadByte(); while (readByte != byteToReadTo);
} 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; long oldPosition = reader.BaseStream.Position;
if(reader.BaseStream.Length < count) return Array.Empty<byte>(); if (reader.BaseStream.Length < count) return Array.Empty<byte>();
reader.BaseStream.Position = reader.BaseStream.Length - count; reader.BaseStream.Position = reader.BaseStream.Length - count;
byte[] data = reader.ReadBytes(count); byte[] data = reader.ReadBytes(count);
if(restoreOldPosition) reader.BaseStream.Position = oldPosition; if (restoreOldPosition) reader.BaseStream.Position = oldPosition;
return data; return data;
} }
// Written with reference from // Written with reference from
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 // 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) // Surprisingly doesn't take seconds. (67ms for a 100kb file)
public static async Task<byte[]> ReadFromPipeReader(PipeReader reader) { public static async Task<byte[]> ReadFromPipeReader(PipeReader reader)
{
List<byte> data = new(); List<byte> data = new();
while(true) { while (true)
{
ReadResult readResult = await reader.ReadAsync(); ReadResult readResult = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = readResult.Buffer; ReadOnlySequence<byte> 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); reader.AdvanceTo(buffer.Start, buffer.End);
if(readResult.IsCompleted) break; if (readResult.IsCompleted) break;
} }
return data.ToArray(); return data.ToArray();

View file

@ -1,5 +1,7 @@
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class EulaHelper { {
public static class EulaHelper
{
public const string License = @" public const string License = @"
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as it under the terms of the GNU Affero General Public License as

View file

@ -3,17 +3,23 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions { namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
{
// https://stackoverflow.com/a/8039737 // https://stackoverflow.com/a/8039737
public static class ExceptionExtensions { public static class ExceptionExtensions
public static string ToDetailedException(this Exception exception) { {
public static string ToDetailedException(this Exception exception)
{
PropertyInfo[] properties = exception.GetType().GetProperties(); PropertyInfo[] properties = exception.GetType().GetProperties();
IEnumerable<string> fields = properties IEnumerable<string> fields = properties.Select
.Select(property => new { (
property => new
{
property.Name, property.Name,
Value = property.GetValue(exception, null), Value = property.GetValue(exception, null),
}) }
)
.Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}"); .Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}");
return string.Join("\n", fields); return string.Join("\n", fields);

View file

@ -4,16 +4,20 @@ using System.Linq;
using System.Text; using System.Text;
using LBPUnion.ProjectLighthouse.Types.Files; using LBPUnion.ProjectLighthouse.Types.Files;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class FileHelper { {
public static class FileHelper
{
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r"); public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash); public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
public static bool IsFileSafe(LbpFile file) { public static bool IsFileSafe(LbpFile file)
if(file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); {
if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
return file.FileType switch { return file.FileType switch
{
LbpFileType.FileArchive => false, LbpFileType.FileArchive => false,
LbpFileType.Painting => true, LbpFileType.Painting => true,
LbpFileType.Unknown => false, LbpFileType.Unknown => false,
@ -30,16 +34,18 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
}; };
} }
public static LbpFileType DetermineFileType(byte[] data) { public static LbpFileType DetermineFileType(byte[] data)
{
using MemoryStream ms = new(data); using MemoryStream ms = new(data);
using BinaryReader reader = new(ms); using BinaryReader reader = new(ms);
string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4)); 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); byte[] header = reader.ReadBytes(3);
return Encoding.ASCII.GetString(header) switch { return Encoding.ASCII.GetString(header) switch
{
"PTG" => LbpFileType.Painting, "PTG" => LbpFileType.Painting,
"TEX" => LbpFileType.Texture, "TEX" => LbpFileType.Texture,
"FSH" => LbpFileType.Script, "FSH" => LbpFileType.Script,
@ -52,8 +58,9 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
public static void EnsureDirectoryCreated(string path) { public static void EnsureDirectoryCreated(string path)
if(!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(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(); public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray();

View file

@ -4,32 +4,22 @@ using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
{
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class HashHelper { public static class HashHelper
{
// private static readonly SHA1 sha1 = SHA1.Create(); // private static readonly SHA1 sha1 = SHA1.Create();
private static readonly SHA256 sha256 = SHA256.Create(); private static readonly SHA256 sha256 = SHA256.Create();
private static readonly Random random = new(); 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
/// <summary> /// <summary>
/// Generates a specified amount of random bytes in an array. /// Generates a specified amount of random bytes in an array.
/// </summary> /// </summary>
/// <param name="count">The amount of bytes to generate.</param> /// <param name="count">The amount of bytes to generate.</param>
/// <returns>The bytes generated</returns> /// <returns>The bytes generated</returns>
public static IEnumerable<byte> GenerateRandomBytes(int count) { public static IEnumerable<byte> GenerateRandomBytes(int count)
{
byte[] b = new byte[count]; byte[] b = new byte[count];
random.NextBytes(b); random.NextBytes(b);
@ -40,10 +30,28 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
/// Generates a random SHA256 & BCrypted token /// Generates a random SHA256 & BCrypted token
/// </summary> /// </summary>
/// <returns>The token as a string.</returns> /// <returns>The token as a string.</returns>
public static string GenerateAuthToken() { public static string GenerateAuthToken()
byte[] bytes = (byte[]) GenerateRandomBytes(256); {
byte[] bytes = (byte[])GenerateRandomBytes(256);
return BCryptHash(Sha256Hash(bytes)); return BCryptHash(Sha256Hash(bytes));
} }
#region Hash Functions
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
public static string Sha256Hash(byte[] bytes)
{
byte[] hash = sha256.ComputeHash(bytes);
return Encoding.UTF8.GetString(hash, 0, hash.Length);
}
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);
public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes));
#endregion
} }
} }

View file

@ -3,14 +3,18 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using LBPUnion.ProjectLighthouse.Types.Match; using LBPUnion.ProjectLighthouse.Types.Match;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class MatchHelper { {
public static IMatchData? Deserialize(string data) { public static class MatchHelper
{
public static IMatchData? Deserialize(string data)
{
string matchType = ""; string matchType = "";
int i = 1; int i = 1;
while(true) { while (true)
if(data[i] == ',') break; {
if (data[i] == ',') break;
matchType += data[i]; matchType += data[i];
i++; i++;
@ -21,8 +25,10 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
return Deserialize(matchType, matchData); return Deserialize(matchType, matchData);
} }
public static IMatchData? Deserialize(string matchType, string matchData) { public static IMatchData? Deserialize(string matchType, string matchData)
return matchType switch { {
return matchType switch
{
"UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData), "UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData),
"UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData), "UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData),
_ => null, _ => null,

View file

@ -1,7 +1,9 @@
using System; using System;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class TimeHelper { {
public static class TimeHelper
{
public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds();
public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds();
} }

View file

@ -1,7 +1,9 @@
using System; using System;
namespace LBPUnion.ProjectLighthouse.Helpers { namespace LBPUnion.ProjectLighthouse.Helpers
public static class TimestampHelper { {
public static class TimestampHelper
{
public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
} }
} }

View file

@ -3,22 +3,23 @@ using Kettu;
using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace LBPUnion.ProjectLighthouse.Logging { namespace LBPUnion.ProjectLighthouse.Logging
public class AspNetToKettuLogger : ILogger { {
public class AspNetToKettuLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state) { public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
return NullScope.Instance;
}
public bool IsEnabled(LogLevel logLevel) => true; public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel);
Logger.Log(state.ToString(), loggerLevel); Logger.Log(state.ToString(), loggerLevel);
if(exception == null) return; if (exception == null) return;
string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n"); 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);
} }
} }
} }

View file

@ -1,15 +1,16 @@
using System; using System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace LBPUnion.ProjectLighthouse.Logging { namespace LBPUnion.ProjectLighthouse.Logging
{
[ProviderAlias("Kettu")] [ProviderAlias("Kettu")]
public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable { public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable
public void Dispose() { {
public void Dispose()
{
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public ILogger CreateLogger(string categoryName) { public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger();
return new AspNetToKettuLogger();
}
} }
} }

View file

@ -3,11 +3,14 @@ using System.IO;
using Kettu; using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
namespace LBPUnion.ProjectLighthouse.Logging { namespace LBPUnion.ProjectLighthouse.Logging
public class LighthouseFileLogger : LoggerBase { {
public class LighthouseFileLogger : LoggerBase
{
private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs");
public override void Send(LoggerLine line) { public override void Send(LoggerLine line)
{
FileHelper.EnsureDirectoryCreated(logsDirectory); FileHelper.EnsureDirectoryCreated(logsDirectory);
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";

View file

@ -1,32 +1,39 @@
using Kettu; using Kettu;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace LBPUnion.ProjectLighthouse.Logging { namespace LBPUnion.ProjectLighthouse.Logging
public class LoggerLevelStartup : LoggerLevel { {
public override string Name => "Startup"; public class LoggerLevelStartup : LoggerLevel
{
public static readonly LoggerLevelStartup Instance = new(); public static readonly LoggerLevelStartup Instance = new();
public override string Name => "Startup";
} }
public class LoggerLevelDatabase : LoggerLevel { public class LoggerLevelDatabase : LoggerLevel
public override string Name => "Database"; {
public static readonly LoggerLevelDatabase Instance = new(); public static readonly LoggerLevelDatabase Instance = new();
public override string Name => "Database";
} }
public class LoggerLevelHttp : LoggerLevel { public class LoggerLevelHttp : LoggerLevel
public override string Name => "HTTP"; {
public static readonly LoggerLevelHttp Instance = new(); public static readonly LoggerLevelHttp Instance = new();
public override string Name => "HTTP";
} }
public class LoggerLevelFilter : LoggerLevel { public class LoggerLevelFilter : LoggerLevel
public override string Name => "Filter"; {
public static readonly LoggerLevelFilter Instance = new(); public static readonly LoggerLevelFilter Instance = new();
public override string Name => "Filter";
} }
public class LoggerLevelAspNet : LoggerLevel { public class LoggerLevelAspNet : LoggerLevel
public override string Name => "AspNet"; {
public LoggerLevelAspNet(LogLevel level) { public LoggerLevelAspNet(LogLevel level)
{
this.Channel = level.ToString(); this.Channel = level.ToString();
} }
public override string Name => "AspNet";
} }
} }

View file

@ -1,12 +1,16 @@
using System; using System;
namespace LBPUnion.ProjectLighthouse.Logging { namespace LBPUnion.ProjectLighthouse.Logging
public class NullScope : IDisposable{ {
public class NullScope : IDisposable
{
private NullScope()
{}
public static NullScope Instance { get; } = new(); public static NullScope Instance { get; } = new();
private NullScope() {} public void Dispose()
{
public void Dispose() {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }

View file

@ -14,9 +14,12 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace LBPUnion.ProjectLighthouse { namespace LBPUnion.ProjectLighthouse
public static class Program { {
public static void Main(string[] args) { public static class Program
{
public static void Main(string[] args)
{
// Log startup time // Log startup time
Stopwatch stopwatch = new(); Stopwatch stopwatch = new();
stopwatch.Start(); stopwatch.Start();
@ -33,13 +36,12 @@ namespace LBPUnion.ProjectLighthouse {
bool dbConnected = ServerSettings.DbConnected; bool dbConnected = ServerSettings.DbConnected;
Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance);
if(!dbConnected) Environment.Exit(1); if (!dbConnected) Environment.Exit(1);
using Database database = new(); using Database database = new();
Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); Logger.Log("Migrating database...", LoggerLevelDatabase.Instance);
MigrateDatabase(database); MigrateDatabase(database);
Logger.Log("Fixing broken timestamps...", LoggerLevelDatabase.Instance); Logger.Log("Fixing broken timestamps...", LoggerLevelDatabase.Instance);
FixTimestamps(database); FixTimestamps(database);
@ -49,7 +51,8 @@ namespace LBPUnion.ProjectLighthouse {
CreateHostBuilder(args).Build().Run(); CreateHostBuilder(args).Build().Run();
} }
public static void MigrateDatabase(Database database) { public static void MigrateDatabase(Database database)
{
Stopwatch stopwatch = new(); Stopwatch stopwatch = new();
stopwatch.Start(); stopwatch.Start();
@ -59,21 +62,16 @@ namespace LBPUnion.ProjectLighthouse {
Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance);
} }
public static void FixTimestamps(Database database) { public static void FixTimestamps(Database database)
{
Stopwatch stopwatch = new(); Stopwatch stopwatch = new();
stopwatch.Start(); stopwatch.Start();
foreach(Slot slot in database.Slots.Where(s => s.FirstUploaded == 0)) { foreach (Slot slot in database.Slots.Where(s => s.FirstUploaded == 0)) slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
}
foreach(Slot slot in database.Slots.Where(s => s.LastUpdated == 0)) { foreach (Slot slot in database.Slots.Where(s => s.LastUpdated == 0)) slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
}
foreach(Comment comment in database.Comments.Where(c => c.Timestamp == 0)) { foreach (Comment comment in database.Comments.Where(c => c.Timestamp == 0)) comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
}
database.SaveChanges(); database.SaveChanges();
@ -81,14 +79,22 @@ namespace LBPUnion.ProjectLighthouse {
Logger.Log($"Fixing timestamps took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); Logger.Log($"Fixing timestamps took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance);
} }
public static IHostBuilder CreateHostBuilder(string[] args) => public static IHostBuilder CreateHostBuilder(string[] args)
Host.CreateDefaultBuilder(args) => Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { .ConfigureWebHostDefaults
(
webBuilder =>
{
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup<Startup>();
}) }
.ConfigureLogging(logging => { )
.ConfigureLogging
(
logging =>
{
logging.ClearProviders(); logging.ClearProviders();
logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToKettuLoggerProvider>()); logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToKettuLoggerProvider>());
}); }
);
} }
} }

View file

@ -8,23 +8,23 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
<PackageReference Include="Kettu" Version="1.1.0" /> <PackageReference Include="Kettu" Version="1.1.0"/>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.11" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.11"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.11" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.11"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.2" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.2"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Types\SlotXsd.cs" /> <Compile Remove="Types\SlotXsd.cs"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="logs" /> <Folder Include="logs"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -2,26 +2,30 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
namespace LBPUnion.ProjectLighthouse.Serialization { namespace LBPUnion.ProjectLighthouse.Serialization
{
/// <summary> /// <summary>
/// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root elements), /// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root
/// elements),
/// so I wrote my own crappy one. /// so I wrote my own crappy one.
/// </summary> /// </summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class LbpSerializer { public static class LbpSerializer
{
public static string BlankElement(string key) => $"<{key}></{key}>"; public static string BlankElement(string key) => $"<{key}></{key}>";
public static string StringElement(KeyValuePair<string, object> pair) => $"<{pair.Key}>{pair.Value}</{pair.Key}>"; public static string StringElement(KeyValuePair<string, object> pair) => $"<{pair.Key}>{pair.Value}</{pair.Key}>";
public static string StringElement(string key, object value) => $"<{key}>{value}</{key}>"; public static string StringElement(string key, object value) => $"<{key}>{value}</{key}>";
public static string TaggedStringElement(KeyValuePair<string, object> pair, KeyValuePair<string, object> tagPair) => public static string TaggedStringElement
$"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}</{pair.Key}>"; (KeyValuePair<string, object> pair, KeyValuePair<string, object> tagPair)
=> $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}</{pair.Key}>";
public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
$"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
public static string Elements(params KeyValuePair<string, object>[] pairs) => public static string Elements
pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); (params KeyValuePair<string, object>[] pairs)
=> pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair));
} }
} }

View file

@ -1,8 +1,11 @@
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
namespace LBPUnion.ProjectLighthouse.Serialization { namespace LBPUnion.ProjectLighthouse.Serialization
public class XmlOutputFormatter : StringOutputFormatter { {
public XmlOutputFormatter() { public class XmlOutputFormatter : StringOutputFormatter
{
public XmlOutputFormatter()
{
this.SupportedMediaTypes.Add("text/xml"); this.SupportedMediaTypes.Add("text/xml");
this.SupportedMediaTypes.Add("application/xml"); this.SupportedMediaTypes.Add("application/xml");
} }

View file

@ -10,33 +10,38 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace LBPUnion.ProjectLighthouse { namespace LBPUnion.ProjectLighthouse
public class Startup { {
public Startup(IConfiguration configuration) { public class Startup
{
public Startup(IConfiguration configuration)
{
this.Configuration = configuration; this.Configuration = configuration;
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) { public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); services.AddControllers();
services.AddMvc(options => services.AddMvc(options => options.OutputFormatters.Add(new XmlOutputFormatter()));
options.OutputFormatters.Add(new XmlOutputFormatter()));
services.AddDbContext<Database>(); services.AddDbContext<Database>();
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if(env.IsDevelopment()) { {
app.UseDeveloperExceptionPage(); if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
}
// Logs every request and the response to it // Logs every request and the response to it
// Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news"
// Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr"
app.Use(async (context, next) => { app.Use
(
async (context, next) =>
{
Stopwatch requestStopwatch = new(); Stopwatch requestStopwatch = new();
requestStopwatch.Start(); requestStopwatch.Start();
@ -45,22 +50,29 @@ namespace LBPUnion.ProjectLighthouse {
requestStopwatch.Stop(); requestStopwatch.Stop();
Logger.Log( Logger.Log
(
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
LoggerLevelHttp.Instance LoggerLevelHttp.Instance
); );
if(context.Request.Method == "POST") { if (context.Request.Method == "POST")
{
context.Request.Body.Position = 0; context.Request.Body.Position = 0;
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
} }
}); }
);
app.UseRouting(); app.UseRouting();
app.UseEndpoints(endpoints => { app.UseEndpoints
(
endpoints =>
{
endpoints.MapControllers(); endpoints.MapControllers();
}); }
);
} }
} }
} }

View file

@ -1,21 +1,24 @@
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
namespace LBPUnion.ProjectLighthouse.Types.Files { namespace LBPUnion.ProjectLighthouse.Types.Files
public class LbpFile { {
public LbpFile(byte[] data) { public class LbpFile
this.Data = data; {
this.FileType = FileHelper.DetermineFileType(this.Data);
}
/// <summary>
/// The type of file.
/// </summary>
public LbpFileType FileType;
/// <summary> /// <summary>
/// A buffer of the file's data. /// A buffer of the file's data.
/// </summary> /// </summary>
public readonly byte[] Data; public readonly byte[] Data;
/// <summary>
/// The type of file.
/// </summary>
public LbpFileType FileType;
public LbpFile(byte[] data)
{
this.Data = data;
this.FileType = FileHelper.DetermineFileType(this.Data);
}
} }
} }

View file

@ -1,5 +1,7 @@
namespace LBPUnion.ProjectLighthouse.Types.Files { namespace LBPUnion.ProjectLighthouse.Types.Files
public enum LbpFileType { {
public enum LbpFileType
{
Script, // .ff, FSH Script, // .ff, FSH
Texture, // TEX Texture, // TEX
Level, // LVL Level, // LVL

View file

@ -2,22 +2,27 @@ using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
public class HeartedProfile { {
public class HeartedProfile
{
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
[Obsolete($"Use {nameof(HeartedUserId)} instead, this is a key which you should never need to use.")] [Obsolete($"Use {nameof(HeartedUserId)} instead, this is a key which you should never need to use.")]
#else #else
[Obsolete("Use HeartedUserId instead, this is a key which you should never need to use.")] [Obsolete("Use HeartedUserId instead, this is a key which you should never need to use.")]
#endif #endif
[Key] public int HeartedProfileId { get; set; } [Key]
public int HeartedProfileId { get; set; }
public int UserId { get; set; } public int UserId { get; set; }
[ForeignKey(nameof(UserId))] public User User { get; set; } [ForeignKey(nameof(UserId))]
public User User { get; set; }
public int HeartedUserId { get; set; } public int HeartedUserId { get; set; }
[ForeignKey(nameof(HeartedUserId))] public User HeartedUser { get; set; } [ForeignKey(nameof(HeartedUserId))]
public User HeartedUser { get; set; }
} }
} }

View file

@ -1,17 +1,22 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace LBPUnion.ProjectLighthouse.Types.Levels { namespace LBPUnion.ProjectLighthouse.Types.Levels
public class HeartedLevel { {
public class HeartedLevel
{
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
[Key] public int HeartedLevelId { get; set; } [Key]
public int HeartedLevelId { get; set; }
public int UserId { get; set; } public int UserId { get; set; }
[ForeignKey(nameof(UserId))] public User User { get; set; } [ForeignKey(nameof(UserId))]
public User User { get; set; }
public int SlotId { get; set; } public int SlotId { get; set; }
[ForeignKey(nameof(SlotId))] public Slot Slot { get; set; } [ForeignKey(nameof(SlotId))]
public Slot Slot { get; set; }
} }
} }

View file

@ -1,12 +1,14 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace LBPUnion.ProjectLighthouse.Types.Levels { namespace LBPUnion.ProjectLighthouse.Types.Levels
{
/// <summary> /// <summary>
/// A series of tags that can be applied to a level /// A series of tags that can be applied to a level
/// </summary> /// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public enum LevelTags { public enum LevelTags
{
Brilliant, Brilliant,
Beautiful, Beautiful,
Funky, Funky,

View file

@ -1,10 +1,13 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace LBPUnion.ProjectLighthouse.Types.Levels { namespace LBPUnion.ProjectLighthouse.Types.Levels
public class QueuedLevel { {
public class QueuedLevel
{
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
[Key] public int QueuedLevelId { get; set; } [Key]
public int QueuedLevelId { get; set; }
public int UserId { get; set; } public int UserId { get; set; }

View file

@ -5,12 +5,15 @@ using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Profiles;
namespace LBPUnion.ProjectLighthouse.Types.Levels { namespace LBPUnion.ProjectLighthouse.Types.Levels
{
/// <summary> /// <summary>
/// A LittleBigPlanet level. /// A LittleBigPlanet level.
/// </summary> /// </summary>
[XmlRoot("slot"), XmlType("slot")] [XmlRoot("slot")]
public class Slot { [XmlType("slot")]
public class Slot
{
[XmlAttribute("type")] [XmlAttribute("type")]
[NotMapped] [NotMapped]
public string Type { get; set; } public string Type { get; set; }
@ -19,7 +22,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels {
[XmlElement("id")] [XmlElement("id")]
public int SlotId { get; set; } public int SlotId { get; set; }
[XmlElement("name")] [XmlElement("name")]
public string Name { get; set; } public string Name { get; set; }
@ -93,13 +95,13 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels {
[XmlIgnore] [XmlIgnore]
public bool MMPick { get; set; } public bool MMPick { get; set; }
public string SerializeResources() { public string SerializeResources()
return this.Resources {
.Aggregate("", (current, resource) => return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource));
current + LbpSerializer.StringElement("resource", resource));
} }
public string Serialize() { public string Serialize()
{
string slotData = LbpSerializer.StringElement("name", this.Name) + string slotData = LbpSerializer.StringElement("name", this.Name) +
LbpSerializer.StringElement("id", this.SlotId) + LbpSerializer.StringElement("id", this.SlotId) +
LbpSerializer.StringElement("game", 1) + LbpSerializer.StringElement("game", 1) +

View file

@ -4,26 +4,36 @@ using System.IO;
using System.Text; using System.Text;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
{
/// <summary> /// <summary>
/// The data sent from POST /LOGIN. /// The data sent from POST /LOGIN.
/// </summary> /// </summary>
public class LoginData { public class LoginData
public string Username { get; set; } = null!; {
public static readonly string UsernamePrefix = Encoding.ASCII.GetString(new byte[] { 0x04, 0x00, 0x20 }); public static readonly string UsernamePrefix = Encoding.ASCII.GetString
(
new byte[]
{
0x04, 0x00, 0x20,
}
);
public string Username { get; set; } = null!;
/// <summary> /// <summary>
/// Converts a X-I-5 Ticket into `LoginData`. /// Converts a X-I-5 Ticket into `LoginData`.
/// https://www.psdevwiki.com/ps3/X-I-5-Ticket /// https://www.psdevwiki.com/ps3/X-I-5-Ticket
/// </summary> /// </summary>
public static LoginData? CreateFromString(string str) { public static LoginData? CreateFromString(string str)
{
str = str.Replace("\b", ""); // Remove backspace characters str = str.Replace("\b", ""); // Remove backspace characters
using MemoryStream ms = new(Encoding.ASCII.GetBytes(str)); using MemoryStream ms = new(Encoding.ASCII.GetBytes(str));
using BinaryReader reader = new(ms); using BinaryReader reader = new(ms);
if(!str.Contains(UsernamePrefix)) return null; if (!str.Contains(UsernamePrefix)) return null;
LoginData loginData = new(); LoginData loginData = new();

View file

@ -2,23 +2,23 @@ using System.Collections.Generic;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
{
/// <summary> /// <summary>
/// Response to POST /login /// Response to POST /login
/// </summary> /// </summary>
[XmlRoot("loginResult"), XmlType("loginResult")] [XmlRoot("loginResult")]
public class LoginResult { [XmlType("loginResult")]
public class LoginResult
{
[XmlElement("authTicket")] [XmlElement("authTicket")]
public string AuthTicket { get; set; } public string AuthTicket { get; set; }
[XmlElement("lbpEnvVer")] [XmlElement("lbpEnvVer")]
public string LbpEnvVer { get; set; } public string LbpEnvVer { get; set; }
public string Serialize() { public string Serialize()
return LbpSerializer.Elements( => LbpSerializer.Elements
new KeyValuePair<string, object>("authTicket", this.AuthTicket), (new KeyValuePair<string, object>("authTicket", this.AuthTicket), new KeyValuePair<string, object>("lbpEnvVer", this.LbpEnvVer));
new KeyValuePair<string, object>("lbpEnvVer", this.LbpEnvVer)
);
}
} }
} }

View file

@ -1,5 +1,5 @@
namespace LBPUnion.ProjectLighthouse.Types.Match { namespace LBPUnion.ProjectLighthouse.Types.Match
public interface IMatchData { {
public interface IMatchData
} {}
} }

View file

@ -1,5 +1,7 @@
namespace LBPUnion.ProjectLighthouse.Types.Match { namespace LBPUnion.ProjectLighthouse.Types.Match
public enum RoomState { {
public enum RoomState
{
Idle = 0, Idle = 0,
LookingForPlayersForLevel = 1, LookingForPlayersForLevel = 1,
Unknown = 2, Unknown = 2,

View file

@ -1,5 +1,7 @@
namespace LBPUnion.ProjectLighthouse.Types.Match { namespace LBPUnion.ProjectLighthouse.Types.Match
public class UpdateMyPlayerData : IMatchData { {
public class UpdateMyPlayerData : IMatchData
{
public string Player; public string Player;
} }
} }

View file

@ -1,7 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace LBPUnion.ProjectLighthouse.Types.Match { namespace LBPUnion.ProjectLighthouse.Types.Match
public class UpdatePlayersInRoom : IMatchData { {
public class UpdatePlayersInRoom : IMatchData
{
public List<string> Players; public List<string> Players;
public List<string> Reservations; public List<string> Reservations;
} }

View file

@ -1,10 +1,12 @@
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.News { namespace LBPUnion.ProjectLighthouse.Types.News
{
/// <summary> /// <summary>
/// Used on the info moon on LBP1. Broken for unknown reasons /// Used on the info moon on LBP1. Broken for unknown reasons
/// </summary> /// </summary>
public class NewsEntry { public class NewsEntry
{
public int Id { get; set; } public int Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Summary { get; set; } public string Summary { get; set; }
@ -13,8 +15,8 @@ namespace LBPUnion.ProjectLighthouse.Types.News {
public string Category { get; set; } public string Category { get; set; }
public long Date { get; set; } public long Date { get; set; }
public string Serialize() { public string Serialize()
return LbpSerializer.StringElement("id", this.Id) + => LbpSerializer.StringElement("id", this.Id) +
LbpSerializer.StringElement("title", this.Title) + LbpSerializer.StringElement("title", this.Title) +
LbpSerializer.StringElement("summary", this.Summary) + LbpSerializer.StringElement("summary", this.Summary) +
LbpSerializer.StringElement("text", this.Text) + LbpSerializer.StringElement("text", this.Text) +
@ -22,5 +24,4 @@ namespace LBPUnion.ProjectLighthouse.Types.News {
this.Image.Serialize() + this.Image.Serialize() +
LbpSerializer.StringElement("category", this.Category); LbpSerializer.StringElement("category", this.Category);
} }
}
} }

View file

@ -1,14 +1,13 @@
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.News { namespace LBPUnion.ProjectLighthouse.Types.News
public class NewsImage { {
public class NewsImage
{
public string Hash { get; set; } public string Hash { get; set; }
public string Alignment { get; set; } public string Alignment { get; set; }
public string Serialize() { public string Serialize()
return LbpSerializer.StringElement("image", => LbpSerializer.StringElement("image", LbpSerializer.StringElement("hash", this.Hash) + LbpSerializer.StringElement("alignment", this.Alignment));
LbpSerializer.StringElement("hash", this.Hash) +
LbpSerializer.StringElement("alignment", this.Alignment));
}
} }
} }

View file

@ -1,22 +1,26 @@
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Types.Profiles { namespace LBPUnion.ProjectLighthouse.Types.Profiles
{
[Keyless] [Keyless]
public class ClientsConnected { public class ClientsConnected
{
public bool Lbp1 { get; set; } public bool Lbp1 { get; set; }
public bool Lbp2 { get; set; } public bool Lbp2 { get; set; }
public bool LbpMe { get; set; } public bool LbpMe { get; set; }
public bool Lbp3Ps3 { get; set; } public bool Lbp3Ps3 { get; set; }
public bool Lbp3Ps4 { get; set; } public bool Lbp3Ps4 { get; set; }
public string Serialize() { public string Serialize()
return LbpSerializer.StringElement("clientsConnected", => LbpSerializer.StringElement
(
"clientsConnected",
LbpSerializer.StringElement("lbp1", this.Lbp1) + LbpSerializer.StringElement("lbp1", this.Lbp1) +
LbpSerializer.StringElement("lbp2", this.Lbp2) + LbpSerializer.StringElement("lbp2", this.Lbp2) +
LbpSerializer.StringElement("lbpme", this.LbpMe) + LbpSerializer.StringElement("lbpme", this.LbpMe) +
LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) + LbpSerializer.StringElement("lbp3ps3", this.Lbp3Ps3) +
LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4)); LbpSerializer.StringElement("lbp3ps4", this.Lbp3Ps4)
} );
} }
} }

View file

@ -3,9 +3,12 @@ using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.Profiles { namespace LBPUnion.ProjectLighthouse.Types.Profiles
[XmlRoot("comment"), XmlType("comment")] {
public class Comment { [XmlRoot("comment")]
[XmlType("comment")]
public class Comment
{
[Key] [Key]
[XmlAttribute("id")] [XmlAttribute("id")]
public int CommentId { get; set; } public int CommentId { get; set; }
@ -24,24 +27,22 @@ namespace LBPUnion.ProjectLighthouse.Types.Profiles {
[XmlElement("message")] [XmlElement("message")]
public string Message { get; set; } public string Message { get; set; }
public int ThumbsUp { get; set; } public int ThumbsUp { get; set; }
public int ThumbsDown { get; set; } public int ThumbsDown { get; set; }
private string serialize() { private string serialize()
return LbpSerializer.StringElement("id", this.CommentId) + => LbpSerializer.StringElement("id", this.CommentId) +
LbpSerializer.StringElement("npHandle", this.Poster.Username) + LbpSerializer.StringElement("npHandle", this.Poster.Username) +
LbpSerializer.StringElement("timestamp", this.Timestamp) + LbpSerializer.StringElement("timestamp", this.Timestamp) +
LbpSerializer.StringElement("message", this.Message) + LbpSerializer.StringElement("message", this.Message) +
LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + LbpSerializer.StringElement("thumbsup", this.ThumbsUp) +
LbpSerializer.StringElement("thumbsdown", this.ThumbsDown); LbpSerializer.StringElement("thumbsdown", this.ThumbsDown);
}
public string Serialize(int yourThumb) { public string Serialize
return LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb)); (int yourThumb)
} => LbpSerializer.StringElement("comment", this.serialize() + LbpSerializer.StringElement("yourthumb", yourThumb));
public string Serialize() { public string Serialize() => LbpSerializer.StringElement("comment", this.serialize());
return LbpSerializer.StringElement("comment", this.serialize());
}
} }
} }

View file

@ -1,8 +1,12 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace LBPUnion.ProjectLighthouse.Types.Profiles { namespace LBPUnion.ProjectLighthouse.Types.Profiles
public class LastMatch { {
[Key] public int UserId { get; set; } public class LastMatch
{
[Key]
public int UserId { get; set; }
public long Timestamp { get; set; } public long Timestamp { get; set; }
} }
} }

View file

@ -1,12 +1,15 @@
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.Profiles { namespace LBPUnion.ProjectLighthouse.Types.Profiles
{
/// <summary> /// <summary>
/// The location of a slot on a planet. /// The location of a slot on a planet.
/// </summary> /// </summary>
[XmlRoot("location"), XmlType("location")] [XmlRoot("location")]
public class Location { [XmlType("location")]
public class Location
{
[XmlIgnore] [XmlIgnore]
public int Id { get; set; } public int Id { get; set; }
@ -16,9 +19,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Profiles {
[XmlElement("y")] [XmlElement("y")]
public int Y { get; set; } public int Y { get; set; }
public string Serialize() { public string Serialize() => LbpSerializer.StringElement("x", this.X) + LbpSerializer.StringElement("y", this.Y);
return LbpSerializer.StringElement("x", this.X) +
LbpSerializer.StringElement("y", this.Y);
}
} }
} }

View file

@ -1,8 +1,11 @@
using System.Xml.Serialization; using System.Xml.Serialization;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
[XmlRoot("resources"), XmlType("resources")] {
public class ResourceList { [XmlRoot("resources")]
[XmlType("resources")]
public class ResourceList
{
[XmlElement("resource")] [XmlElement("resource")]
public string[] Resources; public string[] Resources;
} }

View file

@ -1,15 +1,17 @@
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.Settings { namespace LBPUnion.ProjectLighthouse.Types.Settings
public class PrivacySettings { {
public class PrivacySettings
{
public string LevelVisibility { get; set; } public string LevelVisibility { get; set; }
public string ProfileVisibility { get; set; } public string ProfileVisibility { get; set; }
public string Serialize() { public string Serialize()
return LbpSerializer.StringElement("privacySettings", => LbpSerializer.StringElement
LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + (
LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility) "privacySettings",
LbpSerializer.StringElement("levelVisibility", this.LevelVisibility) + LbpSerializer.StringElement("profileVisibility", this.ProfileVisibility)
); );
} }
}
} }

View file

@ -3,8 +3,10 @@ using System;
using Kettu; using Kettu;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
namespace LBPUnion.ProjectLighthouse.Types.Settings { namespace LBPUnion.ProjectLighthouse.Types.Settings
public static class ServerSettings { {
public static class ServerSettings
{
/// <summary> /// <summary>
/// The maximum amount of slots allowed on users' earth /// The maximum amount of slots allowed on users' earth
/// </summary> /// </summary>
@ -15,11 +17,11 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings {
public const string ServerName = "ProjectLighthouse"; public const string ServerName = "ProjectLighthouse";
private static string? dbConnectionString; private static string? dbConnectionString;
public static string DbConnectionString { public static string DbConnectionString {
get { get {
if(dbConnectionString == null) { if (dbConnectionString == null) return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? "";
return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? "";
}
return dbConnectionString; return dbConnectionString;
} }
set => dbConnectionString = value; set => dbConnectionString = value;
@ -27,10 +29,12 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings {
public static bool DbConnected { public static bool DbConnected {
get { get {
try { try
{
return new Database().Database.CanConnect(); return new Database().Database.CanConnect();
} }
catch(Exception e) { catch(Exception e)
{
Logger.Log(e.ToString(), LoggerLevelDatabase.Instance); Logger.Log(e.ToString(), LoggerLevelDatabase.Instance);
return false; return false;
} }

View file

@ -1,9 +1,13 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
public class Token { {
public class Token
{
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
[Key] public int TokenId { get; set; } [Key]
public int TokenId { get; set; }
public int UserId { get; set; } public int UserId { get; set; }
public string UserToken { get; set; } public string UserToken { get; set; }
} }

View file

@ -4,8 +4,13 @@ using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Profiles;
using LBPUnion.ProjectLighthouse.Types.Settings; using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Types { namespace LBPUnion.ProjectLighthouse.Types
public class User { {
public class User
{
// [NotMapped]
public readonly ClientsConnected ClientsConnected = new();
public int UserId { get; set; } public int UserId { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string IconHash { get; set; } public string IconHash { get; set; }
@ -19,6 +24,7 @@ namespace LBPUnion.ProjectLighthouse.Types {
/// A user-customizable biography shown on the profile card /// A user-customizable biography shown on the profile card
/// </summary> /// </summary>
public string Biography { get; set; } public string Biography { get; set; }
public int ReviewCount { get; set; } public int ReviewCount { get; set; }
public int CommentCount { get; set; } public int CommentCount { get; set; }
public int PhotosByMeCount { get; set; } public int PhotosByMeCount { get; set; }
@ -43,55 +49,8 @@ namespace LBPUnion.ProjectLighthouse.Types {
public string PlanetHash { get; set; } = ""; public string PlanetHash { get; set; } = "";
// [NotMapped] public string Serialize()
public readonly ClientsConnected ClientsConnected = new(); {
#region Slots
/// <summary>
/// The number of used slots on the earth
/// </summary>
[NotMapped]
public int UsedSlots {
get {
using Database database = new();
return database.Slots.Count(s => s.CreatorId == this.UserId);
}
}
/// <summary>
/// The number of slots remaining on the earth
/// </summary>
public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots;
private static readonly string[] slotTypes = {
// "lbp1",
"lbp2",
"lbp3",
"crossControl",
};
private string SerializeSlots() {
string slots = string.Empty;
slots += LbpSerializer.StringElement("lbp1UsedSlots", this.UsedSlots);
slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots);
slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots);
foreach(string slotType in slotTypes) {
slots += LbpSerializer.StringElement(slotType + "UsedSlots", this.UsedSlots);
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots);
// ReSharper disable once StringLiteralTypo
slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0);
slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots);
}
return slots;
}
#endregion Slots
public string Serialize() {
string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) +
LbpSerializer.StringElement("game", this.Game) + LbpSerializer.StringElement("game", this.Game) +
this.SerializeSlots() + this.SerializeSlots() +
@ -120,5 +79,52 @@ namespace LBPUnion.ProjectLighthouse.Types {
return LbpSerializer.TaggedStringElement("user", user, "type", "user"); return LbpSerializer.TaggedStringElement("user", user, "type", "user");
} }
#region Slots
/// <summary>
/// The number of used slots on the earth
/// </summary>
[NotMapped]
public int UsedSlots {
get {
using Database database = new();
return database.Slots.Count(s => s.CreatorId == this.UserId);
}
}
/// <summary>
/// The number of slots remaining on the earth
/// </summary>
public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots;
private static readonly string[] slotTypes =
{
// "lbp1",
"lbp2", "lbp3", "crossControl",
};
private string SerializeSlots()
{
string slots = string.Empty;
slots += LbpSerializer.StringElement("lbp1UsedSlots", this.UsedSlots);
slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots);
slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots);
foreach (string slotType in slotTypes)
{
slots += LbpSerializer.StringElement(slotType + "UsedSlots", this.UsedSlots);
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots);
// ReSharper disable once StringLiteralTypo
slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0);
slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots);
}
return slots;
}
#endregion Slots
} }
} }

View file

@ -1,60 +1,75 @@
# Project Lighthouse # Project Lighthouse
Project Lighthouse is an umbrella project for all work to investigate and develop private servers for LittleBigPlanet. Project Lighthouse is an umbrella project for all work to investigate and develop private servers for LittleBigPlanet.
This project is the main server component that LittleBigPlanet games connect to. This project is the main server component that LittleBigPlanet games connect to.
## WARNING! ## WARNING!
This is beta software, and thus is not ready for public use yet.
We're not responsible if someone connects and hacks your entire machine and deletes all your files. This is beta software, and thus is not ready for public use yet. We're not responsible if someone connects and hacks
your entire machine and deletes all your files.
That said, feel free to develop privately! That said, feel free to develop privately!
## Building ## Building
This will be written when we're out of beta. Consider this your barrier to entry ;). This will be written when we're out of beta. Consider this your barrier to entry ;).
## Running ## Running
Lighthouse requires a MySQL database at this time.
For Linux users running docker, one can be set up using the `docker-compose.yml` file in the root of the project folder.
Next, make sure the `LIGHTHOUSE_DB_CONNECTION_STRING` environment variable is set correctly. Lighthouse requires a MySQL database at this time. For Linux users running docker, one can be set up using
By default, it is `server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse`. If you are running the database via the `docker-compose.yml` file in the root of the project folder.
the above `docker-compose.yml` you shouldn't need to change this. For other development/especially production environments
Next, make sure the `LIGHTHOUSE_DB_CONNECTION_STRING` environment variable is set correctly. By default, it
is `server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse`. If you are running the database via the
above `docker-compose.yml` you shouldn't need to change this. For other development/especially production environments
you will need to change this. you will need to change this.
Once you've gotten MySQL running you can run Lighthouse. It will take care of the rest. Once you've gotten MySQL running you can run Lighthouse. It will take care of the rest.
## Connecting ## Connecting
PS3 is difficult to set up, so I will be going over how to set up RPCS3 instead. A guide will be coming for PS3 closer to release.
You can also follow this guide if you want to learn how to modify your EBOOT.
*Note: This requires a modified copy of RPCS3. You can find a working patch [here](https://gist.github.com/jvyden/0d9619f7dd3dbc49f7583486bdacad75).* PS3 is difficult to set up, so I will be going over how to set up RPCS3 instead. A guide will be coming for PS3 closer
to release. You can also follow this guide if you want to learn how to modify your EBOOT.
Start by getting a copy of LittleBigPlanet 2 installed. It can be digital (NPUA80662) or disc (BCUS98245). *Note: This requires a modified copy of RPCS3. You can find a working
I won't get into how because if you got this far you should already know what you're doing. For those that don't, the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it. patch [here](https://gist.github.com/jvyden/0d9619f7dd3dbc49f7583486bdacad75).*
Next, download [UnionPatcher](https://github.com/LBPUnion/UnionPatcher/). Binaries can be found by reading the README.md file. Start by getting a copy of LittleBigPlanet 2 installed. It can be digital (NPUA80662) or disc (BCUS98245). I won't get
into how because if you got this far you should already know what you're doing. For those that don't,
the [RPCS3 Quickstart Guide](https://rpcs3.net/quickstart) should cover it.
You should have everything you need now, so open up RPCS3 and go to Utilities -> Decrypt PS3 Binaries. Point this to `rpcs3/dev_hdd0/game/(title id)/USRDIR/EBOOT.BIN`. Next, download [UnionPatcher](https://github.com/LBPUnion/UnionPatcher/). Binaries can be found by reading the README.md
file.
This should give you a file named `EBOOT.elf` in the same folder. Next, fire up UnionPatcher (making sure to select the correct project to start, e.g. on Mac launch `UnionPatcher.Gui.MacOS`.) You should have everything you need now, so open up RPCS3 and go to Utilities -> Decrypt PS3 Binaries. Point this
to `rpcs3/dev_hdd0/game/(title id)/USRDIR/EBOOT.BIN`.
Now that you have your decrypted eboot, open UnionPatcher and select the `EBOOT.elf` you got earlier in the top box, enter `http://localhost:10060/LITTLEBIGPLANETPS3_XML` in the second, and the output filename in the third. This should give you a file named `EBOOT.elf` in the same folder. Next, fire up UnionPatcher (making sure to select the
For this guide I'll use `EBOOTlocalhost.elf`. correct project to start, e.g. on Mac launch `UnionPatcher.Gui.MacOS`.)
Now that you have your decrypted eboot, open UnionPatcher and select the `EBOOT.elf` you got earlier in the top box,
enter `http://localhost:10060/LITTLEBIGPLANETPS3_XML` in the second, and the output filename in the third. For this
guide I'll use `EBOOTlocalhost.elf`.
Now, copy the `EBOOTlocalhost.elf` file to where you got your `EBOOT.elf` file from, and you're now good to go. Now, copy the `EBOOTlocalhost.elf` file to where you got your `EBOOT.elf` file from, and you're now good to go.
To launch the game with the patched EBOOT, open up RPCS3, go to File, Boot SELF/ELF, and open up `EBOOTlocalhost.elf`. To launch the game with the patched EBOOT, open up RPCS3, go to File, Boot SELF/ELF, and open up `EBOOTlocalhost.elf`.
Assuming you are running the patched version of RPCS3, you patched the file correctly, the database is migrated, and Lighthouse is running, the game should now connect. Assuming you are running the patched version of RPCS3, you patched the file correctly, the database is migrated, and
Lighthouse is running, the game should now connect.
Finally, take a break. Chances are that took a while. Finally, take a break. Chances are that took a while.
## Contributing Tips ## Contributing Tips
### Database ### Database
Some modifications may require updates to the database schema. You can automatically create a migration file by: Some modifications may require updates to the database schema. You can automatically create a migration file by:
1. Making sure the tools are installed. You can do this by running `dotnet tool restore`. 1. Making sure the tools are installed. You can do this by running `dotnet tool restore`.
2. Making sure `LIGHTHOUSE_DB_CONNECTION_STRING` is set correctly. See the `Running` section for more details. 2. Making sure `LIGHTHOUSE_DB_CONNECTION_STRING` is set correctly. See the `Running` section for more details.
3. Making your changes to the database. I won't cover this since if you're making database changes you should know what you're doing. 3. Making your changes to the database. I won't cover this since if you're making database changes you should know what
you're doing.
4. Running `dotnet ef migrations add <NameOfMigrationInPascalCase> --project ProjectLighthouse`. 4. Running `dotnet ef migrations add <NameOfMigrationInPascalCase> --project ProjectLighthouse`.
## Compatibility across games and platforms ## Compatibility across games and platforms