This commit is contained in:
LumaLivy 2021-11-19 21:49:27 -05:00
commit e82c2afbe4
30 changed files with 400 additions and 110 deletions

View file

@ -60,13 +60,13 @@ jobs:
- name: Process Test Results (Control)
if: ${{ matrix.os.prettyName == 'Linux' }}
uses: im-open/process-dotnet-test-results@v2.0.0
uses: im-open/process-dotnet-test-results@v2.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Process Test Results
if: ${{ matrix.os.prettyName != 'Linux' }}
uses: im-open/process-dotnet-test-results@v2.0.0
uses: im-open/process-dotnet-test-results@v2.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
create-status-check: false

5
.gitignore vendored
View file

@ -13,4 +13,7 @@ riderModule.iml
/ProjectLighthouse/ProjectLighthouse.csproj.user
.vs/
.vscode/
.editorconfig
.editorconfig
lighthouse.config.json
gitBranch.txt
gitVersion.txt

View file

@ -8,8 +8,9 @@ namespace LBPUnion.ProjectLighthouse.Tests
{
public DatabaseFact()
{
ServerSettings.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
if (!ServerSettings.DbConnected)
ServerSettings.Instance = new ServerSettings();
ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
if (!ServerStatics.DbConnected)
{
this.Skip = "Database not available";
}

View file

@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
@ -55,6 +56,14 @@ namespace LBPUnion.ProjectLighthouse.Tests
return this.Client.SendAsync(requestMessage);
}
public async Task<HttpResponseMessage> UploadFileEndpointRequest(string filePath)
{
byte[] bytes = Encoding.UTF8.GetBytes(await File.ReadAllTextAsync(filePath));
string hash = HashHelper.Sha1Hash(bytes);
return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes));
}
public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath)
=> await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath)));

View file

@ -28,7 +28,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
Assert.True(response.IsSuccessStatusCode);
string responseContent = await response.Content.ReadAsStringAsync();
Assert.Contains("MM_AUTH=", responseContent);
Assert.Contains(ServerSettings.ServerName, responseContent);
Assert.Contains(ServerStatics.ServerName, responseContent);
}
[DatabaseFact]
@ -41,7 +41,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
Assert.NotNull(loginResult.LbpEnvVer);
Assert.Contains("MM_AUTH=", loginResult.AuthTicket);
Assert.Equal(ServerSettings.ServerName, loginResult.LbpEnvVer);
Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer);
}
[DatabaseFact]
@ -59,7 +59,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
[DatabaseFact]
public async Task ShouldReturnForbiddenWhenNotAuthenticated()
{
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/eula");
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce");
Assert.False(response.IsSuccessStatusCode);
Assert.True(response.StatusCode == HttpStatusCode.Forbidden);
}

View file

@ -17,13 +17,18 @@ namespace LBPUnion.ProjectLighthouse.Tests
User userA = await database.CreateUser("unitTestUser0");
User userB = await database.CreateUser("unitTestUser1");
Location l = new();
Location l = new()
{
X = 0,
Y = 0,
};
database.Locations.Add(l);
await database.SaveChangesAsync();
Slot slotA = new()
{
Creator = userA,
CreatorId = userA.UserId,
Name = "slotA",
Location = l,
LocationId = l.Id,
@ -33,6 +38,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
Slot slotB = new()
{
Creator = userB,
CreatorId = userB.UserId,
Name = "slotB",
Location = l,
LocationId = l.Id,
@ -49,8 +55,10 @@ namespace LBPUnion.ProjectLighthouse.Tests
LoginResult loginResult = await this.Authenticate();
HttpResponseMessage respMessageA = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0", loginResult.AuthTicket);
HttpResponseMessage respMessageB = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1", loginResult.AuthTicket);
HttpResponseMessage respMessageA = await this.AuthenticatedRequest
("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0&pageStart=1&pageSize=1", loginResult.AuthTicket);
HttpResponseMessage respMessageB = await this.AuthenticatedRequest
("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1&pageStart=1&pageSize=1", loginResult.AuthTicket);
Assert.True(respMessageA.IsSuccessStatusCode);
Assert.True(respMessageB.IsSuccessStatusCode);

View file

@ -17,35 +17,35 @@ namespace LBPUnion.ProjectLighthouse.Tests
[Fact]
public async Task ShouldNotAcceptScript()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff");
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff");
Assert.False(response.IsSuccessStatusCode);
}
[Fact]
public async Task ShouldNotAcceptFarc()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc");
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc");
Assert.False(response.IsSuccessStatusCode);
}
[Fact]
public async Task ShouldNotAcceptGarbage()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin");
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin");
Assert.False(response.IsSuccessStatusCode);
}
[Fact]
public async Task ShouldAcceptTexture()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex");
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex");
Assert.True(response.IsSuccessStatusCode);
}
[Fact]
public async Task ShouldAcceptLevel()
{
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl");
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl");
Assert.True(response.IsSuccessStatusCode);
}
}

