From 22af762a9d21bf7aa78ae215cea8732519e1ac02 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sun, 9 Apr 2023 14:07:30 -0500 Subject: [PATCH] add ServerCommandRequestExecuteEvent implementation --- Application/ApplicationManager.cs | 83 +++++++++++++------ Application/IW4MServer.cs | 6 ++ .../NotifyAfterDelayCompleteEvent.cs | 8 -- .../NotifyAfterDelayRequestEvent.cs | 9 -- .../ServerCommandRequestExecuteEvent.cs | 16 ++++ .../Events/IGameServerEventSubscriptions.cs | 9 +- SharedLibraryCore/Interfaces/IGameServer.cs | 18 ++++ SharedLibraryCore/Server.cs | 2 + 8 files changed, 109 insertions(+), 42 deletions(-) delete mode 100644 SharedLibraryCore/Events/Management/NotifyAfterDelayCompleteEvent.cs delete mode 100644 SharedLibraryCore/Events/Management/NotifyAfterDelayRequestEvent.cs create mode 100644 SharedLibraryCore/Events/Server/ServerCommandRequestExecuteEvent.cs diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index 6c5e33c0b..fe1de1f9b 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -309,6 +309,7 @@ namespace IW4MAdmin.Application #region EVENTS IGameServerEventSubscriptions.ServerValueRequested += OnServerValueRequested; IGameServerEventSubscriptions.ServerValueSetRequested += OnServerValueSetRequested; + IGameServerEventSubscriptions.ServerCommandExecuteRequested += OnServerCommandExecuteRequested; await IManagementEventSubscriptions.InvokeLoadAsync(this, CancellationToken); # endregion @@ -788,9 +789,63 @@ namespace IW4MAdmin.Application } } - private async Task OnServerValueSetRequested(ServerValueSetRequestEvent requestEvent, CancellationToken token) + private Task OnServerValueSetRequested(ServerValueSetRequestEvent requestEvent, CancellationToken token) { - if (requestEvent.Server is not IW4MServer server) + return ExecuteWrapperForServerQuery(requestEvent, token, async (innerEvent) => + { + if (innerEvent.DelayMs.HasValue) + { + await Task.Delay(innerEvent.DelayMs.Value, token); + } + + if (innerEvent.TimeoutMs is not null) + { + using var timeoutTokenSource = new CancellationTokenSource(innerEvent.TimeoutMs.Value); + using var linkedTokenSource = + CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, token); + token = linkedTokenSource.Token; + } + + await innerEvent.Server.SetDvarAsync(innerEvent.ValueName, innerEvent.Value, token); + }, (completed, innerEvent) => + { + QueueEvent(new ServerValueSetCompleteEvent + { + Server = innerEvent.Server, + Source = innerEvent.Server, + Success = completed, + Value = innerEvent.Value, + ValueName = innerEvent.ValueName + }); + return Task.CompletedTask; + }); + } + + private Task OnServerCommandExecuteRequested(ServerCommandRequestExecuteEvent executeEvent, CancellationToken token) + { + return ExecuteWrapperForServerQuery(executeEvent, token, async (innerEvent) => + { + if (innerEvent.DelayMs.HasValue) + { + await Task.Delay(innerEvent.DelayMs.Value, token); + } + + if (innerEvent.TimeoutMs is not null) + { + using var timeoutTokenSource = new CancellationTokenSource(innerEvent.TimeoutMs.Value); + using var linkedTokenSource = + CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, token); + token = linkedTokenSource.Token; + } + + await innerEvent.Server.ExecuteCommandAsync(innerEvent.Command, token); + }, (_, __) => Task.CompletedTask); + } + + private async Task ExecuteWrapperForServerQuery(TEventType serverEvent, CancellationToken token, + Func action, Func complete) where TEventType : GameServerEvent + { + if (serverEvent.Server is not IW4MServer) { return; } @@ -798,20 +853,7 @@ namespace IW4MAdmin.Application var completed = false; try { - if (requestEvent.DelayMs.HasValue) - { - await Task.Delay(requestEvent.DelayMs.Value, token); - } - - if (requestEvent.TimeoutMs is not null) - { - using var timeoutTokenSource = new CancellationTokenSource(requestEvent.TimeoutMs.Value); - using var linkedTokenSource = - CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, token); - token = linkedTokenSource.Token; - } - - await server.SetDvarAsync(requestEvent.ValueName, requestEvent.Value, token); + await action(serverEvent); completed = true; } catch @@ -820,14 +862,7 @@ namespace IW4MAdmin.Application } finally { - QueueEvent(new ServerValueSetCompleteEvent - { - Server = server, - Source = server, - Success = completed, - Value = requestEvent.Value, - ValueName = requestEvent.ValueName - }); + await complete(completed, serverEvent); } } diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index af5e07e59..1f4beab0e 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -1606,6 +1606,12 @@ namespace IW4MAdmin }); } + public override Task ExecuteCommandAsync(string command, CancellationToken token = default) => + Utilities.ExecuteCommandAsync(this, command, token); + + public override Task SetDvarAsync(string name, object value, CancellationToken token = default) => + Utilities.SetDvarAsync(this, name, value, token); + public override async Task TempBan(string reason, TimeSpan length, EFClient targetClient, EFClient originClient) { // ensure player gets kicked if command not performed on them in the same server diff --git a/SharedLibraryCore/Events/Management/NotifyAfterDelayCompleteEvent.cs b/SharedLibraryCore/Events/Management/NotifyAfterDelayCompleteEvent.cs deleted file mode 100644 index c760ff1f5..000000000 --- a/SharedLibraryCore/Events/Management/NotifyAfterDelayCompleteEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace SharedLibraryCore.Events.Management; - -public class NotifyAfterDelayCompleteEvent : ManagementEvent -{ - public Delegate Action { get; init; } -} diff --git a/SharedLibraryCore/Events/Management/NotifyAfterDelayRequestEvent.cs b/SharedLibraryCore/Events/Management/NotifyAfterDelayRequestEvent.cs deleted file mode 100644 index 7386b1f93..000000000 --- a/SharedLibraryCore/Events/Management/NotifyAfterDelayRequestEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace SharedLibraryCore.Events.Management; - -public class NotifyAfterDelayRequestEvent : ManagementEvent -{ - public int DelayMs { get; init; } - public Action Action { get; init; } -} diff --git a/SharedLibraryCore/Events/Server/ServerCommandRequestExecuteEvent.cs b/SharedLibraryCore/Events/Server/ServerCommandRequestExecuteEvent.cs new file mode 100644 index 000000000..883f41b69 --- /dev/null +++ b/SharedLibraryCore/Events/Server/ServerCommandRequestExecuteEvent.cs @@ -0,0 +1,16 @@ +using SharedLibraryCore.Interfaces; + +namespace SharedLibraryCore.Events.Server; + +public class ServerCommandRequestExecuteEvent : GameServerEvent +{ + public ServerCommandRequestExecuteEvent(string command, IGameServer server) + { + Command = command; + Server = server; + } + + public string Command { get; init; } + public int? DelayMs { get; init; } + public int? TimeoutMs { get; init; } +} diff --git a/SharedLibraryCore/Interfaces/Events/IGameServerEventSubscriptions.cs b/SharedLibraryCore/Interfaces/Events/IGameServerEventSubscriptions.cs index b750ecbfc..8469e13c4 100644 --- a/SharedLibraryCore/Interfaces/Events/IGameServerEventSubscriptions.cs +++ b/SharedLibraryCore/Interfaces/Events/IGameServerEventSubscriptions.cs @@ -38,6 +38,11 @@ public interface IGameServerEventSubscriptions /// static event Func ClientDataUpdated; + /// + /// Raised when a command is requested to be executed on a game server + /// + static event Func ServerCommandExecuteRequested; + /// /// Raised when a command was executed on a game server /// @@ -67,7 +72,7 @@ public interface IGameServerEventSubscriptions /// /// static event Func ServerValueSetCompleted; - + static Task InvokeEventAsync(CoreEvent coreEvent, CancellationToken token) { return coreEvent switch @@ -77,6 +82,7 @@ public interface IGameServerEventSubscriptions ConnectionInterruptEvent connectionInterruptEvent => ConnectionInterrupted?.InvokeAsync(connectionInterruptEvent, token) ?? Task.CompletedTask, ConnectionRestoreEvent connectionRestoreEvent => ConnectionRestored?.InvokeAsync(connectionRestoreEvent, token) ?? Task.CompletedTask, ClientDataUpdateEvent clientDataUpdateEvent => ClientDataUpdated?.InvokeAsync(clientDataUpdateEvent, token) ?? Task.CompletedTask, + ServerCommandRequestExecuteEvent serverCommandRequestExecuteEvent => ServerCommandExecuteRequested?.InvokeAsync(serverCommandRequestExecuteEvent, token) ?? Task.CompletedTask, ServerCommandExecuteEvent dataReceiveEvent => ServerCommandExecuted?.InvokeAsync(dataReceiveEvent, token) ?? Task.CompletedTask, ServerValueRequestEvent serverValueRequestEvent => ServerValueRequested?.InvokeAsync(serverValueRequestEvent, token) ?? Task.CompletedTask, ServerValueReceiveEvent serverValueReceiveEvent => ServerValueReceived?.InvokeAsync(serverValueReceiveEvent, token) ?? Task.CompletedTask, @@ -93,6 +99,7 @@ public interface IGameServerEventSubscriptions ConnectionInterrupted = null; ConnectionRestored = null; ClientDataUpdated = null; + ServerCommandExecuteRequested = null; ServerCommandExecuted = null; ServerValueReceived = null; ServerValueRequested = null; diff --git a/SharedLibraryCore/Interfaces/IGameServer.cs b/SharedLibraryCore/Interfaces/IGameServer.cs index c7ee50fc0..e28bd2b1a 100644 --- a/SharedLibraryCore/Interfaces/IGameServer.cs +++ b/SharedLibraryCore/Interfaces/IGameServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Data.Models; using SharedLibraryCore.Database.Models; @@ -17,6 +18,23 @@ namespace SharedLibraryCore.Interfaces /// previous penalty the kick is occuring for (if applicable) /// Task Kick(string reason, EFClient target, EFClient origin, EFPenalty previousPenalty = null); + + /// + /// Execute a server command + /// + /// Server command to execute + /// + /// Collection of console command output lines + Task ExecuteCommandAsync(string command, CancellationToken token = default); + + /// + /// Set value for server dvar + /// + /// Name of the server value to set + /// Value of the server value + /// + /// + Task SetDvarAsync(string name, object value, CancellationToken token = default); /// /// Time the most recent match ended diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 81673fe02..3062e064d 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -163,6 +163,8 @@ namespace SharedLibraryCore public int Port { get; protected set; } public int ListenPort => Port; public abstract Task Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty); + public abstract Task ExecuteCommandAsync(string command, CancellationToken token = default); + public abstract Task SetDvarAsync(string name, object value, CancellationToken token = default); /// /// Returns list of all current players