diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 9121f86..33efe49 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -1,4 +1,5 @@ on: + workflow_dispatch: push: tags: - '*' diff --git a/.github/workflows/publish-client.yml b/.github/workflows/publish-client.yml new file mode 100644 index 0000000..8074118 --- /dev/null +++ b/.github/workflows/publish-client.yml @@ -0,0 +1,78 @@ +name: publish-client-multi-platform + +on: + workflow_dispatch: + push: + tags: + - '*' + +jobs: + build-library: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1' + + # Lib + - name: Build and Pack Library + run: | + dotnet build Palworld.RESTSharp/Palworld.RESTSharp.csproj --configuration Release + dotnet pack Palworld.RESTSharp/Palworld.RESTSharp.csproj --configuration Release --output nupkg + + # add as artifact + - name: Upload NuGet Package + uses: actions/upload-artifact@v2 + with: + name: nuget-package + path: nupkg + + build-windows-client-x64: + needs: build-library + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '8.0' # my pref + + - name: Restore and Build Client Application + run: | + dotnet restore Palworld.RESTSharp.Client/Palworld.RESTSharp.Client.csproj + dotnet publish Palworld.RESTSharp.Client/Palworld.RESTSharp.Client.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish/win-x64 + + - name: Upload WinForms Application + uses: actions/upload-artifact@v2 + with: + name: windows-client + path: publish/win-x64 + + build-windows-client-x32: + needs: build-library + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '8.0' # my pref + + - name: Restore and Build Client Application + run: | + dotnet restore Palworld.RESTSharp.Client/Palworld.RESTSharp.Client.csproj + dotnet publish Palworld.RESTSharp.Client/Palworld.RESTSharp.Client.csproj -c Release -r win-x86 --self-contained true -p:PublishSingleFile=true -o publish/win-x86 + + - name: Upload WinForms Application + uses: actions/upload-artifact@v2 + with: + name: windows-client + path: publish/win-x64 \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index 8aa2645..c74e89c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) [year] [fullname] +Copyright (c) 2024 BalphagoreVR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Palworld.RESTSharp.Client/FormMain.Designer.cs b/Palworld.RESTSharp.Client/FormMain.Designer.cs index 285862a..bbe4008 100644 --- a/Palworld.RESTSharp.Client/FormMain.Designer.cs +++ b/Palworld.RESTSharp.Client/FormMain.Designer.cs @@ -32,15 +32,18 @@ private void InitializeComponent() panelResponse = new Panel(); statusStrip1 = new StatusStrip(); connectionStatus = new ToolStripStatusLabel(); + stsPlayerCount = new ToolStripStatusLabel(); + stsServerUptimeAndFPS = new ToolStripStatusLabel(); tAPIOptions = new TreeView(); label1 = new Label(); btnExecute = new Button(); pageConfigure = new Panel(); + txtConfigPassword = new TextBox(); + cbTrackMetrics = new CheckBox(); label4 = new Label(); txtconfigURL = new TextBox(); btnSaveConfig = new Button(); label3 = new Label(); - txtConfigPassword = new TextBox(); panelRequestParameters = new Panel(); lblHeader = new Label(); statusStrip1.SuspendLayout(); @@ -57,7 +60,8 @@ private void InitializeComponent() // // statusStrip1 // - statusStrip1.Items.AddRange(new ToolStripItem[] { connectionStatus }); + statusStrip1.GripStyle = ToolStripGripStyle.Visible; + statusStrip1.Items.AddRange(new ToolStripItem[] { connectionStatus, stsPlayerCount, stsServerUptimeAndFPS }); statusStrip1.Location = new Point(0, 376); statusStrip1.Name = "statusStrip1"; statusStrip1.Size = new Size(992, 22); @@ -70,6 +74,17 @@ private void InitializeComponent() connectionStatus.Size = new Size(97, 17); connectionStatus.Text = "Not Connected..."; // + // stsPlayerCount + // + stsPlayerCount.Name = "stsPlayerCount"; + stsPlayerCount.Size = new Size(0, 17); + stsPlayerCount.TextAlign = ContentAlignment.MiddleRight; + // + // stsServerUptimeAndFPS + // + stsServerUptimeAndFPS.Name = "stsServerUptimeAndFPS"; + stsServerUptimeAndFPS.Size = new Size(0, 17); + // // tAPIOptions // tAPIOptions.Dock = DockStyle.Left; @@ -100,17 +115,37 @@ private void InitializeComponent() // // pageConfigure // + pageConfigure.Controls.Add(txtConfigPassword); + pageConfigure.Controls.Add(cbTrackMetrics); pageConfigure.Controls.Add(label4); pageConfigure.Controls.Add(txtconfigURL); pageConfigure.Controls.Add(btnSaveConfig); pageConfigure.Controls.Add(label3); - pageConfigure.Controls.Add(txtConfigPassword); pageConfigure.Location = new Point(222, 0); pageConfigure.Name = "pageConfigure"; pageConfigure.Size = new Size(770, 376); pageConfigure.TabIndex = 7; pageConfigure.Paint += pageConfigure_Paint; // + // txtConfigPassword + // + txtConfigPassword.Location = new Point(68, 38); + txtConfigPassword.Name = "txtConfigPassword"; + txtConfigPassword.PasswordChar = '*'; + txtConfigPassword.Size = new Size(683, 23); + txtConfigPassword.TabIndex = 12; + // + // cbTrackMetrics + // + cbTrackMetrics.AutoSize = true; + cbTrackMetrics.Location = new Point(7, 66); + cbTrackMetrics.Name = "cbTrackMetrics"; + cbTrackMetrics.Size = new Size(132, 19); + cbTrackMetrics.TabIndex = 11; + cbTrackMetrics.Text = "Show Server Metrics"; + cbTrackMetrics.UseVisualStyleBackColor = true; + cbTrackMetrics.CheckedChanged += cbTrackMetrics_CheckedChanged; + // // label4 // label4.AutoSize = true; @@ -124,12 +159,13 @@ private void InitializeComponent() // txtconfigURL.Location = new Point(68, 8); txtconfigURL.Name = "txtconfigURL"; + txtconfigURL.PlaceholderText = "http://127.0.0.1:8000"; txtconfigURL.Size = new Size(683, 23); txtconfigURL.TabIndex = 9; // // btnSaveConfig // - btnSaveConfig.Location = new Point(5, 66); + btnSaveConfig.Location = new Point(5, 89); btnSaveConfig.Name = "btnSaveConfig"; btnSaveConfig.Size = new Size(112, 23); btnSaveConfig.TabIndex = 8; @@ -146,13 +182,6 @@ private void InitializeComponent() label3.TabIndex = 1; label3.Text = "Password"; // - // txtConfigPassword - // - txtConfigPassword.Location = new Point(68, 37); - txtConfigPassword.Name = "txtConfigPassword"; - txtConfigPassword.Size = new Size(683, 23); - txtConfigPassword.TabIndex = 0; - // // panelRequestParameters // panelRequestParameters.BorderStyle = BorderStyle.Fixed3D; @@ -186,7 +215,7 @@ private void InitializeComponent() Controls.Add(panelResponse); Icon = (Icon)resources.GetObject("$this.Icon"); Name = "FormMain"; - Text = "Palworld REST API Test Client"; + Text = "Palworld RESTSharp Client"; Load += Form1_Load; statusStrip1.ResumeLayout(false); statusStrip1.PerformLayout(); @@ -206,11 +235,14 @@ private void InitializeComponent() private Button btnExecute; private Panel pageConfigure; private Label label3; - private TextBox txtConfigPassword; private Label label4; private TextBox txtconfigURL; private Button btnSaveConfig; private Panel panelRequestParameters; private Label lblHeader; + private ToolStripStatusLabel stsPlayerCount; + private ToolStripStatusLabel stsServerUptimeAndFPS; + private CheckBox cbTrackMetrics; + private TextBox txtConfigPassword; } } diff --git a/Palworld.RESTSharp.Client/FormMain.cs b/Palworld.RESTSharp.Client/FormMain.cs index cb994f9..c607dba 100644 --- a/Palworld.RESTSharp.Client/FormMain.cs +++ b/Palworld.RESTSharp.Client/FormMain.cs @@ -203,7 +203,7 @@ private void tAPIOptions_AfterSelectAsync(object sender, TreeViewEventArgs e) txtReasonMessage = new TextBox(); lblReason = new Label() { Text = "Shutdown Message" }; Label lblDelay = new Label() { Text = "Delay (Seconds)" }; - + txtReasonMessage.Dock = DockStyle.Top; txtReasonMessage.PlaceholderText = "Enter reason for shutdown"; panelRequestParameters.Controls.Add(txtReasonMessage); @@ -278,8 +278,10 @@ private async void btnSaveConfig_Click(object sender, EventArgs e) this.Text = _defaultTitle; _connected = true; this.Text = _defaultTitle + $" - {serverInfo.serverName}"; + + UpdateServerMetricCounterAsync(); } - else + else // Disconnected { txtconfigURL.ReadOnly = false; txtConfigPassword.ReadOnly = false; @@ -290,14 +292,31 @@ private async void btnSaveConfig_Click(object sender, EventArgs e) this.Text = _defaultTitle; } } - catch(PalworldRESTSharpClientUnauthorizedException pex) + catch (PalworldRESTSharpClientUnauthorizedException pex) { MessageBox.Show(pex.Message, "Invalid Password"); } - catch(Exception ex) + catch (Exception ex) { - MessageBox.Show(ex.Message,"Error connecting"); + MessageBox.Show(ex.Message, "Error connecting"); + } + } + + private async Task UpdateServerMetricCounterAsync() + { + while (_connected && cbTrackMetrics.Checked) + { + ServerMetric serverMetric = await palworldRESTAPIClient.GetServerMetricsASync(); + if (serverMetric != null) + { + stsPlayerCount.Text = $"Players: {serverMetric.totalPlayers}/{serverMetric.maxPlayers}"; + stsServerUptimeAndFPS.Text = $"Uptime: {serverMetric.GetUptimeString()} | FPS: {serverMetric.serverFPS}"; + } + await Task.Delay(1000); } + + stsPlayerCount.Text = ""; + stsServerUptimeAndFPS.Text = ""; } private async void Execute() @@ -370,6 +389,9 @@ private async void Execute() lbServerMetrics.Items.Add($"Frame Rate: {serverMetric.serverFrameRate}"); lbServerMetrics.Items.Add($"Max Players: {serverMetric.maxPlayers}"); lbServerMetrics.Items.Add($"Uptime: {serverMetric.upTime}"); + + stsPlayerCount.Text = $"Players: {serverMetric.totalPlayers}/{serverMetric.maxPlayers}"; + stsServerUptimeAndFPS.Text = $"Uptime: {serverMetric.GetUptimeString()} | FPS: {serverMetric.serverFPS}"; panelResponse.Controls.Add(lbServerMetrics); } @@ -511,7 +533,7 @@ private async void Execute() break; } } - catch(Exception ex) + catch (Exception ex) { MessageBox.Show($"{ex.Message}\n\nException:\n{ex.StackTrace}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } @@ -523,5 +545,19 @@ private async void btnExecute_Click(object sender, EventArgs e) connectionStatus.Text = "Executed"; } + + private void cbTrackMetrics_CheckedChanged(object sender, EventArgs e) + { + if (cbTrackMetrics.Checked) + { + // Show user a warning and prompt to continue or cancel. + DialogResult result = MessageBox.Show("Tracking server metrics will periodically poll the REST API resulting in excessive log spam in the server console. Do you want to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + + if (result == DialogResult.No) + { + cbTrackMetrics.Checked = false; + } + } + } } } diff --git a/Palworld.RESTSharp.Client/FormMain.resx b/Palworld.RESTSharp.Client/FormMain.resx index 2b28b88..78420fc 100644 --- a/Palworld.RESTSharp.Client/FormMain.resx +++ b/Palworld.RESTSharp.Client/FormMain.resx @@ -120,6 +120,9 @@ 17, 17 + + True + diff --git a/Palworld.RESTSharp.sln b/Palworld.RESTSharp.sln index facf02d..aed41fb 100644 --- a/Palworld.RESTSharp.sln +++ b/Palworld.RESTSharp.sln @@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Palworld.RESTSharp.Client", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFiles", "SolutionFiles", "{541EFF06-8B59-48BB-8C93-7B8DC7DA4089}" ProjectSection(SolutionItems) = preProject + LICENSE.txt = LICENSE.txt .github\workflows\nuget-publish.yml = .github\workflows\nuget-publish.yml + .github\workflows\publish-client.yml = .github\workflows\publish-client.yml README.md = README.md RestSharpLogo-128.png = RestSharpLogo-128.png RestSharpLogo-32.png = RestSharpLogo-32.png diff --git a/Palworld.RESTSharp/Models/ServerInfo.cs b/Palworld.RESTSharp/Models/ServerInfo.cs index 04a2ea1..b092473 100644 --- a/Palworld.RESTSharp/Models/ServerInfo.cs +++ b/Palworld.RESTSharp/Models/ServerInfo.cs @@ -23,7 +23,10 @@ public class ServerInfo [JsonProperty("description")] public string description { get; set; } - + /// + /// Returns the string representation of the server info. + /// + /// Server name, version, and description in a single line. public override string ToString() => $"[{serverName}][{version}][{description}]"; } } diff --git a/Palworld.RESTSharp/Models/ServerMetric.cs b/Palworld.RESTSharp/Models/ServerMetric.cs index 413e09e..ff988ad 100644 --- a/Palworld.RESTSharp/Models/ServerMetric.cs +++ b/Palworld.RESTSharp/Models/ServerMetric.cs @@ -33,12 +33,17 @@ public class ServerMetric [JsonProperty("uptime")] public int upTime { get; set; } + /// + /// Gets the uptime of the server expressed in hours, minutes, and seconds. + /// + /// public string GetUptimeString() { + int days = upTime / 86400; int hours = upTime / 3600; int minutes = (upTime % 3600) / 60; int seconds = upTime % 60; - return $"{hours}h {minutes}m {seconds}s"; + return $"{days}d {hours}h {minutes}m {seconds}s"; } } }