View file

@ -90,6 +90,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.Ok();
}
[HttpPost("lolcatftw/clear")]
public async Task<IActionResult> ClearQueuedLevels()
{
User? user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId));
await this.database.SaveChangesAsync();
return this.Ok();
}
#endregion
#region Hearted Levels

View file

@ -65,7 +65,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
new LoginResult
{
AuthTicket = "MM_AUTH=" + token.UserToken,
LbpEnvVer = ServerSettings.ServerName,
LbpEnvVer = ServerStatics.ServerName,
}.Serialize()
);
}

View file

@ -4,6 +4,7 @@ using Kettu;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Controllers
@ -21,11 +22,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
[HttpGet("eula")]
public async Task<IActionResult> Eula()
{
User user = await this.database.UserFromRequest(this.Request);
return user == null ? this.StatusCode(403, "") : this.Ok(EulaHelper.PrivateInstanceNoticeOrBlank + "\n" + $"{EulaHelper.License}\n");
}
public IActionResult Eula() => this.Ok(ServerSettings.Instance.EulaText + "\n" + $"{EulaHelper.License}\n");
[HttpGet("announce")]
public async Task<IActionResult> Announce()
@ -33,7 +30,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
User user = await this.database.UserFromRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
return this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n\n" + EulaHelper.PrivateInstanceNoticeOrBlank);
return this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n\n" + ServerSettings.Instance.EulaText);
}
[HttpGet("notification")]

View file

@ -51,10 +51,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
// TODO: check if this is a valid hash
[HttpPost("upload/{hash}")]
[AllowSynchronousIo]
public async Task<IActionResult> UploadResource(string hash)
{
string assetsDirectory = FileHelper.ResourcePath;
string path = FileHelper.GetResourcePath(hash);
@ -70,6 +68,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.UnprocessableEntity();
}
if (HashHelper.Sha1Hash(file.Data) != hash)
{
Logger.Log($"File hash does not match the uploaded file! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
return this.Conflict();
}
Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
await IOFile.WriteAllBytesAsync(path, file.Data);
return this.Ok();

View file

@ -42,7 +42,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
.Include(s => s.Location)
.Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1)
.Take(Math.Min(pageSize, ServerSettings.EntitledSlots)),
.Take(Math.Min(pageSize, ServerStatics.EntitledSlots)),
string.Empty,
(current, slot) => current + slot.Serialize()
);
@ -56,7 +56,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
new Dictionary<string, object>
{
{
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.EntitledSlots)
"hint_start", pageStart + Math.Min(pageSize, ServerStatics.EntitledSlots)
},
{
"total", user.UsedSlots

View file

@ -1,9 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers
{
@ -20,18 +18,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("playersInPodCount")]
[HttpGet("totalPlayerCount")]
public async Task<IActionResult> TotalPlayerCount()
{
int recentMatches = await this.database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 60).CountAsync();
return this.Ok(recentMatches.ToString());
}
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);
[HttpGet("planetStats")]
public async Task<IActionResult> PlanetStats()
{
int totalSlotCount = await this.database.Slots.CountAsync();
int mmPicksCount = await this.database.Slots.CountAsync(s => s.TeamPick);
int totalSlotCount = await StatisticsHelper.SlotCount();
int mmPicksCount = await StatisticsHelper.MMPicksCount();
return this.Ok
(
@ -41,6 +34,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
[HttpGet("planetStats/totalLevelCount")]
public async Task<IActionResult> TotalLevelCount() => this.Ok((await this.database.Slots.CountAsync()).ToString());
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString());
}
}

