Merge main into crowdin

This commit is contained in:
jvyden 2022-06-10 04:30:23 -04:00
commit d96cc5786b
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
335 changed files with 5039 additions and 3021 deletions

View file

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-ef": { "dotnet-ef": {
"version": "6.0.4", "version": "6.0.5",
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
] ]

View file

@ -14,7 +14,7 @@ jobs:
matrix: matrix:
os: os:
- { prettyName: Linux, fullName: ubuntu-latest, database: true, webTest: true } - { prettyName: Linux, fullName: ubuntu-latest, database: true, webTest: true }
timeout-minutes: 10 timeout-minutes: 5
env: env:
DB_DATABASE: lighthouse DB_DATABASE: lighthouse
DB_USER: root DB_USER: root
@ -44,16 +44,16 @@ jobs:
- name: Run tests on ProjectLighthouse.Tests - name: Run tests on ProjectLighthouse.Tests
continue-on-error: true continue-on-error: true
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests
- name: Run tests on ProjectLighthouse.Tests.GameApiTests - name: Run tests on ProjectLighthouse.Tests.GameApiTests
continue-on-error: true continue-on-error: true
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests
- name: Run tests on ProjectLighthouse.Tests.WebsiteTests - name: Run tests on ProjectLighthouse.Tests.WebsiteTests
if: ${{ matrix.os.webTest }} if: ${{ matrix.os.webTest }}
continue-on-error: true continue-on-error: true
run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests
# Attempt to upload results even if test fails. # Attempt to upload results even if test fails.
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always

1
.gitignore vendored
View file

