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,5 +1,6 @@
<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">
<settings>
<option name="envFilePath" value=""/>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,16 +4,20 @@ using System.Linq;
using System.Text;
using LBPUnion.ProjectLighthouse.Types.Files;
namespace LBPUnion.ProjectLighthouse.Helpers {
public static class FileHelper {
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class FileHelper
{
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
public static bool IsFileSafe(LbpFile file) {
public static bool IsFileSafe(LbpFile file)
{
if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
return file.FileType switch {
return file.FileType switch
{
LbpFileType.FileArchive => false,
LbpFileType.Painting => true,
LbpFileType.Unknown => false,
@ -30,7 +34,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
};
}
public static LbpFileType DetermineFileType(byte[] data) {
public static LbpFileType DetermineFileType(byte[] data)
{
using MemoryStream ms = new(data);
using BinaryReader reader = new(ms);
@ -39,7 +44,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
byte[] header = reader.ReadBytes(3);
return Encoding.ASCII.GetString(header) switch {
return Encoding.ASCII.GetString(header) switch
{
"PTG" => LbpFileType.Painting,
"TEX" => LbpFileType.Texture,
"FSH" => LbpFileType.Script,
@ -52,7 +58,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers {
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)));
}

View file

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

View file

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

View file

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

View file

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

View file

@ -3,15 +3,16 @@ using Kettu;
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using Microsoft.Extensions.Logging;
namespace LBPUnion.ProjectLighthouse.Logging {
public class AspNetToKettuLogger : ILogger {
namespace LBPUnion.ProjectLighthouse.Logging
{
public class AspNetToKettuLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state) {
return NullScope.Instance;
}
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
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);
Logger.Log(state.ToString(), loggerLevel);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,26 +2,30 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace LBPUnion.ProjectLighthouse.Serialization {
namespace LBPUnion.ProjectLighthouse.Serialization
{
/// <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.
/// </summary>
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class LbpSerializer {
public static class LbpSerializer
{
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(string key, object value) => $"<{key}>{value}</{key}>";
public static string TaggedStringElement(KeyValuePair<string, object> pair, KeyValuePair<string, object> tagPair) =>
$"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}</{pair.Key}>";
public static string TaggedStringElement
(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) =>
$"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
public static string Elements(params KeyValuePair<string, object>[] pairs) =>
pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair));
public static string Elements
(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;
namespace LBPUnion.ProjectLighthouse.Serialization {
public class XmlOutputFormatter : StringOutputFormatter {
public XmlOutputFormatter() {
namespace LBPUnion.ProjectLighthouse.Serialization
{
public class XmlOutputFormatter : StringOutputFormatter
{
public XmlOutputFormatter()
{
this.SupportedMediaTypes.Add("text/xml");
this.SupportedMediaTypes.Add("application/xml");
}

View file

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

View file

@ -1,21 +1,24 @@
using LBPUnion.ProjectLighthouse.Helpers;
namespace LBPUnion.ProjectLighthouse.Types.Files {
public class LbpFile {
public LbpFile(byte[] data) {
this.Data = data;
this.FileType = FileHelper.DetermineFileType(this.Data);
}
/// <summary>
/// The type of file.
/// </summary>
public LbpFileType FileType;
namespace LBPUnion.ProjectLighthouse.Types.Files
{
public class LbpFile
{
/// <summary>
/// A buffer of the file's data.
/// </summary>
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 {
public enum LbpFileType {
namespace LBPUnion.ProjectLighthouse.Types.Files
{
public enum LbpFileType
{
Script, // .ff, FSH
Texture, // TEX
Level, // LVL

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,20 +4,30 @@ using System.IO;
using System.Text;
using LBPUnion.ProjectLighthouse.Helpers;
namespace LBPUnion.ProjectLighthouse.Types {
namespace LBPUnion.ProjectLighthouse.Types
{
/// <summary>
/// The data sent from POST /LOGIN.
/// </summary>
public class LoginData {
public string Username { get; set; } = null!;
public class LoginData
{
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>
/// Converts a X-I-5 Ticket into `LoginData`.
/// https://www.psdevwiki.com/ps3/X-I-5-Ticket
/// </summary>
public static LoginData? CreateFromString(string str) {
public static LoginData? CreateFromString(string str)
{
str = str.Replace("\b", ""); // Remove backspace characters
using MemoryStream ms = new(Encoding.ASCII.GetBytes(str));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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