View file

@ -33,7 +33,7 @@ namespace LBPUnion.ProjectLighthouse
public DbSet<RatedReview> RatedReviews { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseMySql(ServerSettings.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
public async Task<User> CreateUser(string username)
{

View file

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

View file

@ -1,5 +1,3 @@
using System.Diagnostics.CodeAnalysis;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class EulaHelper
@ -17,13 +15,5 @@ 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 const string PrivateInstanceNotice = @"This server is a private testing instance.
Please do not make anything public for now, and keep in mind security isn't as tight as a full release would.";
[SuppressMessage("ReSharper", "HeuristicUnreachableCode")]
public const string PrivateInstanceNoticeOrBlank = ShowPrivateInstanceNotice ? PrivateInstanceNotice : "";
public const bool ShowPrivateInstanceNotice = false;
}
}

View file

@ -78,6 +78,18 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
public static int ResourceSize(string hash)
{
try
{
return (int)new FileInfo(GetResourcePath(hash)).Length;
}
catch
{
return 0;
}
}
public static void EnsureDirectoryCreated(string path)
{
if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path)));

View file

@ -0,0 +1,56 @@
using System;
using System.IO;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class GitVersionHelper
{
static GitVersionHelper()
{
try
{
CommitHash = readManifestFile("gitVersion.txt");
Branch = readManifestFile("gitBranch.txt");
CanCheckForUpdates = true;
}
catch
{
Logger.Log
(
"Project Lighthouse was built incorrectly. Please make sure git is available when building. " +
"Because of this, you will not be notified of updates.",
LoggerLevelStartup.Instance
);
CommitHash = "invalid";
Branch = "invalid";
CanCheckForUpdates = false;
}
if (IsDirty)
{
Logger.Log
(
"This is a modified version of Project Lighthouse. " +
"Please make sure you are properly disclosing the source code to any users who may be using this instance.",
LoggerLevelStartup.Instance
);
CanCheckForUpdates = false;
}
}
private static string readManifestFile(string fileName)
{
using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}");
using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null."));
return reader.ReadToEnd().Trim();
}
public static string CommitHash { get; set; }
public static string Branch { get; set; }
public static bool IsDirty => CommitHash.EndsWith("-dirty");
public static bool CanCheckForUpdates { get; set; }
}
}

View file

@ -11,7 +11,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class HashHelper
{
// private static readonly SHA1 sha1 = SHA1.Create();
private static readonly SHA1 sha1 = SHA1.Create();
private static readonly SHA256 sha256 = SHA256.Create();
private static readonly Random random = new();
@ -67,11 +67,11 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
public static string Sha256Hash(byte[] bytes)
{
byte[] hash = sha256.ComputeHash(bytes);
return Encoding.UTF8.GetString(hash, 0, hash.Length);
}
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "");
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", "");
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);

View file

@ -0,0 +1,50 @@
using System.Threading;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Writes;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class InfluxHelper
{
public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken);
public static async void Log()
{
using WriteApi writeApi = Client.GetWriteApi();
PointData point = PointData.Measurement("lighthouse")
.Field("playerCount", await StatisticsHelper.RecentMatches())
.Field("slotCount", await StatisticsHelper.SlotCount());
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
writeApi.Flush();
}
public static async Task StartLogging()
{
await Client.ReadyAsync();
Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance);
Thread t = new
(
delegate()
{
while (true)
{
#pragma warning disable CS4014
Log();
#pragma warning restore CS4014
// Logger.Log("Logged.", LoggerLevelInflux.Instance);
Thread.Sleep(60000);
}
}
);
t.IsBackground = true;
t.Name = "InfluxDB Logger";
t.Start();
}
}
}

View file

@ -0,0 +1,17 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class StatisticsHelper
{
private static readonly Database database = new();
public static async Task<int> RecentMatches() => await database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
public static async Task<int> MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
}
}