@ -23,6 +23,7 @@ png/
/ProjectLighthouse/r/* /ProjectLighthouse/r/*
/ProjectLighthouse/logs/* /ProjectLighthouse/logs/*
lighthouse.config.json lighthouse.config.json
lighthouse.yml
gitBranch.txt gitBranch.txt
gitVersion.txt gitVersion.txt
gitRemotes.txt gitRemotes.txt

View file

@ -3,11 +3,13 @@
<component name="UserContentModel"> <component name="UserContentModel">
<attachedFolders /> <attachedFolders />
<explicitIncludes> <explicitIncludes>
<Path>.config/dotnet-tools.json</Path>
<Path>.github</Path> <Path>.github</Path>
<Path>.gitignore</Path> <Path>.gitignore</Path>
<Path>.idea</Path> <Path>.idea</Path>
<Path>CONTRIBUTING.md</Path> <Path>CONTRIBUTING.md</Path>
<Path>DatabaseMigrations</Path> <Path>DatabaseMigrations</Path>
<Path>LICENSE</Path>
<Path>ProjectLighthouse.sln.DotSettings</Path> <Path>ProjectLighthouse.sln.DotSettings</Path>
<Path>ProjectLighthouse.sln.DotSettings.user</Path> <Path>ProjectLighthouse.sln.DotSettings.user</Path>
<Path>README.md</Path> <Path>README.md</Path>
@ -15,6 +17,7 @@
<Path>crowdin.yml</Path> <Path>crowdin.yml</Path>
<Path>docker-compose.yml</Path> <Path>docker-compose.yml</Path>
<Path>global.json</Path> <Path>global.json</Path>
<Path>scripts-and-tools</Path>
</explicitIncludes> </explicitIncludes>
<explicitExcludes> <explicitExcludes>
<Path>.idea/.idea.ProjectLighthouse/.idea/workspace.xml</Path> <Path>.idea/.idea.ProjectLighthouse/.idea/workspace.xml</Path>

View file

@ -2,6 +2,5 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/ProjectLighthouse/Fomantic" vcs="Git" />
</component> </component>
</project> </project>

View file

@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Development Database" type="docker-deploy" factoryName="docker-compose.yml" <configuration default="false" name="Development Databases" type="docker-deploy" factoryName="docker-compose.yml"
server-name="Docker"> server-name="Docker">
<deployment type="docker-compose.yml"> <deployment type="docker-compose.yml">
<settings> <settings>
@ -7,6 +7,7 @@
<option name="sourceFilePath" value="docker-compose.yml"/> <option name="sourceFilePath" value="docker-compose.yml"/>
</settings> </settings>
</deployment> </deployment>
<EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="true" isPublishEnabled="true"/>
<method v="2"/> <method v="2"/>
</configuration> </configuration>
</component> </component>

View file

@ -0,0 +1,21 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Lighthouse API" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.API/bin/Debug/net6.0/LBPUnion.ProjectLighthouse.Servers.API" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.API/ProjectLighthouse.Servers.API.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net6.0" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Development Databases" run_configuration_type="docker-deploy" />
<option name="Build" />
</method>
</configuration>
</component>

View file

@ -0,0 +1,21 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Lighthouse Gameserver" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.GameServer/bin/Debug/net6.0/LBPUnion.ProjectLighthouse.Servers.GameServer" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.GameServer/ProjectLighthouse.Servers.GameServer.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net6.0" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Development Databases" run_configuration_type="docker-deploy" />
<option name="Build" />
</method>
</configuration>
</component>

View file

@ -0,0 +1,21 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Lighthouse Website" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.Website/bin/Debug/net6.0/LBPUnion.ProjectLighthouse.Servers.Website" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/ProjectLighthouse.Servers.Website/ProjectLighthouse.Servers.Website.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net6.0" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Development Databases" run_configuration_type="docker-deploy" />
<option name="Build" />
</method>
</configuration>
</component>

21
.run/Lighthouse.run.xml Normal file
View file

@ -0,0 +1,21 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Lighthouse" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/ProjectLighthouse/bin/Debug/net6.0/LBPUnion.ProjectLighthouse" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/ProjectLighthouse/ProjectLighthouse.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="Unloaded" />
<option name="PROJECT_TFM" value="net6.0" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Development Databases" run_configuration_type="docker-deploy" />
<option name="Build" />
</method>
</configuration>
</component>

View file

@ -1,15 +1,12 @@
using System; #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Configuration;
using System.Linq; using LBPUnion.ProjectLighthouse.Levels;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Servers.API.Responses;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers;
/// <summary> /// <summary>
/// A collection of endpoints relating to slots. /// A collection of endpoints relating to slots.

View file

@ -1,11 +1,9 @@
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.API.Responses;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Api;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers;
/// <summary> /// <summary>
/// A collection of endpoints relating to statistics. /// A collection of endpoints relating to statistics.

View file

@ -1,14 +1,12 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
// ReSharper disable RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute // ReSharper disable RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute
namespace LBPUnion.ProjectLighthouse.Controllers.Api; namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers;
/// <summary> /// <summary>
/// A collection of endpoints relating to users. /// A collection of endpoints relating to users.
@ -50,7 +48,7 @@ public class UserEndpoints : ApiEndpointController
[HttpGet("user/{id:int}/status")] [HttpGet("user/{id:int}/status")]
[ProducesResponseType(typeof(UserStatus), StatusCodes.Status200OK)] [ProducesResponseType(typeof(UserStatus), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserStatus(int id) public IActionResult GetUserStatus(int id)
{ {
UserStatus userStatus = new(this.database, id); UserStatus userStatus = new(this.database, id);

View file

@ -0,0 +1,36 @@
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet;
using LBPUnion.ProjectLighthouse.Servers.API.Startup;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace LBPUnion.ProjectLighthouse.Servers.API;
public static class Program
{
public static void Main(string[] args)
{
StartupTasks.Run(args, ServerType.Api);
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults
(
webBuilder =>
{
webBuilder.UseStartup<ApiStartup>();
webBuilder.UseUrls(ServerConfiguration.Instance.ApiListenUrl);
}
)
.ConfigureLogging
(
logging =>
{
logging.ClearProviders();
logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToLighthouseLoggerProvider>());
}
);
}

View file

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>LBPUnion.ProjectLighthouse.Servers.API</AssemblyName>
<RootNamespace>LBPUnion.ProjectLighthouse.Servers.API</RootNamespace>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
</ItemGroup>
<ItemGroup>
<None Remove="gitVersion.txt"/>
<EmbeddedResource Include="gitVersion.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitBranch.txt"/>
<EmbeddedResource Include="gitBranch.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitRemotes.txt"/>
<EmbeddedResource Include="gitRemotes.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitUnpushed.txt"/>
<EmbeddedResource Include="gitUnpushed.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;"/>
<Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;"/>
<Exec Command="git remote -v &gt; &quot;$(ProjectDir)/gitRemotes.txt&quot;"/>
<Exec Command="git log --branches --not --remotes --oneline &gt; &quot;$(ProjectDir)/gitUnpushed.txt&quot;"/>
</Target>
</Project>

View file

@ -1,4 +1,8 @@
namespace LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types;
namespace LBPUnion.ProjectLighthouse.Servers.API.Responses;
public struct MinimalSlot public struct MinimalSlot
{ {

View file

@ -1,4 +1,4 @@
namespace LBPUnion.ProjectLighthouse.Types.Api; namespace LBPUnion.ProjectLighthouse.Servers.API.Responses;
public class StatisticsResponse public class StatisticsResponse
{ {

View file

@ -0,0 +1,74 @@
using LBPUnion.ProjectLighthouse.Middlewares;
using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.OpenApi.Models;
namespace LBPUnion.ProjectLighthouse.Servers.API.Startup;
public class ApiStartup
{
public ApiStartup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc
(
options =>
{
options.OutputFormatters.Add(new JsonOutputFormatter());
}
);
services.AddDbContext<Database>();
services.AddSwaggerGen
(
c =>
{
// Give swagger the name and version of our project
c.SwaggerDoc
(
"v1",
new OpenApiInfo
{
Title = "Project Lighthouse API",
Version = "v1",
}
);
// Filter out endpoints not in /api/v1
c.DocumentFilter<SwaggerFilter>();
// Add XMLDoc to swagger
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "LBPUnion.ProjectLighthouse.Servers.API.xml"));
}
);
}
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
#if DEBUG
app.UseDeveloperExceptionPage();
#endif
app.UseSwagger();
app.UseSwaggerUI
(
c =>
{
c.SwaggerEndpoint("v1/swagger.json", "Project Lighthouse API");
}
);
app.UseMiddleware<RequestLogMiddleware>();
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}

View file

@ -0,0 +1,15 @@
using LBPUnion.ProjectLighthouse.Middlewares;
namespace LBPUnion.ProjectLighthouse.Servers.API.Startup;
public class ApiTestStartup : ApiStartup
{
public ApiTestStartup(IConfiguration configuration) : base(configuration)
{}
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<FakeRemoteIPAddressMiddleware>();
base.Configure(app, env);
}
}

View file

@ -1,10 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
namespace LBPUnion.ProjectLighthouse.Helpers; namespace LBPUnion.ProjectLighthouse.Servers.API;
/// <summary>
/// <para>
/// A filter for the swagger documentation endpoint.
/// </para>
/// <para>
/// Makes sure that only endpoints under <c>/api/v1</c> show up.
/// </para>
/// </summary>
public class SwaggerFilter : IDocumentFilter public class SwaggerFilter : IDocumentFilter
{ {
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View file

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View file

@ -1,12 +1,11 @@
#nullable enable #nullable enable
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -25,7 +24,7 @@ public class ClientConfigurationController : ControllerBase
public async Task<IActionResult> NetworkSettings() public async Task<IActionResult> NetworkSettings()
{ {
GameToken? token = await this.database.GameTokenFromRequest(this.Request); GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return null; if (token == null) return this.StatusCode(403, "");
HostString hostname = this.Request.Host; HostString hostname = this.Request.Host;
return this.Ok return this.Ok
@ -33,7 +32,7 @@ public class ClientConfigurationController : ControllerBase
"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 false\nAllowModeratedPoppetItems false\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 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.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\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\nAllowOnlineCreate true" + "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 false\nAllowModeratedPoppetItems false\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 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.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\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\nAllowOnlineCreate true" +
$"TelemetryServer {hostname}\n" + $"TelemetryServer {hostname}\n" +
$"CDNHostName {hostname}\n" + $"CDNHostName {hostname}\n" +
$"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" $"ShowLevelBoos {ServerConfiguration.Instance.UserGeneratedContentLimits.BooingEnabled.ToString().ToLower()}\n"
); );
} }

View file

@ -1,19 +1,15 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]

View file

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]

View file

@ -1,17 +1,16 @@
#nullable enable #nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.StorableLists;
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -57,14 +56,13 @@ public class FriendsController : ControllerBase
blockedUsers.Add(blockedUser.UserId); blockedUsers.Add(blockedUser.UserId);
} }
if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId)) UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId);
{ if (friendStore == null) friendStore = UserFriendStore.CreateUserFriendData(user.UserId);
FriendHelper.FriendIdsByUserId.Remove(user.UserId);
FriendHelper.BlockedIdsByUserId.Remove(user.UserId);
}
FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray()); friendStore.FriendIds = friends.Select(u => u.UserId).ToList();
FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray()); friendStore.BlockedIds = blockedUsers;
UserFriendStore.UpdateFriendData(friendStore);
string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username));
@ -82,11 +80,13 @@ public class FriendsController : ControllerBase
User user = userAndToken.Value.Item1; User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2; GameToken gameToken = userAndToken.Value.Item2;
if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null) UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId);
if (friendStore == null)
return this.Ok(LbpSerializer.BlankElement("myFriends")); return this.Ok(LbpSerializer.BlankElement("myFriends"));
string friends = ""; string friends = "";
foreach (int friendId in friendIds) foreach (int friendId in friendStore.FriendIds)
{ {
User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId); User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId);
if (friend == null) continue; if (friend == null) continue;

View file

@ -1,19 +1,17 @@
#nullable enable #nullable enable
using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Match.Rooms;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Tickets;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using LBPUnion.ProjectLighthouse.Types.Tickets;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/login")] [Route("LITTLEBIGPLANETPS3_XML/login")]
@ -46,14 +44,14 @@ public class LoginController : ControllerBase
if (npTicket == null) if (npTicket == null)
{ {
Logger.Log("npTicket was null, rejecting login", LoggerLevelLogin.Instance); Logger.Warn("npTicket was null, rejecting login", LogArea.Login);
return this.BadRequest(); return this.BadRequest();
} }
IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress; IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress;
if (remoteIpAddress == null) if (remoteIpAddress == null)
{ {
Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance); Logger.Warn("unable to determine ip, rejecting login", LogArea.Login);
return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever
} }
@ -69,7 +67,7 @@ public class LoginController : ControllerBase
token = await this.database.AuthenticateUser(npTicket, ipAddress); token = await this.database.AuthenticateUser(npTicket, ipAddress);
if (token == null) if (token == null)
{ {
Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance); Logger.Warn($"Unable to find/generate a token for username {npTicket.Username}", LogArea.Login);
return this.StatusCode(403, ""); // If not, then 403. return this.StatusCode(403, ""); // If not, then 403.
} }
} }
@ -78,28 +76,12 @@ public class LoginController : ControllerBase
if (user == null || user.Banned) if (user == null || user.Banned)
{ {
Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance); Logger.Error($"Unable to find user {npTicket.Username} from token", LogArea.Login);
return this.StatusCode(403, ""); return this.StatusCode(403, "");
} }
if (ServerSettings.Instance.UseExternalAuth) if (ServerConfiguration.Instance.Authentication.UseExternalAuth)
{ {
if (ServerSettings.Instance.BlockDeniedUsers)
{
string ipAddressAndName = $"{token.UserLocation}|{user.Username}";
if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3)
{
this.database.AuthenticationAttempts.RemoveRange
(this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId));
DeniedAuthenticationHelper.AddAttempt(ipAddressAndName);
await this.database.SaveChangesAsync();
Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance);
return this.StatusCode(403, "");
}
}
if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress)) if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress))
{ {
token.Approved = true; token.Approved = true;
@ -110,7 +92,7 @@ public class LoginController : ControllerBase
{ {
GameToken = token, GameToken = token,
GameTokenId = token.TokenId, GameTokenId = token.TokenId,
Timestamp = TimestampHelper.Timestamp, Timestamp = TimeHelper.Timestamp,
IPAddress = ipAddress, IPAddress = ipAddress,
Platform = npTicket.Platform, Platform = npTicket.Platform,
}; };
@ -127,11 +109,11 @@ public class LoginController : ControllerBase
if (!token.Approved) if (!token.Approved)
{ {
Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance); Logger.Warn($"Token unapproved for user {user.Username}, rejecting login", LogArea.Login);
return this.StatusCode(403, ""); return this.StatusCode(403, "");
} }
Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client", LoggerLevelLogin.Instance); Logger.Success($"Successfully logged in user {user.Username} as {token.GameVersion} client", LogArea.Login);
// After this point we are now considering this session as logged in. // After this point we are now considering this session as logged in.
// We just logged in with the token. Mark it as used so someone else doesnt try to use it, // We just logged in with the token. Mark it as used so someone else doesnt try to use it,
@ -141,14 +123,14 @@ public class LoginController : ControllerBase
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
// Create a new room on LBP2/3/Vita // Create a new room on LBP2/3/Vita
if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion, token.Platform); if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user.UserId, token.GameVersion, token.Platform);
return this.Ok return this.Ok
( (
new LoginResult new LoginResult
{ {
AuthTicket = "MM_AUTH=" + token.UserToken, AuthTicket = "MM_AUTH=" + token.UserToken,
LbpEnvVer = ServerStatics.ServerName, ServerBrand = VersionHelper.FullVersion,
}.Serialize() }.Serialize()
); );
} }

View file

@ -1,13 +1,12 @@
#nullable enable #nullable enable
using System; using LBPUnion.ProjectLighthouse.Levels;
using System.Linq; using LBPUnion.ProjectLighthouse.PlayerData;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Matching; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]

View file

@ -1,20 +1,18 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Extensions;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Match;
using LBPUnion.ProjectLighthouse.Match.MatchCommands;
using LBPUnion.ProjectLighthouse.Match.Rooms;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Match;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Matching; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -48,29 +46,28 @@ public class MatchController : ControllerBase
if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest(); if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest();
Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance); Logger.Info("Received match data: " + bodyString, LogArea.Match);
IMatchData? matchData; IMatchCommand? matchData;
try try
{ {
matchData = MatchHelper.Deserialize(bodyString); matchData = MatchHelper.Deserialize(bodyString);
} }
catch(Exception e) catch(Exception e)
{ {
Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance); Logger.Error("Exception while parsing matchData: ", LogArea.Match);
string[] lines = e.ToDetailedException().Split("\n"); Logger.Error(e.ToDetailedException(), LogArea.Match);
foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance);
return this.BadRequest(); return this.BadRequest();
} }
if (matchData == null) if (matchData == null)
{ {
Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance); Logger.Error($"Could not parse match data: {nameof(matchData)} is null", LogArea.Match);
return this.BadRequest(); return this.BadRequest();
} }
Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance); Logger.Info($"Parsed match from {user.Username} (type: {matchData.GetType()})", LogArea.Match);
#endregion #endregion
@ -81,10 +78,10 @@ public class MatchController : ControllerBase
if (matchData is UpdateMyPlayerData playerData) if (matchData is UpdateMyPlayerData playerData)
{ {
MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation); MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, gameToken.Platform, true); Room? room = RoomHelper.FindRoomByUser(user.UserId, gameToken.GameVersion, gameToken.Platform, true);
if (playerData.RoomState != null) if (playerData.RoomState != null)
if (room != null && Equals(room.Host, user)) if (room != null && Equals(room.HostId, user.UserId))
room.State = (RoomState)playerData.RoomState; room.State = (RoomState)playerData.RoomState;
} }
@ -108,12 +105,12 @@ public class MatchController : ControllerBase
if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1)
{ {
List<User> users = new(); List<int> users = new();
foreach (string playerUsername in createRoom.Players) foreach (string playerUsername in createRoom.Players)
{ {
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse // ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (player != null) users.Add(player); if (player != null) users.Add(player.UserId);
else return this.BadRequest(); else return this.BadRequest();
} }
@ -123,7 +120,7 @@ public class MatchController : ControllerBase
if (matchData is UpdatePlayersInRoom updatePlayersInRoom) if (matchData is UpdatePlayersInRoom updatePlayersInRoom)
{ {
Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user); Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId);
if (room != null) if (room != null)
{ {
@ -136,7 +133,7 @@ public class MatchController : ControllerBase
else return this.BadRequest(); else return this.BadRequest();
} }
room.Players = users; room.PlayerIds = users.Select(u => u.UserId).ToList();
RoomHelper.CleanupRooms(null, room); RoomHelper.CleanupRooms(null, room);
} }
} }

View file

@ -1,14 +1,13 @@
#nullable enable #nullable enable
using System.IO; using LBPUnion.ProjectLighthouse.Configuration;
using System.Threading.Tasks;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -17,6 +16,20 @@ public class MessageController : ControllerBase
{ {
private readonly Database database; private readonly Database database;
private const string license = @"
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.";
public MessageController(Database database) public MessageController(Database database)
{ {
this.database = database; this.database = database;
@ -28,7 +41,7 @@ public class MessageController : ControllerBase
User? user = await this.database.UserFromGameRequest(this.Request); User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}"); return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}");
} }
[HttpGet("announce")] [HttpGet("announce")]
@ -47,7 +60,7 @@ public class MessageController : ControllerBase
GameToken gameToken = userAndToken.Value.Item2; GameToken gameToken = userAndToken.Value.Item2;
#endif #endif
string announceText = ServerSettings.Instance.AnnounceText; string announceText = ServerConfiguration.Instance.AnnounceText;
announceText = announceText.Replace("%user", user.Username); announceText = announceText.Replace("%user", user.Username);
announceText = announceText.Replace("%id", user.UserId.ToString()); announceText = announceText.Replace("%id", user.UserId.ToString());
@ -78,15 +91,15 @@ public class MessageController : ControllerBase
public async Task<IActionResult> Filter() public async Task<IActionResult> Filter()
{ {
User? user = await this.database.UserFromGameRequest(this.Request); User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
string response = await new StreamReader(this.Request.Body).ReadToEndAsync(); string response = await new StreamReader(this.Request.Body).ReadToEndAsync();
string scannedText = CensorHelper.ScanMessage(response); string scannedText = CensorHelper.ScanMessage(response);
Logger.Log($"{user.Username}: {response} / {scannedText}", LoggerLevelFilter.Instance); Logger.Info($"{user.Username}: {response} / {scannedText}", LogArea.Filter);
return this.Ok(scannedText); return this.Ok(scannedText);
} }
} }

View file

@ -1,14 +1,13 @@
#nullable enable #nullable enable
using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Administration.Reports;
using LBPUnion.ProjectLighthouse.Types.Reports;
using Microsoft.AspNetCore.Mvc;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -32,7 +31,7 @@ public class ReportController : ControllerBase
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
XmlSerializer serializer = new(typeof(GriefReport)); XmlSerializer serializer = new(typeof(GriefReport));
GriefReport? report = (GriefReport?) serializer.Deserialize(new StringReader(bodyString)); GriefReport? report = (GriefReport?)serializer.Deserialize(new StringReader(bodyString));
if (report == null) return this.BadRequest(); if (report == null) return this.BadRequest();

View file

@ -1,21 +1,17 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using Discord; using Discord;
using Kettu; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Resources; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -35,7 +31,7 @@ public class PhotosController : ControllerBase
User? user = await this.database.UserFromGameRequest(this.Request); User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest(); if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
@ -59,7 +55,7 @@ public class PhotosController : ControllerBase
if (photo.Subjects.Count > 4) return this.BadRequest(); if (photo.Subjects.Count > 4) return this.BadRequest();
if (photo.Timestamp > TimestampHelper.Timestamp) return this.BadRequest(); if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp;
foreach (PhotoSubject subject in photo.Subjects) foreach (PhotoSubject subject in photo.Subjects)
{ {
@ -68,7 +64,7 @@ public class PhotosController : ControllerBase
if (subject.User == null) continue; if (subject.User == null) continue;
subject.UserId = subject.User.UserId; subject.UserId = subject.User.UserId;
Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance); Logger.Debug($"Adding PhotoSubject (userid {subject.UserId}) to db", LogArea.Photos);
this.database.PhotoSubjects.Add(subject); this.database.PhotoSubjects.Add(subject);
} }
@ -88,7 +84,7 @@ public class PhotosController : ControllerBase
// photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId);
Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance); Logger.Debug($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LogArea.Photos);
this.database.Photos.Add(photo); this.database.Photos.Add(photo);
@ -100,7 +96,7 @@ public class PhotosController : ControllerBase
{ {
Title = "New photo uploaded!", Title = "New photo uploaded!",
Description = $"{user.Username} uploaded a new photo.", Description = $"{user.Username} uploaded a new photo.",
ImageUrl = $"{ServerSettings.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}", ImageUrl = $"{ServerConfiguration.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}",
Color = WebhookHelper.UnionColor, Color = WebhookHelper.UnionColor,
} }
); );

View file

@ -1,18 +1,17 @@
#nullable enable #nullable enable
using System.IO; using System.Buffers;
using System.Linq; using System.IO.Pipelines;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using Kettu; using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Files;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using IOFile = System.IO.File; using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Resources; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources;
[ApiController] [ApiController]
[Produces("text/xml")] [Produces("text/xml")]
@ -63,25 +62,6 @@ public class ResourcesController : ControllerBase
return this.NotFound(); return this.NotFound();
} }
[ResponseCache(Duration = 86400)]
[HttpGet("/gameAssets/{hash}")]
public IActionResult GetGameImage(string hash)
{
string path = Path.Combine("png", $"{hash}.png");
if (IOFile.Exists(path))
{
return this.File(IOFile.OpenRead(path), "image/png");
}
LbpFile? file = LbpFile.FromHash(hash);
if (file != null && ImageHelper.LbpFileToPNG(file))
{
return this.File(IOFile.OpenRead(path), "image/png");
}
return this.NotFound();
}
// TODO: check if this is a valid hash // TODO: check if this is a valid hash
[HttpPost("upload/{hash}/unattributed")] [HttpPost("upload/{hash}/unattributed")]
[HttpPost("upload/{hash}")] [HttpPost("upload/{hash}")]
@ -97,28 +77,46 @@ public class ResourcesController : ControllerBase
// lbp treats code 409 as success and as an indicator that the file is already present // lbp treats code 409 as success and as an indicator that the file is already present
if (FileHelper.ResourceExists(hash)) this.Conflict(); if (FileHelper.ResourceExists(hash)) this.Conflict();
Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance); Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources);
LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader)); LbpFile file = new(await readFromPipeReader(this.Request.BodyReader));
if (!FileHelper.IsFileSafe(file)) if (!FileHelper.IsFileSafe(file))
{ {
Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); Logger.Warn($"File is unsafe (hash: {hash}, type: {file.FileType})", LogArea.Resources);
return this.Conflict(); return this.Conflict();
} }
string calculatedHash = file.Hash; string calculatedHash = file.Hash;
if (calculatedHash != hash) if (calculatedHash != hash)
{ {
Logger.Log Logger.Warn
( ($"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})", LogArea.Resources);
$"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})",
LoggerLevelResources.Instance
);
return this.Conflict(); return this.Conflict();
} }
Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); Logger.Success($"File is OK! (hash: {hash}, type: {file.FileType})", LogArea.Resources);
await IOFile.WriteAllBytesAsync(path, file.Data); await IOFile.WriteAllBytesAsync(path, file.Data);
return this.Ok(); return this.Ok();
} }
// 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)
private static async Task<byte[]> readFromPipeReader(PipeReader reader)
{
List<byte> data = new();
while (true)
{
ReadResult readResult = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = readResult.Buffer;
if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray());
reader.AdvanceTo(buffer.Start, buffer.End);
if (readResult.IsCompleted) break;
}
return data.ToArray();
}
} }

View file

@ -1,17 +1,15 @@
#nullable enable #nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.Levels.Categories;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Categories;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -35,7 +33,7 @@ public class CollectionController : ControllerBase
User? user = await this.database.UserFromGameRequest(this.Request); User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, ""); if (user == null) return this.StatusCode(403, "");
string categoriesSerialized = CollectionHelper.Categories.Aggregate string categoriesSerialized = CategoryHelper.Categories.Aggregate
( (
string.Empty, string.Empty,
(current, category) => (current, category) =>
@ -64,7 +62,7 @@ public class CollectionController : ControllerBase
"hint_start", 1 "hint_start", 1
}, },
{ {
"total", CollectionHelper.Categories.Count "total", CategoryHelper.Categories.Count
}, },
} }
) )
@ -82,10 +80,10 @@ public class CollectionController : ControllerBase
User user = userAndToken.Value.Item1; User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2; GameToken gameToken = userAndToken.Value.Item2;
Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
if (category == null) return this.NotFound(); if (category == null) return this.NotFound();
Logger.Log("Found category " + category, LoggerLevelCategory.Instance); Logger.Debug("Found category " + category, LogArea.Category);
List<Slot> slots; List<Slot> slots;
int totalSlots; int totalSlots;

View file

@ -1,8 +1,7 @@
using System; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/tags")] [Route("LITTLEBIGPLANETPS3_XML/tags")]

View file

@ -1,15 +1,13 @@
#nullable enable #nullable enable
using System; using LBPUnion.ProjectLighthouse.Levels;
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.PlayerData;
using System.Linq; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]

View file

@ -1,20 +1,17 @@
#nullable enable #nullable enable
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Files;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -56,7 +53,7 @@ public class PublishController : ControllerBase
if (oldSlot == null) return this.NotFound(); if (oldSlot == null) return this.NotFound();
if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
} }
else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerSettings.Instance.EntitledSlots) else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
{ {
return this.StatusCode(403, ""); return this.StatusCode(403, "");
} }
@ -89,9 +86,9 @@ public class PublishController : ControllerBase
if (slot.Location == null) return this.BadRequest(); if (slot.Location == null) return this.BadRequest();
if (slot.Description.Length > 200) return this.BadRequest(); if (slot.Description.Length > 500) return this.BadRequest();
if (slot.Name.Length > 100) return this.BadRequest(); if (slot.Name.Length > 64) return this.BadRequest();
if (slot.Resources.Any(resource => !FileHelper.ResourceExists(resource))) if (slot.Resources.Any(resource => !FileHelper.ResourceExists(resource)))
{ {
@ -167,7 +164,7 @@ public class PublishController : ControllerBase
return this.Ok(oldSlot.Serialize(gameToken.GameVersion)); return this.Ok(oldSlot.Serialize(gameToken.GameVersion));
} }
if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerSettings.Instance.EntitledSlots) if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
{ {
return this.StatusCode(403, ""); return this.StatusCode(403, "");
} }
@ -198,7 +195,7 @@ public class PublishController : ControllerBase
await WebhookHelper.SendWebhook await WebhookHelper.SendWebhook
( (
"New level published!", "New level published!",
$"**{user.Username}** just published a new level: [**{slot.Name}**]({ServerSettings.Instance.ExternalUrl}/slot/{slot.SlotId})\n{slot.Description}" $"**{user.Username}** just published a new level: [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId})\n{slot.Description}"
); );
return this.Ok(slot.Serialize(gameToken.GameVersion)); return this.Ok(slot.Serialize(gameToken.GameVersion));
@ -232,7 +229,7 @@ public class PublishController : ControllerBase
XmlSerializer serializer = new(typeof(Slot)); XmlSerializer serializer = new(typeof(Slot));
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString)); Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
SanitizationHelper.SanitizeStringsInClass(slot); SanitizationHelper.SanitizeStringsInClass(slot);
return slot; return slot;

View file

@ -1,20 +1,18 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Administration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Reviews;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -98,7 +96,7 @@ public class ReviewController : ControllerBase
Review? newReview = await this.getReviewFromBody(); Review? newReview = await this.getReviewFromBody();
if (newReview == null) return this.BadRequest(); if (newReview == null) return this.BadRequest();
if (newReview.Text.Length > 100) return this.BadRequest(); if (newReview.Text.Length > 512) return this.BadRequest();
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
@ -167,18 +165,17 @@ public class ReviewController : ControllerBase
List<Review?> reviewList = reviews.ToList(); List<Review?> reviewList = reviews.ToList();
string inner = reviewList string inner = reviewList.Aggregate
.Aggregate (
( string.Empty,
string.Empty, (current, review) =>
(current, review) => {
{ if (review == null) return current;
if (review == null) return current;
RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
return current + review.Serialize(null, yourThumb); return current + review.Serialize(null, yourThumb);
} }
); );
string response = LbpSerializer.TaggedStringElement string response = LbpSerializer.TaggedStringElement
( (
"reviews", "reviews",

View file

@ -1,18 +1,15 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -108,7 +105,15 @@ public class ScoreController : ControllerBase
} }
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private string getScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5, string rootName = "scores") private string getScores
(
int slotId,
int type,
User user,
int pageStart = -1,
int pageSize = 5,
string rootName = "scores"
)
{ {
// This is hella ugly but it technically assigns the proper rank to a score // This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT // var needed for Anonymous type returned from SELECT

View file

@ -1,15 +1,12 @@
#nullable enable #nullable enable
using System; using LBPUnion.ProjectLighthouse.Levels;
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.PlayerData;
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -47,7 +44,7 @@ public class SearchController : ControllerBase
( (
s => s.Name.ToLower().Contains(keyword) || s => s.Name.ToLower().Contains(keyword) ||
s.Description.ToLower().Contains(keyword) || s.Description.ToLower().Contains(keyword) ||
s.Creator.Username.ToLower().Contains(keyword) || s.Creator!.Username.ToLower().Contains(keyword) ||
s.SlotId.ToString().Equals(keyword) s.SlotId.ToString().Equals(keyword)
); );

View file

@ -1,19 +1,17 @@
#nullable enable #nullable enable
using System; using LBPUnion.ProjectLighthouse.Configuration;
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Extensions;
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Reviews;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -42,7 +40,7 @@ public class SlotsController : ControllerBase
this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId, true) this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId, true)
.Where(s => s.Creator!.Username == user.Username) .Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1) .Skip(pageStart - 1)
.Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)), .Take(Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)),
string.Empty, string.Empty,
(current, slot) => current + slot.Serialize(token.GameVersion) (current, slot) => current + slot.Serialize(token.GameVersion)
); );
@ -56,7 +54,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", user.UsedSlots "total", user.UsedSlots
@ -135,7 +133,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.SlotCount() "total", await StatisticsHelper.SlotCount()
@ -169,7 +167,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.TeamPickCount() "total", await StatisticsHelper.TeamPickCount()
@ -200,7 +198,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.SlotCount() "total", await StatisticsHelper.SlotCount()
@ -244,7 +242,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.SlotCount() "total", await StatisticsHelper.SlotCount()
@ -302,7 +300,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.SlotCount() "total", await StatisticsHelper.SlotCount()
@ -346,7 +344,7 @@ public class SlotsController : ControllerBase
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ {
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)
}, },
{ {
"total", await StatisticsHelper.SlotCount() "total", await StatisticsHelper.SlotCount()
@ -403,4 +401,4 @@ public class SlotsController : ControllerBase
return whereSlots.Include(s => s.Creator).Include(s => s.Location); return whereSlots.Include(s => s.Creator).Include(s => s.Location);
} }
} }

View file

@ -1,21 +1,14 @@
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/plain")] [Produces("text/plain")]
public class StatisticsController : ControllerBase public class StatisticsController : ControllerBase
{ {
private readonly Database database;
public StatisticsController(Database database)
{
this.database = database;
}
[HttpGet("playersInPodCount")] [HttpGet("playersInPodCount")]
[HttpGet("totalPlayerCount")] [HttpGet("totalPlayerCount")]
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!); public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);

View file

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]

View file

@ -1,19 +1,16 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
[ApiController] [ApiController]
[Route("LITTLEBIGPLANETPS3_XML/")] [Route("LITTLEBIGPLANETPS3_XML/")]
@ -37,12 +34,17 @@ public class UserController : ControllerBase
{ {
// use an anonymous type to only fetch certain columns // use an anonymous type to only fetch certain columns
var partialUser = await this.database.Users.Where(u => u.Username == username) var partialUser = await this.database.Users.Where(u => u.Username == username)
.Select(u => new .Select
{ (
u.Username, u => new
u.IconHash, {
}).FirstOrDefaultAsync(); u.Username,
u.IconHash,
}
)
.FirstOrDefaultAsync();
if (partialUser == null) return null; if (partialUser == null) return null;
string user = LbpSerializer.TaggedStringElement("npHandle", partialUser.Username, "icon", partialUser.IconHash); string user = LbpSerializer.TaggedStringElement("npHandle", partialUser.Username, "icon", partialUser.IconHash);
return LbpSerializer.TaggedStringElement("user", user, "type", "user"); return LbpSerializer.TaggedStringElement("user", user, "type", "user");
} }
@ -84,13 +86,12 @@ public class UserController : ControllerBase
User user = userAndToken.Value.Item1; User user = userAndToken.Value.Item1;
GameToken gameToken = userAndToken.Value.Item2; GameToken gameToken = userAndToken.Value.Item2;
this.Request.Body.Position = 0; this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
// xml hack so we can use one class to deserialize different root names // xml hack so we can use one class to deserialize different root names
string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user"; string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user";
XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement)); XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement));
UserUpdate? update = (UserUpdate?) serializer.Deserialize(new StringReader(bodyString)); UserUpdate? update = (UserUpdate?)serializer.Deserialize(new StringReader(bodyString));
if (update == null) return this.BadRequest(); if (update == null) return this.BadRequest();
@ -103,11 +104,14 @@ public class UserController : ControllerBase
user.Biography = update.Biography; user.Biography = update.Biography;
} }
foreach (string? resource in new[] {update.IconHash, update.YayHash, update.MehHash, update.BooHash, update.PlanetHash,}) foreach (string? resource in new[]
{
update.IconHash, update.YayHash, update.MehHash, update.BooHash, update.PlanetHash,
})
{ {
if (resource != null && !FileHelper.ResourceExists(resource)) return this.BadRequest(); if (resource != null && !FileHelper.ResourceExists(resource)) return this.BadRequest();
} }
if (update.IconHash != null) user.IconHash = update.IconHash; if (update.IconHash != null) user.IconHash = update.IconHash;
if (update.YayHash != null) user.YayHash = update.YayHash; if (update.YayHash != null) user.YayHash = update.YayHash;
@ -181,4 +185,4 @@ public class UserController : ControllerBase
return this.Ok("[{\"StatusCode\":200}]"); return this.Ok("[{\"StatusCode\":200}]");
} }
} }

View file

@ -0,0 +1,36 @@
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet;
using LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace LBPUnion.ProjectLighthouse.Servers.GameServer;
public static class Program
{
public static void Main(string[] args)
{
StartupTasks.Run(args, ServerType.GameServer);
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults
(
webBuilder =>
{
webBuilder.UseStartup<GameServerStartup>();
webBuilder.UseUrls(ServerConfiguration.Instance.GameApiListenUrl);
}
)
.ConfigureLogging
(
logging =>
{
logging.ClearProviders();
logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToLighthouseLoggerProvider>());
}
);
}

View file

@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>LBPUnion.ProjectLighthouse.Servers.GameServer</AssemblyName>
<RootNamespace>LBPUnion.ProjectLighthouse.Servers.GameServer</RootNamespace>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/>
</ItemGroup>
<ItemGroup>
<None Remove="gitVersion.txt"/>
<EmbeddedResource Include="gitVersion.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitBranch.txt"/>
<EmbeddedResource Include="gitBranch.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitRemotes.txt"/>
<EmbeddedResource Include="gitRemotes.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitUnpushed.txt"/>
<EmbeddedResource Include="gitUnpushed.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;"/>
<Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;"/>
<Exec Command="git remote -v &gt; &quot;$(ProjectDir)/gitRemotes.txt&quot;"/>
<Exec Command="git log --branches --not --remotes --oneline &gt; &quot;$(ProjectDir)/gitUnpushed.txt&quot;"/>
</Target>
</Project>

View file

@ -1,36 +1,18 @@
using System; using LBPUnion.ProjectLighthouse.Configuration;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Localization;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Middlewares;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
#if RELEASE
using Microsoft.Extensions.Hosting.Internal;
#endif
namespace LBPUnion.ProjectLighthouse.Startup; namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
public class Startup public class GameServerStartup
{ {
public Startup(IConfiguration configuration) public GameServerStartup(IConfiguration configuration)
{ {
this.Configuration = configuration; this.Configuration = configuration;
} }
@ -41,11 +23,6 @@ public class Startup
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddControllers(); services.AddControllers();
#if DEBUG
services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation();
#else
services.AddRazorPages().WithRazorPagesAtContentRoot();
#endif
services.AddMvc services.AddMvc
( (
@ -65,51 +42,6 @@ public class Startup
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
} }
); );
services.AddSwaggerGen
(
c =>
{
// Give swagger the name and version of our project
c.SwaggerDoc
(
"v1",
new OpenApiInfo
{
Title = "Project Lighthouse API",
Version = "v1",
}
);
// Filter out endpoints not in /api/v1
c.DocumentFilter<SwaggerFilter>();
// Add XMLDoc to swagger
string xmlDocs = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlDocs));
}
);
services.Configure<RequestLocalizationOptions>
(
config =>
{
List<CultureInfo> languages = LocalizationManager.GetAvailableLanguages()
.Select(l => new CultureInfo(LocalizationManager.MapLanguage(l)))
.ToList();
config.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US"));
config.SupportedCultures = languages;
config.SupportedUICultures = languages;
}
);
#if DEBUG
services.AddSingleton<IHostLifetime, DebugWarmupLifetime>();
#else
services.AddSingleton<IHostLifetime, ConsoleLifetime>();
#endif
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -117,13 +49,13 @@ public class Startup
{ {
bool computeDigests = true; bool computeDigests = true;
if (string.IsNullOrEmpty(ServerSettings.Instance.ServerDigestKey)) if (string.IsNullOrEmpty(ServerConfiguration.Instance.DigestKey.PrimaryDigestKey))
{ {
Logger.Log Logger.Warn
( (
"The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " + "The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " +
"To increase security, it is recommended that you find and set this variable.", "To increase security, it is recommended that you find and set this variable.",
LoggerLevelStartup.Instance LogArea.Startup
); );
computeDigests = false; computeDigests = false;
} }
@ -134,52 +66,7 @@ public class Startup
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseSwagger(); app.UseMiddleware<RequestLogMiddleware>();
app.UseSwaggerUI
(
c =>
{
c.SwaggerEndpoint("v1/swagger.json", "Project Lighthouse API");
}
);
app.UseRequestLocalization();
// Logs every request and the response to it
// Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news"
// Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr"
app.Use
(
async (context, next) =>
{
Stopwatch requestStopwatch = new();
requestStopwatch.Start();
context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging
// Log all headers.
// foreach (KeyValuePair<string, StringValues> header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}");
await next(context); // Handle the request so we can get the status code from it
requestStopwatch.Stop();
Logger.Log
(
$"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}",
LoggerLevelHttp.Instance
);
#if DEBUG
// Log post body
if (context.Request.Method == "POST")
{
context.Request.Body.Position = 0;
Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance);
}
#endif
}
);
// Digest check // Digest check
app.Use app.Use
@ -187,7 +74,7 @@ public class Startup
async (context, next) => async (context, next) =>
{ {
// Client digest check. // Client digest check.
if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string? authCookie) || authCookie == null) authCookie = string.Empty;
string digestPath = context.Request.Path; string digestPath = context.Request.Path;
Stream body = context.Request.Body; Stream body = context.Request.Body;
@ -195,7 +82,8 @@ public class Startup
if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML"))
{ {
string clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.ServerDigestKey); string clientRequestDigest = await CryptoHelper.ComputeDigest
(digestPath, authCookie, body, ServerConfiguration.Instance.DigestKey.PrimaryDigestKey);
// Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match. // Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match.
if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest)) if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest))
@ -208,13 +96,14 @@ public class Startup
// Reset the body stream // Reset the body stream
body.Position = 0; body.Position = 0;
clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.AlternateDigestKey); clientRequestDigest = await CryptoHelper.ComputeDigest
(digestPath, authCookie, body, ServerConfiguration.Instance.DigestKey.AlternateDigestKey);
if (clientRequestDigest != sentDigest) if (clientRequestDigest != sentDigest)
{ {
#if DEBUG #if DEBUG
Console.WriteLine("Digest failed"); Console.WriteLine("Digest failed");
Console.WriteLine("digestKey: " + ServerSettings.Instance.ServerDigestKey); Console.WriteLine("digestKey: " + ServerConfiguration.Instance.DigestKey.PrimaryDigestKey);
Console.WriteLine("altDigestKey: " + ServerSettings.Instance.AlternateDigestKey); Console.WriteLine("altDigestKey: " + ServerConfiguration.Instance.DigestKey.AlternateDigestKey);
Console.WriteLine("computed digest: " + clientRequestDigest); Console.WriteLine("computed digest: " + clientRequestDigest);
#endif #endif
// We still failed to validate. Abort the request. // We still failed to validate. Abort the request.
@ -241,10 +130,12 @@ public class Startup
{ {
responseBuffer.Position = 0; responseBuffer.Position = 0;
string digestKey = usedAlternateDigestKey ? ServerSettings.Instance.AlternateDigestKey : ServerSettings.Instance.ServerDigestKey; string digestKey = usedAlternateDigestKey
? ServerConfiguration.Instance.DigestKey.AlternateDigestKey
: ServerConfiguration.Instance.DigestKey.PrimaryDigestKey;
// Compute the digest for the response. // Compute the digest for the response.
string serverDigest = await HashHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey); string serverDigest = await CryptoHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey);
context.Response.Headers.Add("X-Digest-A", serverDigest); context.Response.Headers.Add("X-Digest-A", serverDigest);
} }
@ -283,8 +174,6 @@ public class Startup
app.UseRouting(); app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(endpoints => endpoints.MapControllers()); app.UseEndpoints(endpoints => endpoints.MapControllers());
app.UseEndpoints(endpoints => endpoints.MapRazorPages()); app.UseEndpoints(endpoints => endpoints.MapRazorPages());
} }

View file

@ -0,0 +1,15 @@
using LBPUnion.ProjectLighthouse.Middlewares;
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
public class GameServerTestStartup : GameServerStartup
{
public GameServerTestStartup(IConfiguration configuration) : base(configuration)
{}
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<FakeRemoteIPAddressMiddleware>();
base.Configure(app, env);
}
}

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View file

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View file

@ -1,7 +1,7 @@
#nullable enable #nullable enable
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin;
[ApiController] [ApiController]
[Route("/admin")] [Route("/admin")]

View file

@ -1,13 +1,11 @@
#nullable enable #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Administration.Reports;
using System.IO; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Reports;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin;
[ApiController] [ApiController]
[Route("admin/report/{id:int}")] [Route("admin/report/{id:int}")]

View file

@ -1,11 +1,11 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin;
[ApiController] [ApiController]
[Route("admin/slot/{id:int}")] [Route("admin/slot/{id:int}")]

View file

@ -0,0 +1,103 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin;
[ApiController]
[Route("admin/user/{id:int}")]
public class AdminUserController : ControllerBase
{
private readonly Database database;
public AdminUserController(Database database)
{
this.database = database;
}
[HttpGet("unban")]
public async Task<IActionResult> UnbanUser([FromRoute] int id)
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsAdmin) return this.NotFound();
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
if (targetedUser == null) return this.NotFound();
targetedUser.Banned = false;
targetedUser.BannedReason = null;
await this.database.SaveChangesAsync();
return this.Redirect($"/user/{targetedUser.UserId}");
}
/// <summary>
/// Resets the user's earth decorations to a blank state. Useful for users who abuse audio for example.
/// </summary>
[HttpGet("wipePlanets")]
public async Task<IActionResult> WipePlanets([FromRoute] int id) {
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsAdmin) return this.NotFound();
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
if (targetedUser == null) return this.NotFound();
string[] hashes = {
targetedUser.PlanetHashLBP2,
targetedUser.PlanetHashLBP3,
targetedUser.PlanetHashLBPVita,
};
// This will also wipe users' earth with the same hashes.
foreach (string hash in hashes)
{
// Don't try to remove empty hashes. That's a horrible idea.
if (string.IsNullOrWhiteSpace(hash)) continue;
// Find users with a matching hash
List<User> users = await this.database.Users
.Where(u => u.PlanetHashLBP2 == hash ||
u.PlanetHashLBP3 == hash ||
u.PlanetHashLBPVita == hash)
.ToListAsync();
// We should match at least the targeted user...
System.Diagnostics.Debug.Assert(users.Count != 0);
// Reset each users' hash.
foreach (User userWithPlanet in users)
{
userWithPlanet.PlanetHashLBP2 = "";
userWithPlanet.PlanetHashLBP3 = "";
userWithPlanet.PlanetHashLBPVita = "";
Logger.Success($"Deleted planets for {userWithPlanet.Username} (id:{userWithPlanet.UserId})", LogArea.Admin);
}
// And finally, attempt to remove the resource from the filesystem. We don't want that taking up space.
try
{
IOFile.Delete(FileHelper.GetResourcePath(hash));
Logger.Success($"Deleted planet resource {hash}",
LogArea.Admin);
}
catch(DirectoryNotFoundException)
{
// This is certainly a strange case, but it's not worth doing anything about since we were about
// to delete the file anyways. Carry on~
}
catch(Exception e)
{
// Welp, guess I'll die then. We tried~
Logger.Error($"Failed to delete planet resource {hash}\n{e}", LogArea.Admin);
}
}
await this.database.SaveChangesAsync();
return this.Redirect($"/user/{targetedUser.UserId}");
}
}

View file

@ -1,12 +1,12 @@
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Extensions;
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Match.Rooms;
using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Debug; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Debug;
[ApiController] [ApiController]
[Route("debug/roomVisualizer")] [Route("debug/roomVisualizer")]
@ -25,12 +25,12 @@ public class RoomVisualizerController : ControllerBase
#if !DEBUG #if !DEBUG
return this.NotFound(); return this.NotFound();
#else #else
List<User> users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).ToListAsync(); List<int> users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).Select(u => u.UserId).ToListAsync();
RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2, Platform.PS3); RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2, Platform.PS3);
foreach (User user in users) foreach (int user in users)
{ {
MatchHelper.SetUserLocation(user.UserId, "127.0.0.1"); MatchHelper.SetUserLocation(user, "127.0.0.1");
} }
return this.Redirect("/debug/roomVisualizer"); return this.Redirect("/debug/roomVisualizer");
#endif #endif
@ -42,7 +42,7 @@ public class RoomVisualizerController : ControllerBase
#if !DEBUG #if !DEBUG
return this.NotFound(); return this.NotFound();
#else #else
RoomHelper.Rooms.RemoveAll(_ => true); lock(RoomHelper.RoomLock) RoomHelper.Rooms.RemoveAll();
return this.Redirect("/debug/roomVisualizer"); return this.Redirect("/debug/roomVisualizer");
#endif #endif
} }

View file

@ -1,13 +1,11 @@
#nullable enable #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.PlayerData;
using System.Linq; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.ExternalAuth;
[ApiController] [ApiController]
[Route("/authentication")] [Route("/authentication")]
@ -57,8 +55,6 @@ public class AuthenticationController : ControllerBase
this.database.GameTokens.Remove(authAttempt.GameToken); this.database.GameTokens.Remove(authAttempt.GameToken);
this.database.AuthenticationAttempts.Remove(authAttempt); this.database.AuthenticationAttempts.Remove(authAttempt);
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
return this.Redirect("~/authentication"); return this.Redirect("~/authentication");
@ -79,8 +75,6 @@ public class AuthenticationController : ControllerBase
{ {
this.database.GameTokens.Remove(authAttempt.GameToken); this.database.GameTokens.Remove(authAttempt.GameToken);
this.database.AuthenticationAttempts.Remove(authAttempt); this.database.AuthenticationAttempts.Remove(authAttempt);
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
} }
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();

View file

@ -1,10 +1,11 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.ExternalAuth;
[ApiController] [ApiController]
[Route("/authentication")] [Route("/authentication")]

View file

@ -0,0 +1,29 @@
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Helpers;
using Microsoft.AspNetCore.Mvc;
using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers;
[ApiController]
public class ResourcesController : ControllerBase
{
[ResponseCache(Duration = 86400)]
[HttpGet("/gameAssets/{hash}")]
public IActionResult GetGameImage(string hash)
{
string path = Path.Combine("png", $"{hash}.png");
if (IOFile.Exists(path))
{
return this.File(IOFile.OpenRead(path), "image/png");
}
LbpFile? file = LbpFile.FromHash(hash);
if (file != null && FileHelper.LbpFileToPNG(file))
{
return this.File(IOFile.OpenRead(path), "image/png");
}
return this.NotFound();
}
}

View file

@ -1,10 +1,9 @@
#nullable enable #nullable enable
using System.Threading.Tasks;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -13,7 +12,7 @@ using Microsoft.EntityFrameworkCore;
// TODO: Clean up this file // TODO: Clean up this file
// - jvyden // - jvyden
namespace LBPUnion.ProjectLighthouse.Controllers.Website; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers;
[ApiController] [ApiController]
[Route("slot/{id:int}")] [Route("slot/{id:int}")]
@ -45,14 +44,14 @@ public class SlotPageController : ControllerBase
if (msg == null) if (msg == null)
{ {
Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance); Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments);
return this.Redirect("~/slot/" + id); return this.Redirect("~/slot/" + id);
} }
msg = SanitizationHelper.SanitizeString(msg); msg = SanitizationHelper.SanitizeString(msg);
await this.database.PostComment(user, id, CommentType.Level, msg); await this.database.PostComment(user, id, CommentType.Level, msg);
Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance); Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments);
return this.Redirect("~/slot/" + id); return this.Redirect("~/slot/" + id);
} }

View file

@ -1,13 +1,12 @@
#nullable enable #nullable enable
using System.Threading.Tasks;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers.Website; namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers;
[ApiController] [ApiController]
[Route("user/{id:int}")] [Route("user/{id:int}")]
@ -39,14 +38,14 @@ public class UserPageController : ControllerBase
if (msg == null) if (msg == null)
{ {
Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance); Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments);
return this.Redirect("~/user/" + id); return this.Redirect("~/user/" + id);
} }
msg = SanitizationHelper.SanitizeString(msg); msg = SanitizationHelper.SanitizeString(msg);
await this.database.PostComment(user, id, CommentType.Profile, msg); await this.database.PostComment(user, id, CommentType.Profile, msg);
Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance); Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments);
return this.Redirect("~/user/" + id); return this.Redirect("~/user/" + id);
} }

View file

@ -1,5 +1,5 @@
@page "/admin/user/{id:int}/ban" @page "/admin/user/{id:int}/ban"
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminBanUserPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminBanUserPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,12 +1,11 @@
#nullable enable #nullable enable
using System.Linq; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin;
public class AdminBanUserPage : BaseLayout public class AdminBanUserPage : BaseLayout
{ {

View file

@ -1,15 +1,27 @@
@page "/admin" @page "/admin"
@using LBPUnion.ProjectLighthouse.Administration
@using LBPUnion.ProjectLighthouse.Administration.Maintenance
@using LBPUnion.ProjectLighthouse.Extensions
@using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Helpers
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
@using LBPUnion.ProjectLighthouse.Maintenance
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
Model.Title = "Admin Panel"; Model.Title = "Admin Panel";
} }
@if (Model.Log != null)
{
<div class="ui bottom attached message">
<h2>Command Output</h2>
@foreach (string line in Model.Log.Split("\n"))
{
<code>@line.TrimEnd()</code><br>
}
</div>
}
@if (!this.Request.IsMobile()) @if (!this.Request.IsMobile())
{ {
<div class="ui center aligned grid"> <div class="ui center aligned grid">

View file

@ -1,13 +1,15 @@
#nullable enable #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Administration;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Administration.Maintenance;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Maintenance; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin;
public class AdminPanelPage : BaseLayout public class AdminPanelPage : BaseLayout
{ {
@ -17,7 +19,9 @@ public class AdminPanelPage : BaseLayout
public List<AdminPanelStatistic> Statistics = new(); public List<AdminPanelStatistic> Statistics = new();
public async Task<IActionResult> OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob) public string? Log;
public async Task<IActionResult> OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob, [FromQuery] string? log)
{ {
User? user = this.Database.UserFromWebRequest(this.Request); User? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("~/login"); if (user == null) return this.Redirect("~/login");
@ -33,8 +37,9 @@ public class AdminPanelPage : BaseLayout
args ??= ""; args ??= "";
args = command + " " + args; args = command + " " + args;
string[] split = args.Split(" "); string[] split = args.Split(" ");
await MaintenanceHelper.RunCommand(split);
return this.Redirect("~/admin"); List<LogLine> runCommand = await MaintenanceHelper.RunCommand(split);
return this.Redirect($"~/admin?log={CryptoHelper.ToBase64(runCommand.ToLogString())}");
} }
if (!string.IsNullOrEmpty(maintenanceJob)) if (!string.IsNullOrEmpty(maintenanceJob))
@ -43,6 +48,11 @@ public class AdminPanelPage : BaseLayout
return this.Redirect("~/admin"); return this.Redirect("~/admin");
} }
if (!string.IsNullOrEmpty(log))
{
this.Log = CryptoHelper.FromBase64(log);
}
return this.Page(); return this.Page();
} }
} }

View file

@ -1,6 +1,7 @@
@page "/admin/users" @page "/admin/users"
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelUsersPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelUsersPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,20 +1,17 @@
#nullable enable #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Linq; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin;
public class AdminPanelUsersPage : BaseLayout public class AdminPanelUsersPage : BaseLayout
{ {
public int UserCount; public int UserCount;
public List<User> Users; public List<User> Users = new();
public AdminPanelUsersPage(Database database) : base(database) public AdminPanelUsersPage(Database database) : base(database)
{} {}

View file

@ -1,5 +1,5 @@
@page "/admin/user/{id:int}/setGrantedSlots" @page "/admin/user/{id:int}/setGrantedSlots"
@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminSetGrantedSlotsPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminSetGrantedSlotsPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,11 +1,11 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages.Admin; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin;
public class AdminSetGrantedSlotsPage : BaseLayout public class AdminSetGrantedSlotsPage : BaseLayout
{ {

View file

@ -1,5 +1,5 @@
@page "/verifyEmail" @page "/verifyEmail"
@model LBPUnion.ProjectLighthouse.Pages.CompleteEmailVerificationPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.CompleteEmailVerificationPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,25 +1,24 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration;
using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles.Email;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class CompleteEmailVerificationPage : BaseLayout public class CompleteEmailVerificationPage : BaseLayout
{ {
public CompleteEmailVerificationPage([NotNull] Database database) : base(database) public CompleteEmailVerificationPage(Database database) : base(database)
{} {}
public string? Error = null; public string? Error;
public async Task<IActionResult> OnGet(string token) public async Task<IActionResult> OnGet(string token)
{ {
if (!ServerSettings.Instance.SMTPEnabled) return this.NotFound(); if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound();
User? user = this.Database.UserFromWebRequest(this.Request); User? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("~/login"); if (user == null) return this.Redirect("~/login");

View file

@ -1,5 +1,5 @@
@page "/debug/filter" @page "/debug/filter"
@model LBPUnion.ProjectLighthouse.Pages.Debug.FilterTestPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.FilterTestPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,20 +1,19 @@
#nullable enable #nullable enable
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages.Debug; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug;
public class FilterTestPage : BaseLayout public class FilterTestPage : BaseLayout
{ {
public FilterTestPage(Database database) : base(database) public FilterTestPage(Database database) : base(database)
{} {}
public string? FilteredText = null; public string? FilteredText;
public string? Text = null; public string? Text;
public async Task<IActionResult> OnGet(string? text = null) public IActionResult OnGet(string? text = null)
{ {
#if !DEBUG #if !DEBUG
return this.NotFound(); return this.NotFound();

View file

@ -1,8 +1,11 @@
@page "/debug/roomVisualizer" @page "/debug/roomVisualizer"
@using LBPUnion.ProjectLighthouse.Extensions
@using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Helpers
@using LBPUnion.ProjectLighthouse.Match.Rooms
@using LBPUnion.ProjectLighthouse.PlayerData
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@using LBPUnion.ProjectLighthouse.Types.Match @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.RoomVisualizerPage
@model LBPUnion.ProjectLighthouse.Pages.Debug.RoomVisualizerPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
@ -35,7 +38,7 @@
<meta http-equiv="refresh" content="@refreshSeconds"> <meta http-equiv="refresh" content="@refreshSeconds">
</noscript> </noscript>
<p>@RoomHelper.Rooms.Count rooms</p> <p>@RoomHelper.Rooms.Count() rooms</p>
<a href="/debug/roomVisualizer/createFakeRoom"> <a href="/debug/roomVisualizer/createFakeRoom">
<div class="ui blue button">Create Fake Room</div> <div class="ui blue button">Create Fake Room</div>
@ -50,6 +53,7 @@
<h2>Best rooms for each game version</h2> <h2>Best rooms for each game version</h2>
@foreach (GameVersion version in Enum.GetValues<GameVersion>()) @foreach (GameVersion version in Enum.GetValues<GameVersion>())
{ {
#nullable enable
if (version == GameVersion.LittleBigPlanet1 || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) continue; if (version == GameVersion.LittleBigPlanet1 || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) continue;
FindBestRoomResponse? response = RoomHelper.FindBestRoom(null, version, null, null, null); FindBestRoomResponse? response = RoomHelper.FindBestRoom(null, version, null, null, null);
@ -62,7 +66,7 @@
@foreach (Room room in RoomHelper.Rooms) @foreach (Room room in RoomHelper.Rooms)
{ {
bool userInRoom = room.Players.Select(p => p.Username).Contains(Model.User?.Username); bool userInRoom = room.PlayerIds.Contains(Model.User?.UserId ?? -1);
string color = userInRoom ? "green" : "blue"; string color = userInRoom ? "green" : "blue";
<div class="ui @color inverted segment"> <div class="ui @color inverted segment">
<h3>Room @room.RoomId</h3> <h3>Room @room.RoomId</h3>
@ -72,9 +76,9 @@
<b>You are currently in this room.</b> <b>You are currently in this room.</b>
</p> </p>
} }
<p>@room.Players.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString()on paltform @room.RoomPlatform</p> <p>@room.PlayerIds.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString() on platform @room.RoomPlatform</p>
<p>Slot type: @room.Slot.SlotType, slot id: @room.Slot.SlotId</p> <p>Slot type: @room.Slot.SlotType, slot id: @room.Slot.SlotId</p>
@foreach (User player in room.Players) @foreach (User player in room.GetPlayers(Model.Database))
{ {
<div class="ui segment">@player.Username</div> <div class="ui segment">@player.Username</div>
} }

View file

@ -1,26 +1,26 @@
#nullable enable #nullable enable
using System.Threading.Tasks;
using JetBrains.Annotations;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages.Debug; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using Microsoft.AspNetCore.Mvc;
#if !DEBUG
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types;
#endif
namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug;
public class RoomVisualizerPage : BaseLayout public class RoomVisualizerPage : BaseLayout
{ {
public RoomVisualizerPage([NotNull] Database database) : base(database) public RoomVisualizerPage(Database database) : base(database)
{} {}
public async Task<IActionResult> OnGet() public IActionResult OnGet()
{ {
#if !DEBUG #if !DEBUG
User? user = this.Database.UserFromWebRequest(this.Request); User? user = this.Database.UserFromWebRequest(this.Request);
if (user == null || !user.IsAdmin) return this.NotFound(); if (user == null || !user.IsAdmin) return this.NotFound();
#endif
return this.Page(); return this.Page();
#else
return this.Page();
#endif
} }
} }

View file

@ -1,6 +1,6 @@
@page "/debug/version" @page "/debug/version"
@using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Helpers
@model LBPUnion.ProjectLighthouse.Pages.Debug.VersionInfoPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.VersionInfoPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,8 +1,8 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages.Debug; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug;
public class VersionInfoPage : BaseLayout public class VersionInfoPage : BaseLayout
{ {

View file

@ -1,6 +1,7 @@
@page "/authentication" @page "/authentication"
@using LBPUnion.ProjectLighthouse.PlayerData
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.AuthenticationPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth.AuthenticationPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,28 +1,26 @@
#nullable enable #nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth;
public class AuthenticationPage : BaseLayout public class AuthenticationPage : BaseLayout
{ {
public List<AuthenticationAttempt> AuthenticationAttempts; public List<AuthenticationAttempt> AuthenticationAttempts = new();
public IPAddress? IpAddress; public IPAddress? IpAddress;
public AuthenticationPage(Database database) : base(database) public AuthenticationPage(Database database) : base(database)
{} {}
public async Task<IActionResult> OnGet() public IActionResult OnGet()
{ {
if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound(); if (!ServerConfiguration.Instance.Authentication.UseExternalAuth) return this.NotFound();
if (this.User == null) return this.StatusCode(403, ""); if (this.User == null) return this.StatusCode(403, "");
this.IpAddress = this.HttpContext.Connection.RemoteIpAddress; this.IpAddress = this.HttpContext.Connection.RemoteIpAddress;

View file

@ -1,6 +1,7 @@
@page "/authentication/autoApprovals" @page "/authentication/autoApprovals"
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,18 +1,16 @@
#nullable enable #nullable enable
using System.Collections.Generic; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using System.Linq; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth;
public class ManageUserApprovedIpAddressesPage : BaseLayout public class ManageUserApprovedIpAddressesPage : BaseLayout
{ {
public List<UserApprovedIpAddress> ApprovedIpAddresses = new();
public List<UserApprovedIpAddress> ApprovedIpAddresses;
public ManageUserApprovedIpAddressesPage(Database database) : base(database) public ManageUserApprovedIpAddressesPage(Database database) : base(database)
{} {}

View file

@ -0,0 +1,81 @@
@page "/"
@using LBPUnion.ProjectLighthouse.Configuration
@using LBPUnion.ProjectLighthouse.Extensions
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Levels
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LandingPage
@{
Layout = "Layouts/BaseLayout";
Model.ShowTitleInPage = false;
bool isMobile = this.Request.IsMobile();
}
<h1>Welcome to <b>@ServerConfiguration.Instance.Customization.ServerName</b>!</h1>
@if (Model.User != null)
{
<p>You are currently logged in as <b>@Model.User.Username</b>.</p>
if (ServerConfiguration.Instance.Authentication.UseExternalAuth && Model.AuthenticationAttemptsCount > 0)
{
<p>
<b>You have @Model.AuthenticationAttemptsCount authentication attempts pending. Click <a href="/authentication">here</a> to view them.</b>
</p>
}
}
@if (Model.PlayersOnlineCount == 1)
{
<p>There is 1 user currently online:</p>
@foreach (User user in Model.PlayersOnline)
{
<a href="/user/@user.UserId" title="@user.Status.ToString()">@user.Username</a>
}
}
else if (Model.PlayersOnlineCount == 0)
{
<p>There are no users online. Why not hop on?</p>
}
else
{
<p>There are currently @Model.PlayersOnlineCount users online:</p>
@foreach (User user in Model.PlayersOnline)
{
<a href="/user/@user.UserId" title="@user.Status.ToString()">@user.Username</a>
}
}
<br>
<div class="@(isMobile ? "" : "ui center aligned grid")">
<div class="eight wide column">
<div class="ui pink segment">
<h1><i class="ribbon icon"></i>Latest Team Picks</h1>
<div class="ui divider"></div>
<div class="ui left aligned segment">
@foreach (Slot slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@
{
@await Html.PartialAsync("Partials/SlotCardPartial", slot, Model.GetSlotViewData(slot.SlotId, isMobile))
<br>
}
</div>
</div>
</div>
@if (isMobile)
{
<br>
}
<div class="eight wide column">
<div class="ui blue segment">
<h1><i class="certificate icon"></i>Newest Levels</h1>
<div class="ui divider"></div>
<div class="ui left aligned segment">
@foreach (Slot slot in Model.NewestLevels!) @* Can't reach a point where this is null *@
{
@await Html.PartialAsync("Partials/SlotCardPartial", slot, Model.GetSlotViewData(slot.SlotId, isMobile))
<br>
}
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,76 @@
#nullable enable
using JetBrains.Annotations;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Levels;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class LandingPage : BaseLayout
{
public LandingPage(Database database) : base(database)
{}
public int AuthenticationAttemptsCount;
public List<User> PlayersOnline = new();
public int PlayersOnlineCount;
public List<Slot>? LatestTeamPicks;
public List<Slot>? NewestLevels;
[UsedImplicitly]
public async Task<IActionResult> OnGet()
{
User? user = this.Database.UserFromWebRequest(this.Request);
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
this.PlayersOnlineCount = await StatisticsHelper.RecentMatches();
if (user != null)
this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include
(a => a.GameToken)
.CountAsync(a => a.GameToken.UserId == user.UserId);
List<int> userIds = await this.Database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
const int maxShownLevels = 5;
this.LatestTeamPicks = await this.Database.Slots.Where
(s => s.TeamPick)
.OrderByDescending(s => s.FirstUploaded)
.Take(maxShownLevels)
.Include(s => s.Creator)
.ToListAsync();
this.NewestLevels = await this.Database.Slots.OrderByDescending(s => s.FirstUploaded).Take(maxShownLevels).Include(s => s.Creator).ToListAsync();
return this.Page();
}
public ViewDataDictionary GetSlotViewData(int slotId, bool isMobile = false)
=> new(ViewData)
{
{
"User", this.User
},
{
"CallbackUrl", $"~/slot/{slotId}"
},
{
"ShowLink", true
},
{
"IsMini", true
},
{
"IsMobile", isMobile
},
};
}

View file

@ -1,9 +1,9 @@
@using LBPUnion.ProjectLighthouse.Configuration
@using LBPUnion.ProjectLighthouse.Extensions
@using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Helpers
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
@using LBPUnion.ProjectLighthouse.Localization.StringLists @using LBPUnion.ProjectLighthouse.Localization.StringLists
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@using LBPUnion.ProjectLighthouse.Types.Settings @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts.BaseLayout
@model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout
@{ @{
if (Model!.User == null) if (Model!.User == null)
@ -12,7 +12,7 @@
} }
else else
{ {
if (ServerSettings.Instance.UseExternalAuth) if (ServerConfiguration.Instance.Authentication.UseExternalAuth)
{ {
Model.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderAuthentication, "/authentication", "key")); Model.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderAuthentication, "/authentication", "key"));
} }
@ -26,7 +26,6 @@
} }
Model.IsMobile = Model.Request.IsMobile(); Model.IsMobile = Model.Request.IsMobile();
long timeStarted = TimestampHelper.TimestampMillis;
} }
<!DOCTYPE html> <!DOCTYPE html>
@ -35,11 +34,11 @@
<head> <head>
@if (Model.Title == string.Empty) @if (Model.Title == string.Empty)
{ {
<title>Project Lighthouse</title> <title>@ServerConfiguration.Instance.Customization.ServerName</title>
} }
else else
{ {
<title>Project Lighthouse - @Model.Title</title> <title>@ServerConfiguration.Instance.Customization.ServerName - @Model.Title</title>
} }
<link rel="stylesheet" type="text/css" href="~/css/styles.css"> <link rel="stylesheet" type="text/css" href="~/css/styles.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.8/dist/semantic.min.css"> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.8/dist/semantic.min.css">
@ -54,7 +53,7 @@
@* Embed Stuff *@ @* Embed Stuff *@
<meta name="theme-color" data-react-helmet="true" content="#008cff"> <meta name="theme-color" data-react-helmet="true" content="#008cff">
<meta content="Project Lighthouse - @Model.Title" property="og:title"> <meta content="@ServerConfiguration.Instance.Customization.ServerName - @Model.Title" property="og:title">
@if (!string.IsNullOrEmpty(Model.Description)) @if (!string.IsNullOrEmpty(Model.Description))
{ {
<meta content="@Model.Description" property="og:description"> <meta content="@Model.Description" property="og:description">
@ -63,16 +62,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@* Google Analytics *@ @* Google Analytics *@
@if (ServerSettings.Instance.GoogleAnalyticsEnabled) @if (ServerConfiguration.Instance.GoogleAnalytics.AnalyticsEnabled)
{ {
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=@ServerSettings.Instance.GoogleAnalyticsId"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=@ServerConfiguration.Instance.GoogleAnalytics.Id"></script>
<script> <script>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('js', new Date());
gtag('config', '@ServerSettings.Instance.GoogleAnalyticsId'); gtag('config', '@ServerConfiguration.Instance.GoogleAnalytics.Id');
</script> </script>
} }
</head> </head>
@ -180,7 +179,6 @@
<p>Model.Title: @Model.Title</p> <p>Model.Title: @Model.Title</p>
<p>Model.Description: @Model.Description</p> <p>Model.Description: @Model.Description</p>
<p>Model.User.UserId: @(Model.User?.UserId.ToString() ?? "(not logged in)")</p> <p>Model.User.UserId: @(Model.User?.UserId.ToString() ?? "(not logged in)")</p>
<p>Render time: ~@(TimestampHelper.TimestampMillis - timeStarted)ms</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -3,11 +3,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using LBPUnion.ProjectLighthouse.Localization; using LBPUnion.ProjectLighthouse.Localization;
using LBPUnion.ProjectLighthouse.Localization.StringLists; using LBPUnion.ProjectLighthouse.Localization.StringLists;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace LBPUnion.ProjectLighthouse.Pages.Layouts; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
public class BaseLayout : PageModel public class BaseLayout : PageModel
{ {

View file

@ -1,6 +1,6 @@
@page "/login" @page "/login"
@using LBPUnion.ProjectLighthouse.Types.Settings @using LBPUnion.ProjectLighthouse.Configuration
@model LBPUnion.ProjectLighthouse.Pages.LoginForm @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LoginForm
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
@ -50,13 +50,13 @@
</div> </div>
</div> </div>
@if (ServerSettings.Instance.HCaptchaEnabled) @if (ServerConfiguration.Instance.Captcha.CaptchaEnabled)
{ {
@await Html.PartialAsync("Partials/CaptchaPartial") @await Html.PartialAsync("Partials/CaptchaPartial")
} }
<input type="submit" value="Log in" id="submit" class="ui blue button"> <input type="submit" value="Log in" id="submit" class="ui blue button">
@if (ServerSettings.Instance.RegistrationEnabled) @if (ServerConfiguration.Instance.Authentication.RegistrationEnabled)
{ {
<a href="/register"> <a href="/register">
<div class="ui button"> <div class="ui button">

View file

@ -1,27 +1,25 @@
#nullable enable #nullable enable
using System;
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Kettu; using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Profiles.Email;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class LoginForm : BaseLayout public class LoginForm : BaseLayout
{ {
public LoginForm(Database database) : base(database) public LoginForm(Database database) : base(database)
{} {}
public string Error { get; private set; } public string? Error { get; private set; }
[UsedImplicitly] [UsedImplicitly]
public async Task<IActionResult> OnPost(string username, string password) public async Task<IActionResult> OnPost(string username, string password)
@ -38,7 +36,7 @@ public class LoginForm : BaseLayout
return this.Page(); return this.Page();
} }
if (!await Request.CheckCaptchaValidity()) if (!await this.Request.CheckCaptchaValidity())
{ {
this.Error = "You must complete the captcha correctly."; this.Error = "You must complete the captcha correctly.";
return this.Page(); return this.Page();
@ -47,34 +45,34 @@ public class LoginForm : BaseLayout
User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) if (user == null)
{ {
Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance); Logger.Warn($"User {username} failed to login on web due to invalid username", LogArea.Login);
this.Error = "The username or password you entered is invalid."; this.Error = "The username or password you entered is invalid.";
return this.Page(); return this.Page();
} }
if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) if (!BCrypt.Net.BCrypt.Verify(password, user.Password))
{ {
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance); Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LogArea.Login);
this.Error = "The username or password you entered is invalid."; this.Error = "The username or password you entered is invalid.";
return this.Page(); return this.Page();
} }
if (user.Banned) if (user.Banned)
{ {
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance); Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LogArea.Login);
this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason; this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason;
return this.Page(); return this.Page();
} }
if (user.EmailAddress == null && ServerSettings.Instance.SMTPEnabled) if (user.EmailAddress == null && ServerConfiguration.Instance.Mail.MailEnabled)
{ {
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login; email not set", LoggerLevelLogin.Instance); Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login; email not set", LogArea.Login);
EmailSetToken emailSetToken = new() EmailSetToken emailSetToken = new()
{ {
UserId = user.UserId, UserId = user.UserId,
User = user, User = user,
EmailToken = HashHelper.GenerateAuthToken(), EmailToken = CryptoHelper.GenerateAuthToken(),
}; };
this.Database.EmailSetTokens.Add(emailSetToken); this.Database.EmailSetTokens.Add(emailSetToken);
@ -86,7 +84,7 @@ public class LoginForm : BaseLayout
WebToken webToken = new() WebToken webToken = new()
{ {
UserId = user.UserId, UserId = user.UserId,
UserToken = HashHelper.GenerateAuthToken(), UserToken = CryptoHelper.GenerateAuthToken(),
}; };
this.Database.WebTokens.Add(webToken); this.Database.WebTokens.Add(webToken);
@ -102,18 +100,14 @@ public class LoginForm : BaseLayout
} }
); );
Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LoggerLevelLogin.Instance); Logger.Success($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LogArea.Login);
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
if (ServerSettings.Instance.SMTPEnabled && !user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail"); if (ServerConfiguration.Instance.Mail.MailEnabled && !user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail");
return this.RedirectToPage(nameof(LandingPage)); return this.RedirectToPage(nameof(LandingPage));
} }
[UsedImplicitly] [UsedImplicitly]
public async Task<IActionResult> OnGet() public IActionResult OnGet() => this.Page();
{
this.Error = string.Empty;
return this.Page();
}
} }

View file

@ -1,5 +1,5 @@
@page "/logout" @page "/logout"
@model LBPUnion.ProjectLighthouse.Pages.LogoutPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LogoutPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,10 +1,10 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class LogoutPage : BaseLayout public class LogoutPage : BaseLayout
{ {

View file

@ -1,4 +1,4 @@
@model LBPUnion.ProjectLighthouse.Types.AdminPanelStatistic @model LBPUnion.ProjectLighthouse.Administration.AdminPanelStatistic
<div class="three wide column"> <div class="three wide column">
<div class="ui center aligned blue segment"> <div class="ui center aligned blue segment">

View file

@ -1,4 +1,4 @@
@model LBPUnion.ProjectLighthouse.Types.User @model LBPUnion.ProjectLighthouse.PlayerData.Profiles.User
<form method="post" action="/admin/user/@Model.UserId/setGrantedSlots"> <form method="post" action="/admin/user/@Model.UserId/setGrantedSlots">
@Html.AntiForgeryToken() @Html.AntiForgeryToken()

View file

@ -0,0 +1,6 @@
@using LBPUnion.ProjectLighthouse.Configuration
@if (ServerConfiguration.Instance.Captcha.CaptchaEnabled)
{
<div class="h-captcha" data-sitekey="@ServerConfiguration.Instance.Captcha.SiteKey"></div>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
}

View file

@ -1,6 +1,5 @@
@using System.IO @using System.Web
@using System.Web @using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Types.Profiles
<div class="ui yellow segment" id="comments"> <div class="ui yellow segment" id="comments">
<h2>Comments</h2> <h2>Comments</h2>
@if (Model.Comments.Count == 0 && Model.CommentsEnabled) @if (Model.Comments.Count == 0 && Model.CommentsEnabled)
@ -28,6 +27,10 @@
</div> </div>
<input type="submit" class="ui blue button"> <input type="submit" class="ui blue button">
</form> </form>
@if (Model.Comments.Count > 0)
{
<div class="ui divider"></div>
}
} }
@for(int i = 0; i < Model.Comments.Count; i++) @for(int i = 0; i < Model.Comments.Count; i++)
@ -38,7 +41,8 @@
HttpUtility.HtmlDecode(comment.getComment(), messageWriter); HttpUtility.HtmlDecode(comment.getComment(), messageWriter);
string decodedMessage = messageWriter.ToString(); string decodedMessage = messageWriter.ToString();
string url = Url.RouteUrl(ViewContext.RouteData.Values); string? url = Url.RouteUrl(ViewContext.RouteData.Values);
if (url == null) continue;
int rating = comment.ThumbsUp - comment.ThumbsDown; int rating = comment.ThumbsUp - comment.ThumbsDown;

View file

@ -1,5 +1,6 @@
@using LBPUnion.ProjectLighthouse.PlayerData
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Types.Photo @model LBPUnion.ProjectLighthouse.PlayerData.Photo
<div style="position: relative"> <div style="position: relative">

View file

@ -1,16 +1,19 @@
@using LBPUnion.ProjectLighthouse @using LBPUnion.ProjectLighthouse
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Configuration
@using LBPUnion.ProjectLighthouse.Types.Settings @using LBPUnion.ProjectLighthouse.PlayerData
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@model LBPUnion.ProjectLighthouse.Types.Levels.Slot @model LBPUnion.ProjectLighthouse.Levels.Slot
@{ @{
User user = (User)ViewData["User"]; User? user = (User?)ViewData["User"];
await using Database database = new(); await using Database database = new();
string slotName = string.IsNullOrEmpty(Model.Name) ? "Unnamed Level" : Model.Name; string slotName = string.IsNullOrEmpty(Model.Name) ? "Unnamed Level" : Model.Name;
bool isMobile = (bool?)ViewData["IsMobile"] ?? false; bool isMobile = (bool?)ViewData["IsMobile"] ?? false;
bool mini = (bool?)ViewData["IsMini"] ?? false;
bool isQueued = false; bool isQueued = false;
bool isHearted = false; bool isHearted = false;
@ -18,37 +21,55 @@
if (user != null) if (user != null)
{ {
isQueued = await database.QueuedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; isQueued = await database.QueuedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null;
isHearted = await database.HeartedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; isHearted = await database.HeartedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null;
} }
string callbackUrl = (string)ViewData["CallbackUrl"]; string callbackUrl = (string)ViewData["CallbackUrl"]!;
bool showLink = (bool?)ViewData["ShowLink"] ?? false; bool showLink = (bool?)ViewData["ShowLink"] ?? false;
string iconHash = Model.IconHash; string iconHash = Model.IconHash;
if (string.IsNullOrWhiteSpace(iconHash) || iconHash.StartsWith('g')) iconHash = ServerSettings.Instance.MissingIconHash; if (string.IsNullOrWhiteSpace(iconHash) || iconHash.StartsWith('g')) iconHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash;
} }
<div class="card"> <div class="card">
@{ @{
int size = isMobile ? 50 : 100; int size = isMobile || mini ? 50 : 100;
} }
<div> <div>
<img src="~/assets/slotCardOverlay.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; pointer-events: none; position: absolute"> <img src="~/assets/slotCardOverlay.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; pointer-events: none; position: absolute">
<img class="cardIcon slotCardIcon" src="/gameAssets/@iconHash" style="min-width: @(size)px; width: @(size)px; height: @(size)px"> <img class="cardIcon slotCardIcon" src="/gameAssets/@iconHash" style="min-width: @(size)px; width: @(size)px; height: @(size)px">
</div> </div>
<div class="cardStats"> <div class="cardStats">
@if (showLink) @if (!mini)
{ {
<h2> @if (showLink)
<a href="~/slot/@Model.SlotId">@slotName</a> {
</h2> <h2>
<a href="~/slot/@Model.SlotId">@slotName</a>
</h2>
}
else
{
<h1>
@slotName
</h1>
}
} }
else else
{ {
<h1> @if (showLink)
@slotName {
</h1> <h3>
<a href="~/slot/@Model.SlotId">@slotName</a>
</h3>
}
else
{
<h3>
@slotName
</h3>
}
} }
<div class="cardStatsUnderTitle"> <div class="cardStatsUnderTitle">
<i class="pink heart icon" title="Hearts"></i> <span>@Model.Hearts</span> <i class="pink heart icon" title="Hearts"></i> <span>@Model.Hearts</span>
<i class="blue play icon" title="Plays"></i> <span>@Model.PlaysUnique</span> <i class="blue play icon" title="Plays"></i> <span>@Model.PlaysUnique</span>
@ -67,7 +88,7 @@
</div> </div>
<div class="cardButtons"> <div class="cardButtons">
<br> <br>
@if (user != null) @if (user != null && !mini)
{ {
if (isHearted) if (isHearted)
{ {

View file

@ -1,4 +1,4 @@
@model LBPUnion.ProjectLighthouse.Types.User @model LBPUnion.ProjectLighthouse.PlayerData.Profiles.User
@{ @{
bool showLink = (bool?)ViewData["ShowLink"] ?? false; bool showLink = (bool?)ViewData["ShowLink"] ?? false;

View file

@ -1,5 +1,5 @@
@page "/passwordReset" @page "/passwordReset"
@model LBPUnion.ProjectLighthouse.Pages.PasswordResetPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PasswordResetPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,19 +1,20 @@
#nullable enable #nullable enable
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class PasswordResetPage : BaseLayout public class PasswordResetPage : BaseLayout
{ {
public PasswordResetPage(Database database) : base(database) public PasswordResetPage(Database database) : base(database)
{} {}
public string Error { get; private set; } public string? Error { get; private set; }
[UsedImplicitly] [UsedImplicitly]
public async Task<IActionResult> OnPost(string password, string confirmPassword) public async Task<IActionResult> OnPost(string password, string confirmPassword)
@ -33,12 +34,13 @@ public class PasswordResetPage : BaseLayout
return this.Page(); return this.Page();
} }
user.Password = HashHelper.BCryptHash(password); user.Password = CryptoHelper.BCryptHash(password);
user.PasswordResetRequired = false; user.PasswordResetRequired = false;
await this.Database.SaveChangesAsync(); await this.Database.SaveChangesAsync();
if (!user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail"); if (!user.EmailAddressVerified && ServerConfiguration.Instance.Mail.MailEnabled)
return this.Redirect("~/login/sendVerificationEmail");
return this.Redirect("~/"); return this.Redirect("~/");
} }

View file

@ -1,5 +1,5 @@
@page "/passwordResetRequired" @page "/passwordResetRequired"
@model LBPUnion.ProjectLighthouse.Pages.PasswordResetRequiredPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PasswordResetRequiredPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";

View file

@ -1,20 +1,19 @@
#nullable enable #nullable enable
using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class PasswordResetRequiredPage : BaseLayout public class PasswordResetRequiredPage : BaseLayout
{ {
public PasswordResetRequiredPage([NotNull] Database database) : base(database) public PasswordResetRequiredPage(Database database) : base(database)
{} {}
public bool WasResetRequest { get; private set; } public bool WasResetRequest { get; private set; }
public async Task<IActionResult> OnGet() public IActionResult OnGet()
{ {
User? user = this.Database.UserFromWebRequest(this.Request); User? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("~/login"); if (user == null) return this.Redirect("~/login");

View file

@ -1,6 +1,7 @@
@page "/photos/{pageNumber:int}" @page "/photos/{pageNumber:int}"
@using LBPUnion.ProjectLighthouse.PlayerData
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.PhotosPage @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PhotosPage
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
@ -26,10 +27,10 @@
@if (Model.PageNumber != 0) @if (Model.PageNumber != 0)
{ {
<a href="/photos/@(Model.PageNumber - 1)@(Model.SearchValue.Length == 0 ? "" : "?name=" + Model.SearchValue)">Previous Page</a> <a href="/photos/@(Model.PageNumber - 1)@(Model.SearchValue?.Length == 0 ? "" : "?name=" + Model.SearchValue)">Previous Page</a>
} }
@(Model.PageNumber + 1) / @(Model.PageAmount) @(Model.PageNumber + 1) / @(Model.PageAmount)
@if (Model.PageNumber < Model.PageAmount - 1) @if (Model.PageNumber < Model.PageAmount - 1)
{ {
<a href="/photos/@(Model.PageNumber + 1)@(Model.SearchValue.Length == 0 ? "" : "?name=" + Model.SearchValue)">Next Page</a> <a href="/photos/@(Model.PageNumber + 1)@(Model.SearchValue?.Length == 0 ? "" : "?name=" + Model.SearchValue)">Next Page</a>
} }

Some files were not shown because too many files have changed in this diff Show more