mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-23 04:57:17 +01:00
.env files for engine installations
This commit is contained in:
@@ -852,6 +852,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in <see langword="true"/> if compilation succeeded, <see langword="false"/> otherwise.</returns>
|
||||
async ValueTask<bool> RunDreamMaker(IEngineExecutableLock engineLock, Models.CompileJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
var environment = await engineLock.LoadEnv(logger, true, cancellationToken);
|
||||
var arguments = engineLock.FormatCompilerArguments($"{job.DmeName}.{DmeExtension}");
|
||||
|
||||
await using var dm = processExecutor.LaunchProcess(
|
||||
@@ -859,6 +860,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
ioManager.ResolvePath(
|
||||
job.DirectoryName!.Value.ToString()),
|
||||
arguments,
|
||||
environment,
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Engine
|
||||
{
|
||||
@@ -75,6 +76,7 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByondInstallation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="installationIOManager">The <see cref="IIOManager"/> for the <see cref="EngineInstallationBase"/>.</param>
|
||||
/// <param name="installationTask">The value of <see cref="InstallationTask"/>.</param>
|
||||
/// <param name="version">The value of <see cref="Version"/>.</param>
|
||||
/// <param name="dreamDaemonPath">The value of <see cref="ServerExePath"/>.</param>
|
||||
@@ -82,12 +84,14 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <param name="supportsCli">If a CLI application is being used.</param>
|
||||
/// <param name="supportsMapThreads">The value of <see cref="supportsMapThreads"/>.</param>
|
||||
public ByondInstallation(
|
||||
IIOManager installationIOManager,
|
||||
Task installationTask,
|
||||
EngineVersion version,
|
||||
string dreamDaemonPath,
|
||||
string dreamMakerPath,
|
||||
bool supportsCli,
|
||||
bool supportsMapThreads)
|
||||
: base(installationIOManager)
|
||||
{
|
||||
InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
@@ -88,21 +88,22 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
{
|
||||
CheckVersionValidity(version);
|
||||
|
||||
var binPathForVersion = IOManager.ConcatPath(path, ByondBinPath);
|
||||
var installationIOManager = new ResolvingIOManager(IOManager, path);
|
||||
var supportsMapThreads = version.Version >= MapThreadsVersion;
|
||||
|
||||
return new ByondInstallation(
|
||||
installationIOManager,
|
||||
installationTask,
|
||||
version,
|
||||
IOManager.ResolvePath(
|
||||
IOManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
installationIOManager.ResolvePath(
|
||||
installationIOManager.ConcatPath(
|
||||
ByondBinPath,
|
||||
GetDreamDaemonName(
|
||||
version.Version!,
|
||||
out var supportsCli))),
|
||||
IOManager.ResolvePath(
|
||||
IOManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
installationIOManager.ResolvePath(
|
||||
installationIOManager.ConcatPath(
|
||||
ByondBinPath,
|
||||
DreamMakerName)),
|
||||
supportsCli,
|
||||
supportsMapThreads);
|
||||
|
||||
@@ -62,5 +62,9 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
accessIdentifier,
|
||||
port,
|
||||
cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<Dictionary<string, string>?> LoadEnv(ILogger logger, bool forCompiler, CancellationToken cancellationToken)
|
||||
=> Instance.LoadEnv(logger, forCompiler, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
using DotEnv.Core;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Engine
|
||||
@@ -38,6 +42,11 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <inheritdoc />
|
||||
public abstract Task InstallationTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> pointing to the installation directory.
|
||||
/// </summary>
|
||||
protected IIOManager InstallationIOManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Encode given parameters for passing as world.params on the command line.
|
||||
/// </summary>
|
||||
@@ -56,6 +65,15 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
return parametersString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EngineInstallationBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="installationIOManager">The value of <see cref="InstallationIOManager"/>.</param>
|
||||
public EngineInstallationBase(IIOManager installationIOManager)
|
||||
{
|
||||
InstallationIOManager = installationIOManager ?? throw new ArgumentNullException(nameof(installationIOManager));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string FormatCompilerArguments(string dmePath);
|
||||
|
||||
@@ -69,10 +87,45 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <inheritdoc />
|
||||
public virtual async ValueTask StopServerProcess(ILogger logger, IProcess process, string accessIdentifier, ushort port, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
logger.LogTrace("Terminating engine server process...");
|
||||
process.Terminate();
|
||||
await process.Lifetime;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<Dictionary<string, string>?> LoadEnv(ILogger logger, bool forCompiler, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
var envFile = forCompiler
|
||||
? "compiler.env"
|
||||
: "server.env";
|
||||
|
||||
if (!await InstallationIOManager.FileExists(envFile, cancellationToken))
|
||||
{
|
||||
logger.LogTrace("No {envFile} present in engine installation {version}", envFile, Version);
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.LogDebug("Loading {envFile} for engine installation {version}...", envFile, Version);
|
||||
|
||||
var fileBytes = await InstallationIOManager.ReadAllBytes(envFile, cancellationToken);
|
||||
var fileContents = Encoding.UTF8.GetString(fileBytes);
|
||||
var parser = new EnvParser();
|
||||
|
||||
try
|
||||
{
|
||||
var variables = parser.Parse(fileContents);
|
||||
|
||||
return variables.ToDictionary();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Unable to parse {envFile}!", envFile);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,5 +82,14 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask StopServerProcess(ILogger logger, IProcess process, string accessIdentifier, ushort port, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the environment settings for either the server or compiler.
|
||||
/// </summary>
|
||||
/// <param name="logger">The <see cref="ILogger"/> to write to.</param>
|
||||
/// <param name="forCompiler">If <see langword="false"/> server.env will be loaded. If <see langword="true"/> compiler.env will be loaded.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the environment <see cref="Dictionary{TKey, TValue}"/> or <see langword="null"/> if the target environment file doesn't exist.</returns>
|
||||
ValueTask<Dictionary<string, string>?> LoadEnv(ILogger logger, bool forCompiler, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,6 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <inheritdoc />
|
||||
public override Task InstallationTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="OpenDreamInstallation"/>.
|
||||
/// </summary>
|
||||
readonly IIOManager ioManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAsyncDelayer"/> for the <see cref="OpenDreamInstallation"/>.
|
||||
/// </summary>
|
||||
@@ -65,7 +60,7 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenDreamInstallation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="installationIOManager">The <see cref="IIOManager"/> for the <see cref="EngineInstallationBase"/>.</param>
|
||||
/// <param name="asyncDelayer">The value of <see cref="asyncDelayer"/>.</param>
|
||||
/// <param name="httpClientFactory">The value of <see cref="httpClientFactory"/>.</param>
|
||||
/// <param name="serverExePath">The value of <see cref="ServerExePath"/>.</param>
|
||||
@@ -73,15 +68,15 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
/// <param name="installationTask">The value of <see cref="InstallationTask"/>.</param>
|
||||
/// <param name="version">The value of <see cref="Version"/>.</param>
|
||||
public OpenDreamInstallation(
|
||||
IIOManager ioManager,
|
||||
IIOManager installationIOManager,
|
||||
IAsyncDelayer asyncDelayer,
|
||||
IAbstractHttpClientFactory httpClientFactory,
|
||||
string serverExePath,
|
||||
string compilerExePath,
|
||||
Task installationTask,
|
||||
EngineVersion version)
|
||||
: base(installationIOManager)
|
||||
{
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
|
||||
this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||
ServerExePath = serverExePath ?? throw new ArgumentNullException(nameof(serverExePath));
|
||||
@@ -109,7 +104,7 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
|
||||
var parametersString = EncodeParameters(parameters, launchParameters);
|
||||
|
||||
var arguments = $"--cvar {(logFilePath != null ? $"log.path=\"{ioManager.GetDirectoryName(logFilePath)}\" --cvar log.format=\"{ioManager.GetFileName(logFilePath)}\"" : "log.enabled=false")} --cvar watchdog.token={accessIdentifier} --cvar log.runtimelog=false --cvar net.port={launchParameters.Port!.Value} --cvar opendream.topic_port=0 --cvar opendream.world_params=\"{parametersString}\" --cvar opendream.json_path=\"./{dmbProvider.DmbName}\"";
|
||||
var arguments = $"--cvar {(logFilePath != null ? $"log.path=\"{InstallationIOManager.GetDirectoryName(logFilePath)}\" --cvar log.format=\"{InstallationIOManager.GetFileName(logFilePath)}\"" : "log.enabled=false")} --cvar watchdog.token={accessIdentifier} --cvar log.runtimelog=false --cvar net.port={launchParameters.Port!.Value} --cvar opendream.topic_port=0 --cvar opendream.world_params=\"{parametersString}\" --cvar opendream.json_path=\"./{dmbProvider.DmbName}\"";
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@@ -125,6 +120,8 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
ushort port,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
const int MaximumTerminationSeconds = 5;
|
||||
|
||||
logger.LogTrace("Attempting Robust.Server graceful exit (Timeout: {seconds}s)...", MaximumTerminationSeconds);
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Tgstation.Server.Host.Components.Engine
|
||||
CheckVersionValidity(version);
|
||||
GetExecutablePaths(path, out var serverExePath, out var compilerExePath);
|
||||
return new OpenDreamInstallation(
|
||||
IOManager,
|
||||
new ResolvingIOManager(IOManager, path),
|
||||
asyncDelayer,
|
||||
httpClientFactory,
|
||||
serverExePath,
|
||||
|
||||
@@ -490,6 +490,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// important to run on all ports to allow port changing
|
||||
var environment = await engineLock.LoadEnv(logger, false, cancellationToken);
|
||||
var arguments = engineLock.FormatServerArguments(
|
||||
dmbProvider,
|
||||
new Dictionary<string, string>
|
||||
@@ -507,7 +508,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
engineLock.ServerExePath,
|
||||
dmbProvider.Directory,
|
||||
arguments,
|
||||
null,
|
||||
environment,
|
||||
logFilePath,
|
||||
engineLock.HasStandardOutput,
|
||||
true);
|
||||
|
||||
@@ -69,6 +69,8 @@
|
||||
<PackageReference Include="Cyberboss.AspNetCore.AsyncInitializer" Version="1.2.0" />
|
||||
<!-- Usage: IRC interop -->
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.7" />
|
||||
<!-- Usage: .env file parsing -->
|
||||
<PackageReference Include="DotEnv.Core" Version="3.0.0" />
|
||||
<!-- Usage: Text formatter for Elasticsearch logging plugin -->
|
||||
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.6.1" />
|
||||
<!-- Usage: GitLab interop -->
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
cd /D "%~dp0"
|
||||
cd ../../Byond/%1
|
||||
echo # Comment > server.env
|
||||
echo NOTA=Real Comment>> server.env
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "../../Byond/$1"
|
||||
|
||||
echo -e '# This is a comment\nNOTA=Real Comment\n\n\n' > server.env
|
||||
@@ -118,18 +118,24 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
await using var memoryStream2 = new MemoryStream(Encoding.UTF8.GetBytes("bbb"));
|
||||
await configurationClient.Write(staticFile2, memoryStream2, cancellationToken);
|
||||
|
||||
var shellScriptExtension = new PlatformIdentifier().IsWindows ? ".bat" : ".sh";
|
||||
var scriptName = $"PreCompile-GenerateRandomResource{shellScriptExtension}";
|
||||
var resourcingScript = new ConfigurationFileRequest
|
||||
async ValueTask UploadScript(string scriptId)
|
||||
{
|
||||
Path = $"/EventScripts/{scriptName}"
|
||||
};
|
||||
var shellScriptExtension = new PlatformIdentifier().IsWindows ? ".bat" : ".sh";
|
||||
var scriptName = $"{scriptId}{shellScriptExtension}";
|
||||
var resourcingScript = new ConfigurationFileRequest
|
||||
{
|
||||
Path = $"/EventScripts/{scriptName}"
|
||||
};
|
||||
|
||||
await using var readStream = ioManager.GetFileStream($"../../../../DMAPI/LongRunning/{scriptName}", false);
|
||||
await configurationClient.Write(
|
||||
resourcingScript,
|
||||
readStream,
|
||||
cancellationToken);
|
||||
await using var readStream = ioManager.GetFileStream($"../../../../DMAPI/LongRunning/{scriptName}", false);
|
||||
await configurationClient.Write(
|
||||
resourcingScript,
|
||||
readStream,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await UploadScript("PreCompile-GenerateRandomResource");
|
||||
await UploadScript("EngineActiveVersionChange-SetupEnv");
|
||||
}
|
||||
|
||||
return ValueTaskExtensions.WhenAll(
|
||||
|
||||
@@ -155,6 +155,8 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LongRunning", "LongRunning", "{EB1DDE8C-CA6F-4BE3-947B-597CA8EABEA5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
tests\DMAPI\LongRunning\Config.dm = tests\DMAPI\LongRunning\Config.dm
|
||||
tests\DMAPI\LongRunning\EngineActiveVersionChange-SetupEnv.bat = tests\DMAPI\LongRunning\EngineActiveVersionChange-SetupEnv.bat
|
||||
tests\DMAPI\LongRunning\EngineActiveVersionChange-SetupEnv.sh = tests\DMAPI\LongRunning\EngineActiveVersionChange-SetupEnv.sh
|
||||
tests\DMAPI\LongRunning\long_running_test.dme = tests\DMAPI\LongRunning\long_running_test.dme
|
||||
tests\DMAPI\LongRunning\long_running_test_copy.dme = tests\DMAPI\LongRunning\long_running_test_copy.dme
|
||||
tests\DMAPI\LongRunning\long_running_test_rooted.dme = tests\DMAPI\LongRunning\long_running_test_rooted.dme
|
||||
@@ -171,8 +173,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiFree", "ApiFree", "{7B8F
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BasicOperation", "BasicOperation", "{F32B9514-AAD9-429D-841A-ED810FC2598C}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
tests\DMAPI\BasicOperation\Config.dm = tests\DMAPI\BasicOperation\Config.dm
|
||||
tests\DMAPI\BasicOperation\basic operation_test.dme = tests\DMAPI\BasicOperation\basic operation_test.dme
|
||||
tests\DMAPI\BasicOperation\Config.dm = tests\DMAPI\BasicOperation\Config.dm
|
||||
tests\DMAPI\BasicOperation\Test.dm = tests\DMAPI\BasicOperation\Test.dm
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
||||
Reference in New Issue
Block a user