1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2024-11-25 07:42:58 +01:00

Compare commits

..

1 Commits

Author SHA1 Message Date
df4b64cf8d
Implement actors in Agent via Akka.NET 2024-03-29 10:23:03 +01:00
7 changed files with 34 additions and 40 deletions

View File

@ -59,7 +59,7 @@ sealed class BackupScheduler : CancellableBackgroundTask {
}
try {
return await context.Actor.Request(new InstanceActor.BackupInstanceCommand(backupManager));
return await context.Actor.Request(new InstanceActor.BackupInstanceCommand(backupManager, CancellationToken));
} finally {
backupSemaphore.Release();
}

View File

@ -13,32 +13,28 @@ using Phantom.Utils.Logging;
namespace Phantom.Agent.Services.Instances;
sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
public readonly record struct Init(AgentState AgentState, Guid InstanceGuid, string ShortName, InstanceServices InstanceServices, InstanceTicketManager InstanceTicketManager, CancellationToken ShutdownCancellationToken);
public readonly record struct Init(Guid InstanceGuid, string ShortName, InstanceServices Services, AgentState AgentState);
public static Props<ICommand> Factory(Init init) {
return Props<ICommand>.Create(() => new InstanceActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume, MailboxType = UnboundedJumpAheadMailbox.Name });
}
private readonly AgentState agentState;
private readonly CancellationToken shutdownCancellationToken;
private readonly Guid instanceGuid;
private readonly InstanceServices instanceServices;
private readonly InstanceTicketManager instanceTicketManager;
private readonly InstanceServices services;
private readonly AgentState agentState;
private readonly InstanceContext context;
private IInstanceStatus currentStatus = InstanceStatus.NotRunning;
private InstanceRunningState? runningState = null;
private InstanceActor(Init init) {
this.agentState = init.AgentState;
this.instanceGuid = init.InstanceGuid;
this.instanceServices = init.InstanceServices;
this.instanceTicketManager = init.InstanceTicketManager;
this.shutdownCancellationToken = init.ShutdownCancellationToken;
this.services = init.Services;
this.agentState = init.AgentState;
var logger = PhantomLogger.Create<InstanceActor>(init.ShortName);
this.context = new InstanceContext(instanceGuid, init.ShortName, logger, instanceServices, SelfTyped);
this.context = new InstanceContext(instanceGuid, init.ShortName, logger, services, SelfTyped);
Receive<ReportInstanceStatusCommand>(ReportInstanceStatus);
ReceiveAsync<LaunchInstanceCommand>(LaunchInstance);
@ -56,7 +52,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
private void ReportCurrentStatus() {
agentState.UpdateInstance(new Instance(instanceGuid, currentStatus));
instanceServices.ControllerConnection.Send(new ReportInstanceStatusMessage(instanceGuid, currentStatus));
services.ControllerConnection.Send(new ReportInstanceStatusMessage(instanceGuid, currentStatus));
}
private void TransitionState(InstanceRunningState? newState) {
@ -64,6 +60,10 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return;
}
if (runningState is not null && newState is null) {
context.Services.InstanceTicketManager.Release(runningState.Ticket);
}
runningState?.Dispose();
runningState = newState;
runningState?.Initialize();
@ -73,13 +73,13 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
public sealed record ReportInstanceStatusCommand : ICommand;
public sealed record LaunchInstanceCommand(InstanceConfiguration Configuration, IServerLauncher Launcher, InstanceTicketManager.Ticket Ticket, bool IsRestarting) : ICommand;
public sealed record LaunchInstanceCommand(InstanceConfiguration Configuration, IServerLauncher Launcher, InstanceTicketManager.Ticket Ticket, bool IsRestarting, CancellationToken CancellationToken) : ICommand;
public sealed record StopInstanceCommand(MinecraftStopStrategy StopStrategy) : ICommand;
public sealed record StopInstanceCommand(MinecraftStopStrategy StopStrategy, CancellationToken CancellationToken) : ICommand;
public sealed record SendCommandToInstanceCommand(string Command) : ICommand, ICanReply<SendCommandToInstanceResult>;
public sealed record SendCommandToInstanceCommand(string Command, CancellationToken CancellationToken) : ICommand, ICanReply<SendCommandToInstanceResult>;
public sealed record BackupInstanceCommand(BackupManager BackupManager) : ICommand, ICanReply<BackupCreationResult>;
public sealed record BackupInstanceCommand(BackupManager BackupManager, CancellationToken CancellationToken) : ICommand, ICanReply<BackupCreationResult>;
public sealed record HandleProcessEndedCommand(IInstanceStatus Status) : ICommand, IJumpAhead;
@ -93,9 +93,9 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
if (command.IsRestarting || runningState is null) {
SetAndReportStatus(command.IsRestarting ? InstanceStatus.Restarting : InstanceStatus.Launching);
var newState = await InstanceLaunchProcedure.Run(context, command.Configuration, command.Launcher, instanceTicketManager, command.Ticket, SetAndReportStatus, shutdownCancellationToken);
var newState = await InstanceLaunchProcedure.Run(context, command.Configuration, command.Launcher, command.Ticket, SetAndReportStatus, command.CancellationToken);
if (newState is null) {
instanceTicketManager.Release(command.Ticket);
context.Services.InstanceTicketManager.Release(command.Ticket);
}
TransitionState(newState);
@ -110,8 +110,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
IInstanceStatus oldStatus = currentStatus;
SetAndReportStatus(InstanceStatus.Stopping);
if (await InstanceStopProcedure.Run(context, command.StopStrategy, runningState, SetAndReportStatus, shutdownCancellationToken)) {
instanceTicketManager.Release(runningState.Ticket);
if (await InstanceStopProcedure.Run(context, command.StopStrategy, runningState, SetAndReportStatus, command.CancellationToken)) {
TransitionState(null);
}
else {
@ -124,7 +123,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return SendCommandToInstanceResult.InstanceNotRunning;
}
else {
return await runningState.SendCommand(command.Command, shutdownCancellationToken);
return await runningState.SendCommand(command.Command, command.CancellationToken);
}
}
@ -133,7 +132,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return new BackupCreationResult(BackupCreationResultKind.InstanceNotRunning);
}
else {
return await command.BackupManager.CreateBackup(context.ShortName, runningState.Process, shutdownCancellationToken);
return await command.BackupManager.CreateBackup(context.ShortName, runningState.Process, command.CancellationToken);
}
}
@ -141,13 +140,12 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
if (runningState is { Process.HasEnded: true }) {
SetAndReportStatus(command.Status);
context.ReportEvent(InstanceEvent.Stopped);
instanceTicketManager.Release(runningState.Ticket);
TransitionState(null);
}
}
private async Task Shutdown(ShutdownCommand command) {
await StopInstance(new StopInstanceCommand(MinecraftStopStrategy.Instant));
await StopInstance(new StopInstanceCommand(MinecraftStopStrategy.Instant, CancellationToken.None));
Context.Stop(Self);
}
}

