Compare commits

...

3 Commits

Author SHA1 Message Date
chylex 651a660cfc
WIP 2024-04-01 18:37:08 +02:00
chylex 5e6cb91b64
WIP 2024-04-01 18:20:33 +02:00
chylex 80d6bcbcac
Fix backups not working with Forge log format & add timeouts while waiting for server output 2024-04-01 18:13:15 +02:00
8 changed files with 69 additions and 20 deletions

View File

@ -1,4 +1,5 @@
using System.Text;
using System.Collections.ObjectModel;
using System.Text;
using Phantom.Agent.Minecraft.Instance;
using Phantom.Agent.Minecraft.Java;
using Phantom.Agent.Minecraft.Server;
@ -11,6 +12,7 @@ public abstract class BaseLauncher : IServerLauncher {
private readonly InstanceProperties instanceProperties;
protected string MinecraftVersion => instanceProperties.ServerVersion;
protected string InstanceFolder => instanceProperties.InstanceFolder;
private protected BaseLauncher(InstanceProperties instanceProperties) {
this.instanceProperties = instanceProperties;
@ -51,16 +53,14 @@ public abstract class BaseLauncher : IServerLauncher {
var processConfigurator = new ProcessConfigurator {
FileName = javaRuntimeExecutable.ExecutablePath,
WorkingDirectory = instanceProperties.InstanceFolder,
WorkingDirectory = InstanceFolder,
RedirectInput = true,
UseShellExecute = false
};
var processArguments = processConfigurator.ArgumentList;
PrepareJvmArguments(serverJar).Build(processArguments);
processArguments.Add("-jar");
processArguments.Add(serverJar.FilePath);
processArguments.Add("nogui");
PrepareJavaProcessArguments(processArguments, serverJar.FilePath);
var process = processConfigurator.CreateProcess();
var instanceProcess = new InstanceProcess(instanceProperties, process);
@ -99,6 +99,12 @@ public abstract class BaseLauncher : IServerLauncher {
private protected virtual void CustomizeJvmArguments(JvmArgumentBuilder arguments) {}
protected virtual void PrepareJavaProcessArguments(Collection<string> processArguments, string serverJarFilePath) {
processArguments.Add("-jar");
processArguments.Add(serverJarFilePath);
processArguments.Add("nogui");
}
private protected virtual Task<ServerJarInfo> PrepareServerJar(ILogger logger, string serverJarPath, CancellationToken cancellationToken) {
return Task.FromResult(new ServerJarInfo(serverJarPath));
}

View File

@ -0,0 +1,29 @@
using System.Collections.ObjectModel;
using Phantom.Agent.Minecraft.Instance;
using Phantom.Agent.Minecraft.Java;
using Serilog;
namespace Phantom.Agent.Minecraft.Launcher.Types;
public sealed class ForgeLauncher : BaseLauncher {
public ForgeLauncher(InstanceProperties instanceProperties) : base(instanceProperties) {}
private protected override void CustomizeJvmArguments(JvmArgumentBuilder arguments) {
arguments.AddProperty("terminal.ansi", "true"); // TODO
}
protected override void PrepareJavaProcessArguments(Collection<string> processArguments, string serverJarFilePath) {
if (OperatingSystem.IsWindows()) {
processArguments.Add("@libraries/net/minecraftforge/forge/1.20.1-47.2.0/win_args.txt");
}
else {
processArguments.Add("@libraries/net/minecraftforge/forge/1.20.1-47.2.0/unix_args.txt");
}
processArguments.Add("nogui");
}
private protected override Task<ServerJarInfo> PrepareServerJar(ILogger logger, string serverJarPath, CancellationToken cancellationToken) {
return Task.FromResult(new ServerJarInfo(Path.Combine(InstanceFolder, "run.sh")));
}
}

View File

@ -38,7 +38,11 @@ sealed class MinecraftServerExecutableDownloader {
if (--listeners <= 0) {
Logger.Debug("Unregistered last download listener, cancelling download.");
cancellationTokenSource.Cancel();
try {
cancellationTokenSource.Cancel();
} catch (ObjectDisposedException) {
// TODO THIS IS BAD
}
}
else {
Logger.Debug("Unregistered download listener, current listener count: {Listeners}", listeners);

View File

@ -67,6 +67,10 @@ sealed class BackupManager : IDisposable {
resultBuilder.Kind = BackupCreationResultKind.BackupCancelled;
logger.Warning("Backup creation was cancelled.");
return null;
} catch (TimeoutException) {
resultBuilder.Kind = BackupCreationResultKind.BackupTimedOut;
logger.Warning("Backup creation timed out.");
return null;
} catch (Exception e) {
resultBuilder.Kind = BackupCreationResultKind.UnknownError;
logger.Error(e, "Caught exception while creating an instance backup.");
@ -76,6 +80,8 @@ sealed class BackupManager : IDisposable {
await dispatcher.EnableAutomaticSaving();
} catch (OperationCanceledException) {
// Ignore.
} catch (TimeoutException) {
logger.Warning("Timed out waiting for automatic saving to be re-enabled.");
} catch (Exception e) {
resultBuilder.Warnings |= BackupCreationWarnings.CouldNotRestoreAutomaticSaving;
logger.Error(e, "Caught exception while enabling automatic saving after creating an instance backup.");
@ -120,6 +126,7 @@ sealed class BackupManager : IDisposable {
BackupCreationResultKind.Success => "Backup created successfully.",
BackupCreationResultKind.InstanceNotRunning => "Instance is not running.",
BackupCreationResultKind.BackupCancelled => "Backup cancelled.",
BackupCreationResultKind.BackupTimedOut => "Backup timed out.",
BackupCreationResultKind.BackupAlreadyRunning => "A backup is already being created.",
BackupCreationResultKind.BackupFileAlreadyExists => "Backup with the same name already exists.",
BackupCreationResultKind.CouldNotCreateBackupFolder => "Could not create backup folder.",

View File

@ -7,7 +7,7 @@ using Serilog;
namespace Phantom.Agent.Services.Backups;
sealed partial class BackupServerCommandDispatcher : IDisposable {
[GeneratedRegex(@"^\[(?:.*?)\] \[Server thread/INFO\]: (.*?)$", RegexOptions.NonBacktracking)]
[GeneratedRegex(@"^\[(?:.*?)\] \[Server thread/INFO\](?:.*?): (.*?)$", RegexOptions.NonBacktracking)]
private static partial Regex ServerThreadInfoRegex();
private readonly ILogger logger;
@ -32,18 +32,18 @@ sealed partial class BackupServerCommandDispatcher : IDisposable {
public async Task DisableAutomaticSaving() {
await process.SendCommand(MinecraftCommand.SaveOff, cancellationToken);
await automaticSavingDisabled.Task.WaitAsync(cancellationToken);
await automaticSavingDisabled.Task.WaitAsync(TimeSpan.FromSeconds(30), cancellationToken);
}
public async Task SaveAllChunks() {
// TODO Try if not flushing and waiting a few seconds before flushing reduces lag.
await process.SendCommand(MinecraftCommand.SaveAll(flush: true), cancellationToken);
await savedTheGame.Task.WaitAsync(cancellationToken);
await savedTheGame.Task.WaitAsync(TimeSpan.FromMinutes(1), cancellationToken);
}
public async Task EnableAutomaticSaving() {
await process.SendCommand(MinecraftCommand.SaveOn, cancellationToken);
await automaticSavingEnabled.Task.WaitAsync(cancellationToken);
await automaticSavingEnabled.Task.WaitAsync(TimeSpan.FromMinutes(1), cancellationToken);
}
private void OnOutput(object? sender, string? line) {

View File

@ -102,6 +102,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
IServerLauncher launcher = configuration.MinecraftServerKind switch {
MinecraftServerKind.Vanilla => new VanillaLauncher(properties),
MinecraftServerKind.Fabric => new FabricLauncher(properties),
MinecraftServerKind.Forge => new ForgeLauncher(properties),
_ => InvalidLauncher.Instance
};

View File

@ -1,15 +1,16 @@
namespace Phantom.Common.Data.Backups;
public enum BackupCreationResultKind : byte {
UnknownError,
Success,
InstanceNotRunning,
BackupCancelled,
BackupAlreadyRunning,
BackupFileAlreadyExists,
CouldNotCreateBackupFolder,
CouldNotCopyWorldToTemporaryFolder,
CouldNotCreateWorldArchive
UnknownError = 0,
Success = 1,
InstanceNotRunning = 2,
BackupTimedOut = 3,
BackupCancelled = 4,
BackupAlreadyRunning = 5,
BackupFileAlreadyExists = 6,
CouldNotCreateBackupFolder = 7,
CouldNotCopyWorldToTemporaryFolder = 8,
CouldNotCreateWorldArchive = 9
}
public static class BackupCreationResultSummaryExtensions {

View File

@ -2,5 +2,6 @@
public enum MinecraftServerKind : ushort {
Vanilla = 1,
Fabric = 2
Fabric = 2,
Forge = 3
}