mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-10-30 05:42:47 +01:00
173 lines
5.6 KiB
C#
173 lines
5.6 KiB
C#
using Phantom.Agent.Minecraft.Launcher;
|
|
using Phantom.Agent.Rpc;
|
|
using Phantom.Agent.Services.Instances.States;
|
|
using Phantom.Common.Data.Instance;
|
|
using Phantom.Common.Data.Replies;
|
|
using Phantom.Common.Logging;
|
|
using Phantom.Common.Messages.ToServer;
|
|
using Serilog;
|
|
|
|
namespace Phantom.Agent.Services.Instances;
|
|
|
|
sealed class Instance : IDisposable {
|
|
private static uint loggerSequenceId = 0;
|
|
|
|
private static string GetLoggerName(Guid guid) {
|
|
var prefix = guid.ToString();
|
|
return prefix[..prefix.IndexOf('-')] + "/" + Interlocked.Increment(ref loggerSequenceId);
|
|
}
|
|
|
|
public static async Task<Instance> Create(InstanceConfiguration configuration, BaseLauncher launcher, LaunchServices launchServices, PortManager portManager) {
|
|
var instance = new Instance(configuration, launcher, launchServices, portManager);
|
|
await instance.ReportLastStatus();
|
|
return instance;
|
|
}
|
|
|
|
public InstanceConfiguration Configuration { get; private set; }
|
|
private BaseLauncher Launcher { get; set; }
|
|
|
|
private readonly string shortName;
|
|
private readonly ILogger logger;
|
|
|
|
private readonly LaunchServices launchServices;
|
|
private readonly PortManager portManager;
|
|
|
|
private InstanceStatus currentStatus;
|
|
private IInstanceState currentState;
|
|
private readonly SemaphoreSlim stateTransitioningActionSemaphore = new (1, 1);
|
|
|
|
private Instance(InstanceConfiguration configuration, BaseLauncher launcher, LaunchServices launchServices, PortManager portManager) {
|
|
this.shortName = GetLoggerName(configuration.InstanceGuid);
|
|
this.logger = PhantomLogger.Create<Instance>(shortName);
|
|
|
|
this.Configuration = configuration;
|
|
this.Launcher = launcher;
|
|
|
|
this.launchServices = launchServices;
|
|
this.portManager = portManager;
|
|
this.currentState = new InstanceNotRunningState();
|
|
this.currentStatus = InstanceStatus.IsNotRunning;
|
|
}
|
|
|
|
private async Task ReportLastStatus() {
|
|
await ServerMessaging.SendMessage(new ReportInstanceStatusMessage(Configuration.InstanceGuid, currentStatus));
|
|
}
|
|
|
|
private bool TransitionState(IInstanceState newState) {
|
|
if (currentState == newState) {
|
|
return false;
|
|
}
|
|
|
|
if (currentState is IDisposable disposable) {
|
|
disposable.Dispose();
|
|
}
|
|
|
|
currentState = newState;
|
|
return true;
|
|
}
|
|
|
|
public async Task Reconfigure(InstanceConfiguration configuration, BaseLauncher launcher, CancellationToken cancellationToken) {
|
|
await stateTransitioningActionSemaphore.WaitAsync(cancellationToken);
|
|
try {
|
|
Configuration = configuration;
|
|
Launcher = launcher;
|
|
await ReportLastStatus();
|
|
} finally {
|
|
stateTransitioningActionSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task<LaunchInstanceResult> Launch(CancellationToken cancellationToken) {
|
|
await stateTransitioningActionSemaphore.WaitAsync(cancellationToken);
|
|
try {
|
|
if (TransitionState(currentState.Launch(new InstanceContextImpl(this)))) {
|
|
return LaunchInstanceResult.LaunchInitiated;
|
|
}
|
|
|
|
return currentState switch {
|
|
InstanceLaunchingState => LaunchInstanceResult.InstanceAlreadyLaunching,
|
|
InstanceRunningState => LaunchInstanceResult.InstanceAlreadyRunning,
|
|
InstanceStoppingState => LaunchInstanceResult.InstanceIsStopping,
|
|
_ => LaunchInstanceResult.UnknownError
|
|
};
|
|
} finally {
|
|
stateTransitioningActionSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task<StopInstanceResult> Stop() {
|
|
await stateTransitioningActionSemaphore.WaitAsync();
|
|
try {
|
|
if (TransitionState(currentState.Stop())) {
|
|
return StopInstanceResult.StopInitiated;
|
|
}
|
|
|
|
return currentState switch {
|
|
InstanceNotRunningState => StopInstanceResult.InstanceAlreadyStopped,
|
|
InstanceLaunchingState => StopInstanceResult.StopInitiated,
|
|
InstanceStoppingState => StopInstanceResult.InstanceAlreadyStopping,
|
|
_ => StopInstanceResult.UnknownError
|
|
};
|
|
} finally {
|
|
stateTransitioningActionSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task StopAndWait(TimeSpan waitTime) {
|
|
await Stop();
|
|
|
|
using var waitTokenSource = new CancellationTokenSource(waitTime);
|
|
var waitToken = waitTokenSource.Token;
|
|
|
|
while (currentState is not InstanceNotRunningState) {
|
|
await Task.Delay(TimeSpan.FromMilliseconds(250), waitToken);
|
|
}
|
|
}
|
|
|
|
public async Task<bool> SendCommand(string command, CancellationToken cancellationToken) {
|
|
return await currentState.SendCommand(command, cancellationToken);
|
|
}
|
|
|
|
private sealed class InstanceContextImpl : InstanceContext {
|
|
private readonly Instance instance;
|
|
private int statusUpdateCounter;
|
|
|
|
public InstanceContextImpl(Instance instance) : base(instance.Configuration, instance.Launcher) {
|
|
this.instance = instance;
|
|
}
|
|
|
|
public override LaunchServices LaunchServices => instance.launchServices;
|
|
public override PortManager PortManager => instance.portManager;
|
|
public override ILogger Logger => instance.logger;
|
|
public override string ShortName => instance.shortName;
|
|
|
|
public override void ReportStatus(InstanceStatus newStatus) {
|
|
int myStatusUpdateCounter = Interlocked.Increment(ref statusUpdateCounter);
|
|
|
|
Task.Run(async () => {
|
|
if (myStatusUpdateCounter == statusUpdateCounter) {
|
|
instance.currentStatus = newStatus;
|
|
await ServerMessaging.SendMessage(new ReportInstanceStatusMessage(Configuration.InstanceGuid, newStatus));
|
|
}
|
|
});
|
|
}
|
|
|
|
public override void TransitionState(Func<IInstanceState> newState) {
|
|
instance.stateTransitioningActionSemaphore.Wait();
|
|
try {
|
|
instance.TransitionState(newState());
|
|
} finally {
|
|
instance.stateTransitioningActionSemaphore.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose() {
|
|
if (currentState is IDisposable disposable) {
|
|
disposable.Dispose();
|
|
}
|
|
|
|
stateTransitioningActionSemaphore.Dispose();
|
|
}
|
|
}
|