View File

@ -20,7 +20,7 @@ namespace Phantom.Agent.Services.Instances;
sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand> {
private static readonly ILogger Logger = PhantomLogger.Create<InstanceManagerActor>();
public readonly record struct Init(ControllerConnection ControllerConnection, AgentFolders AgentFolders, AgentState AgentState, JavaRuntimeRepository JavaRuntimeRepository, InstanceTicketManager InstanceTicketManager, TaskManager TaskManager, BackupManager BackupManager);
public readonly record struct Init(ControllerConnection ControllerConnection, AgentFolders AgentFolders, AgentState AgentState, JavaRuntimeRepository JavaRuntimeRepository, InstanceTicketManager TicketManager, TaskManager TaskManager, BackupManager BackupManager);
public static Props<ICommand> Factory(Init init) {
return Props<ICommand>.Create(() => new InstanceManagerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
@ -30,7 +30,6 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
private readonly string basePath;
private readonly InstanceServices instanceServices;
private readonly InstanceTicketManager instanceTicketManager;
private readonly Dictionary<Guid, InstanceInfo> instances = new ();
private readonly CancellationTokenSource shutdownCancellationTokenSource = new ();
@ -41,13 +40,12 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
private InstanceManagerActor(Init init) {
this.agentState = init.AgentState;
this.basePath = init.AgentFolders.InstancesFolderPath;
this.instanceTicketManager = init.InstanceTicketManager;
this.shutdownCancellationToken = shutdownCancellationTokenSource.Token;
var minecraftServerExecutables = new MinecraftServerExecutables(init.AgentFolders.ServerExecutableFolderPath);
var launchServices = new LaunchServices(minecraftServerExecutables, init.JavaRuntimeRepository);
this.instanceServices = new InstanceServices(init.ControllerConnection, init.TaskManager, init.BackupManager, launchServices);
this.instanceServices = new InstanceServices(init.ControllerConnection, init.TaskManager, init.TicketManager, init.BackupManager, launchServices);
ReceiveAndReply<ConfigureInstanceCommand, InstanceActionResult<ConfigureInstanceResult>>(ConfigureInstance);
ReceiveAndReply<LaunchInstanceCommand, InstanceActionResult<LaunchInstanceResult>>(LaunchInstance);
@ -118,7 +116,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
}
else {
var instanceInit = new InstanceActor.Init(agentState, instanceGuid, GetInstanceLoggerName(instanceGuid), instanceServices, instanceTicketManager, shutdownCancellationToken);
var instanceInit = new InstanceActor.Init(instanceGuid, GetInstanceLoggerName(instanceGuid), instanceServices, agentState);
instances[instanceGuid] = instance = new InstanceInfo(Context.ActorOf(InstanceActor.Factory(instanceInit), "Instance-" + instanceGuid), configuration, launcher);
Logger.Information("Created instance \"{Name}\" (GUID {Guid}).", configuration.InstanceName, instanceGuid);
@ -139,7 +137,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
return InstanceActionResult.General<LaunchInstanceResult>(InstanceActionGeneralResult.InstanceDoesNotExist);
}
var ticket = instanceTicketManager.Reserve(instanceInfo.Configuration);
var ticket = instanceServices.InstanceTicketManager.Reserve(instanceInfo.Configuration);
if (ticket is Result<InstanceTicketManager.Ticket, LaunchInstanceResult>.Fail fail) {
return InstanceActionResult.Concrete(fail.Error);
}
@ -155,7 +153,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
// TODO report status?
instanceInfo.Actor.Tell(new InstanceActor.LaunchInstanceCommand(instanceInfo.Configuration, instanceInfo.Launcher, ticket.Value, IsRestarting: false));
instanceInfo.Actor.Tell(new InstanceActor.LaunchInstanceCommand(instanceInfo.Configuration, instanceInfo.Launcher, ticket.Value, IsRestarting: false, shutdownCancellationToken));
return InstanceActionResult.Concrete(LaunchInstanceResult.LaunchInitiated);
}
@ -175,7 +173,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
}
instanceInfo.Actor.Tell(new InstanceActor.StopInstanceCommand(command.StopStrategy));
instanceInfo.Actor.Tell(new InstanceActor.StopInstanceCommand(command.StopStrategy, shutdownCancellationToken));
return InstanceActionResult.Concrete(StopInstanceResult.StopInitiated);
}
@ -186,7 +184,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
try {
return InstanceActionResult.Concrete(await instanceInfo.Actor.Request(new InstanceActor.SendCommandToInstanceCommand(command.Command), shutdownCancellationToken));
return InstanceActionResult.Concrete(await instanceInfo.Actor.Request(new InstanceActor.SendCommandToInstanceCommand(command.Command, shutdownCancellationToken), shutdownCancellationToken));
} catch (OperationCanceledException) {
return InstanceActionResult.General<SendCommandToInstanceResult>(InstanceActionGeneralResult.AgentShuttingDown);
}

