HostCommandDispatcher, a host-side command dispatcher for the game engine. It validates authentication and permissions, parses and queues command requests, enforces policy and lifecycle controls, records audit entries, and returns command results asynchronously.
using SboxSuperuser.Core.Commands;
using SboxSuperuser.Engine.Authority;
using SboxSuperuser.Engine.Audit;
namespace SboxSuperuser.Engine.Commands;
internal sealed partial class HostCommandDispatcher : ICommandDispatcher, IDisposable
{
private readonly HostPolicyService _policy;
private readonly CommandRegistry _registry;
private readonly Func<bool> _available;
private readonly Func<IReadOnlyList<CommandPlayer>> _players;
private readonly Action<Exception> _report;
private readonly CancellationTokenSource _lifetime = new();
private readonly RequestQueue<CommandResult> _queue;
private readonly RequestQueue<CommandResult> _controlQueue;
private bool _disposed;
private readonly IAuditWriter _audit;
internal HostCommandDispatcher( HostPolicyService policy, CommandRegistry registry, Func<bool> available,
Func<IReadOnlyList<CommandPlayer>> players, Action<Exception> report, Func<DateTimeOffset> clock = null, IAuditWriter audit = null )
{
_policy = policy;
_registry = registry;
_available = available;
_players = players;
_report = report;
_audit = audit;
_queue = new RequestQueue<CommandResult>( clock ?? (() => DateTimeOffset.UtcNow), Result( CommandStatus.Cancelled, "Cancelled before execution." ) );
_controlQueue = new RequestQueue<CommandResult>( clock ?? (() => DateTimeOffset.UtcNow),
Result( CommandStatus.Cancelled, "Cancelled before execution." ), 8, 2, 128, 32 );
}
internal async Task<CommandResult> SubmitAsync( Func<ActorIdentity> authenticate, CommandOrigin origin, CommandRequest request,
CancellationToken cancellation, CommandService service = null )
{
if ( !_available() || _disposed )
{
return Result( CommandStatus.Unsupported, "An eligible authoritative runtime on the main thread is required." );
}
if ( request is null )
{
return Result( CommandStatus.InvalidArguments, "A command request is required." );
}
using var linked = CancellationTokenSource.CreateLinkedTokenSource( cancellation, _lifetime.Token );
var actor = authenticate();
if ( actor is null )
{
return Result( CommandStatus.Denied, "The caller could not be authenticated." );
}
var context = new CommandContext( actor, origin, request.RequestId, linked.Token, new CommandExecutionServices( this, authenticate, service ) );
return await DispatchAsync( context, request );
}
public async Task<CommandResult> DispatchAsync( CommandContext context, CommandRequest request )
{
var audit = new AuditSubmission();
var result = await DispatchCoreAsync( context, request, audit );
if ( _audit is not null && audit.Action is not null && !audit.Handled )
{
await _audit.RecordAsync( audit.Action, result.Status, CancellationToken.None );
}
return result;
}
private async Task<CommandResult> DispatchCoreAsync( CommandContext context, CommandRequest request, AuditSubmission audit )
{
try
{
if ( context is null || request is null || context.RequestId != request.RequestId
|| context.Services is not CommandExecutionServices services || !ReferenceEquals( services.Issuer, this ) )
{
return Result( CommandStatus.Denied, "Host-issued matching request context is required." );
}
var authorization = ValidateCaller( context, services );
// Only a still-authenticated transport identity may be attributed in the journal.
if ( authorization is null )
{
audit.Action = new AuditAction( context.Actor, context.Origin, request.RequestId,
"command.unresolved", DateTimeOffset.UtcNow );
}
if ( authorization is not null )
{
return authorization;
}
if ( request.Command.Length > 96 || CommandTokenizer.ValidateTokens( request.Arguments ) is not null )
{
return Result( CommandStatus.InvalidArguments, "Command input exceeds its limits or contains unsupported characters." );
}
var key = new RequestKey( (int)context.Actor.Kind, context.Actor.SubjectId, (int)context.Origin, context.RequestId );
var fingerprint = RequestFingerprint.Compute( request.Command, request.Arguments );
var retry = _queue.FindRetry( key, fingerprint ) ?? _controlQueue.FindRetry( key, fingerprint );
if ( retry is not null )
{
audit.Handled = retry.Admission != QueueAdmission.Conflict;
return retry.Admission == QueueAdmission.Conflict
? Result( CommandStatus.Conflict, "This request ID was already used with a different payload." ) : await retry.Result;
}
var command = _registry.Find( request.Command );
if ( command is null )
{
return Result( CommandStatus.NotFound, "Command is not registered." );
}
audit.Action = CreateAuditAction( context, command.Descriptor, null );
audit.Handled = !command.RecordsAudit;
if ( context.Origin == CommandOrigin.Chat && command.Descriptor.Arguments.Any( argument =>
argument.Audit == CommandArgumentAudit.Sensitive || AuditRedaction.SensitiveName( argument.Name ) ) )
{
return Result( CommandStatus.Denied, "Sensitive commands require a private non-chat transport." );
}
if ( !HasPermission( context, services, command.Descriptor.Permission ) )
{
return Result( CommandStatus.Denied, "Permission denied." );
}
if ( command.IsLifecycleControl && context.Actor.Kind != ActorKind.DedicatedConsole
&& (context.Actor.Kind != ActorKind.Player || !_policy.IsOwner( context.Actor.SubjectId )) )
{
return Result( CommandStatus.Denied, "Module lifecycle requires current owner or dedicated console authority." );
}
var parsed = CommandArgumentParser.Parse( command.Descriptor, request.Arguments, _players() );
if ( !parsed.IsSuccess )
{
return new CommandResult( CommandStatus.InvalidArguments, parsed.Error, parsed.PlayerChoices.Count == 0 ? null
: new Dictionary<string, string> { [ "choices" ] = string.Join( ",", parsed.PlayerChoices.Select( player => player.SteamId ) ) } );
}
audit.Action = CreateAuditAction( context, command.Descriptor, parsed.Arguments );
using var moduleCancellation = CancellationTokenSource.CreateLinkedTokenSource( context.Cancellation,
command.Execution?.Cancellation ?? CancellationToken.None );
var executionContext = new CommandContext( context.Actor, context.Origin, context.RequestId, moduleCancellation.Token, context.Services );
Task<CommandResult> Execute() => ExecuteAuditedAsync( executionContext, services, request.Command, command, parsed.Arguments, audit );
var queue = command.IsLifecycleControl ? _controlQueue : _queue;
var admission = queue.Submit( key, fingerprint,
() => command.Execution is null ? Execute() : command.Execution.RunAsync( Execute ), moduleCancellation.Token );
return admission.Admission switch
{
QueueAdmission.Conflict => Result( CommandStatus.Conflict, "This request ID was already used with a different payload." ),
QueueAdmission.Busy => Result( CommandStatus.Conflict, "Command capacity is full; retry later." ),
_ => await admission.Result
};
}
catch ( Exception error )
{
_report( error );
return Result( CommandStatus.Unknown, "The command outcome could not be determined. See host diagnostics." );
}
}
public void Dispose()
{
if ( _disposed )
{
return;
}
_disposed = true;
_lifetime.Cancel();
_lifetime.Dispose();
}
private static CommandResult Result( CommandStatus status, string message ) => new( status, message );
}