mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-05-25 07:34:05 +02:00
Move fetching server jar download information exclusively to the Server
This commit is contained in:
parent
51d8585f05
commit
bb7de48d24
Agent
Common
Phantom.Common.Data
Phantom.Common.Messages/ToAgent
Phantom.Common.Minecraft
Server
Phantom.Server.Minecraft
Phantom.Server.Services
Phantom.Server.Web/Shared
Phantom.Server
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Phantom.Agent.Minecraft.Java;
|
using Phantom.Agent.Minecraft.Java;
|
||||||
using Phantom.Agent.Minecraft.Properties;
|
using Phantom.Agent.Minecraft.Properties;
|
||||||
|
using Phantom.Common.Data.Instance;
|
||||||
|
|
||||||
namespace Phantom.Agent.Minecraft.Instance;
|
namespace Phantom.Agent.Minecraft.Instance;
|
||||||
|
|
||||||
@ -11,5 +12,6 @@ public sealed record InstanceProperties(
|
|||||||
ImmutableArray<string> JvmArguments,
|
ImmutableArray<string> JvmArguments,
|
||||||
string InstanceFolder,
|
string InstanceFolder,
|
||||||
string ServerVersion,
|
string ServerVersion,
|
||||||
ServerProperties ServerProperties
|
ServerProperties ServerProperties,
|
||||||
|
InstanceLaunchProperties LaunchProperties
|
||||||
);
|
);
|
||||||
|
@ -25,7 +25,7 @@ public abstract class BaseLauncher {
|
|||||||
return new LaunchResult.InvalidJvmArguments();
|
return new LaunchResult.InvalidJvmArguments();
|
||||||
}
|
}
|
||||||
|
|
||||||
var vanillaServerJarPath = await services.ServerExecutables.DownloadAndGetPath(instanceProperties.ServerVersion, downloadProgressEventHandler, cancellationToken);
|
var vanillaServerJarPath = await services.ServerExecutables.DownloadAndGetPath(instanceProperties.LaunchProperties.ServerDownloadInfo, instanceProperties.ServerVersion, downloadProgressEventHandler, cancellationToken);
|
||||||
if (vanillaServerJarPath == null) {
|
if (vanillaServerJarPath == null) {
|
||||||
return new LaunchResult.CouldNotDownloadMinecraftServer();
|
return new LaunchResult.CouldNotDownloadMinecraftServer();
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Collections\Phantom.Utils.Collections.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Collections\Phantom.Utils.Collections.csproj" />
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Cryptography\Phantom.Utils.Cryptography.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Cryptography\Phantom.Utils.Cryptography.csproj" />
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.IO\Phantom.Utils.IO.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.IO\Phantom.Utils.IO.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Minecraft;
|
|
||||||
using Phantom.Utils.Cryptography;
|
using Phantom.Utils.Cryptography;
|
||||||
using Phantom.Utils.IO;
|
using Phantom.Utils.IO;
|
||||||
using Phantom.Utils.Runtime;
|
using Phantom.Utils.Runtime;
|
||||||
@ -11,8 +11,6 @@ namespace Phantom.Agent.Minecraft.Server;
|
|||||||
sealed class MinecraftServerExecutableDownloader {
|
sealed class MinecraftServerExecutableDownloader {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftServerExecutableDownloader>();
|
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftServerExecutableDownloader>();
|
||||||
|
|
||||||
private readonly MinecraftVersions minecraftVersions;
|
|
||||||
|
|
||||||
public Task<string?> Task { get; }
|
public Task<string?> Task { get; }
|
||||||
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
|
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
|
||||||
public event EventHandler? Completed;
|
public event EventHandler? Completed;
|
||||||
@ -20,11 +18,9 @@ sealed class MinecraftServerExecutableDownloader {
|
|||||||
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
||||||
private int listeners = 0;
|
private int listeners = 0;
|
||||||
|
|
||||||
public MinecraftServerExecutableDownloader(MinecraftVersions minecraftVersions, string version, string filePath, MinecraftServerExecutableDownloadListener listener) {
|
public MinecraftServerExecutableDownloader(FileDownloadInfo fileDownloadInfo, string minecraftVersion, string filePath, MinecraftServerExecutableDownloadListener listener) {
|
||||||
this.minecraftVersions = minecraftVersions;
|
|
||||||
|
|
||||||
Register(listener);
|
Register(listener);
|
||||||
Task = DownloadAndGetPath(version, filePath);
|
Task = DownloadAndGetPath(fileDownloadInfo, minecraftVersion, filePath);
|
||||||
Task.ContinueWith(OnCompleted, TaskScheduler.Default);
|
Task.ContinueWith(OnCompleted, TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,33 +68,26 @@ sealed class MinecraftServerExecutableDownloader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string?> DownloadAndGetPath(string version, string filePath) {
|
private async Task<string?> DownloadAndGetPath(FileDownloadInfo fileDownloadInfo, string minecraftVersion, string filePath) {
|
||||||
Logger.Information("Downloading server version {Version}...", version);
|
|
||||||
|
|
||||||
string tmpFilePath = filePath + ".tmp";
|
string tmpFilePath = filePath + ".tmp";
|
||||||
|
|
||||||
var cancellationToken = cancellationTokenSource.Token;
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
try {
|
try {
|
||||||
var serverExecutableInfo = await minecraftVersions.GetServerExecutableInfo(version, cancellationToken);
|
Logger.Information("Downloading server version {Version} from: {Url} ({Size})", minecraftVersion, fileDownloadInfo.DownloadUrl, fileDownloadInfo.Size.ToHumanReadable(decimalPlaces: 1));
|
||||||
if (serverExecutableInfo == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Information("Downloading server executable from: {Url} ({Size})", serverExecutableInfo.DownloadUrl, serverExecutableInfo.Size.ToHumanReadable(decimalPlaces: 1));
|
|
||||||
try {
|
try {
|
||||||
using var http = new HttpClient();
|
using var http = new HttpClient();
|
||||||
await FetchServerExecutableFile(http, new DownloadProgressCallback(this), serverExecutableInfo, tmpFilePath, cancellationToken);
|
await FetchServerExecutableFile(http, new DownloadProgressCallback(this), fileDownloadInfo, tmpFilePath, cancellationToken);
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
||||||
TryDeleteExecutableAfterFailure(tmpFilePath);
|
TryDeleteExecutableAfterFailure(tmpFilePath);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Move(tmpFilePath, filePath, true);
|
File.Move(tmpFilePath, filePath, true);
|
||||||
Logger.Information("Server version {Version} downloaded.", version);
|
Logger.Information("Server version {Version} downloaded.", minecraftVersion);
|
||||||
|
|
||||||
return filePath;
|
return filePath;
|
||||||
} catch (OperationCanceledException) {
|
} catch (OperationCanceledException) {
|
||||||
Logger.Information("Download for server version {Version} was cancelled.", version);
|
Logger.Information("Download for server version {Version} was cancelled.", minecraftVersion);
|
||||||
throw;
|
throw;
|
||||||
} catch (StopProcedureException) {
|
} catch (StopProcedureException) {
|
||||||
return null;
|
return null;
|
||||||
@ -110,17 +99,17 @@ sealed class MinecraftServerExecutableDownloader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task FetchServerExecutableFile(HttpClient http, DownloadProgressCallback progressCallback, MinecraftServerExecutableInfo info, string filePath, CancellationToken cancellationToken) {
|
private static async Task FetchServerExecutableFile(HttpClient http, DownloadProgressCallback progressCallback, FileDownloadInfo fileDownloadInfo, string filePath, CancellationToken cancellationToken) {
|
||||||
Sha1String downloadedFileHash;
|
Sha1String downloadedFileHash;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var response = await http.GetAsync(info.DownloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
var response = await http.GetAsync(fileDownloadInfo.DownloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
await using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
|
await using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
|
||||||
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
|
|
||||||
using var streamCopier = new MinecraftServerDownloadStreamCopier(progressCallback, info.Size.Bytes);
|
using var streamCopier = new MinecraftServerDownloadStreamCopier(progressCallback, fileDownloadInfo.Size.Bytes);
|
||||||
downloadedFileHash = await streamCopier.Copy(responseStream, fileStream, cancellationToken);
|
downloadedFileHash = await streamCopier.Copy(responseStream, fileStream, cancellationToken);
|
||||||
} catch (OperationCanceledException) {
|
} catch (OperationCanceledException) {
|
||||||
throw;
|
throw;
|
||||||
@ -129,8 +118,8 @@ sealed class MinecraftServerExecutableDownloader {
|
|||||||
throw StopProcedureException.Instance;
|
throw StopProcedureException.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!downloadedFileHash.Equals(info.Hash)) {
|
if (!downloadedFileHash.Equals(fileDownloadInfo.Hash)) {
|
||||||
Logger.Error("Downloaded server executable has mismatched SHA1 hash. Expected {Expected}, got {Actual}.", info.Hash, downloadedFileHash);
|
Logger.Error("Downloaded server executable has mismatched SHA1 hash. Expected {Expected}, got {Actual}.", fileDownloadInfo.Hash, downloadedFileHash);
|
||||||
throw StopProcedureException.Instance;
|
throw StopProcedureException.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,37 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Minecraft;
|
|
||||||
using Phantom.Utils.IO;
|
using Phantom.Utils.IO;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Minecraft.Server;
|
namespace Phantom.Agent.Minecraft.Server;
|
||||||
|
|
||||||
public sealed partial class MinecraftServerExecutables : IDisposable {
|
public sealed partial class MinecraftServerExecutables {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftServerExecutables>();
|
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftServerExecutables>();
|
||||||
|
|
||||||
[GeneratedRegex(@"[^a-zA-Z0-9_\-\.]", RegexOptions.Compiled)]
|
[GeneratedRegex(@"[^a-zA-Z0-9_\-\.]", RegexOptions.Compiled)]
|
||||||
private static partial Regex VersionFolderSanitizeRegex();
|
private static partial Regex VersionFolderSanitizeRegex();
|
||||||
|
|
||||||
private readonly string basePath;
|
private readonly string basePath;
|
||||||
private readonly MinecraftVersions minecraftVersions = new ();
|
|
||||||
private readonly Dictionary<string, MinecraftServerExecutableDownloader> runningDownloadersByVersion = new ();
|
private readonly Dictionary<string, MinecraftServerExecutableDownloader> runningDownloadersByVersion = new ();
|
||||||
|
|
||||||
public MinecraftServerExecutables(string basePath) {
|
public MinecraftServerExecutables(string basePath) {
|
||||||
this.basePath = basePath;
|
this.basePath = basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<string?> DownloadAndGetPath(string version, EventHandler<DownloadProgressEventArgs> progressEventHandler, CancellationToken cancellationToken) {
|
internal async Task<string?> DownloadAndGetPath(FileDownloadInfo? fileDownloadInfo, string minecraftVersion, EventHandler<DownloadProgressEventArgs> progressEventHandler, CancellationToken cancellationToken) {
|
||||||
string serverExecutableFolderPath = Path.Combine(basePath, VersionFolderSanitizeRegex().Replace(version, "_"));
|
string serverExecutableFolderPath = Path.Combine(basePath, VersionFolderSanitizeRegex().Replace(minecraftVersion, "_"));
|
||||||
string serverExecutableFilePath = Path.Combine(serverExecutableFolderPath, "server.jar");
|
string serverExecutableFilePath = Path.Combine(serverExecutableFolderPath, "server.jar");
|
||||||
|
|
||||||
if (File.Exists(serverExecutableFilePath)) {
|
if (File.Exists(serverExecutableFilePath)) {
|
||||||
return serverExecutableFilePath;
|
return serverExecutableFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fileDownloadInfo == null) {
|
||||||
|
Logger.Error("Unable to download server executable for version {Version} because no download info was provided.", minecraftVersion);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Directories.Create(serverExecutableFolderPath, Chmod.URWX_GRX);
|
Directories.Create(serverExecutableFolderPath, Chmod.URWX_GRX);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -39,26 +43,22 @@ public sealed partial class MinecraftServerExecutables : IDisposable {
|
|||||||
MinecraftServerExecutableDownloadListener listener = new (progressEventHandler, cancellationToken);
|
MinecraftServerExecutableDownloadListener listener = new (progressEventHandler, cancellationToken);
|
||||||
|
|
||||||
lock (this) {
|
lock (this) {
|
||||||
if (runningDownloadersByVersion.TryGetValue(version, out downloader)) {
|
if (runningDownloadersByVersion.TryGetValue(minecraftVersion, out downloader)) {
|
||||||
Logger.Information("A download for server version {Version} is already running, waiting for it to finish...", version);
|
Logger.Information("A download for server version {Version} is already running, waiting for it to finish...", minecraftVersion);
|
||||||
downloader.Register(listener);
|
downloader.Register(listener);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
downloader = new MinecraftServerExecutableDownloader(minecraftVersions, version, serverExecutableFilePath, listener);
|
downloader = new MinecraftServerExecutableDownloader(fileDownloadInfo, minecraftVersion, serverExecutableFilePath, listener);
|
||||||
downloader.Completed += (_, _) => {
|
downloader.Completed += (_, _) => {
|
||||||
lock (this) {
|
lock (this) {
|
||||||
runningDownloadersByVersion.Remove(version);
|
runningDownloadersByVersion.Remove(minecraftVersion);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
runningDownloadersByVersion[version] = downloader;
|
runningDownloadersByVersion[minecraftVersion] = downloader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await downloader.Task.WaitAsync(cancellationToken);
|
return await downloader.Task.WaitAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
minecraftVersions.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ sealed class InstanceSessionManager : IDisposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<ConfigureInstanceResult>> Configure(InstanceConfiguration configuration) {
|
public async Task<InstanceActionResult<ConfigureInstanceResult>> Configure(InstanceConfiguration configuration, InstanceLaunchProperties launchProperties) {
|
||||||
return await AcquireSemaphoreAndRun(async () => {
|
return await AcquireSemaphoreAndRun(async () => {
|
||||||
var instanceGuid = configuration.InstanceGuid;
|
var instanceGuid = configuration.InstanceGuid;
|
||||||
var instanceFolder = Path.Combine(basePath, instanceGuid.ToString());
|
var instanceFolder = Path.Combine(basePath, instanceGuid.ToString());
|
||||||
@ -90,7 +90,8 @@ sealed class InstanceSessionManager : IDisposable {
|
|||||||
configuration.JvmArguments,
|
configuration.JvmArguments,
|
||||||
instanceFolder,
|
instanceFolder,
|
||||||
configuration.MinecraftVersion,
|
configuration.MinecraftVersion,
|
||||||
new ServerProperties(configuration.ServerPort, configuration.RconPort)
|
new ServerProperties(configuration.ServerPort, configuration.RconPort),
|
||||||
|
launchProperties
|
||||||
);
|
);
|
||||||
|
|
||||||
BaseLauncher launcher = new VanillaLauncher(properties);
|
BaseLauncher launcher = new VanillaLauncher(properties);
|
||||||
@ -179,7 +180,6 @@ sealed class InstanceSessionManager : IDisposable {
|
|||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
DisposeAllInstances();
|
DisposeAllInstances();
|
||||||
minecraftServerExecutables.Dispose();
|
|
||||||
shutdownCancellationTokenSource.Dispose();
|
shutdownCancellationTokenSource.Dispose();
|
||||||
semaphore.Dispose();
|
semaphore.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Phantom.Agent.Rpc;
|
using Phantom.Agent.Rpc;
|
||||||
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages;
|
using Phantom.Common.Messages;
|
||||||
@ -26,12 +27,15 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
public async Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message) {
|
public async Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message) {
|
||||||
Logger.Information("Agent authentication successful.");
|
Logger.Information("Agent authentication successful.");
|
||||||
|
|
||||||
foreach (var instanceInfo in message.InitialInstances) {
|
void ShutdownAfterConfigurationFailed(InstanceConfiguration configuration) {
|
||||||
var result = await agent.InstanceSessionManager.Configure(instanceInfo);
|
Logger.Fatal("Unable to configure instance \"{Name}\" (GUID {Guid}), shutting down.", configuration.InstanceName, configuration.InstanceGuid);
|
||||||
|
shutdownTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var configureInstanceMessage in message.InitialInstanceConfigurations) {
|
||||||
|
var result = await HandleConfigureInstance(configureInstanceMessage);
|
||||||
if (!result.Is(ConfigureInstanceResult.Success)) {
|
if (!result.Is(ConfigureInstanceResult.Success)) {
|
||||||
Logger.Fatal("Unable to configure instance \"{Name}\" (GUID {Guid}), shutting down.", instanceInfo.InstanceName, instanceInfo.InstanceGuid);
|
ShutdownAfterConfigurationFailed(configureInstanceMessage.Configuration);
|
||||||
|
|
||||||
shutdownTokenSource.Cancel();
|
|
||||||
return NoReply.Instance;
|
return NoReply.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +60,7 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message) {
|
public async Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message) {
|
||||||
return await agent.InstanceSessionManager.Configure(message.Configuration);
|
return await agent.InstanceSessionManager.Configure(message.Configuration, message.LaunchProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
public async Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Minecraft;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Instance;
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial record InstanceLaunchProperties(
|
||||||
|
[property: MemoryPackOrder(0)] FileDownloadInfo? ServerDownloadInfo
|
||||||
|
);
|
30
Common/Phantom.Common.Data/Minecraft/FileDownloadInfo.cs
Normal file
30
Common/Phantom.Common.Data/Minecraft/FileDownloadInfo.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Utils.Cryptography;
|
||||||
|
using Phantom.Utils.IO;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Minecraft;
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial class FileDownloadInfo {
|
||||||
|
[MemoryPackOrder(0)]
|
||||||
|
public string DownloadUrl { get; }
|
||||||
|
|
||||||
|
[MemoryPackOrder(1)]
|
||||||
|
[MemoryPackInclude]
|
||||||
|
private readonly string hash;
|
||||||
|
|
||||||
|
[MemoryPackIgnore]
|
||||||
|
public Sha1String Hash => Sha1String.FromString(hash);
|
||||||
|
|
||||||
|
[MemoryPackOrder(2)]
|
||||||
|
public FileSize Size { get; }
|
||||||
|
|
||||||
|
public FileDownloadInfo(string downloadUrl, Sha1String hash, FileSize size) : this(downloadUrl, hash.ToString(), size) {}
|
||||||
|
|
||||||
|
[MemoryPackConstructor]
|
||||||
|
private FileDownloadInfo(string downloadUrl, string hash, FileSize size) {
|
||||||
|
this.DownloadUrl = downloadUrl;
|
||||||
|
this.hash = hash;
|
||||||
|
this.Size = size;
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,8 @@ namespace Phantom.Common.Messages.ToAgent;
|
|||||||
|
|
||||||
[MemoryPackable]
|
[MemoryPackable]
|
||||||
public sealed partial record ConfigureInstanceMessage(
|
public sealed partial record ConfigureInstanceMessage(
|
||||||
[property: MemoryPackOrder(0)] InstanceConfiguration Configuration
|
[property: MemoryPackOrder(0)] InstanceConfiguration Configuration,
|
||||||
|
[property: MemoryPackOrder(1)] InstanceLaunchProperties LaunchProperties
|
||||||
) : IMessageToAgent<InstanceActionResult<ConfigureInstanceResult>> {
|
) : IMessageToAgent<InstanceActionResult<ConfigureInstanceResult>> {
|
||||||
public Task<InstanceActionResult<ConfigureInstanceResult>> Accept(IMessageToAgentListener listener) {
|
public Task<InstanceActionResult<ConfigureInstanceResult>> Accept(IMessageToAgentListener listener) {
|
||||||
return listener.HandleConfigureInstance(this);
|
return listener.HandleConfigureInstance(this);
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Instance;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable]
|
[MemoryPackable]
|
||||||
public sealed partial record RegisterAgentSuccessMessage(
|
public sealed partial record RegisterAgentSuccessMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<InstanceConfiguration> InitialInstances
|
[property: MemoryPackOrder(0)] ImmutableArray<ConfigureInstanceMessage> InitialInstanceConfigurations
|
||||||
) : IMessageToAgent {
|
) : IMessageToAgent {
|
||||||
public Task<NoReply> Accept(IMessageToAgentListener listener) {
|
public Task<NoReply> Accept(IMessageToAgentListener listener) {
|
||||||
return listener.HandleRegisterAgentSuccess(this);
|
return listener.HandleRegisterAgentSuccess(this);
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
using Phantom.Utils.Cryptography;
|
|
||||||
using Phantom.Utils.IO;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Minecraft;
|
|
||||||
|
|
||||||
public sealed record MinecraftServerExecutableInfo(
|
|
||||||
string DownloadUrl,
|
|
||||||
Sha1String Hash,
|
|
||||||
FileSize Size
|
|
||||||
);
|
|
@ -6,11 +6,4 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.IO\Phantom.Utils.IO.csproj" />
|
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
|
|
||||||
<ProjectReference Include="..\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Database", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Database.Postgres", "Server\Phantom.Server.Database.Postgres\Phantom.Server.Database.Postgres.csproj", "{81625B4A-3DB6-48BD-A739-D23DA02107D1}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Database.Postgres", "Server\Phantom.Server.Database.Postgres\Phantom.Server.Database.Postgres.csproj", "{81625B4A-3DB6-48BD-A739-D23DA02107D1}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Minecraft", "Server\Phantom.Server.Minecraft\Phantom.Server.Minecraft.csproj", "{4B3B73E6-48DD-4846-87FD-DFB86619B67C}"
|
||||||
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Rpc", "Server\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj", "{79312D72-44E0-431D-96A4-4C0A066B9671}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Rpc", "Server\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj", "{79312D72-44E0-431D-96A4-4C0A066B9671}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Services", "Server\Phantom.Server.Services\Phantom.Server.Services.csproj", "{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Services", "Server\Phantom.Server.Services\Phantom.Server.Services.csproj", "{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}"
|
||||||
@ -118,6 +120,10 @@ Global
|
|||||||
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
{81625B4A-3DB6-48BD-A739-D23DA02107D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4B3B73E6-48DD-4846-87FD-DFB86619B67C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4B3B73E6-48DD-4846-87FD-DFB86619B67C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4B3B73E6-48DD-4846-87FD-DFB86619B67C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4B3B73E6-48DD-4846-87FD-DFB86619B67C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{79312D72-44E0-431D-96A4-4C0A066B9671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{79312D72-44E0-431D-96A4-4C0A066B9671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{79312D72-44E0-431D-96A4-4C0A066B9671}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{79312D72-44E0-431D-96A4-4C0A066B9671}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{79312D72-44E0-431D-96A4-4C0A066B9671}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{79312D72-44E0-431D-96A4-4C0A066B9671}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -184,6 +190,7 @@ Global
|
|||||||
{A0F1C595-96B6-4DBF-8C16-6B99223F8F35} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{A0F1C595-96B6-4DBF-8C16-6B99223F8F35} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
{E3AD566F-384A-489A-A3BB-EA3BA400C18C} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{E3AD566F-384A-489A-A3BB-EA3BA400C18C} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
{81625B4A-3DB6-48BD-A739-D23DA02107D1} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{81625B4A-3DB6-48BD-A739-D23DA02107D1} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
|
{4B3B73E6-48DD-4846-87FD-DFB86619B67C} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
{79312D72-44E0-431D-96A4-4C0A066B9671} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{79312D72-44E0-431D-96A4-4C0A066B9671} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
{7CA2E5FE-E507-4DC6-930C-E18711A9F856} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
{7CA2E5FE-E507-4DC6-930C-E18711A9F856} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
|
||||||
|
@ -9,7 +9,7 @@ using Phantom.Utils.IO;
|
|||||||
using Phantom.Utils.Runtime;
|
using Phantom.Utils.Runtime;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Common.Minecraft;
|
namespace Phantom.Server.Minecraft;
|
||||||
|
|
||||||
public sealed class MinecraftVersions : IDisposable {
|
public sealed class MinecraftVersions : IDisposable {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftVersions>();
|
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftVersions>();
|
||||||
@ -35,12 +35,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
return earlyResult;
|
return earlyResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
await cachedVersionsSemaphore.WaitAsync(cancellationToken);
|
||||||
await cachedVersionsSemaphore.WaitAsync(cancellationToken);
|
|
||||||
} catch (OperationCanceledException) {
|
|
||||||
return ImmutableArray<MinecraftVersion>.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (CachedVersionsUnlessExpired is {} racedResult) {
|
if (CachedVersionsUnlessExpired is {} racedResult) {
|
||||||
return racedResult;
|
return racedResult;
|
||||||
@ -64,7 +59,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MinecraftServerExecutableInfo?> GetServerExecutableInfo(string version, CancellationToken cancellationToken) {
|
public async Task<FileDownloadInfo?> GetServerExecutableInfo(string version, CancellationToken cancellationToken) {
|
||||||
return await FetchOrFailSilently(async () => {
|
return await FetchOrFailSilently(async () => {
|
||||||
var versions = await GetVersions(cancellationToken);
|
var versions = await GetVersions(cancellationToken);
|
||||||
var versionObject = versions.FirstOrDefault(v => v.Id == version);
|
var versionObject = versions.FirstOrDefault(v => v.Id == version);
|
||||||
@ -81,8 +76,6 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
private static async Task<T?> FetchOrFailSilently<T>(Func<Task<T?>> task) {
|
private static async Task<T?> FetchOrFailSilently<T>(Func<Task<T?>> task) {
|
||||||
try {
|
try {
|
||||||
return await task();
|
return await task();
|
||||||
} catch (OperationCanceledException) {
|
|
||||||
return default;
|
|
||||||
} catch (StopProcedureException) {
|
} catch (StopProcedureException) {
|
||||||
return default;
|
return default;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -96,9 +89,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return await http.GetFromJsonAsync<JsonElement>(url, cancellationToken);
|
return await http.GetFromJsonAsync<JsonElement>(url, cancellationToken);
|
||||||
} catch (OperationCanceledException) {
|
} catch (HttpRequestException e) {
|
||||||
throw StopProcedureException.Instance;
|
|
||||||
} catch (HttpRequestException e) {
|
|
||||||
Logger.Error(e, "Unable to download {Description}.", description);
|
Logger.Error(e, "Unable to download {Description}.", description);
|
||||||
throw StopProcedureException.Instance;
|
throw StopProcedureException.Instance;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -117,7 +108,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
} catch (StopProcedureException) {}
|
} catch (StopProcedureException) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundVersions.ToImmutable();
|
return foundVersions.MoveToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MinecraftVersion GetVersionFromManifestEntry(JsonElement versionElement) {
|
private static MinecraftVersion GetVersionFromManifestEntry(JsonElement versionElement) {
|
||||||
@ -148,7 +139,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
return new MinecraftVersion(id, type, url);
|
return new MinecraftVersion(id, type, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MinecraftServerExecutableInfo GetServerExecutableInfoFromMetadata(JsonElement versionMetadata) {
|
private static FileDownloadInfo GetServerExecutableInfoFromMetadata(JsonElement versionMetadata) {
|
||||||
JsonElement downloadsElement = GetJsonPropertyOrThrow(versionMetadata, "downloads", JsonValueKind.Object, "version metadata");
|
JsonElement downloadsElement = GetJsonPropertyOrThrow(versionMetadata, "downloads", JsonValueKind.Object, "version metadata");
|
||||||
JsonElement serverElement = GetJsonPropertyOrThrow(downloadsElement, "server", JsonValueKind.Object, "downloads object in version metadata");
|
JsonElement serverElement = GetJsonPropertyOrThrow(downloadsElement, "server", JsonValueKind.Object, "downloads object in version metadata");
|
||||||
JsonElement urlElement = GetJsonPropertyOrThrow(serverElement, "url", JsonValueKind.String, "downloads.server object in version metadata");
|
JsonElement urlElement = GetJsonPropertyOrThrow(serverElement, "url", JsonValueKind.String, "downloads.server object in version metadata");
|
||||||
@ -182,7 +173,7 @@ public sealed class MinecraftVersions : IDisposable {
|
|||||||
throw StopProcedureException.Instance;
|
throw StopProcedureException.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MinecraftServerExecutableInfo(url, hash, new FileSize(size));
|
return new FileDownloadInfo(url, hash, new FileSize(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonElement GetJsonPropertyOrThrow(JsonElement parentElement, string propertyKey, JsonValueKind expectedKind, string location) {
|
private static JsonElement GetJsonPropertyOrThrow(JsonElement parentElement, string propertyKey, JsonValueKind expectedKind, string location) {
|
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Common\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -82,7 +82,9 @@ public sealed class AgentManager {
|
|||||||
|
|
||||||
Logger.Information("Registered agent \"{Name}\" (GUID {Guid}).", agent.Name, agent.Guid);
|
Logger.Information("Registered agent \"{Name}\" (GUID {Guid}).", agent.Name, agent.Guid);
|
||||||
|
|
||||||
await connection.Send(new RegisterAgentSuccessMessage(instanceManager.GetInstanceConfigurationsForAgent(agent.Guid)));
|
var instanceConfigurations = await instanceManager.GetInstanceConfigurationsForAgent(agent.Guid);
|
||||||
|
await connection.Send(new RegisterAgentSuccessMessage(instanceConfigurations));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,17 +5,19 @@ public enum AddOrEditInstanceResult : byte {
|
|||||||
Success,
|
Success,
|
||||||
InstanceNameMustNotBeEmpty,
|
InstanceNameMustNotBeEmpty,
|
||||||
InstanceMemoryMustNotBeZero,
|
InstanceMemoryMustNotBeZero,
|
||||||
|
MinecraftVersionDownloadInfoNotFound,
|
||||||
AgentNotFound
|
AgentNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AddOrEditInstanceResultExtensions {
|
public static class AddOrEditInstanceResultExtensions {
|
||||||
public static string ToSentence(this AddOrEditInstanceResult reason) {
|
public static string ToSentence(this AddOrEditInstanceResult reason) {
|
||||||
return reason switch {
|
return reason switch {
|
||||||
AddOrEditInstanceResult.Success => "Success.",
|
AddOrEditInstanceResult.Success => "Success.",
|
||||||
AddOrEditInstanceResult.InstanceNameMustNotBeEmpty => "Instance name must not be empty.",
|
AddOrEditInstanceResult.InstanceNameMustNotBeEmpty => "Instance name must not be empty.",
|
||||||
AddOrEditInstanceResult.InstanceMemoryMustNotBeZero => "Memory must not be 0 MB.",
|
AddOrEditInstanceResult.InstanceMemoryMustNotBeZero => "Memory must not be 0 MB.",
|
||||||
AddOrEditInstanceResult.AgentNotFound => "Agent not found.",
|
AddOrEditInstanceResult.MinecraftVersionDownloadInfoNotFound => "Could not find download information for the selected Minecraft version.",
|
||||||
_ => "Unknown error."
|
AddOrEditInstanceResult.AgentNotFound => "Agent not found.",
|
||||||
|
_ => "Unknown error."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using Phantom.Common.Messages.ToAgent;
|
|||||||
using Phantom.Common.Minecraft;
|
using Phantom.Common.Minecraft;
|
||||||
using Phantom.Server.Database;
|
using Phantom.Server.Database;
|
||||||
using Phantom.Server.Database.Entities;
|
using Phantom.Server.Database.Entities;
|
||||||
|
using Phantom.Server.Minecraft;
|
||||||
using Phantom.Server.Services.Agents;
|
using Phantom.Server.Services.Agents;
|
||||||
using Phantom.Utils.Collections;
|
using Phantom.Utils.Collections;
|
||||||
using Phantom.Utils.Events;
|
using Phantom.Utils.Events;
|
||||||
@ -26,12 +27,14 @@ public sealed class InstanceManager {
|
|||||||
|
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
private readonly AgentManager agentManager;
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly MinecraftVersions minecraftVersions;
|
||||||
private readonly DatabaseProvider databaseProvider;
|
private readonly DatabaseProvider databaseProvider;
|
||||||
private readonly SemaphoreSlim modifyInstancesSemaphore = new (1, 1);
|
private readonly SemaphoreSlim modifyInstancesSemaphore = new (1, 1);
|
||||||
|
|
||||||
public InstanceManager(ServiceConfiguration configuration, AgentManager agentManager, DatabaseProvider databaseProvider) {
|
public InstanceManager(ServiceConfiguration configuration, AgentManager agentManager, MinecraftVersions minecraftVersions, DatabaseProvider databaseProvider) {
|
||||||
this.cancellationToken = configuration.CancellationToken;
|
this.cancellationToken = configuration.CancellationToken;
|
||||||
this.agentManager = agentManager;
|
this.agentManager = agentManager;
|
||||||
|
this.minecraftVersions = minecraftVersions;
|
||||||
this.databaseProvider = databaseProvider;
|
this.databaseProvider = databaseProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +76,11 @@ public sealed class InstanceManager {
|
|||||||
return InstanceActionResult.Concrete(AddOrEditInstanceResult.InstanceMemoryMustNotBeZero);
|
return InstanceActionResult.Concrete(AddOrEditInstanceResult.InstanceMemoryMustNotBeZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serverExecutableInfo = await minecraftVersions.GetServerExecutableInfo(configuration.MinecraftVersion, cancellationToken);
|
||||||
|
if (serverExecutableInfo == null) {
|
||||||
|
return InstanceActionResult.Concrete(AddOrEditInstanceResult.MinecraftVersionDownloadInfoNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
InstanceActionResult<AddOrEditInstanceResult> result;
|
InstanceActionResult<AddOrEditInstanceResult> result;
|
||||||
bool isNewInstance;
|
bool isNewInstance;
|
||||||
|
|
||||||
@ -80,8 +88,9 @@ public sealed class InstanceManager {
|
|||||||
try {
|
try {
|
||||||
var instance = new Instance(configuration);
|
var instance = new Instance(configuration);
|
||||||
instances.ByGuid.AddOrReplace(instance.Configuration.InstanceGuid, instance, out var oldInstance);
|
instances.ByGuid.AddOrReplace(instance.Configuration.InstanceGuid, instance, out var oldInstance);
|
||||||
|
|
||||||
var reply = await agentManager.SendMessage<ConfigureInstanceMessage, InstanceActionResult<ConfigureInstanceResult>>(configuration.AgentGuid, new ConfigureInstanceMessage(configuration), TimeSpan.FromSeconds(10));
|
var message = new ConfigureInstanceMessage(configuration, new InstanceLaunchProperties(serverExecutableInfo));
|
||||||
|
var reply = await agentManager.SendMessage<ConfigureInstanceMessage, InstanceActionResult<ConfigureInstanceResult>>(configuration.AgentGuid, message, TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
result = reply.DidNotReplyIfNull().Map(static result => result switch {
|
result = reply.DidNotReplyIfNull().Map(static result => result switch {
|
||||||
ConfigureInstanceResult.Success => AddOrEditInstanceResult.Success,
|
ConfigureInstanceResult.Success => AddOrEditInstanceResult.Success,
|
||||||
@ -199,8 +208,15 @@ public sealed class InstanceManager {
|
|||||||
return await SendInstanceActionMessage<SendCommandToInstanceMessage, SendCommandToInstanceResult>(instanceGuid, new SendCommandToInstanceMessage(instanceGuid, command));
|
return await SendInstanceActionMessage<SendCommandToInstanceMessage, SendCommandToInstanceResult>(instanceGuid, new SendCommandToInstanceMessage(instanceGuid, command));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ImmutableArray<InstanceConfiguration> GetInstanceConfigurationsForAgent(Guid agentGuid) {
|
internal async Task<ImmutableArray<ConfigureInstanceMessage>> GetInstanceConfigurationsForAgent(Guid agentGuid) {
|
||||||
return instances.ByGuid.ValuesCopy.Select(static instance => instance.Configuration).Where(configuration => configuration.AgentGuid == agentGuid).ToImmutableArray();
|
var configurationMessages = ImmutableArray.CreateBuilder<ConfigureInstanceMessage>();
|
||||||
|
|
||||||
|
foreach (var configuration in instances.ByGuid.ValuesCopy.Select(static instance => instance.Configuration).Where(configuration => configuration.AgentGuid == agentGuid)) {
|
||||||
|
var serverExecutableInfo = await minecraftVersions.GetServerExecutableInfo(configuration.MinecraftVersion, cancellationToken);
|
||||||
|
configurationMessages.Add(new ConfigureInstanceMessage(configuration, new InstanceLaunchProperties(serverExecutableInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return configurationMessages.ToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ObservableInstances : ObservableState<ImmutableDictionary<Guid, Instance>> {
|
private sealed class ObservableInstances : ObservableState<ImmutableDictionary<Guid, Instance>> {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Collections\Phantom.Utils.Collections.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Collections\Phantom.Utils.Collections.csproj" />
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Events\Phantom.Utils.Events.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Events\Phantom.Utils.Events.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Database\Phantom.Server.Database.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Database\Phantom.Server.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\Phantom.Server.Minecraft\Phantom.Server.Minecraft.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@using Phantom.Common.Data.Minecraft
|
@using Phantom.Common.Data.Minecraft
|
||||||
@using Phantom.Common.Minecraft
|
@using Phantom.Common.Minecraft
|
||||||
|
@using Phantom.Server.Minecraft
|
||||||
@using Phantom.Server.Services.Agents
|
@using Phantom.Server.Services.Agents
|
||||||
@using Phantom.Server.Services.Audit
|
@using Phantom.Server.Services.Audit
|
||||||
@using Phantom.Server.Services.Instances
|
@using Phantom.Server.Services.Instances
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
<ProjectReference Include="..\..\Common\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Minecraft\Phantom.Common.Minecraft.csproj" />
|
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.IO\Phantom.Utils.IO.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.IO\Phantom.Utils.IO.csproj" />
|
||||||
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Database.Postgres\Phantom.Server.Database.Postgres.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Database.Postgres\Phantom.Server.Database.Postgres.csproj" />
|
||||||
|
<ProjectReference Include="..\Phantom.Server.Minecraft\Phantom.Server.Minecraft.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Services\Phantom.Server.Services.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Services\Phantom.Server.Services.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Server.Web\Phantom.Server.Web.csproj" />
|
<ProjectReference Include="..\Phantom.Server.Web\Phantom.Server.Web.csproj" />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Common.Minecraft;
|
using Phantom.Server.Minecraft;
|
||||||
using Phantom.Server.Services;
|
using Phantom.Server.Services;
|
||||||
using Phantom.Server.Services.Agents;
|
using Phantom.Server.Services.Agents;
|
||||||
using Phantom.Server.Services.Audit;
|
using Phantom.Server.Services.Audit;
|
||||||
|
Loading…
Reference in New Issue
Block a user