Recreated plugin with new project template. Started implementing server system daemon

This commit is contained in:
2026-03-01 21:09:29 +01:00
parent f6b71f4de6
commit 52dbd13fb5
350 changed files with 2795 additions and 21553 deletions

View File

@@ -1,161 +1,111 @@
using MoonlightServers.Daemon.ServerSystem.Enums;
using MoonlightServers.Daemon.ServerSystem.Interfaces;
using MoonlightServers.Daemon.ServerSystem.Models;
using Stateless;
using MoonlightServers.Daemon.Models;
using MoonlightServers.Daemon.ServerSystem.Abstractions;
using MoonlightServers.Daemon.Services;
namespace MoonlightServers.Daemon.ServerSystem;
public partial class Server : IAsyncDisposable
{
public int Identifier => InnerContext.Identifier;
public ServerContext Context => InnerContext;
public ServerState State { get; private set; }
public IConsole Console { get; }
public IFileSystem RuntimeFileSystem { get; }
public IFileSystem InstallationFileSystem { get; }
public IInstallation Installation { get; }
public IOnlineDetector OnlineDetector { get; }
public IReporter Reporter { get; }
public IRestorer Restorer { get; }
public IRuntime Runtime { get; }
public IStatistics Statistics { get; }
public StateMachine<ServerState, ServerTrigger> StateMachine { get; private set; }
private IRuntimeEnvironment? RuntimeEnvironment;
private RuntimeConfiguration RuntimeConfiguration;
private IRuntimeStorage? RuntimeStorage;
private readonly IServerStateHandler[] Handlers;
private IInstallEnvironment? InstallEnvironment;
private InstallConfiguration InstallConfiguration;
private IInstallStorage? InstallStorage;
private readonly IServerComponent[] AllComponents;
private readonly ServerContext InnerContext;
private readonly IRuntimeEnvironmentService RuntimeEnvironmentService;
private readonly IInstallEnvironmentService InstallEnvironmentService;
private readonly IRuntimeStorageService RuntimeStorageService;
private readonly IInstallStorageService InstallStorageService;
private readonly ServerConfigurationService ConfigurationService;
private readonly string Uuid;
private readonly ILogger Logger;
private readonly SemaphoreSlim Lock = new(1, 1);
public Server(
ILogger logger,
ServerContext context,
IConsole console,
IFileSystem runtimeFileSystem,
IFileSystem installationFileSystem,
IInstallation installation,
IOnlineDetector onlineDetector,
IReporter reporter,
IRestorer restorer,
IRuntime runtime,
IStatistics statistics,
IEnumerable<IServerStateHandler> handlers,
IEnumerable<IServerComponent> additionalComponents
string uuid,
IRuntimeEnvironmentService runtimeEnvironmentService,
IInstallEnvironmentService installEnvironmentService,
IRuntimeStorageService runtimeStorageService,
IInstallStorageService installStorageService,
ServerConfigurationService configurationService,
ILogger logger
)
{
Uuid = uuid;
RuntimeEnvironmentService = runtimeEnvironmentService;
InstallEnvironmentService = installEnvironmentService;
RuntimeStorageService = runtimeStorageService;
InstallStorageService = installStorageService;
ConfigurationService = configurationService;
Logger = logger;
InnerContext = context;
Console = console;
RuntimeFileSystem = runtimeFileSystem;
InstallationFileSystem = installationFileSystem;
Installation = installation;
OnlineDetector = onlineDetector;
Reporter = reporter;
Restorer = restorer;
Runtime = runtime;
Statistics = statistics;
IEnumerable<IServerComponent> defaultComponents =
[
Console, RuntimeFileSystem, InstallationFileSystem, Installation, OnlineDetector, Reporter, Restorer,
Runtime, Statistics
];
AllComponents = defaultComponents.Concat(additionalComponents).ToArray();
Handlers = handlers.ToArray();
}
private void ConfigureStateMachine(ServerState initialState)
{
StateMachine = new StateMachine<ServerState, ServerTrigger>(
initialState, FiringMode.Queued
);
StateMachine.Configure(ServerState.Offline)
.Permit(ServerTrigger.Start, ServerState.Starting)
.Permit(ServerTrigger.Install, ServerState.Installing)
.PermitReentry(ServerTrigger.Fail);
StateMachine.Configure(ServerState.Starting)
.Permit(ServerTrigger.DetectOnline, ServerState.Online)
.Permit(ServerTrigger.Fail, ServerState.Offline)
.Permit(ServerTrigger.Exited, ServerState.Offline)
.Permit(ServerTrigger.Stop, ServerState.Stopping)
.Permit(ServerTrigger.Kill, ServerState.Stopping);
StateMachine.Configure(ServerState.Online)
.Permit(ServerTrigger.Stop, ServerState.Stopping)
.Permit(ServerTrigger.Kill, ServerState.Stopping)
.Permit(ServerTrigger.Exited, ServerState.Offline);
StateMachine.Configure(ServerState.Stopping)
.PermitReentry(ServerTrigger.Fail)
.PermitReentry(ServerTrigger.Kill)
.Permit(ServerTrigger.Exited, ServerState.Offline);
StateMachine.Configure(ServerState.Installing)
.Permit(ServerTrigger.Fail, ServerState.Offline) // TODO: Add kill
.Permit(ServerTrigger.Exited, ServerState.Offline);
}
private void ConfigureStateMachineEvents()
{
// Configure the calling of the handlers
StateMachine.OnTransitionedAsync(async transition =>
{
var hasFailed = false;
foreach (var handler in Handlers)
{
try
{
await handler.ExecuteAsync(transition);
}
catch (Exception e)
{
Logger.LogError(
e,
"Handler {name} has thrown an unexpected exception",
handler.GetType().FullName
);
hasFailed = true;
break;
}
}
if(!hasFailed)
return; // Everything went fine, we can exit now
// Something has failed, lets check if we can handle the error
// via a fail trigger
if(!StateMachine.CanFire(ServerTrigger.Fail))
return;
// Trigger the fail so the server gets a chance to handle the error softly
await StateMachine.FireAsync(ServerTrigger.Fail);
});
}
public async Task InitializeAsync()
{
foreach (var component in AllComponents)
await component.InitializeAsync();
Logger.LogTrace("Initializing");
var restoredState = ServerState.Offline;
await Lock.WaitAsync();
ConfigureStateMachine(restoredState);
ConfigureStateMachineEvents();
try
{
// Restore state
State = await RestoreAsync();
Logger.LogTrace("Initialization complete, restored to state {State}", State);
}
finally
{
Lock.Release();
}
}
private async Task OnConsoleMessageAsync(string message)
{
Console.WriteLine($"Console: {message}");
}
private async Task OnStatisticsReceivedAsync(ServerStatistics statistics)
{
}
private Task ChangeStateAsync(ServerState newState)
{
Logger.LogTrace("State changed from {OldState} to {NewState}", State, newState);
State = newState;
return Task.CompletedTask;
}
public async ValueTask DisposeAsync()
{
foreach (var handler in Handlers)
await handler.DisposeAsync();
foreach (var component in AllComponents)
await component.DisposeAsync();
Logger.LogTrace("Disposing");
if (RuntimeEnvironment != null)
{
Logger.LogTrace("Detaching and disposing runtime environment");
RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync;
RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync;
RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync;
await RuntimeEnvironment.DisposeAsync();
}
if (InstallEnvironment != null)
{
Logger.LogTrace("Detaching and disposing install environment");
InstallEnvironment.Console.OnOutput -= OnConsoleMessageAsync;
InstallEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync;
InstallEnvironment.OnExited -= OnInstallExitedAsync;
await InstallEnvironment.DisposeAsync();
}
}
}