View File

@ -5,4 +5,4 @@ using Phantom.Utils.Tasks;
namespace Phantom.Agent.Services.Instances;
sealed record InstanceServices(ControllerConnection ControllerConnection, TaskManager TaskManager, BackupManager BackupManager, LaunchServices LaunchServices);
sealed record InstanceServices(ControllerConnection ControllerConnection, TaskManager TaskManager, InstanceTicketManager InstanceTicketManager, BackupManager BackupManager, LaunchServices LaunchServices);

View File

@ -7,12 +7,12 @@ using Phantom.Utils.Tasks;
namespace Phantom.Agent.Services.Instances.State;
static class InstanceLaunchProcedure {
public static async Task<InstanceRunningState?> Run(InstanceContext context, InstanceConfiguration configuration, IServerLauncher launcher, InstanceTicketManager ticketManager, InstanceTicketManager.Ticket ticket, Action<IInstanceStatus> reportStatus, CancellationToken cancellationToken) {
public static async Task<InstanceRunningState?> Run(InstanceContext context, InstanceConfiguration configuration, IServerLauncher launcher, InstanceTicketManager.Ticket ticket, Action<IInstanceStatus> reportStatus, CancellationToken cancellationToken) {
context.Logger.Information("Session starting...");
Result<InstanceProcess, InstanceLaunchFailReason> result;
if (ticketManager.IsValid(ticket)) {
if (context.Services.InstanceTicketManager.IsValid(ticket)) {
try {
result = await LaunchInstance(context, launcher, reportStatus, cancellationToken);
} catch (OperationCanceledException) {

View File

@ -72,7 +72,7 @@ sealed class InstanceRunningState : IDisposable {
else {
context.Logger.Information("Session ended unexpectedly, restarting...");
context.ReportEvent(InstanceEvent.Crashed);
context.Actor.Tell(new InstanceActor.LaunchInstanceCommand(configuration, launcher, Ticket, IsRestarting: true));
context.Actor.Tell(new InstanceActor.LaunchInstanceCommand(configuration, launcher, Ticket, IsRestarting: true, cancellationToken));
}
}

View File

@ -75,8 +75,6 @@ static class InstanceStopProcedure {
// Ignore.
} catch (ObjectDisposedException e) when (e.ObjectName == typeof(Process).FullName && process.HasEnded) {
// Ignore.
} catch (IOException e) when (e.HResult == -2147024664 /* The pipe is being closed */) {
// Ignore.
} catch (Exception e) {
context.Logger.Warning(e, "Caught exception while sending stop command.");
}