From 5784e3ca2f65272a36a1795e8dfadb471bf1f83a Mon Sep 17 00:00:00 2001 From: X39 Date: Wed, 1 Nov 2023 20:14:45 +0100 Subject: [PATCH] state --- .../X39.UnitedTacticalForces.Api/Constants.cs | 10 +- .../DayZStandaloneGameServerController.cs | 157 ++++++++++++++++++ .../Api.Generated.cs | 40 +++-- .../LifetimeStatusExtensions.cs | 1 + 4 files changed, 189 insertions(+), 19 deletions(-) create mode 100644 source/X39.UnitedTacticalForces.Api/Services/GameServerController/Controllers/DayZStandaloneGameServerController.cs diff --git a/source/X39.UnitedTacticalForces.Api/Constants.cs b/source/X39.UnitedTacticalForces.Api/Constants.cs index 5a4d692..a3707c3 100644 --- a/source/X39.UnitedTacticalForces.Api/Constants.cs +++ b/source/X39.UnitedTacticalForces.Api/Constants.cs @@ -8,12 +8,15 @@ namespace X39.UnitedTacticalForces.Api; internal static class Constants { public const string PasswordReplacement = "****************"; + public static class Steam { public static class AppId { - public const long Arma3Server = 233780; - public const long Arma3 = 107410; + public const long Arma3Server = 233780; + public const long Arma3 = 107410; + public const long DayZStandalone = 221100; + public const long DayZStandaloneServer = 223350; } } @@ -42,6 +45,7 @@ public static class AuthorizationSchemas public const string Api = "api"; public const string Banned = "banned"; } + public static class Discord { public static class Commands @@ -80,7 +84,7 @@ public static class Bot public const string ApplicationId = nameof(Discord) + ":" + nameof(Bot) + ":" + nameof(ApplicationId); public const string PublicKey = nameof(Discord) + ":" + nameof(Bot) + ":" + nameof(PublicKey); public const string BotToken = nameof(Discord) + ":" + nameof(Bot) + ":" + nameof(BotToken); - public const string EmbedColor = nameof(Discord) + ":" + nameof(Bot) + ":" + nameof(EmbedColor); + public const string EmbedColor = nameof(Discord) + ":" + nameof(Bot) + ":" + nameof(EmbedColor); } } diff --git a/source/X39.UnitedTacticalForces.Api/Services/GameServerController/Controllers/DayZStandaloneGameServerController.cs b/source/X39.UnitedTacticalForces.Api/Services/GameServerController/Controllers/DayZStandaloneGameServerController.cs new file mode 100644 index 0000000..fd0407f --- /dev/null +++ b/source/X39.UnitedTacticalForces.Api/Services/GameServerController/Controllers/DayZStandaloneGameServerController.cs @@ -0,0 +1,157 @@ +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using X39.UnitedTacticalForces.Api.Data; +using X39.UnitedTacticalForces.Api.Data.Authority; +using X39.UnitedTacticalForces.Api.Data.Hosting; +using X39.UnitedTacticalForces.Api.Services.UpdateStreamService; + +namespace X39.UnitedTacticalForces.Api.Services.GameServerController.Controllers; + +/// +/// Implementation of for DayZ Standalone game servers. +/// +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public class DayZStandaloneGameServerController : SteamGameServerControllerBase, IGameServerControllerCreatable +{ + /// + public DayZStandaloneGameServerController( + GameServer gameServer, + IDbContextFactory dbContextFactory, + ILogger logger, + IConfiguration configuration, + IUpdateStreamService updateStreamService) + : base(configuration, gameServer, dbContextFactory, updateStreamService, logger) + { + } + + /// + public override bool AllowAnyConfigurationEntry => false; + + /// + public override bool CanModifyGameFiles => false; + + /// + protected override long ServerAppId => Constants.Steam.AppId.DayZStandaloneServer; + + /// + protected override long GameAppId => Constants.Steam.AppId.DayZStandalone; + + /// + protected override bool RequireLogin => true; + + /// + protected override bool RequirePurchaseForWorkshop => true; + + /// + public static string Identifier => $"dayz-{Constants.Steam.AppId.DayZStandaloneServer}"; + + /// + public override IEnumerable GetConfigurationEntryDefinitions(CultureInfo cultureInfo) + { + return Enumerable.Empty(); + } + + /// + public override Task> GetGameFoldersAsync( + CultureInfo cultureInfo, + CancellationToken cancellationToken = default) + { + return Task.FromResult>(Array.Empty()); + } + + /// + public override Task> GetGameFolderFilesAsync( + GameFolder folder, + CultureInfo cultureInfo, + CancellationToken cancellationToken = default) + { + return Task.FromResult>(Array.Empty()); + } + + /// + public override Task GetGameFolderFileAsync( + GameFolder folder, + GameFileInfo file, + CancellationToken cancellationToken = default) + { + return Task.FromResult(new MemoryStream()); + } + + /// + public override Task UploadFileAsync(GameFolder folder, GameFileInfo file, Stream stream) + { + throw new NotSupportedException(); + } + + /// + public override Task DeleteFileAsync(GameFolder folder, GameFileInfo file) + { + throw new NotSupportedException(); + } + + /// + public override Task GetCommonConfigurationAsync( + ECommonConfiguration commonConfig, + CultureInfo cultureInfo, + CancellationToken cancellationToken = default) + { + return commonConfig switch + { + ECommonConfiguration.Title => Task.FromResult("DayZ Standalone"), + ECommonConfiguration.Port => Task.FromResult("2302"), + ECommonConfiguration.Password => Task.FromResult(""), + _ => throw new ArgumentOutOfRangeException(nameof(commonConfig), commonConfig, null) + }; + } + + /// + protected override Task DoUpdateConfigurationAsync() + { + return Task.CompletedTask; + } + + /// + protected override ValueTask GetProcessStartInfoAsync(ApiDbContext dbContext, User? executingUser) + { + var fileName = Path.Combine( + GameInstallPath, + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "DayZServer_x64.exe" + : "dayzserver_x64"); + var psi = new ProcessStartInfo + { + FileName = fileName, + RedirectStandardError = true, + RedirectStandardInput = false, + RedirectStandardOutput = true, + StandardErrorEncoding = Encoding.UTF8, + // StandardInputEncoding = Encoding.UTF8, + StandardOutputEncoding = Encoding.UTF8, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + WorkingDirectory = GameInstallPath, + }; + return ValueTask.FromResult(psi); + } + + /// + public static Task CreateAsync( + IServiceProvider serviceProvider, + IConfiguration configuration, + GameServer gameServer, + IUpdateStreamService updateStreamService) + { + var controller = new DayZStandaloneGameServerController( + gameServer, + serviceProvider.GetRequiredService>(), + serviceProvider.GetRequiredService().CreateLogger(), + configuration, + updateStreamService); + return Task.FromResult(controller); + } +} \ No newline at end of file diff --git a/source/X39.UnitedTacticalForces.WebApp/Api.Generated.cs b/source/X39.UnitedTacticalForces.WebApp/Api.Generated.cs index eac5325..117f608 100644 --- a/source/X39.UnitedTacticalForces.WebApp/Api.Generated.cs +++ b/source/X39.UnitedTacticalForces.WebApp/Api.Generated.cs @@ -661,7 +661,7 @@ public string BaseUrl /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Api.Data.Authority.EEventAcceptance.Accepted. + /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Contract.Event.EEventAcceptance.Accepted. /// /// The id for the X39.UnitedTacticalForces.Api.Data.Eventing.Event to change the acceptance of. /// No Content @@ -737,7 +737,7 @@ public string BaseUrl /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Api.Data.Authority.EEventAcceptance.Maybe. + /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Contract.Event.EEventAcceptance.Maybe. /// /// /// Any slot selection for the X39.UnitedTacticalForces.Api.Data.Eventing.Event will be removed in this process. @@ -816,7 +816,7 @@ public string BaseUrl /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Api.Data.Authority.EEventAcceptance.Rejected. + /// Changes the calling users acceptance status for the given event to X39.UnitedTacticalForces.Contract.Event.EEventAcceptance.Rejected. /// /// /// Any slot selection for the X39.UnitedTacticalForces.Api.Data.Eventing.Event will be removed in this process. @@ -1168,7 +1168,7 @@ public string BaseUrl /// The id of the X39.UnitedTacticalForces.Api.Data.Eventing.EventSlot. /// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task EventsSlottingAssignPostAsync(System.Guid eventId, long slotNumber, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public virtual async System.Threading.Tasks.Task EventsSlottingAssignPostAsync(System.Guid eventId, int slotNumber, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { if (eventId == null) throw new System.ArgumentNullException("eventId"); @@ -7585,13 +7585,15 @@ public enum EEventAcceptance /// /// ///
- ///
0 = Stopped (Status indicating that something is currently not running in any way.) + ///
0 = Stopped ///
- ///
1 = Starting (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running. (Implies that a lifetime change was requested.)) + ///
1 = Starting ///
- ///
2 = Stopping (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped. (Implies that a lifetime change was requested.)) + ///
2 = Stopping ///
- ///
3 = Running (Status indicating that something is currently running.) + ///
3 = Running + ///
+ ///
4 = Updating ///
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum ELifetimeStatus @@ -7605,6 +7607,8 @@ public enum ELifetimeStatus Running = 3, + Updating = 4, + } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] @@ -7978,13 +7982,15 @@ public partial class GameServer /// /// The lifetime state of this entity. ///
- ///
0 = Stopped (Status indicating that something is currently not running in any way.) + ///
0 = Stopped ///
- ///
1 = Starting (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running. (Implies that a lifetime change was requested.)) + ///
1 = Starting ///
- ///
2 = Stopping (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped. (Implies that a lifetime change was requested.)) + ///
2 = Stopping ///
- ///
3 = Running (Status indicating that something is currently running.) + ///
3 = Running + ///
+ ///
4 = Updating ///
[System.Text.Json.Serialization.JsonPropertyName("status")] @@ -8256,13 +8262,15 @@ public partial class LifetimeEvent /// /// The status changed into. ///
- ///
0 = Stopped (Status indicating that something is currently not running in any way.) + ///
0 = Stopped + ///
+ ///
1 = Starting ///
- ///
1 = Starting (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running. (Implies that a lifetime change was requested.)) + ///
2 = Stopping ///
- ///
2 = Stopping (Status indicating a transition from X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Running to X39.UnitedTacticalForces.Api.Data.Hosting.ELifetimeStatus.Stopped. (Implies that a lifetime change was requested.)) + ///
3 = Running ///
- ///
3 = Running (Status indicating that something is currently running.) + ///
4 = Updating ///
[System.Text.Json.Serialization.JsonPropertyName("status")] diff --git a/source/X39.UnitedTacticalForces.WebApp/ExtensionMethods/LifetimeStatusExtensions.cs b/source/X39.UnitedTacticalForces.WebApp/ExtensionMethods/LifetimeStatusExtensions.cs index 6f41099..b7f7b7f 100644 --- a/source/X39.UnitedTacticalForces.WebApp/ExtensionMethods/LifetimeStatusExtensions.cs +++ b/source/X39.UnitedTacticalForces.WebApp/ExtensionMethods/LifetimeStatusExtensions.cs @@ -10,6 +10,7 @@ public static class LifetimeStatusExtensions ELifetimeStatus.Starting => Icons.Material.Filled.ChangeCircle, ELifetimeStatus.Stopping => Icons.Material.Filled.ChangeCircle, ELifetimeStatus.Running => Icons.Material.Filled.PlayCircle, + ELifetimeStatus.Updating => Icons.Material.Filled.ChangeCircle, null => Icons.Material.Filled.Circle, _ => throw new ArgumentOutOfRangeException(nameof(self), self, null), };