View file

@ -0,0 +1,27 @@
using InfluxDB.Client;
using InfluxDB.Client.Writes;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Logging
{
public class InfluxLogger : LoggerBase
{
public override void Send(LoggerLine line)
{
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";
string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}";
string content = line.LineData;
using WriteApi writeApi = InfluxHelper.Client.GetWriteApi();
PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content);
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
}
public override bool AllowMultiple => false;
}
}

View file

@ -51,6 +51,18 @@ namespace LBPUnion.ProjectLighthouse.Logging
public override string Name => "Photos";
}
public class LoggerLevelConfig : LoggerLevel
{
public static readonly LoggerLevelConfig Instance = new();
public override string Name => "Config";
}
public class LoggerLevelInflux : LoggerLevel
{
public static readonly LoggerLevelInflux Instance = new();
public override string Name => "Influx";
}
public class LoggerLevelAspNet : LoggerLevel
{

View file

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using Kettu;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Hosting;
@ -28,8 +29,13 @@ namespace LBPUnion.ProjectLighthouse
Logger.AddLogger(new LighthouseFileLogger());
Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance);
Logger.Log($"Running {ServerStatics.ServerName} {GitVersionHelper.CommitHash}@{GitVersionHelper.Branch}", LoggerLevelStartup.Instance);
// This loads the config, see ServerSettings.cs for more information
Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance);
Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance);
bool dbConnected = ServerSettings.DbConnected;
bool dbConnected = ServerStatics.DbConnected;
Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance);
if (!dbConnected) Environment.Exit(1);
@ -38,6 +44,15 @@ namespace LBPUnion.ProjectLighthouse
Logger.Log("Migrating database...", LoggerLevelDatabase.Instance);
MigrateDatabase(database);
if (ServerSettings.Instance.InfluxEnabled)
{
Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance);
#pragma warning disable CS4014
InfluxHelper.StartLogging();
#pragma warning restore CS4014
if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger());
}
stopwatch.Stop();
Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance);

View file

@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
<PackageReference Include="InfluxDB.Client" Version="3.1.0"/>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
<PackageReference Include="Kettu" Version="1.2.1"/>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.0"/>
@ -21,7 +22,19 @@
</ItemGroup>
<ItemGroup>
<Compile Remove="Types\SlotXsd.cs"/>
<None Remove="gitVersion.txt"/>
<EmbeddedResource Include="gitVersion.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitBranch.txt"/>
<EmbeddedResource Include="gitBranch.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;"/>
</Target>
</Project>

View file

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Profiles;
using LBPUnion.ProjectLighthouse.Types.Reviews;
@ -206,7 +207,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
public string SerializeResources()
{
return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource));
return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) +
LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize));
}
public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, Review? yourReview = null)

View file

