Implemented restorer, runtime and dummy statistics. Added service registering and fixed server factory. Moved logger to server context
This commit is contained in:
@@ -17,7 +17,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Moonlight.ApiServer" Version="2.1.*" />
|
<PackageReference Include="Moonlight.ApiServer" Version="2.1.*" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7"/>
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.3.0-next.2" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -57,6 +57,13 @@
|
|||||||
<_ContentIncludedByDefault Remove="volumes\3\version_history.json" />
|
<_ContentIncludedByDefault Remove="volumes\3\version_history.json" />
|
||||||
<_ContentIncludedByDefault Remove="volumes\3\whitelist.json" />
|
<_ContentIncludedByDefault Remove="volumes\3\whitelist.json" />
|
||||||
<_ContentIncludedByDefault Remove="storage\volumes\69\plugins\spark\config.json" />
|
<_ContentIncludedByDefault Remove="storage\volumes\69\plugins\spark\config.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\banned-ips.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\banned-players.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\ops.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\plugins\spark\config.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\usercache.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\version_history.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="storage\volumes\6\whitelist.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class DockerConsole : IConsole
|
|||||||
{
|
{
|
||||||
DockerClient = dockerClient;
|
DockerClient = dockerClient;
|
||||||
Context = context;
|
Context = context;
|
||||||
|
Logger = Context.Logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InitializeAsync()
|
public Task InitializeAsync()
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ namespace MoonlightServers.Daemon.ServerSystem.Docker;
|
|||||||
|
|
||||||
public static class DockerConstants
|
public static class DockerConstants
|
||||||
{
|
{
|
||||||
public const string RuntimeNameTemplate = "monnlight-runtime-{0}";
|
public const string RuntimeNameTemplate = "moonlight-runtime-{0}";
|
||||||
public const string InstallationNameTemplate = "monnlight-installation-{0}";
|
public const string InstallationNameTemplate = "moonlight-installation-{0}";
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ public class DockerInstallation : IInstallation
|
|||||||
private readonly DockerImageService ImageService;
|
private readonly DockerImageService ImageService;
|
||||||
private readonly ServerContext ServerContext;
|
private readonly ServerContext ServerContext;
|
||||||
private readonly DockerClient DockerClient;
|
private readonly DockerClient DockerClient;
|
||||||
private readonly IReporter Reporter;
|
private IReporter Reporter => ServerContext.Server.Reporter;
|
||||||
|
|
||||||
private readonly EventSource<int> ExitEventSource = new();
|
private readonly EventSource<int> ExitEventSource = new();
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ public class DockerInstallation : IInstallation
|
|||||||
ServerContext serverContext,
|
ServerContext serverContext,
|
||||||
ServerConfigurationMapper mapper,
|
ServerConfigurationMapper mapper,
|
||||||
DockerImageService imageService,
|
DockerImageService imageService,
|
||||||
IReporter reporter,
|
|
||||||
DockerEventService dockerEventService
|
DockerEventService dockerEventService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -36,7 +35,6 @@ public class DockerInstallation : IInstallation
|
|||||||
ServerContext = serverContext;
|
ServerContext = serverContext;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
ImageService = imageService;
|
ImageService = imageService;
|
||||||
Reporter = reporter;
|
|
||||||
DockerEventService = dockerEventService;
|
DockerEventService = dockerEventService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +117,8 @@ public class DockerInstallation : IInstallation
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
await DockerClient.Containers.CreateContainerAsync(parameters);
|
var response = await DockerClient.Containers.CreateContainerAsync(parameters);
|
||||||
|
ContainerId = response.ID;
|
||||||
|
|
||||||
await Reporter.StatusAsync("Created container");
|
await Reporter.StatusAsync("Created container");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using Docker.DotNet;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Daemon.ServerSystem.Docker;
|
||||||
|
|
||||||
|
public class DockerRestorer : IRestorer
|
||||||
|
{
|
||||||
|
private readonly DockerClient DockerClient;
|
||||||
|
private readonly ServerContext Context;
|
||||||
|
|
||||||
|
public DockerRestorer(DockerClient dockerClient, ServerContext context)
|
||||||
|
{
|
||||||
|
DockerClient = dockerClient;
|
||||||
|
Context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InitializeAsync()
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public async Task<bool> HandleRuntimeAsync()
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var container = await DockerClient.Containers.InspectContainerAsync(
|
||||||
|
containerName
|
||||||
|
);
|
||||||
|
|
||||||
|
return container.State.Running;
|
||||||
|
}
|
||||||
|
catch (DockerContainerNotFoundException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HandleInstallationAsync()
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.InstallationNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var container = await DockerClient.Containers.InspectContainerAsync(
|
||||||
|
containerName
|
||||||
|
);
|
||||||
|
|
||||||
|
return container.State.Running;
|
||||||
|
}
|
||||||
|
catch (DockerContainerNotFoundException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
=> ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
177
MoonlightServers.Daemon/ServerSystem/Docker/DockerRuntime.cs
Normal file
177
MoonlightServers.Daemon/ServerSystem/Docker/DockerRuntime.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
using Docker.DotNet;
|
||||||
|
using Docker.DotNet.Models;
|
||||||
|
using MoonCore.Events;
|
||||||
|
using MoonlightServers.Daemon.Mappers;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
using MoonlightServers.Daemon.Services;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Daemon.ServerSystem.Docker;
|
||||||
|
|
||||||
|
public class DockerRuntime : IRuntime
|
||||||
|
{
|
||||||
|
private readonly DockerClient DockerClient;
|
||||||
|
private readonly ServerContext Context;
|
||||||
|
private readonly ServerConfigurationMapper Mapper;
|
||||||
|
private readonly DockerEventService DockerEventService;
|
||||||
|
private readonly DockerImageService ImageService;
|
||||||
|
private readonly EventSource<int> ExitEventSource = new();
|
||||||
|
|
||||||
|
private IReporter Reporter => Context.Server.Reporter;
|
||||||
|
private IAsyncDisposable ContainerEventSubscription;
|
||||||
|
private string ContainerId;
|
||||||
|
|
||||||
|
public DockerRuntime(
|
||||||
|
DockerClient dockerClient,
|
||||||
|
ServerContext context,
|
||||||
|
ServerConfigurationMapper mapper,
|
||||||
|
DockerEventService dockerEventService,
|
||||||
|
DockerImageService imageService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DockerClient = dockerClient;
|
||||||
|
Context = context;
|
||||||
|
Mapper = mapper;
|
||||||
|
DockerEventService = dockerEventService;
|
||||||
|
ImageService = imageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
ContainerEventSubscription = await DockerEventService.SubscribeContainerAsync(OnContainerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask OnContainerEvent(Message message)
|
||||||
|
{
|
||||||
|
// Only handle events for our own container
|
||||||
|
if (message.ID != ContainerId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only handle die events
|
||||||
|
if (message.Action != "die")
|
||||||
|
return;
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
|
||||||
|
if (message.Actor.Attributes.TryGetValue("exitCode", out var exitCodeStr))
|
||||||
|
{
|
||||||
|
if (!int.TryParse(exitCodeStr, out exitCode))
|
||||||
|
exitCode = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exitCode = 0;
|
||||||
|
|
||||||
|
|
||||||
|
await ExitEventSource.InvokeAsync(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CheckExistsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
await DockerClient.Containers.InspectContainerAsync(
|
||||||
|
containerName
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (DockerContainerNotFoundException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateAsync(string path)
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
var parameters = Mapper.ToRuntimeParameters(
|
||||||
|
Context.Configuration,
|
||||||
|
path,
|
||||||
|
containerName
|
||||||
|
);
|
||||||
|
|
||||||
|
// Docker image
|
||||||
|
await Reporter.StatusAsync("Downloading docker image");
|
||||||
|
|
||||||
|
await ImageService.Download(
|
||||||
|
Context.Configuration.DockerImage,
|
||||||
|
async status => { await Reporter.StatusAsync(status); }
|
||||||
|
);
|
||||||
|
|
||||||
|
await Reporter.StatusAsync("Downloaded docker image");
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
var response = await DockerClient.Containers.CreateContainerAsync(parameters);
|
||||||
|
ContainerId = response.ID;
|
||||||
|
|
||||||
|
await Reporter.StatusAsync("Created container");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync()
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
await DockerClient.Containers.StartContainerAsync(containerName, new());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task UpdateAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task KillAsync()
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
await DockerClient.Containers.KillContainerAsync(containerName, new());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DestroyAsync()
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var container = await DockerClient.Containers.InspectContainerAsync(containerName);
|
||||||
|
|
||||||
|
if (container.State.Running)
|
||||||
|
await DockerClient.Containers.KillContainerAsync(containerName, new());
|
||||||
|
|
||||||
|
await DockerClient.Containers.RemoveContainerAsync(containerName, new()
|
||||||
|
{
|
||||||
|
Force = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (DockerContainerNotFoundException)
|
||||||
|
{
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IAsyncDisposable> SubscribeExited(Func<int, ValueTask> callback)
|
||||||
|
=> await ExitEventSource.SubscribeAsync(callback);
|
||||||
|
|
||||||
|
public async Task RestoreAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id);
|
||||||
|
|
||||||
|
var container = await DockerClient.Containers.InspectContainerAsync(containerName);
|
||||||
|
ContainerId = container.ID;
|
||||||
|
}
|
||||||
|
catch (DockerContainerNotFoundException)
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await ContainerEventSubscription.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Daemon.ServerSystem.Docker;
|
||||||
|
|
||||||
|
public class DockerStatistics : IStatistics
|
||||||
|
{
|
||||||
|
public Task InitializeAsync()
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task AttachRuntimeAsync()
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task AttachInstallationAsync()
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task ClearCacheAsync()
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task<IEnumerable<StatisticsData>> GetCacheAsync()
|
||||||
|
=> Task.FromResult<IEnumerable<StatisticsData>>([]);
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
=> ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ public class RawInstallationFs : IFileSystem
|
|||||||
BaseDirectory = Path.Combine(
|
BaseDirectory = Path.Combine(
|
||||||
Directory.GetCurrentDirectory(),
|
Directory.GetCurrentDirectory(),
|
||||||
"storage",
|
"storage",
|
||||||
"volumes",
|
"install",
|
||||||
context.Configuration.Id.ToString()
|
context.Configuration.Id.ToString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class ShutdownHandler : IServerStateHandler
|
|||||||
public async Task ExecuteAsync(StateMachine<ServerState, ServerTrigger>.Transition transition)
|
public async Task ExecuteAsync(StateMachine<ServerState, ServerTrigger>.Transition transition)
|
||||||
{
|
{
|
||||||
// Filter (we only want to handle exists from the runtime, so we filter out the installing state)
|
// Filter (we only want to handle exists from the runtime, so we filter out the installing state)
|
||||||
if (transition is
|
if (transition is not
|
||||||
{
|
{
|
||||||
Destination: ServerState.Offline,
|
Destination: ServerState.Offline,
|
||||||
Source: not ServerState.Installing,
|
Source: not ServerState.Installing,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class StartupHandler : IServerStateHandler
|
|||||||
await Server.Runtime.StartAsync();
|
await Server.Runtime.StartAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnRuntimeExited(int exitCode)
|
private async ValueTask OnRuntimeExited(int exitCode)
|
||||||
{
|
{
|
||||||
// TODO: Notify the crash handler component of the exit code
|
// TODO: Notify the crash handler component of the exit code
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ namespace MoonlightServers.Daemon.ServerSystem.Implementations;
|
|||||||
public class RegexOnlineDetector : IOnlineDetector
|
public class RegexOnlineDetector : IOnlineDetector
|
||||||
{
|
{
|
||||||
private readonly ServerContext Context;
|
private readonly ServerContext Context;
|
||||||
private readonly ILogger Logger;
|
|
||||||
|
|
||||||
private Regex? Expression;
|
private Regex? Expression;
|
||||||
|
|
||||||
public RegexOnlineDetector(ServerContext context, ILogger logger)
|
public RegexOnlineDetector(ServerContext context)
|
||||||
{
|
{
|
||||||
Context = context;
|
Context = context;
|
||||||
Logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InitializeAsync()
|
public Task InitializeAsync()
|
||||||
@@ -25,14 +23,7 @@ public class RegexOnlineDetector : IOnlineDetector
|
|||||||
if(string.IsNullOrEmpty(Context.Configuration.OnlineDetection))
|
if(string.IsNullOrEmpty(Context.Configuration.OnlineDetection))
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Expression = new Regex(Context.Configuration.OnlineDetection, RegexOptions.Compiled);
|
Expression = new Regex(Context.Configuration.OnlineDetection, RegexOptions.Compiled);
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.LogError(e, "An error occured while creating regex. Check the regex expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.ServerSystem.Implementations;
|
namespace MoonlightServers.Daemon.ServerSystem.Implementations;
|
||||||
|
|
||||||
public class ServerReporter : IReporter
|
public class ServerReporter : IReporter
|
||||||
{
|
{
|
||||||
private readonly IConsole Console;
|
private readonly ServerContext Context;
|
||||||
private readonly ILogger Logger;
|
|
||||||
|
|
||||||
private const string StatusTemplate =
|
private const string StatusTemplate =
|
||||||
"\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[3;38;2;200;200;200m{0}\x1b[0m\n\r";
|
"\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[3;38;2;200;200;200m{0}\x1b[0m\n\r";
|
||||||
@@ -13,10 +13,9 @@ public class ServerReporter : IReporter
|
|||||||
private const string ErrorTemplate =
|
private const string ErrorTemplate =
|
||||||
"\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[1;38;2;255;0;0m{0}\x1b[0m\n\r";
|
"\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[1;38;2;255;0;0m{0}\x1b[0m\n\r";
|
||||||
|
|
||||||
public ServerReporter(IConsole console, ILogger logger)
|
public ServerReporter(ServerContext context)
|
||||||
{
|
{
|
||||||
Console = console;
|
Context = context;
|
||||||
Logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InitializeAsync()
|
public Task InitializeAsync()
|
||||||
@@ -24,18 +23,18 @@ public class ServerReporter : IReporter
|
|||||||
|
|
||||||
public async Task StatusAsync(string message)
|
public async Task StatusAsync(string message)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Status: {message}", message);
|
Context.Logger.LogInformation("Status: {message}", message);
|
||||||
|
|
||||||
await Console.WriteStdOutAsync(
|
await Context.Server.Console.WriteStdOutAsync(
|
||||||
string.Format(StatusTemplate, message)
|
string.Format(StatusTemplate, message)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ErrorAsync(string message)
|
public async Task ErrorAsync(string message)
|
||||||
{
|
{
|
||||||
Logger.LogError("Error: {message}", message);
|
Context.Logger.LogError("Error: {message}", message);
|
||||||
|
|
||||||
await Console.WriteStdOutAsync(
|
await Context.Server.Console.WriteStdOutAsync(
|
||||||
string.Format(ErrorTemplate, message)
|
string.Format(ErrorTemplate, message)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public interface IRuntime : IServerComponent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="callback">Callback gets invoked whenever the runtime exites</param>
|
/// <param name="callback">Callback gets invoked whenever the runtime exites</param>
|
||||||
/// <returns>Subscription disposable to unsubscribe from the event</returns>
|
/// <returns>Subscription disposable to unsubscribe from the event</returns>
|
||||||
public Task<IAsyncDisposable> SubscribeExited(Func<int, Task> callback);
|
public Task<IAsyncDisposable> SubscribeExited(Func<int, ValueTask> callback);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connects an existing runtime to this abstraction in order to restore it.
|
/// Connects an existing runtime to this abstraction in order to restore it.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MoonlightServers.Daemon.Models.Cache;
|
using MoonlightServers.Daemon.Models.Cache;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.ServerSystem.Models;
|
namespace MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
|
||||||
@@ -8,4 +9,5 @@ public class ServerContext
|
|||||||
public int Identifier { get; set; }
|
public int Identifier { get; set; }
|
||||||
public AsyncServiceScope ServiceScope { get; set; }
|
public AsyncServiceScope ServiceScope { get; set; }
|
||||||
public Server Server { get; set; }
|
public Server Server { get; set; }
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
}
|
}
|
||||||
@@ -136,32 +136,6 @@ public partial class Server : IAsyncDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleSaveAsync(Func<Task> callback)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await callback.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.LogError(e, "An error occured while handling");
|
|
||||||
|
|
||||||
await StateMachine.FireAsync(ServerTrigger.Fail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleIgnoredAsync(Func<Task> callback)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await callback.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.LogError(e, "An error occured while handling");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
foreach (var component in AllComponents)
|
foreach (var component in AllComponents)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using MoonlightServers.Daemon.Models.Cache;
|
using MoonlightServers.Daemon.Models.Cache;
|
||||||
using MoonlightServers.Daemon.ServerSystem.Docker;
|
using MoonlightServers.Daemon.ServerSystem.Docker;
|
||||||
using MoonlightServers.Daemon.ServerSystem.FileSystems;
|
using MoonlightServers.Daemon.ServerSystem.FileSystems;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Handlers;
|
||||||
using MoonlightServers.Daemon.ServerSystem.Implementations;
|
using MoonlightServers.Daemon.ServerSystem.Implementations;
|
||||||
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
using MoonlightServers.Daemon.ServerSystem.Interfaces;
|
||||||
using MoonlightServers.Daemon.ServerSystem.Models;
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
@@ -16,7 +17,7 @@ public class ServerFactory
|
|||||||
ServiceProvider = serviceProvider;
|
ServiceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Server> Create(ServerConfiguration configuration)
|
public async Task<Server> CreateAsync(ServerConfiguration configuration)
|
||||||
{
|
{
|
||||||
var scope = ServiceProvider.CreateAsyncScope();
|
var scope = ServiceProvider.CreateAsyncScope();
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ public class ServerFactory
|
|||||||
context.Identifier = configuration.Id;
|
context.Identifier = configuration.Id;
|
||||||
context.Configuration = configuration;
|
context.Configuration = configuration;
|
||||||
context.ServiceScope = scope;
|
context.ServiceScope = scope;
|
||||||
|
context.Logger = logger;
|
||||||
|
|
||||||
// Define all required components
|
// Define all required components
|
||||||
|
|
||||||
@@ -43,12 +45,21 @@ public class ServerFactory
|
|||||||
|
|
||||||
// Resolve the components
|
// Resolve the components
|
||||||
|
|
||||||
console = ActivatorUtilities.CreateInstance<DockerConsole>(scope.ServiceProvider, logger);
|
console = ActivatorUtilities.CreateInstance<DockerConsole>(scope.ServiceProvider);
|
||||||
reporter = ActivatorUtilities.CreateInstance<ServerReporter>(scope.ServiceProvider, console, logger);
|
reporter = ActivatorUtilities.CreateInstance<ServerReporter>(scope.ServiceProvider);
|
||||||
runtimeFs = ActivatorUtilities.CreateInstance<RawRuntimeFs>(scope.ServiceProvider, logger, reporter);
|
runtimeFs = ActivatorUtilities.CreateInstance<RawRuntimeFs>(scope.ServiceProvider);
|
||||||
installFs = ActivatorUtilities.CreateInstance<RawInstallationFs>(scope.ServiceProvider, logger, reporter);
|
installFs = ActivatorUtilities.CreateInstance<RawInstallationFs>(scope.ServiceProvider);
|
||||||
installation = ActivatorUtilities.CreateInstance<DockerInstallation>(scope.ServiceProvider, logger, reporter);
|
installation = ActivatorUtilities.CreateInstance<DockerInstallation>(scope.ServiceProvider);
|
||||||
onlineDetector = ActivatorUtilities.CreateInstance<RegexOnlineDetector>(scope.ServiceProvider, logger, reporter);
|
onlineDetector = ActivatorUtilities.CreateInstance<RegexOnlineDetector>(scope.ServiceProvider);
|
||||||
|
restorer = ActivatorUtilities.CreateInstance<DockerRestorer>(scope.ServiceProvider);
|
||||||
|
runtime = ActivatorUtilities.CreateInstance<DockerRuntime>(scope.ServiceProvider);
|
||||||
|
statistics = ActivatorUtilities.CreateInstance<DockerStatistics>(scope.ServiceProvider);
|
||||||
|
|
||||||
|
// Resolve handlers
|
||||||
|
var handlers = new List<IServerStateHandler>();
|
||||||
|
|
||||||
|
handlers.Add(ActivatorUtilities.CreateInstance<StartupHandler>(scope.ServiceProvider));
|
||||||
|
handlers.Add(ActivatorUtilities.CreateInstance<ShutdownHandler>(scope.ServiceProvider));
|
||||||
|
|
||||||
// TODO: Add a plugin hook for dynamically resolving components and checking if any is unset
|
// TODO: Add a plugin hook for dynamically resolving components and checking if any is unset
|
||||||
|
|
||||||
@@ -67,7 +78,7 @@ public class ServerFactory
|
|||||||
runtime,
|
runtime,
|
||||||
statistics,
|
statistics,
|
||||||
// And now all the handlers
|
// And now all the handlers
|
||||||
[]
|
handlers.ToArray()
|
||||||
);
|
);
|
||||||
|
|
||||||
context.Server = server;
|
context.Server = server;
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ using MoonCore.Logging;
|
|||||||
using MoonlightServers.Daemon.Configuration;
|
using MoonlightServers.Daemon.Configuration;
|
||||||
using MoonlightServers.Daemon.Helpers;
|
using MoonlightServers.Daemon.Helpers;
|
||||||
using MoonlightServers.Daemon.Http.Hubs;
|
using MoonlightServers.Daemon.Http.Hubs;
|
||||||
|
using MoonlightServers.Daemon.Mappers;
|
||||||
|
using MoonlightServers.Daemon.Models.Cache;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Docker;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Enums;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.FileSystems;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Handlers;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Implementations;
|
||||||
|
using MoonlightServers.Daemon.ServerSystem.Models;
|
||||||
|
using MoonlightServers.Daemon.Services;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon;
|
namespace MoonlightServers.Daemon;
|
||||||
|
|
||||||
@@ -60,6 +70,64 @@ public class Startup
|
|||||||
await MapBase();
|
await MapBase();
|
||||||
await MapHubs();
|
await MapHubs();
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var serverConfig = new ServerConfiguration()
|
||||||
|
{
|
||||||
|
Id = 69,
|
||||||
|
Allocations =
|
||||||
|
[
|
||||||
|
new ServerConfiguration.AllocationConfiguration()
|
||||||
|
{
|
||||||
|
IpAddress = "0.0.0.0",
|
||||||
|
Port = 25565
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Cpu = 400,
|
||||||
|
Disk = 10240,
|
||||||
|
Memory = 4096,
|
||||||
|
OnlineDetection = "\\! For help, type ",
|
||||||
|
StartupCommand =
|
||||||
|
"java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}",
|
||||||
|
DockerImage = "ghcr.io/nexocrew-hq/moonlightdockerimages:java21",
|
||||||
|
StopCommand = "stop",
|
||||||
|
UseVirtualDisk = false,
|
||||||
|
Bandwidth = 0,
|
||||||
|
Variables = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "SERVER_JARFILE", "server.jar" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var factory = WebApplication.Services.GetRequiredService<ServerFactory>();
|
||||||
|
|
||||||
|
Console.Write("Press enter to create and init server");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
var s = await factory.CreateAsync(serverConfig);
|
||||||
|
|
||||||
|
await s.InitializeAsync();
|
||||||
|
|
||||||
|
s.StateMachine.OnTransitionCompleted(transition =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(transition.Destination);
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.Write("Press enter to start server");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
await s.StateMachine.FireAsync(ServerTrigger.Start);
|
||||||
|
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await WebApplication.RunAsync();
|
await WebApplication.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +193,10 @@ public class Startup
|
|||||||
).CreateClient();
|
).CreateClient();
|
||||||
|
|
||||||
WebApplicationBuilder.Services.AddSingleton(dockerClient);
|
WebApplicationBuilder.Services.AddSingleton(dockerClient);
|
||||||
|
WebApplicationBuilder.Services.AddScoped<DockerImageService>();
|
||||||
|
|
||||||
|
WebApplicationBuilder.Services.AddSingleton<DockerEventService>();
|
||||||
|
WebApplicationBuilder.Services.AddHostedService(sp => sp.GetRequiredService<DockerEventService>());
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -245,6 +317,10 @@ public class Startup
|
|||||||
|
|
||||||
private Task RegisterServers()
|
private Task RegisterServers()
|
||||||
{
|
{
|
||||||
|
WebApplicationBuilder.Services.AddScoped<ServerContext>();
|
||||||
|
WebApplicationBuilder.Services.AddSingleton<ServerFactory>();
|
||||||
|
WebApplicationBuilder.Services.AddSingleton<ServerConfigurationMapper>();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user