diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0d54e95..d6c60c2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,6 +24,11 @@ jobs:
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
+ - name: Run QDNET Scan
+ uses: github/codeql-action/upload-sarif@v2
+ with:
+ sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
+
build:
name: Build and Upload Artifacts
runs-on: ubuntu-latest
@@ -36,30 +41,47 @@ jobs:
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
+ - name: Restore Dependencies
+ run: dotnet restore
- name: Get Commit Hash
id: hash
run: echo "hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- - name: Restore dependencies
- run: dotnet restore
-
- - name: Publish for Windows x64
- run: dotnet publish -c Release -r win-x64 --self-contained
- - name: Publish for Linux x64
- run: dotnet publish -c Release -r linux-x64 --self-contained
+ - name: Publish CLI for Windows x64
+ run: dotnet publish -c Release -r win-x64 --self-contained PLRPC
+ - name: Publish CLI for Linux x64
+ run: dotnet publish -c Release -r linux-x64 --self-contained PLRPC
+ - name: Publish GUI for Windows x64
+ run: dotnet publish -c Release -r win-x64 --self-contained PLRPC.GUI.Windows
+ - name: Publish GUI for Linux x64
+ run: dotnet publish -c Release -r linux-x64 --self-contained PLRPC.GUI.Linux
- - name: Upload Windows x64 build
+ - name: Upload Windows x64 CLI build
uses: actions/upload-artifact@v3.1.1
with:
- name: PLRPC Windows x64 [${{ steps.hash.outputs.hash }}]
+ name: PLRPC CLI Windows x64 [${{ steps.hash.outputs.hash }}]
path: "/home/runner/work/PLRPC/PLRPC/PLRPC/bin/Release/net7.0/win-x64/publish/"
if-no-files-found: error
retention-days: 3
- - name: Upload Linux x64 build
+ - name: Upload Linux x64 CLI build
uses: actions/upload-artifact@v3.1.1
with:
- name: PLRPC Linux x64 [${{ steps.hash.outputs.hash }}]
+ name: PLRPC CLI Linux x64 [${{ steps.hash.outputs.hash }}]
path: "/home/runner/work/PLRPC/PLRPC/PLRPC/bin/Release/net7.0/linux-x64/publish/"
if-no-files-found: error
retention-days: 3
+ - name: Upload Windows x64 GUI build
+ uses: actions/upload-artifact@v3.1.1
+ with:
+ name: PLRPC GUI Windows x64 [${{ steps.hash.outputs.hash }}]
+ path: "/home/runner/work/PLRPC/PLRPC/PLRPC.GUI.Windows/bin/Release/net7.0-windows/win-x64/publish/"
+ if-no-files-found: error
+ retention-days: 3
+ - name: Upload Linux x64 GUI build
+ uses: actions/upload-artifact@v3.1.1
+ with:
+ name: PLRPC GUI Linux x64 [${{ steps.hash.outputs.hash }}]
+ path: "/home/runner/work/PLRPC/PLRPC/PLRPC.GUI.Linux/bin/Release/net7.0/linux-x64/publish/"
+ if-no-files-found: error
+ retention-days: 3
\ No newline at end of file
diff --git a/PLRPC.GUI.Linux/PLRPC.GUI.Linux.csproj b/PLRPC.GUI.Linux/PLRPC.GUI.Linux.csproj
new file mode 100644
index 0000000..c7bf914
--- /dev/null
+++ b/PLRPC.GUI.Linux/PLRPC.GUI.Linux.csproj
@@ -0,0 +1,25 @@
+
+
+
+ LBPUnion.PLRPC.GUI
+ enable
+ disable
+
+
+
+ Exe
+ true
+ net7.0
+ linux-x64
+
+
+
+ false
+
+
+
+
+
+
+
+
diff --git a/PLRPC.GUI.Linux/Program.cs b/PLRPC.GUI.Linux/Program.cs
new file mode 100644
index 0000000..2dc1374
--- /dev/null
+++ b/PLRPC.GUI.Linux/Program.cs
@@ -0,0 +1,10 @@
+namespace LBPUnion.PLRPC.GUI;
+
+public static class Program
+{
+ [STAThread]
+ public static void Main()
+ {
+ Gui.Initialize();
+ }
+}
\ No newline at end of file
diff --git a/PLRPC.GUI.Windows/PLRPC.GUI.Windows.csproj b/PLRPC.GUI.Windows/PLRPC.GUI.Windows.csproj
new file mode 100644
index 0000000..ce31494
--- /dev/null
+++ b/PLRPC.GUI.Windows/PLRPC.GUI.Windows.csproj
@@ -0,0 +1,30 @@
+
+
+
+ LBPUnion.PLRPC.GUI
+ enable
+ disable
+
+
+
+ Exe
+ true
+ net7.0-windows
+ win-x64
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PLRPC.GUI.Windows/Program.cs b/PLRPC.GUI.Windows/Program.cs
new file mode 100644
index 0000000..2dc1374
--- /dev/null
+++ b/PLRPC.GUI.Windows/Program.cs
@@ -0,0 +1,10 @@
+namespace LBPUnion.PLRPC.GUI;
+
+public static class Program
+{
+ [STAThread]
+ public static void Main()
+ {
+ Gui.Initialize();
+ }
+}
\ No newline at end of file
diff --git a/PLRPC.GUI/Forms/MainForm.cs b/PLRPC.GUI/Forms/MainForm.cs
new file mode 100644
index 0000000..a8d87cb
--- /dev/null
+++ b/PLRPC.GUI/Forms/MainForm.cs
@@ -0,0 +1,170 @@
+using System.Text;
+using Eto.Drawing;
+using Eto.Forms;
+using LBPUnion.PLRPC.Helpers;
+using LBPUnion.PLRPC.Types.Logging;
+using Serilog;
+
+namespace LBPUnion.PLRPC.GUI.Forms;
+
+public class MainForm : Form
+{
+ private static readonly TextBox username;
+ private static readonly TextBox serverUrl;
+ private static readonly TextBox applicationId;
+
+ public MainForm()
+ {
+ this.Title = "PLRPC";
+ this.ClientSize = new Size(400, -1);
+ this.Resizable = false;
+
+ this.Content = this.tableLayout;
+
+ Log.Logger = Program.Logger;
+ }
+
+ private static readonly GroupBox configurationEntries = new()
+ {
+ Text = Strings.MainForm.Configuration,
+ Content = new TableLayout
+ {
+ Padding = new Padding(3, 3, 3, 3),
+ Spacing = new Size(3, 3),
+ Rows =
+ {
+ new TableRow(new List
+ {
+ new(new Label
+ {
+ Text = Strings.MainForm.Username,
+ }),
+ new(username = new TextBox()),
+ }),
+ new TableRow(new List
+ {
+ new(new Label
+ {
+ Text = Strings.MainForm.ServerUrl,
+ }),
+ new(serverUrl = new TextBox
+ {
+ Text = "https://lighthouse.lbpunion.com/",
+ Enabled = false,
+ }),
+ }),
+ new TableRow(new List
+ {
+ new(new Label
+ {
+ Text = Strings.MainForm.ApplicationId,
+ }),
+ new(applicationId = new TextBox
+ {
+ Text = "1060973475151495288",
+ Enabled = false,
+ }),
+ }),
+ },
+ },
+ };
+
+ private static readonly Button connectButton = new(InitializeClientHandler)
+ {
+ Text = Strings.MainForm.Connect,
+ };
+
+ private static readonly Button unlockDefaultsButton = new(UnlockDefaultsHandler)
+ {
+ Text = Strings.MainForm.UnlockDefaults,
+ };
+
+ private readonly TableLayout tableLayout = new()
+ {
+ Padding = new Padding(10, 10, 10, 10),
+ Spacing = new Size(5, 5),
+ Rows =
+ {
+ new TableRow(configurationEntries),
+ new TableRow(connectButton),
+ new TableRow(unlockDefaultsButton),
+ },
+ };
+
+ private static async void InitializeClientHandler(object sender, EventArgs eventArgs)
+ {
+ List arguments = new()
+ {
+ serverUrl,
+ username,
+ applicationId,
+ };
+
+ switch (arguments)
+ {
+ case not null when arguments.Any(a => string.IsNullOrWhiteSpace(a.Text)):
+ {
+ MessageBox.Show(Strings.MainForm.BlankFieldsError, MessageBoxButtons.OK, MessageBoxType.Error);
+ return;
+ }
+ case not null when !ValidationHelper.IsValidUsername(username.Text):
+ {
+ MessageBox.Show(Strings.MainForm.InvalidUsernameError, MessageBoxButtons.OK, MessageBoxType.Error);
+ return;
+ }
+ case not null when !ValidationHelper.IsValidUrl(serverUrl.Text):
+ {
+ MessageBox.Show(Strings.MainForm.InvalidUrlError, MessageBoxButtons.OK, MessageBoxType.Error);
+ return;
+ }
+ }
+
+ try
+ {
+ // Text changes
+ connectButton.Text = Strings.MainForm.Connected;
+
+ // Button states
+ connectButton.Enabled = false;
+ unlockDefaultsButton.Enabled = false;
+
+ // Field states
+ serverUrl.Enabled = false;
+ username.Enabled = false;
+ applicationId.Enabled = false;
+
+ await Program.InitializeLighthouseClient(serverUrl.Text.Trim('/'), username.Text, applicationId.Text);
+ }
+ catch (Exception exception)
+ {
+ StringBuilder exceptionBuilder = new();
+
+ exceptionBuilder.AppendLine($"{Strings.MainForm.InitializationError}\n");
+ exceptionBuilder.AppendLine($"{exception.Message}\n");
+ exceptionBuilder.AppendLine($"{exception.Source}");
+
+ Log.Error(exception, "{@Area}: Failed to initialize the client",
+ LogArea.LighthouseClient);
+
+ MessageBox.Show(exceptionBuilder.ToString(), MessageBoxButtons.OK, MessageBoxType.Error);
+ }
+ }
+
+ private static void UnlockDefaultsHandler(object sender, EventArgs eventArgs)
+ {
+ // Text changes
+ unlockDefaultsButton.Text = Strings.MainForm.UnlockedDefaults;
+
+ // Button states
+ unlockDefaultsButton.Enabled = false;
+
+ // Field states
+ serverUrl.Enabled = true;
+ applicationId.Enabled = true;
+
+ MessageBox.Show(Strings.MainForm.UnlockedDefaultsWarning,
+ "Warning",
+ MessageBoxButtons.OK,
+ MessageBoxType.Warning);
+ }
+}
\ No newline at end of file
diff --git a/PLRPC.GUI/Gui.cs b/PLRPC.GUI/Gui.cs
new file mode 100644
index 0000000..45f8768
--- /dev/null
+++ b/PLRPC.GUI/Gui.cs
@@ -0,0 +1,12 @@
+using Eto.Forms;
+using LBPUnion.PLRPC.GUI.Forms;
+
+namespace LBPUnion.PLRPC.GUI;
+
+public static class Gui
+{
+ public static void Initialize()
+ {
+ new Application().Run(new MainForm());
+ }
+}
\ No newline at end of file
diff --git a/PLRPC.GUI/PLRPC.GUI.csproj b/PLRPC.GUI/PLRPC.GUI.csproj
new file mode 100644
index 0000000..0e5bd1e
--- /dev/null
+++ b/PLRPC.GUI/PLRPC.GUI.csproj
@@ -0,0 +1,27 @@
+
+
+
+ true
+ net7.0
+ win-x64;linux-x64
+
+
+
+ false
+
+
+
+ LBPUnion.PLRPC.GUI
+ enable
+ disable
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/PLRPC.GUI/Strings/MainForm.cs b/PLRPC.GUI/Strings/MainForm.cs
new file mode 100644
index 0000000..6bfc4fa
--- /dev/null
+++ b/PLRPC.GUI/Strings/MainForm.cs
@@ -0,0 +1,18 @@
+namespace LBPUnion.PLRPC.GUI.Strings;
+
+public static class MainForm
+{
+ public const string Configuration = "Configuration";
+ public const string Username = "Username";
+ public const string ServerUrl = "Server URL";
+ public const string ApplicationId = "Application ID";
+ public const string Connect = "Connect";
+ public const string Connected = "Connected";
+ public const string UnlockDefaults = "Unlock Defaults";
+ public const string UnlockedDefaults = "Unlocked Defaults";
+ public const string BlankFieldsError = "Please fill in all fields and try again.";
+ public const string InitializationError = "An error occurred while initializing the PLRPC client.";
+ public const string InvalidUrlError = "The URL specified is in an invalid format. Please try again.";
+ public const string InvalidUsernameError = "The username specified is invalid. Please try again.";
+ public const string UnlockedDefaultsWarning = "You have just unlocked defaults. Support will not be provided whilst using modified defaults. Continue at your own risk.";
+}
\ No newline at end of file
diff --git a/PLRPC.sln b/PLRPC.sln
index 78ec5cd..953028e 100644
--- a/PLRPC.sln
+++ b/PLRPC.sln
@@ -2,6 +2,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLRPC", "PLRPC\PLRPC.csproj", "{60270A0A-A34A-40FB-BF97-F070B1325157}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLRPC.GUI.Windows", "PLRPC.GUI.Windows\PLRPC.GUI.Windows.csproj", "{7B4DD2AF-4912-40C2-B6E4-C8FAE3695680}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLRPC.GUI", "PLRPC.GUI\PLRPC.GUI.csproj", "{20C3CEC9-6328-43A5-AB59-FDCF9D637C40}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLRPC.GUI.Linux", "PLRPC.GUI.Linux\PLRPC.GUI.Linux.csproj", "{5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GUI", "GUI", "{112C0D49-EA43-4443-A1D5-9BCBBEC7A9DB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{074150AA-731C-465B-8599-C41B56657C81}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +22,23 @@ Global
{60270A0A-A34A-40FB-BF97-F070B1325157}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60270A0A-A34A-40FB-BF97-F070B1325157}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60270A0A-A34A-40FB-BF97-F070B1325157}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B4DD2AF-4912-40C2-B6E4-C8FAE3695680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B4DD2AF-4912-40C2-B6E4-C8FAE3695680}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B4DD2AF-4912-40C2-B6E4-C8FAE3695680}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B4DD2AF-4912-40C2-B6E4-C8FAE3695680}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20C3CEC9-6328-43A5-AB59-FDCF9D637C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20C3CEC9-6328-43A5-AB59-FDCF9D637C40}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20C3CEC9-6328-43A5-AB59-FDCF9D637C40}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20C3CEC9-6328-43A5-AB59-FDCF9D637C40}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {20C3CEC9-6328-43A5-AB59-FDCF9D637C40} = {112C0D49-EA43-4443-A1D5-9BCBBEC7A9DB}
+ {5BC9F0D6-8F49-4EC4-BEA0-1C78DF3E0864} = {112C0D49-EA43-4443-A1D5-9BCBBEC7A9DB}
+ {7B4DD2AF-4912-40C2-B6E4-C8FAE3695680} = {112C0D49-EA43-4443-A1D5-9BCBBEC7A9DB}
+ {60270A0A-A34A-40FB-BF97-F070B1325157} = {074150AA-731C-465B-8599-C41B56657C81}
EndGlobalSection
EndGlobal
diff --git a/PLRPC.sln.DotSettings b/PLRPC.sln.DotSettings
index af44414..310008c 100644
--- a/PLRPC.sln.DotSettings
+++ b/PLRPC.sln.DotSettings
@@ -51,7 +51,7 @@
NEVER
False
NEVER
- ALWAYS
+ IF_OWNER_IS_SINGLE_LINE
False
True
True
@@ -64,7 +64,7 @@
NEXT_LINE
True
False
- CHOP_IF_LONG
+ WRAP_IF_LONG
CHOP_IF_LONG
True
True
@@ -75,6 +75,7 @@
CHOP_IF_LONG
CHOP_IF_LONG
CHOP_IF_LONG
+ 140
CHOP_ALWAYS
CHOP_IF_LONG
CHOP_IF_LONG
diff --git a/PLRPC/Types/ApiRepositoryImpl.cs b/PLRPC/ApiRepositoryImpl.cs
similarity index 69%
rename from PLRPC/Types/ApiRepositoryImpl.cs
rename to PLRPC/ApiRepositoryImpl.cs
index 159e621..2ed1595 100644
--- a/PLRPC/Types/ApiRepositoryImpl.cs
+++ b/PLRPC/ApiRepositoryImpl.cs
@@ -1,21 +1,23 @@
using System.Text.Json;
using LBPUnion.PLRPC.Types.Entities;
+using LBPUnion.PLRPC.Types.Enums;
+using LBPUnion.PLRPC.Types.Interfaces;
-namespace LBPUnion.PLRPC.Types;
+namespace LBPUnion.PLRPC;
public class ApiRepositoryImpl : IApiRepository
{
- private readonly int cacheExpirationTimeMs;
+ private readonly TimeSpan cacheExpirationTime;
private readonly HttpClient httpClient;
private readonly Dictionary slotCache = new();
private readonly Dictionary userCache = new();
private readonly Dictionary userStatusCache = new();
- public ApiRepositoryImpl(HttpClient httpClient, int cacheExpirationTimeMs)
+ public ApiRepositoryImpl(HttpClient httpClient, TimeSpan cacheExpirationTime)
{
this.httpClient = httpClient;
- this.cacheExpirationTimeMs = cacheExpirationTimeMs;
+ this.cacheExpirationTime = cacheExpirationTime;
}
private static long TimestampMillis => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@@ -24,9 +26,10 @@ public ApiRepositoryImpl(HttpClient httpClient, int cacheExpirationTimeMs)
{
if (this.GetFromCache(this.userCache, username, out User? cachedUser)) return cachedUser;
- string userJson = await this.httpClient.GetStringAsync($"username/{username}");
+ HttpResponseMessage userReq = await this.httpClient.GetAsync($"username/{username}");
+ if (!userReq.IsSuccessStatusCode) return null;
- User? user = JsonSerializer.Deserialize(userJson);
+ User? user = JsonSerializer.Deserialize(await userReq.Content.ReadAsStringAsync());
if (user == null) return null;
this.userCache.TryAdd(username, (user, TimestampMillis));
@@ -37,9 +40,10 @@ public ApiRepositoryImpl(HttpClient httpClient, int cacheExpirationTimeMs)
{
if (this.GetFromCache(this.slotCache, slotId, out Slot? cachedSlot)) return cachedSlot;
- string slotJson = await this.httpClient.GetStringAsync($"slot/{slotId}");
+ HttpResponseMessage slotReq = await this.httpClient.GetAsync($"slot/{slotId}");
+ if (!slotReq.IsSuccessStatusCode) return null;
- Slot? slot = JsonSerializer.Deserialize(slotJson);
+ Slot? slot = JsonSerializer.Deserialize(await slotReq.Content.ReadAsStringAsync());
if (slot == null) return null;
this.slotCache.TryAdd(slotId, (slot, TimestampMillis));
@@ -50,9 +54,10 @@ public ApiRepositoryImpl(HttpClient httpClient, int cacheExpirationTimeMs)
{
if (this.GetFromCache(this.userStatusCache, userId, out UserStatus? cachedUserStatus)) return cachedUserStatus;
- string userStatusJson = await this.httpClient.GetStringAsync($"user/{userId}/status");
+ HttpResponseMessage userStatusReq = await this.httpClient.GetAsync($"user/{userId}/status");
+ if (!userStatusReq.IsSuccessStatusCode) return null;
- UserStatus? userStatus = JsonSerializer.Deserialize(userStatusJson);
+ UserStatus? userStatus = JsonSerializer.Deserialize(await userStatusReq.Content.ReadAsStringAsync());
if (userStatus == null) return null;
/*
@@ -86,7 +91,7 @@ private bool GetFromCache(IReadOnlyDictionary cache, T1
val = default;
if (!cache.TryGetValue(key, out (T2, long) entry)) return false;
- if (entry.Item2 + this.cacheExpirationTimeMs > TimestampMillis) return false;
+ if (entry.Item2 + this.cacheExpirationTime.Milliseconds > TimestampMillis) return false;
val = entry.Item1;
return true;
diff --git a/PLRPC/Configuration.cs b/PLRPC/Configuration.cs
new file mode 100644
index 0000000..e50cdc8
--- /dev/null
+++ b/PLRPC/Configuration.cs
@@ -0,0 +1,48 @@
+using LBPUnion.PLRPC.Types.Configuration;
+using LBPUnion.PLRPC.Types.Logging;
+using System.Text.Json;
+using Serilog;
+
+namespace LBPUnion.PLRPC;
+
+public static class Configuration
+{
+ private static readonly JsonSerializerOptions lenientJsonOptions = new()
+ {
+ AllowTrailingCommas = true,
+ WriteIndented = true,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ };
+
+ public static async Task LoadFromConfiguration()
+ {
+ if (!File.Exists("./config.json"))
+ {
+ Log.Warning("{@Area}: No configuration file exists, creating a base configuration", LogArea.Configuration);
+ Log.Warning("{@Area}: Please populate the configuration file and restart the program", LogArea.Configuration);
+
+ PlrpcConfiguration defaultConfig = new();
+
+ await File.WriteAllTextAsync("./config.json", JsonSerializer.Serialize(defaultConfig, lenientJsonOptions));
+
+ return null;
+ }
+
+ string configurationJson = await File.ReadAllTextAsync("./config.json");
+
+ try
+ {
+ PlrpcConfiguration? configuration = JsonSerializer.Deserialize(configurationJson, lenientJsonOptions);
+
+ if (configuration is { ServerUrl: not null, Username: not null, ApplicationId: not null })
+ return configuration;
+
+ throw new JsonException("Deserialized configuration contains one or more null values");
+ }
+ catch (Exception exception)
+ {
+ Log.Fatal(exception, "{@Area}: Failed to deserialize configuration file", LogArea.Configuration);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PLRPC/Extensions/GameVersionExtensions.cs b/PLRPC/Extensions/GameVersionExtensions.cs
index 3e67ebc..df40dd6 100644
--- a/PLRPC/Extensions/GameVersionExtensions.cs
+++ b/PLRPC/Extensions/GameVersionExtensions.cs
@@ -1,4 +1,4 @@
-using LBPUnion.PLRPC.Types;
+using LBPUnion.PLRPC.Types.Enums;
namespace LBPUnion.PLRPC.Extensions;
diff --git a/PLRPC/Extensions/PermissionLevelExtensions.cs b/PLRPC/Extensions/PermissionLevelExtensions.cs
index 9e08014..e8c7947 100644
--- a/PLRPC/Extensions/PermissionLevelExtensions.cs
+++ b/PLRPC/Extensions/PermissionLevelExtensions.cs
@@ -1,4 +1,4 @@
-using LBPUnion.PLRPC.Types;
+using LBPUnion.PLRPC.Types.Enums;
namespace LBPUnion.PLRPC.Extensions;
diff --git a/PLRPC/Helpers/ValidationHelper.cs b/PLRPC/Helpers/ValidationHelper.cs
index 4f048f1..3b9f2d4 100644
--- a/PLRPC/Helpers/ValidationHelper.cs
+++ b/PLRPC/Helpers/ValidationHelper.cs
@@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
+using LBPUnion.PLRPC.Types.Logging;
using Serilog;
namespace LBPUnion.PLRPC.Helpers;
@@ -9,7 +10,9 @@ public static bool IsValidUrl(string url)
{
if (Uri.TryCreate(url, UriKind.Absolute, out _)) return true;
- Log.Error("The URL specified is in an invalid format. Please try again");
+ Log.Error("{@Area}: The URL specified is in an invalid format. Please try again",
+ LogArea.Validation);
+
return false;
}
@@ -17,10 +20,13 @@ public static bool IsValidUsername(string username)
{
if (UsernameRegex().IsMatch(username)) return true;
- Log.Error("The username specified is invalid. Please try again");
+ Log.Error("{@Area}: The username specified is invalid. Please try again",
+ LogArea.Validation);
+
return false;
}
+ // Getting an error here? Roslyn issue - don't worry about it :)
[GeneratedRegex("^[a-zA-Z0-9_.-]{3,16}$")]
private static partial Regex UsernameRegex();
}
\ No newline at end of file
diff --git a/PLRPC/LighthouseClient.cs b/PLRPC/LighthouseClient.cs
index 168b664..93fdc42 100644
--- a/PLRPC/LighthouseClient.cs
+++ b/PLRPC/LighthouseClient.cs
@@ -1,8 +1,9 @@
using DiscordRPC;
-using DiscordRPC.Logging;
using LBPUnion.PLRPC.Extensions;
-using LBPUnion.PLRPC.Types;
using LBPUnion.PLRPC.Types.Entities;
+using LBPUnion.PLRPC.Types.Enums;
+using LBPUnion.PLRPC.Types.Interfaces;
+using LBPUnion.PLRPC.Types.Logging;
using Serilog;
using User = LBPUnion.PLRPC.Types.Entities.User;
@@ -11,32 +12,37 @@ namespace LBPUnion.PLRPC;
public class LighthouseClient
{
private readonly IApiRepository apiRepository;
- private readonly DiscordRpcClient discordClient;
+ private readonly DiscordRpcClient discordRpcClient;
private readonly SemaphoreSlim readySemaphore = new(0, 1);
private readonly string serverUrl;
private readonly string username;
- public LighthouseClient(string username, string serverUrl, IApiRepository apiRepository, DiscordRpcClient rpcClient)
+ public LighthouseClient(string username, string serverUrl, IApiRepository apiRepository, DiscordRpcClient discordRpcClient)
{
this.username = username;
this.serverUrl = serverUrl;
this.apiRepository = apiRepository;
- this.discordClient = rpcClient;
- this.discordClient.Initialize();
- this.discordClient.Logger = new ConsoleLogger
- {
- Level = LogLevel.Warning,
- };
+ this.discordRpcClient = discordRpcClient;
+ this.discordRpcClient.Initialize();
- this.discordClient.OnReady += (_, e) =>
- {
- Log.Information("Connected to Discord Account {Username}", e.User.Username);
- this.readySemaphore.Release();
- };
+ this.discordRpcClient.OnReady += (_, _) => this.readySemaphore.Release();
+
+ this.discordRpcClient.OnReady += (_, _) =>
+ Log.Information("{@Area}: Successfully established ready connection",
+ LogArea.LighthouseClient);
- this.discordClient.OnPresenceUpdate += (_, e) =>
- Log.Information("{@Presence}: Presence updated", e.Presence.GetType());
+ this.discordRpcClient.OnConnectionEstablished += (_, e) =>
+ Log.Information("{@Area}: Successfully acquired the lock on RPC ({Pipe})",
+ LogArea.LighthouseClient, e.ConnectedPipe);
+
+ this.discordRpcClient.OnConnectionFailed += (_, e) =>
+ Log.Warning("{@Area}: Failed to acquire the lock on RPC ({Pipe})",
+ LogArea.LighthouseClient, e.FailedPipe);
+
+ this.discordRpcClient.OnPresenceUpdate += (_, e) =>
+ Log.Information("{@Area}: Updated client presence ({Party})",
+ LogArea.RichPresence, e.Presence.Party.ID);
}
private async Task UpdatePresence()
@@ -44,14 +50,16 @@ private async Task UpdatePresence()
User? user = await this.apiRepository.GetUser(this.username);
if (user == null || user.PermissionLevel == PermissionLevel.Banned)
{
- Log.Warning("Failed to get user from the server");
+ Log.Warning("{@Area}: Failed to get user from the server",
+ LogArea.ApiRepositoryImpl);
return;
}
UserStatus? status = await this.apiRepository.GetStatus(user.UserId);
if (status?.CurrentRoom?.Slot?.SlotId == null || status.CurrentRoom.PlayerIds == null)
{
- Log.Warning("Failed to get user status from the server");
+ Log.Warning("{@Area}: Failed to get user status from the server",
+ LogArea.ApiRepositoryImpl);
return;
}
@@ -63,7 +71,8 @@ private async Task UpdatePresence()
slot = await this.apiRepository.GetSlot(status.CurrentRoom.Slot.SlotId);
if (slot == null)
{
- Log.Warning("Failed to get user's current level from the server");
+ Log.Warning("{@Area}: Failed to get user's current level from the server",
+ LogArea.ApiRepositoryImpl);
return;
}
}
@@ -73,6 +82,7 @@ private async Task UpdatePresence()
{
SlotType.Pod => "9c412649a07a8cb678a2a25214ed981001dd08ca",
SlotType.Moon => "a891bbcf9ad3518b80c210813cce8ed292ed4c62",
+ SlotType.RemoteMoon => "a891bbcf9ad3518b80c210813cce8ed292ed4c62",
SlotType.Developer => "7d3df5ce61ca90a80f600452cd3445b7a775d47e",
SlotType.DeveloperAdventure => "7d3df5ce61ca90a80f600452cd3445b7a775d47e",
SlotType.DlcLevel => "2976e45d66b183f6d3242eaf01236d231766295f",
@@ -95,6 +105,7 @@ private async Task UpdatePresence()
SlotType.User => $"{slot.Name}",
SlotType.Pod => "Dwelling in the Pod",
SlotType.Moon => "Creating on the Moon",
+ SlotType.RemoteMoon => "Creating on a Remote Moon",
SlotType.Developer => "Playing a Story Level",
SlotType.DeveloperAdventure => "Playing an Adventure Level",
SlotType.DlcLevel => "Playing a DLC Level",
@@ -139,24 +150,28 @@ private async Task UpdatePresence()
},
},
};
- this.discordClient.SetPresence(newPresence);
- Log.Information("{@Presence}: Sending presence update", newPresence.GetType());
+
+ Log.Information("{@Area}: Updating client presence ({Party})",
+ LogArea.RichPresence, newPresence.Party.ID);
+
+ this.discordRpcClient.SetPresence(newPresence);
}
public async Task StartUpdateLoop()
{
await this.readySemaphore.WaitAsync();
this.readySemaphore.Dispose();
+
while (true)
{
try
{
await this.UpdatePresence();
}
- catch (Exception exception)
+ catch (Exception)
{
- this.discordClient.Dispose();
- Log.Fatal(exception, "Failed to update presence");
+ this.discordRpcClient.ClearPresence();
+ this.discordRpcClient.Dispose();
return;
}
await Task.Delay(30000);
diff --git a/PLRPC/PLRPC.csproj b/PLRPC/PLRPC.csproj
index bb58968..cb69f22 100644
--- a/PLRPC/PLRPC.csproj
+++ b/PLRPC/PLRPC.csproj
@@ -10,14 +10,23 @@
2.0.0
MIT License Copyright (c) 2023 LBP Union
LBP Union
- Exe
- net7.0
enable
enable
LBPUnion.PLRPC
LBPUnion.PLRPC
+
+ Exe
+ true
+ net7.0
+ win-x64;linux-x64
+
+
+
+ false
+
+
diff --git a/PLRPC/Program.cs b/PLRPC/Program.cs
index 2b6ddf5..3224c5f 100644
--- a/PLRPC/Program.cs
+++ b/PLRPC/Program.cs
@@ -1,131 +1,101 @@
using System.Diagnostics.CodeAnalysis;
-using System.Text.Json;
using CommandLine;
using DiscordRPC;
using JetBrains.Annotations;
using LBPUnion.PLRPC.Helpers;
-using LBPUnion.PLRPC.Types;
+using LBPUnion.PLRPC.Types.Configuration;
+using LBPUnion.PLRPC.Types.Logging;
using LBPUnion.PLRPC.Types.Updater;
using Serilog;
+using Serilog.Core;
namespace LBPUnion.PLRPC;
public static class Program
{
- private static readonly JsonSerializerOptions lenientJsonOptions = new()
- {
- AllowTrailingCommas = true,
- WriteIndented = true,
- ReadCommentHandling = JsonCommentHandling.Skip,
- };
+ public static readonly Logger Logger = new LoggerConfiguration()
+ .MinimumLevel.Information()
+ .Enrich.With()
+ .WriteTo.Console(outputTemplate: "[{ProcessId} {Timestamp:HH:mm:ss} {Level:u3}] {Message:l}{NewLine}{Exception}")
+ .CreateLogger();
public static async Task Main(string[] args)
{
- Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Information()
- .Enrich.With()
- .WriteTo.Console(outputTemplate: "[{ProcessId} {Timestamp:HH:mm:ss} {Level:u3}] {Message:l}{NewLine}{Exception}")
- .CreateLogger();
+ Log.Logger = Logger;
#if !DEBUG
await InitializeUpdateCheck();
#endif
- await Parser.Default.ParseArguments(args).WithParsedAsync(ParseArguments);
+ await Parser.Default
+ .ParseArguments(args)
+ .WithParsedAsync(ProcessArguments);
}
- private static async Task ParseArguments(CommandLineArguments arguments)
+ // TODO: Make command line argument parsing more uniform and clean
+ private static async Task ProcessArguments(CommandLineArguments arguments)
{
switch (arguments)
{
case { UseConfig: true }:
{
- PlrpcConfiguration? configuration = LoadFromConfiguration().Result;
+ PlrpcConfiguration? configuration = Configuration.LoadFromConfiguration().Result;
if (configuration is { ServerUrl: not null, Username: not null, ApplicationId: not null })
- await InitializeLighthouseClient(configuration.ServerUrl.TrimEnd('/'),
- configuration.Username,
- configuration.ApplicationId);
+ await InitializeLighthouseClient(configuration.ServerUrl, configuration.Username, configuration.ApplicationId);
break;
}
case { ServerUrl: not null, Username: not null } when !ValidationHelper.IsValidUrl(arguments.ServerUrl):
case { ServerUrl: not null, Username: not null } when !ValidationHelper.IsValidUsername(arguments.Username):
return;
case { ServerUrl: not null, Username: not null, ApplicationId: not null }:
- await InitializeLighthouseClient(arguments.ServerUrl.TrimEnd('/'),
- arguments.Username,
- arguments.ApplicationId);
+ await InitializeLighthouseClient(arguments.ServerUrl, arguments.Username, arguments.ApplicationId);
break;
default:
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
Log.Error(arguments is { ServerUrl: null, Username: null, UseConfig: false }
- ? "No arguments were passed to the client. Ensure you're running PLRPC through CLI"
- : "Invalid argument(s) were passed to the client, please check them and try running again");
+ ? "{@Area}: No arguments were passed to the client. Ensure you're running PLRPC through CLI"
+ : "{@Area}: Invalid argument(s) were passed to the client, please check them and try running again",
+ LogArea.Configuration);
Console.ReadLine();
break;
}
}
- private static async Task LoadFromConfiguration()
- {
- if (!File.Exists("./config.json"))
- {
- Log.Warning("No configuration file exists, creating a base configuration");
- Log.Warning("Please populate the configuration file and restart the program");
-
- PlrpcConfiguration defaultConfig = new();
-
- await File.WriteAllTextAsync("./config.json", JsonSerializer.Serialize(defaultConfig, lenientJsonOptions));
-
- return null;
- }
-
- string configurationJson = await File.ReadAllTextAsync("./config.json");
-
- try
- {
- PlrpcConfiguration? configuration =
- JsonSerializer.Deserialize(configurationJson, lenientJsonOptions);
-
- if (configuration is { ServerUrl: not null, Username: not null, ApplicationId: not null })
- return configuration;
-
- throw new JsonException("Deserialized configuration contains one or more null values");
- }
- catch (Exception exception)
- {
- Log.Fatal(exception, "Failed to deserialize configuration file");
- return null;
- }
- }
-
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private static async Task InitializeUpdateCheck()
{
HttpClient updateClient = new();
+ Updater updater = new(updateClient);
+ // Required by GitHub's API
updateClient.DefaultRequestHeaders.UserAgent.ParseAdd("LBPUnion/1.0 (PLRPC; github-release) UpdateClient/1.1");
- Updater updater = new(updateClient);
Release? updateResult = await updater.CheckForUpdate();
if (updateResult != null)
{
- Log.Information("***************************************");
- Log.Information("A new version of PLRPC is available!");
- Log.Information("{UpdateTag}: {UpdateUrl}", updateResult.TagName, updateResult.Url);
- Log.Information("***************************************");
+ Log.Information("{@Area}: A new version of PLRPC is available!",
+ LogArea.Updater);
+ Log.Information("{@Area}: {UpdateTag}: {UpdateUrl}",
+ LogArea.Updater, updateResult.TagName, updateResult.Url);
}
else
{
- Log.Information("There are no new updates available");
+ Log.Information("{@Area}: There are no new updates available",
+ LogArea.Updater);
}
}
- private static async Task InitializeLighthouseClient(string serverUrl, string username, string? applicationId)
+ public static async Task InitializeLighthouseClient(string serverUrl, string username, string? applicationId)
{
+ Log.Information("{@Area}: Initializing new client and dependencies",
+ LogArea.LighthouseClient);
+
+ string trimmedServerUrl = serverUrl.TrimEnd('/'); // trailing slashes cause issues with requests
+
HttpClient apiClient = new()
{
- BaseAddress = new Uri(serverUrl + "/api/v1/"),
+ BaseAddress = new Uri(trimmedServerUrl + "/api/v1/"),
DefaultRequestHeaders =
{
{
@@ -134,13 +104,11 @@ private static async Task InitializeLighthouseClient(string serverUrl, string us
},
};
- const int cacheExpirationTime = 60 * 60 * 1000; // 1 hour
+ TimeSpan cacheExpirationTime = TimeSpan.FromHours(1);
ApiRepositoryImpl apiRepository = new(apiClient, cacheExpirationTime);
DiscordRpcClient discordRpcClient = new(applicationId);
- LighthouseClient lighthouseClient = new(username, serverUrl, apiRepository, discordRpcClient);
-
- Log.Information("Initializing client...");
+ LighthouseClient lighthouseClient = new(username, trimmedServerUrl, apiRepository, discordRpcClient);
await lighthouseClient.StartUpdateLoop();
}
diff --git a/PLRPC/Types/PlrpcConfiguration.cs b/PLRPC/Types/Configuration/PlrpcConfiguration.cs
similarity index 89%
rename from PLRPC/Types/PlrpcConfiguration.cs
rename to PLRPC/Types/Configuration/PlrpcConfiguration.cs
index 671af04..cde762f 100644
--- a/PLRPC/Types/PlrpcConfiguration.cs
+++ b/PLRPC/Types/Configuration/PlrpcConfiguration.cs
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
-namespace LBPUnion.PLRPC.Types;
+namespace LBPUnion.PLRPC.Types.Configuration;
public class PlrpcConfiguration
{
diff --git a/PLRPC/Types/Entities/Slot.cs b/PLRPC/Types/Entities/Slot.cs
index d74c3b0..b40f48a 100644
--- a/PLRPC/Types/Entities/Slot.cs
+++ b/PLRPC/Types/Entities/Slot.cs
@@ -29,7 +29,7 @@ public enum SlotType
// DeveloperGroup = 4,
Pod = 5,
// Fake = 6,
- // RemoteMoon = 7,
+ RemoteMoon = 7,
DlcLevel = 8,
// DLCPack = 9,
// Playlist = 10,
diff --git a/PLRPC/Types/Entities/User.cs b/PLRPC/Types/Entities/User.cs
index b4388ea..94e871f 100644
--- a/PLRPC/Types/Entities/User.cs
+++ b/PLRPC/Types/Entities/User.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using LBPUnion.PLRPC.Types.Enums;
using Newtonsoft.Json;
namespace LBPUnion.PLRPC.Types.Entities;
diff --git a/PLRPC/Types/Entities/UserStatus.cs b/PLRPC/Types/Entities/UserStatus.cs
index 15f3253..d13f049 100644
--- a/PLRPC/Types/Entities/UserStatus.cs
+++ b/PLRPC/Types/Entities/UserStatus.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using LBPUnion.PLRPC.Types.Enums;
using Newtonsoft.Json;
namespace LBPUnion.PLRPC.Types.Entities;
diff --git a/PLRPC/Types/GameVersion.cs b/PLRPC/Types/Enums/GameVersion.cs
similarity index 78%
rename from PLRPC/Types/GameVersion.cs
rename to PLRPC/Types/Enums/GameVersion.cs
index 8729982..a151d1d 100644
--- a/PLRPC/Types/GameVersion.cs
+++ b/PLRPC/Types/Enums/GameVersion.cs
@@ -1,4 +1,4 @@
-namespace LBPUnion.PLRPC.Types;
+namespace LBPUnion.PLRPC.Types.Enums;
public enum GameVersion
{
diff --git a/PLRPC/Types/PermissionLevel.cs b/PLRPC/Types/Enums/PermissionLevel.cs
similarity index 78%
rename from PLRPC/Types/PermissionLevel.cs
rename to PLRPC/Types/Enums/PermissionLevel.cs
index 972de89..76cb814 100644
--- a/PLRPC/Types/PermissionLevel.cs
+++ b/PLRPC/Types/Enums/PermissionLevel.cs
@@ -1,4 +1,4 @@
-namespace LBPUnion.PLRPC.Types;
+namespace LBPUnion.PLRPC.Types.Enums;
public enum PermissionLevel
{
diff --git a/PLRPC/Types/IApiRepository.cs b/PLRPC/Types/Interfaces/IApiRepository.cs
similarity index 83%
rename from PLRPC/Types/IApiRepository.cs
rename to PLRPC/Types/Interfaces/IApiRepository.cs
index 292f899..8f504ea 100644
--- a/PLRPC/Types/IApiRepository.cs
+++ b/PLRPC/Types/Interfaces/IApiRepository.cs
@@ -1,6 +1,6 @@
using LBPUnion.PLRPC.Types.Entities;
-namespace LBPUnion.PLRPC.Types;
+namespace LBPUnion.PLRPC.Types.Interfaces;
public interface IApiRepository
{
diff --git a/PLRPC/Types/LogEnrichers.cs b/PLRPC/Types/LogEnrichers.cs
deleted file mode 100644
index b1a2d20..0000000
--- a/PLRPC/Types/LogEnrichers.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Serilog.Core;
-using Serilog.Events;
-
-namespace LBPUnion.PLRPC.Types;
-
-public class LogEnrichers : ILogEventEnricher
-{
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
- {
- logEvent.AddPropertyIfAbsent(
- propertyFactory.CreateProperty(
- "ProcessId",
- Environment.ProcessId));
- }
-}
\ No newline at end of file
diff --git a/PLRPC/Types/Logging/LogArea.cs b/PLRPC/Types/Logging/LogArea.cs
new file mode 100644
index 0000000..e1eff24
--- /dev/null
+++ b/PLRPC/Types/Logging/LogArea.cs
@@ -0,0 +1,11 @@
+namespace LBPUnion.PLRPC.Types.Logging;
+
+public enum LogArea
+{
+ ApiRepositoryImpl,
+ Configuration,
+ LighthouseClient,
+ RichPresence,
+ Updater,
+ Validation,
+}
\ No newline at end of file
diff --git a/PLRPC/Types/Logging/LogEnrichers.cs b/PLRPC/Types/Logging/LogEnrichers.cs
new file mode 100644
index 0000000..4e6c46b
--- /dev/null
+++ b/PLRPC/Types/Logging/LogEnrichers.cs
@@ -0,0 +1,12 @@
+using Serilog.Core;
+using Serilog.Events;
+
+namespace LBPUnion.PLRPC.Types.Logging;
+
+public class LogEnrichers : ILogEventEnricher
+{
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ProcessId", Environment.ProcessId));
+ }
+}
\ No newline at end of file
diff --git a/PLRPC/Updater.cs b/PLRPC/Updater.cs
index b076427..8f168c8 100644
--- a/PLRPC/Updater.cs
+++ b/PLRPC/Updater.cs
@@ -1,4 +1,5 @@
using System.Text.Json;
+using LBPUnion.PLRPC.Types.Logging;
using LBPUnion.PLRPC.Types.Updater;
using Serilog;
@@ -17,13 +18,15 @@ public Updater(HttpClient updaterClient)
{
if (!File.Exists("./manifest.json"))
{
- Log.Warning("No update manifest file exists, creating a base manifest");
+ Log.Warning("{@Area} No update manifest file exists, creating a base manifest",
+ LogArea.Updater);
await this.GenerateManifest();
}
string releaseManifest =
await this.updaterHttpClient.GetStringAsync("https://api.github.com/repos/LBPUnion/PLRPC/releases/latest");
- string programManifest = await File.ReadAllTextAsync("./manifest.json");
+ string programManifest =
+ await File.ReadAllTextAsync("./manifest.json");
Release? releaseObject = JsonSerializer.Deserialize(releaseManifest);
Manifest? programObject = JsonSerializer.Deserialize(programManifest);
diff --git a/README.md b/README.md
index 13a8c41..6e8d2d1 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
# PLRPC
[![Build Artifacts](https://github.com/LBPUnion/PLRPC/actions/workflows/build.yml/badge.svg)](https://github.com/LBPUnion/PLRPC/actions/workflows/build.yml)
-[![CodeQL Analysis](https://github.com/LBPUnion/PLRPC/actions/workflows/codeql.yml/badge.svg)](https://github.com/LBPUnion/PLRPC/actions/workflows/codeql.yml)
PLRPC (short for ProjectLighthouse Rich Presence Client) is a continuation of the LighthouseRichPresence client under
the same premise.
@@ -18,29 +17,37 @@ the same premise.
- [ ] Stability (no fires)
## Installation Instructions
+
+**GUI Installation Steps (recommended)**
-**Installation Steps:**
+1. Navigate to the Releases Tab
+2. Download the latest GUI build for Windows or Linux
+3. Extract the build to any folder
+4. Run the client
+ * **Windows:** Run the GUI by double clicking on the `PLRPC.GUI.Windows` executable
+ * **Linux:** Run the GUI by double clicking on the `PLRPC.GUI.Linux` executable
+ * You may need to mark the program as executable first, or run it from the command line
+
+**CLI Installation Steps (advanced)**
+
+> **Warning**
+> These steps are only for advanced users who are comfortable with the command line.
+> If you are not comfortable with the command line, please use the GUI instead.
1. Navigate to the Releases Tab
-2. Download the latest build (or major version if you like somewhat-stability)
+2. Download the latest CLI build for Windows or Linux
3. Extract the build to any folder
4. Run the client
- **Configuration Mode:** `./path/to/PLRPC --config` (use `--config` each time)
- **Manual Mode:** `./path/to/PLRPC --server https://lighthouse.instance.url --username instanceusername`
-> **Warning** for **Windows Users**:
->
-> Currently, you are unable to run the .exe file directly. You **must** open a Command Prompt or PowerShell
-> window, navigate to the file path, and execute the binary manually. Refer
-to [Installation Step #4](https://github.com/LBPUnion/PLRPC/blob/master/README.md#installation-instructions)
-> for instructions on how to further configure and run the client.
-
**Post Install:**
Please create an Issue if you encounter any bugs or weird errors.
## Helpful Information
-* You can use the `--applicationid` command line argument, or change the `applicationId` entry in your configuration,
- to override the default Discord Application ID. This can be useful if your Lighthouse instance or other service is
- compatible with the PLRPC protocol and you want to display your own application name.
\ No newline at end of file
+* You can use the `--applicationid` command line argument, change the `applicationId` entry in your configuration,
+ or if using the GUI, unlock defaults and change the `Application ID` entry in the options to override the default
+ Discord Application ID. This can be useful if your Lighthouse instance or other service is compatible with the PLRPC
+ protocol and you want to display your own application name.
\ No newline at end of file
diff --git a/global.json b/global.json
index 934805f..7cd6a1f 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
- "sdk": {
- "version": "7.0.0",
- "rollForward": "latestMajor",
- "allowPrerelease": true
- }
+ "sdk": {
+ "version": "7.0.0",
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
+ }
}
\ No newline at end of file