@ -1,44 +1,91 @@
#nullable enable
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
namespace LBPUnion.ProjectLighthouse.Types.Settings
{
public static class ServerSettings
[Serializable]
public class ServerSettings
{
/// <summary>
/// The maximum amount of slots allowed on users' earth
/// </summary>
public const int EntitledSlots = 50;
static ServerSettings()
{
if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own
public const int ListsQuota = 50;
if (File.Exists(ConfigFileName))
{
string configFile = File.ReadAllText(ConfigFileName);
public const string ServerName = "ProjectLighthouse";
Instance = JsonSerializer.Deserialize<ServerSettings>(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName));
private static string? dbConnectionString;
if (Instance.ConfigVersion >= CurrentConfigVersion) return;
public static string DbConnectionString {
get {
if (dbConnectionString == null) return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? "";
Logger.Log($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LoggerLevelConfig.Instance);
Instance.ConfigVersion = CurrentConfigVersion;
configFile = JsonSerializer.Serialize
(
Instance,
typeof(ServerSettings),
new JsonSerializerOptions
{
WriteIndented = true,
}
);
return dbConnectionString;
File.WriteAllText(ConfigFileName, configFile);
}
set => dbConnectionString = value;
}
else
{
string configFile = JsonSerializer.Serialize
(
new ServerSettings(),
typeof(ServerSettings),
new JsonSerializerOptions
{
WriteIndented = true,
}
);
public static bool DbConnected {
get {
try
{
return new Database().Database.CanConnect();
}
catch(Exception e)
{
Logger.Log(e.ToString(), LoggerLevelDatabase.Instance);
return false;
}
File.WriteAllText(ConfigFileName, configFile);
Logger.Log
(
"The configuration file was not found. " +
"A blank configuration file has been created for you at " +
$"{Path.Combine(Environment.CurrentDirectory, ConfigFileName)}",
LoggerLevelConfig.Instance
);
Environment.Exit(1);
}
}
#region Meta
[NotNull]
public static ServerSettings Instance;
public const int CurrentConfigVersion = 4;
[JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")]
public int ConfigVersion { get; set; } = CurrentConfigVersion;
public const string ConfigFileName = "lighthouse.config.json";
#endregion Meta
public bool InfluxEnabled { get; set; }
public bool InfluxLoggingEnabled { get; set; }
public string InfluxOrg { get; set; } = "lighthouse";
public string InfluxBucket { get; set; } = "lighthouse";
public string InfluxToken { get; set; } = "";
public string InfluxUrl { get; set; } = "http://localhost:8086";
public string EulaText { get; set; } = "";
public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
}
}

View file

@ -0,0 +1,36 @@
#nullable enable
using System;
using System.Linq;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
namespace LBPUnion.ProjectLighthouse.Types.Settings
{
public static class ServerStatics
{
/// <summary>
/// The maximum amount of slots allowed on users' earth
/// </summary>
public const int EntitledSlots = 50;
public const int ListsQuota = 50;
public const string ServerName = "ProjectLighthouse";
public static bool DbConnected {
get {
try
{
return new Database().Database.CanConnect();
}
catch(Exception e)
{
Logger.Log(e.ToString(), LoggerLevelDatabase.Instance);
return false;
}
}
}
public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit"));
}
}

View file

@ -105,7 +105,7 @@ namespace LBPUnion.ProjectLighthouse.Types
LbpSerializer.StringElement("game", this.Game) +
this.SerializeSlots(gameVersion == GameVersion.LittleBigPlanetVita) +
LbpSerializer.StringElement("lists", this.Lists) +
LbpSerializer.StringElement("lists_quota", ServerSettings.ListsQuota) + // technically not a part of the user but LBP expects it
LbpSerializer.StringElement("lists_quota", ServerStatics.ListsQuota) + // technically not a part of the user but LBP expects it
LbpSerializer.StringElement("biography", this.Biography) +
LbpSerializer.StringElement("reviewCount", this.Reviews) +
LbpSerializer.StringElement("commentCount", this.Comments) +
@ -147,7 +147,7 @@ namespace LBPUnion.ProjectLighthouse.Types
/// <summary>
/// The number of slots remaining on the earth
/// </summary>
public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots;
public int FreeSlots => ServerStatics.EntitledSlots - this.UsedSlots;
private static readonly string[] slotTypes =
{
@ -177,12 +177,12 @@ namespace LBPUnion.ProjectLighthouse.Types
slotTypesLocal = slotTypes;
}
slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots);
slots += LbpSerializer.StringElement("entitledSlots", ServerStatics.EntitledSlots);
slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots);
foreach (string slotType in slotTypesLocal)
{
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots);
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerStatics.EntitledSlots);
// ReSharper disable once StringLiteralTypo
slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0);
slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots);

View file

@ -62,7 +62,7 @@ Finally, take a break. Chances are that took a while.
## Contributing Tips
### Database
### Database migrations
Some modifications may require updates to the database schema. You can automatically create a migration file by:
@ -72,6 +72,12 @@ Some modifications may require updates to the database schema. You can automatic
you're doing.
4. Running `dotnet ef migrations add <NameOfMigrationInPascalCase> --project ProjectLighthouse`.
### Running tests
You can run tests either through your IDE or by running `dotnet tests`.
Keep in mind while running database tests you need to have `LIGHTHOUSE_DB_CONNECTION_STRING` set.
## Compatibility across games and platforms
| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) |