namespace MoonlightServers.Daemon.ServerSystem; public partial class Server { public async Task StartAsync() { await Lock.WaitAsync(); try { if (State != ServerState.Offline) throw new InvalidOperationException("Server is not offline"); // Check for any pre-existing runtime environment, if we don't have a reference already RuntimeEnvironment ??= await RuntimeEnvironmentService.FindAsync(Uuid); RuntimeStorage ??= await RuntimeStorageService.FindAsync(Uuid); // Remove any pre-existing environment if (RuntimeEnvironment != null) { Logger.LogTrace("Destroying pre-existing runtime environment"); if (await RuntimeEnvironment.IsRunningAsync()) { Logger.LogTrace("Pre-existing runtime environment is still running, killing it"); await RuntimeEnvironment.KillAsync(); } // Make sure no event handler is there anymore RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync; RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync; // Finally remove it await RuntimeEnvironmentService.DeleteAsync(RuntimeEnvironment); RuntimeEnvironment = null; Logger.LogTrace("Pre-existing runtime environment destroyed"); } // Fetch the latest config Logger.LogTrace("Fetching latest configuration"); RuntimeConfiguration = await ConfigurationService.GetRuntimeConfigurationAsync(Uuid); // Ensure runtime storage if (RuntimeStorage == null) { Logger.LogTrace("Creating runtime storage"); RuntimeStorage = await RuntimeStorageService.CreateAsync(Uuid, RuntimeConfiguration); } else { Logger.LogTrace("Updating runtime storage"); await RuntimeStorageService.UpdateAsync(RuntimeStorage, RuntimeConfiguration); } // Create the environment Logger.LogTrace("Creating runtime environment"); RuntimeEnvironment = await RuntimeEnvironmentService.CreateAsync(Uuid, RuntimeConfiguration, RuntimeStorage); // Set event handlers Logger.LogTrace("Attaching to runtime environment"); RuntimeEnvironment.Console.OnOutput += OnConsoleMessageAsync; RuntimeEnvironment.Statistics.OnStatisticsReceived += OnStatisticsReceivedAsync; RuntimeEnvironment.OnExited += OnRuntimeExitedAsync; // Attach console & statistics await RuntimeEnvironment.Console.AttachAsync(); await RuntimeEnvironment.Statistics.AttachAsync(); // Start up Logger.LogTrace("Starting runtime environment"); await RuntimeEnvironment.StartAsync(); await ChangeStateAsync(ServerState.Starting); } finally { Lock.Release(); } } public async Task StopAsync() { await Lock.WaitAsync(); try { if (State is not (ServerState.Starting or ServerState.Online)) throw new InvalidOperationException("Server is not starting or online"); if (RuntimeEnvironment == null) throw new InvalidOperationException("Runtime environment is not set"); Logger.LogTrace("Sending stop command to runtime environment"); await RuntimeEnvironment.Console.WriteInputAsync("stop\n\r"); await ChangeStateAsync(ServerState.Stopping); } finally { Lock.Release(); } } public async Task KillAsync() { await Lock.WaitAsync(); try { if (State is not (ServerState.Starting or ServerState.Online or ServerState.Stopping)) throw new InvalidOperationException("Server is not starting, stopping or online"); if (RuntimeEnvironment == null) throw new InvalidOperationException("Runtime environment is not set"); Logger.LogTrace("Killing runtime environment"); await RuntimeEnvironment.KillAsync(); await ChangeStateAsync(ServerState.Stopping); } finally { Lock.Release(); } } private async Task OnRuntimeExitedAsync() { Logger.LogTrace("Runtime environment exited, checking result and cleaning up"); await Lock.WaitAsync(); try { // TODO: Handle crash if (RuntimeEnvironment == null) throw new InvalidOperationException("Runtime environment is not set"); // Make sure no event handler is there anymore RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync; RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync; // Finally remove it await RuntimeEnvironmentService.DeleteAsync(RuntimeEnvironment); RuntimeEnvironment = null; Logger.LogTrace("Runtime environment cleaned up"); } finally { Lock.Release(); } await ChangeStateAsync(ServerState.Offline); } }