got it working on docker

fixed log closing exception
This commit is contained in:
z__
2022-02-03 17:03:49 +07:00
parent 8b862df6ea
commit 357e0446df
20 changed files with 202 additions and 333 deletions

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM mcr.microsoft.com/windows/servercore:ltsc2022
USER ContainerAdministrator
ADD https://aka.ms/highdpimfc2013x64enu vc_redist2013.exe
ADD https://aka.ms/vs/16/release/vc_redist.x64.exe vc_redist.exe
RUN vc_redist2013.exe /passive /norestart
RUN vc_redist.exe /passive /norestart
RUN del vc_redist2013.exe && del vc_redist.exe
USER ContainerUser
COPY . .
ENV TORCH_GAME_PATH="c:\dedi"
ENV TORCH_INSTANCE="c:\instance"
ENV TORCH_SERVICE="true"
ENTRYPOINT ["Torch.Server.exe"]
CMD ["-noupdate"]

View File

@@ -1,22 +0,0 @@
pushd
$steamData = "C:/Steam/Data/"
$steamCMDPath = "C:/Steam/steamcmd/"
$steamCMDZip = "C:/Steam/steamcmd.zip"
Add-Type -AssemblyName System.IO.Compression.FileSystem
if (!(Test-Path $steamData)) {
mkdir "$steamData"
}
if (!(Test-Path $steamCMDPath)) {
if (!(Test-Path $steamCMDZip)) {
(New-Object System.Net.WebClient).DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", "$steamCMDZip");
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($steamCMDZip, $steamCMDPath)
}
cd "$steamData"
& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740 validate" "+quit"
popd

View File

@@ -1,52 +0,0 @@
param([string] $ApiBase, [string]$tagName, [string]$authinfo, [string[]] $assetPaths)
Add-Type -AssemblyName "System.Web"
$headers = @{
Authorization = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authinfo))
Accept = "application/vnd.github.v3+json"
}
try
{
Write-Output("Checking if release with tag " + $tagName + " already exists...")
$release = Invoke-RestMethod -Uri ($ApiBase+"releases/tags/$tagName") -Method "GET" -Headers $headers
Write-Output(" Using existing release " + $release.id + " at " + $release.html_url)
} catch {
Write-Output(" Doesn't exist")
$rel_arg = @{
tag_name=$tagName
name="Generated $tagName"
body=""
draft=$TRUE
prerelease=$tagName.Contains("alpha") -or $tagName.Contains("beta")
}
Write-Output("Creating new release " + $tagName + "...")
$release = Invoke-RestMethod -Uri ($ApiBase+"releases") -Method "POST" -Headers $headers -Body (ConvertTo-Json($rel_arg))
Write-Output(" Created new release " + $tagName + " at " + $release.html_url)
}
$assetsApiBase = $release.assets_url
Write-Output("Checking for existing assets...")
$existingAssets = Invoke-RestMethod -Uri ($assetsApiBase) -Method "GET" -Headers $headers
$assetLabels = ($assetPaths | ForEach-Object {[System.IO.Path]::GetFileName($_)})
foreach ($asset in $existingAssets) {
if ($assetLabels -contains $asset.name) {
$uri = $asset.url
Write-Output(" Deleting old asset " + $asset.name + " (id " + $asset.id + "); URI=" + $uri)
$result = Invoke-RestMethod -Uri $uri -Method "DELETE" -Headers $headers
}
}
Write-Output("Uploading assets...")
$uploadUrl = $release.upload_url.Substring(0, $release.upload_url.LastIndexOf('{'))
foreach ($asset in $assetPaths) {
$assetName = [System.IO.Path]::GetFileName($asset)
$assetType = [System.Web.MimeMapping]::GetMimeMapping($asset)
$assetData = [System.IO.File]::ReadAllBytes($asset)
$headerExtra = $headers + @{
"Content-Type" = $assetType
Name = $assetName
}
$uri = $uploadUrl + "?name=" + $assetName
Write-Output(" Uploading " + $asset + " as " + $assetType + "; URI=" + $uri)
$result = Invoke-RestMethod -Uri $uri -Method "POST" -Headers $headerExtra -Body $assetData
Write-Output(" ID=" + $result.id + ", found at=" + $result.browser_download_url)
}

77
Jenkinsfile vendored
View File

@@ -1,77 +0,0 @@
def packageAndArchive(buildMode, packageName) {
zipFile = "bin\\${packageName}.zip"
packageDir = "bin\\${packageName}\\"
bat "IF EXIST ${zipFile} DEL ${zipFile}"
bat "IF EXIST ${packageDir} RMDIR /S /Q ${packageDir}"
bat "xcopy bin\\x64\\${buildMode} ${packageDir}"
bat "del ${packageDir}VRage.*"
bat "del ${packageDir}Sandbox.*"
bat "del ${packageDir}SpaceEngineers.*"
powershell "Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\"\$PWD\\${packageDir}\", \"\$PWD\\${zipFile}\")"
archiveArtifacts artifacts: zipFile, caseSensitive: false, onlyIfSuccessful: true
}
node('windows') {
stage('Checkout') {
checkout scm
bat 'git pull https://github.com/TorchAPI/Torch/ master --tags'
}
stage('Acquire SE') {
bat 'powershell -File Jenkins/jenkins-grab-se.ps1'
bat 'IF EXIST GameBinaries RMDIR GameBinaries'
bat 'mklink /J GameBinaries "C:/Steam/Data/DedicatedServer64/"'
bat 'dir GameBinaries'
}
stage('Acquire NuGet Packages') {
bat 'cd C:\\Program Files\\Jenkins'
bat '"C:\\Program Files\\Jenkins\\nuget.exe" restore Torch.sln'
}
stage('Build') {
currentBuild.description = bat(returnStdout: true, script: '@powershell -File Versioning/version.ps1').trim()
if (env.BRANCH_NAME == "master" || env.BRANCH_NAME == "Patron" || env.BRANCH_NAME == "publictest") {
buildMode = "Release"
} else {
buildMode = "Release"
}
bat "IF EXIST \"bin\" rmdir /Q /S \"bin\""
bat "IF EXIST \"bin-test\" rmdir /Q /S \"bin-test\""
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=${buildMode} /p:Platform=x64 /t:Clean"
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=${buildMode} /p:Platform=x64"
}
stage('Archive') {
archiveArtifacts artifacts: "bin/x64/${buildMode}/Torch*", caseSensitive: false, fingerprint: true, onlyIfSuccessful: true
packageAndArchive(buildMode, "torch-server")
/*packageAndArchive(buildMode, "torch-client", "Torch.Server*")*/
}
/* Disabled because they fail builds more often than they detect actual problems
stage('Test') {
bat 'IF NOT EXIST reports MKDIR reports'
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/${buildMode}/Torch.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Server.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
step([
$class: 'XUnitBuilder',
thresholdMode: 1,
thresholds: [[$class: 'FailedThreshold', failureThreshold: '1']],
tools: [[
$class: 'XUnitDotNetTestType',
deleteOutputFiles: true,
failIfNotNew: true,
pattern: 'reports/*.xml',
skipNoTestFiles: false,
stopProcessingIfError: true
]]
])
}
*/
}

View File

@@ -151,6 +151,11 @@ namespace Torch.API
/// </summary> /// </summary>
string InstancePath { get; } string InstancePath { get; }
/// <summary>
/// Name of the dedicated instance.
/// </summary>
string InstanceName { get; }
/// <summary> /// <summary>
/// Raised when the server's Init() method has completed. /// Raised when the server's Init() method has completed.
/// </summary> /// </summary>

View File

@@ -10,7 +10,9 @@ namespace Torch
bool ForceUpdate { get; set; } bool ForceUpdate { get; set; }
bool GetPluginUpdates { get; set; } bool GetPluginUpdates { get; set; }
bool GetTorchUpdates { get; set; } bool GetTorchUpdates { get; set; }
[Obsolete("Use Torch.InstanceName instead")]
string InstanceName { get; set; } string InstanceName { get; set; }
[Obsolete("Use Torch.InstancePath instead")]
string InstancePath { get; set; } string InstancePath { get; set; }
bool NoGui { get; set; } bool NoGui { get; set; }
bool NoUpdate { get; set; } bool NoUpdate { get; set; }

View File

@@ -5,7 +5,7 @@
<Product>Torch</Product> <Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWpf>True</UseWpf> <UseWpf>True</UseWpf>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>

View File

@@ -22,40 +22,35 @@ namespace Torch.Server
{ {
public class Initializer public class Initializer
{ {
[Obsolete("It's hack. Do not use it!")]
internal static Initializer Instance { get; private set; } internal static Initializer Instance { get; private set; }
private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer)); private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer));
private bool _init; private bool _init;
private const string STEAMCMD_DIR = "steamcmd"; private const string STEAMCMD_DIR = "steamcmd";
private const string STEAMCMD_ZIP = "temp.zip"; private const string STEAMCMD_ZIP = "temp.zip";
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe"; private static readonly string STEAMCMD_EXE = "steamcmd.exe";
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt"; private static readonly string RUNSCRIPT_FILE = "runscript.txt";
private const string RUNSCRIPT = @"force_install_dir ../ private const string RUNSCRIPT = @"force_install_dir ../
login anonymous login anonymous
app_update 298740 app_update 298740
quit"; quit";
private TorchServer _server; private TorchServer _server;
private string _basePath;
internal Persistent<TorchConfig> ConfigPersistent { get; private set; } internal Persistent<TorchConfig> ConfigPersistent { get; }
public TorchConfig Config => ConfigPersistent?.Data; public TorchConfig Config => ConfigPersistent?.Data;
public TorchServer Server => _server; public TorchServer Server => _server;
public Initializer(string basePath) public Initializer(string basePath, Persistent<TorchConfig> torchConfig)
{ {
_basePath = basePath;
Instance = this; Instance = this;
ConfigPersistent = torchConfig;
} }
public bool Initialize(string[] args) public bool Initialize(string[] args)
{ {
if (_init) if (_init)
return false; return false;
AppDomain.CurrentDomain.UnhandledException += HandleException;
#if DEBUG #if DEBUG
//enables logging debug messages when built in debug mode. Amazing. //enables logging debug messages when built in debug mode. Amazing.
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "main"); LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "main");
@@ -69,37 +64,6 @@ quit";
if (!Enumerable.Contains(args, "-noupdate")) if (!Enumerable.Contains(args, "-noupdate"))
RunSteamCmd(); RunSteamCmd();
var basePath = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
var apiSource = Path.Combine(basePath, "DedicatedServer64", "steam_api64.dll");
var apiTarget = Path.Combine(basePath, "steam_api64.dll");
if (!File.Exists(apiTarget))
{
File.Copy(apiSource, apiTarget);
}
else if (File.GetLastWriteTime(apiTarget) < File.GetLastWriteTime(apiSource))
{
File.Delete(apiTarget);
File.Copy(apiSource, apiTarget);
}
var havokSource = Path.Combine(basePath, "DedicatedServer64", "Havok.dll");
var havokTarget = Path.Combine(basePath, "Havok.dll");
if (!File.Exists(havokTarget))
{
File.Copy(havokSource, havokTarget);
}
else if (File.GetLastWriteTime(havokTarget) < File.GetLastWriteTime(havokSource))
{
File.Delete(havokTarget);
File.Copy(havokSource, havokTarget);
}
InitConfig();
if (!Config.Parse(args))
return false;
if (!string.IsNullOrEmpty(Config.WaitForPID)) if (!string.IsNullOrEmpty(Config.WaitForPID))
{ {
try try
@@ -114,9 +78,9 @@ quit";
Thread.Sleep(1000); Thread.Sleep(1000);
} }
} }
catch catch (Exception e)
{ {
// ignored Log.Warn(e);
} }
} }
@@ -124,11 +88,11 @@ quit";
return true; return true;
} }
public void Run() public void Run(bool isService, string instanceName, string instancePath)
{ {
_server = new TorchServer(Config); _server = new TorchServer(Config, instancePath, instanceName);
if (Config.NoGui) if (isService || Config.NoGui)
{ {
_server.Init(); _server.Init();
_server.Start(); _server.Start();
@@ -165,34 +129,23 @@ quit";
} }
} }
private void InitConfig()
{
var configName = "Torch.cfg";
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
if (File.Exists(configName))
{
Log.Info($"Loading config {configName}");
}
else
{
Log.Info($"Generating default config at {configPath}");
}
ConfigPersistent = Persistent<TorchConfig>.Load(configPath);
}
public static void RunSteamCmd() public static void RunSteamCmd()
{ {
var log = LogManager.GetLogger("SteamCMD"); var log = LogManager.GetLogger("SteamCMD");
if (!Directory.Exists(STEAMCMD_DIR)) var path = Environment.GetEnvironmentVariable("TORCH_STEAMCMD") ?? Path.GetFullPath(STEAMCMD_DIR);
if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(STEAMCMD_DIR); Directory.CreateDirectory(path);
} }
if (!File.Exists(RUNSCRIPT_PATH)) var runScriptPath = Path.Combine(path, RUNSCRIPT_FILE);
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT); if (!File.Exists(runScriptPath))
File.WriteAllText(runScriptPath, RUNSCRIPT);
if (!File.Exists(STEAMCMD_PATH)) var steamCmdExePath = Path.Combine(path, STEAMCMD_EXE);
if (!File.Exists(steamCmdExePath))
{ {
try try
{ {
@@ -201,22 +154,21 @@ quit";
using (var file = File.Create(STEAMCMD_ZIP)) using (var file = File.Create(STEAMCMD_ZIP))
client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file); client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR); ZipFile.ExtractToDirectory(STEAMCMD_ZIP, path);
File.Delete(STEAMCMD_ZIP); File.Delete(STEAMCMD_ZIP);
log.Info("SteamCMD downloaded successfully!"); log.Info("SteamCMD downloaded successfully!");
} }
catch (Exception e) catch (Exception e)
{ {
log.Error("Failed to download SteamCMD, unable to update the DS."); log.Error(e, "Failed to download SteamCMD, unable to update the DS.");
log.Error(e);
return; return;
} }
} }
log.Info("Checking for DS updates."); log.Info("Checking for DS updates.");
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt") var steamCmdProc = new ProcessStartInfo(steamCmdExePath, "+runscript runscript.txt")
{ {
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR), WorkingDirectory = path,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII StandardOutputEncoding = Encoding.ASCII
@@ -230,29 +182,5 @@ quit";
Thread.Sleep(100); Thread.Sleep(100);
} }
} }
private void HandleException(object sender, UnhandledExceptionEventArgs e)
{
_server.FatalException = true;
if (Debugger.IsAttached)
return;
var ex = (Exception)e.ExceptionObject;
Log.Fatal(ex.ToStringDemystified());
LogManager.Flush();
if (Config.RestartOnCrash)
{
Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000);
var exe = typeof(Program).Assembly.Location;
Config.WaitForPID = Environment.ProcessId.ToString();
Process.Start(exe, Config.ToString());
}
else
{
MessageBox.Show("Torch encountered a fatal error and needs to close. Please check the logs for details.");
}
Environment.Exit(1);
}
} }
} }

View File

@@ -41,9 +41,11 @@ namespace Torch.Server.Managers
[Dependency] [Dependency]
private FilesystemManager _filesystemManager; private FilesystemManager _filesystemManager;
public InstanceManager(ITorchBase torchInstance) : base(torchInstance) private new ITorchServer Torch { get; }
{
public InstanceManager(ITorchServer torchInstance) : base(torchInstance)
{
Torch = torchInstance;
} }
public IWorld SelectedWorld => DedicatedConfig.SelectedWorld; public IWorld SelectedWorld => DedicatedConfig.SelectedWorld;
@@ -74,7 +76,7 @@ namespace Torch.Server.Managers
DedicatedConfig = new ConfigDedicatedViewModel((MyConfigDedicated<MyObjectBuilder_SessionSettings>) MySandboxGame.ConfigDedicated); DedicatedConfig = new ConfigDedicatedViewModel((MyConfigDedicated<MyObjectBuilder_SessionSettings>) MySandboxGame.ConfigDedicated);
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves")); var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.InstancePath, "Saves"));
foreach (var f in worldFolders) foreach (var f in worldFolders)
{ {
@@ -226,7 +228,7 @@ namespace Torch.Server.Managers
{ {
if (!((TorchServer)Torch).HasRun) if (!((TorchServer)Torch).HasRun)
{ {
DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME)); DedicatedConfig.Save(Path.Combine(Torch.InstancePath, CONFIG_NAME));
Log.Info("Saved dedicated config."); Log.Info("Saved dedicated config.");
} }

View File

@@ -1,17 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Threading;
#if TORCH_SERVICE
using Microsoft.VisualBasic.Devices;
#endif
using NLog;
using NLog.Fluent;
using NLog.Targets; using NLog.Targets;
using Torch.Utils; using Torch.Utils;
@@ -19,19 +7,17 @@ namespace Torch.Server
{ {
internal static class Program internal static class Program
{ {
/// <remarks>
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
/// </remarks>
[STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
var isService = Environment.GetEnvironmentVariable("TORCH_SERVICE")
?.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) ?? false;
Target.Register<LogViewerTarget>(nameof(LogViewerTarget)); Target.Register<LogViewerTarget>(nameof(LogViewerTarget));
//Ensures that all the files are downloaded in the Torch directory. //Ensures that all the files are downloaded in the Torch directory.
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory!.FullName; var workingDir = AppContext.BaseDirectory;
var binDir = Path.Combine(workingDir, "DedicatedServer64"); var binDir = Path.Combine(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir, "DedicatedServer64");
Directory.SetCurrentDirectory(workingDir); Directory.SetCurrentDirectory(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir);
if (Directory.Exists(binDir)) if (!isService && Directory.Exists(binDir))
foreach (var file in Directory.GetFiles(binDir, "System.*.dll")) foreach (var file in Directory.GetFiles(binDir, "System.*.dll"))
{ {
File.Delete(file); File.Delete(file);
@@ -49,11 +35,71 @@ namespace Torch.Server
} }
#endif #endif
var initializer = new Initializer(workingDir); var instanceName = Environment.GetEnvironmentVariable("TORCH_INSTANCE") ?? "Instance";
if (!initializer.Initialize(args)) string instancePath;
return;
initializer.Run(); if (Path.IsPathRooted(instanceName))
{
instancePath = instanceName;
instanceName = Path.GetDirectoryName(instanceName);
}
else
{
instancePath = Path.GetFullPath(instanceName);
}
var oldTorchCfg = Path.Combine(workingDir, "Torch.cfg");
var torchCfg = Path.Combine(instancePath, "Torch.cfg");
if (File.Exists(oldTorchCfg))
File.Move(oldTorchCfg, torchCfg, true);
var config = Persistent<TorchConfig>.Load(torchCfg);
config.Data.InstanceName = instanceName;
config.Data.InstancePath = instancePath;
if (!config.Data.Parse(args))
{
Console.WriteLine("Invalid arguments");
Environment.Exit(1);
}
var handler = new UnhandledExceptionHandler(config.Data, isService);
AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException;
var initializer = new Initializer(workingDir, config);
if (!initializer.Initialize(args))
Environment.Exit(1);
CopyNative(binDir);
initializer.Run(isService, instanceName, instancePath);
}
private static void CopyNative(string binPath)
{
var apiSource = Path.Combine(binPath, "steam_api64.dll");
var apiTarget = Path.Combine(AppContext.BaseDirectory, "steam_api64.dll");
if (!File.Exists(apiTarget))
{
File.Copy(apiSource, apiTarget);
}
else if (File.GetLastWriteTime(apiTarget) < File.GetLastWriteTime(binPath))
{
File.Delete(apiTarget);
File.Copy(apiSource, apiTarget);
}
var havokSource = Path.Combine(binPath, "Havok.dll");
var havokTarget = Path.Combine(AppContext.BaseDirectory, "Havok.dll");
if (!File.Exists(havokTarget))
{
File.Copy(havokSource, havokTarget);
}
else if (File.GetLastWriteTime(havokTarget) < File.GetLastWriteTime(havokSource))
{
File.Delete(havokTarget);
File.Copy(havokSource, havokTarget);
}
} }
} }
} }

View File

@@ -5,31 +5,22 @@
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision> <ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion> <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust> <UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<AssemblyTitle>Torch Server</AssemblyTitle> <AssemblyTitle>Torch Server</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
<IsPackable>false</IsPackable>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>Torch.Server.Program</StartupObject> <StartupObject>Torch.Server.Program</StartupObject>
@@ -169,8 +160,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Remove="Views\WorldSelectControl.xaml" /> <Page Remove="Views\WorldSelectControl.xaml" />
<None Include="..\NLog.config" Visible="false" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Always" />
<None Include="..\Dockerfile" Visible="false" CopyToPublishDirectory="Always" />
</ItemGroup> </ItemGroup>
<Target Name="CopyLoggingConfig" AfterTargets="Build">
<Copy SourceFiles="$(SolutionDir)NLog.config" DestinationFolder="$(TargetDir)" />
</Target>
</Project> </Project>

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeEditing/Localization/Localizable/@EntryValue">No</s:String></wpf:ResourceDictionary>

View File

@@ -18,8 +18,6 @@ namespace Torch.Server
public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate; public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate;
public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate; public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate;
private string _instancePath = Path.GetFullPath("Instance");
private string _instanceName = "Instance";
private bool _autostart; private bool _autostart;
private bool _restartOnCrash; private bool _restartOnCrash;
private bool _noGui; private bool _noGui;
@@ -40,21 +38,6 @@ namespace Torch.Server
private UGCServiceType _ugcServiceType = UGCServiceType.Steam; private UGCServiceType _ugcServiceType = UGCServiceType.Steam;
private bool _entityManagerEnabled = true; private bool _entityManagerEnabled = true;
/// <inheritdoc />
[Arg("instancename", "The name of the Torch instance.")]
[Display(Name = "Instance Name", Description = "The name of the Torch instance.", GroupName = "Server")]
public string InstanceName { get => _instanceName; set => Set(value, ref _instanceName); }
/// <inheritdoc />
[Arg("instancepath", "Server data folder where saves and mods are stored.")]
[Display(Name = "Instance Path", Description = "Server data folder where saves and mods are stored.", GroupName = "Server")]
public string InstancePath
{
get => _instancePath;
set => Set(value, ref _instancePath);
}
/// <inheritdoc /> /// <inheritdoc />
[XmlIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")] [XmlIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
public bool NoUpdate { get; set; } public bool NoUpdate { get; set; }
@@ -81,6 +64,8 @@ namespace Torch.Server
[Display(Name = "Restart On Crash", Description = "Automatically restart the server if it crashes.", GroupName = "Server")] [Display(Name = "Restart On Crash", Description = "Automatically restart the server if it crashes.", GroupName = "Server")]
public bool RestartOnCrash { get => _restartOnCrash; set => Set(value, ref _restartOnCrash); } public bool RestartOnCrash { get => _restartOnCrash; set => Set(value, ref _restartOnCrash); }
public string InstancePath { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[Arg("nogui", "Do not show the Torch UI.")] [Arg("nogui", "Do not show the Torch UI.")]
[Display(Name = "No GUI", Description = "Do not show the Torch UI.", GroupName = "Window")] [Display(Name = "No GUI", Description = "Do not show the Torch UI.", GroupName = "Window")]
@@ -94,6 +79,8 @@ namespace Torch.Server
[Display(Name = "Update Torch", Description = "Check every start for new versions of torch.", GroupName = "Server")] [Display(Name = "Update Torch", Description = "Check every start for new versions of torch.", GroupName = "Server")]
public bool GetTorchUpdates { get => _getTorchUpdates; set => Set(value, ref _getTorchUpdates); } public bool GetTorchUpdates { get => _getTorchUpdates; set => Set(value, ref _getTorchUpdates); }
public string InstanceName { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[Display(Name = "Update Plugins", Description = "Check every start for new versions of plugins.", GroupName = "Server")] [Display(Name = "Update Plugins", Description = "Check every start for new versions of plugins.", GroupName = "Server")]
public bool GetPluginUpdates { get => _getPluginUpdates; set => Set(value, ref _getPluginUpdates); } public bool GetPluginUpdates { get => _getPluginUpdates; set => Set(value, ref _getPluginUpdates); }

View File

@@ -55,8 +55,10 @@ namespace Torch.Server
//Here to trigger rebuild //Here to trigger rebuild
/// <inheritdoc /> /// <inheritdoc />
public TorchServer(TorchConfig config) : base(config) public TorchServer(ITorchConfig config, string instancePath, string instanceName) : base(config)
{ {
InstancePath = instancePath;
InstanceName = instanceName;
DedicatedInstance = new InstanceManager(this); DedicatedInstance = new InstanceManager(this);
AddManager(DedicatedInstance); AddManager(DedicatedInstance);
if (config.EntityManagerEnabled) if (config.EntityManagerEnabled)
@@ -116,7 +118,7 @@ namespace Torch.Server
public InstanceManager DedicatedInstance { get; } public InstanceManager DedicatedInstance { get; }
/// <inheritdoc /> /// <inheritdoc />
public string InstanceName => Config?.InstanceName; public string InstanceName { get; }
/// <inheritdoc /> /// <inheritdoc />
protected override uint SteamAppId => 244850; protected override uint SteamAppId => 244850;
@@ -130,7 +132,7 @@ namespace Torch.Server
public event Action<ITorchServer> Initialized; public event Action<ITorchServer> Initialized;
/// <inheritdoc /> /// <inheritdoc />
public string InstancePath => Config?.InstancePath; public string InstancePath { get; }
public int OnlinePlayers { get => _players; private set => SetValue(ref _players, value); } public int OnlinePlayers { get => _players; private set => SetValue(ref _players, value); }
@@ -141,10 +143,10 @@ namespace Torch.Server
MySandboxGame.IsDedicated = true; MySandboxGame.IsDedicated = true;
base.Init(); base.Init();
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged; Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
GetManager<InstanceManager>().LoadInstance(Config.InstancePath); GetManager<InstanceManager>().LoadInstance(InstancePath);
CanRun = true; CanRun = true;
Initialized?.Invoke(this); Initialized?.Invoke(this);
Log.Info($"Initialized server '{Config.InstanceName}' at '{Config.InstancePath}'"); Log.Info($"Initialized server '{InstanceName}' at '{InstancePath}'");
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -0,0 +1,49 @@
using System;
using System.Diagnostics;
using System.Threading;
using NLog;
using VRage;
namespace Torch.Server;
internal class UnhandledExceptionHandler
{
private readonly TorchConfig _config;
private readonly bool _isService;
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
public UnhandledExceptionHandler(TorchConfig config, bool isService)
{
_config = config;
_isService = isService;
}
internal void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (Debugger.IsAttached)
return;
var ex = (Exception)e.ExceptionObject;
Log.Fatal(ex.ToStringDemystified());
LogManager.Flush();
if (_isService)
Environment.Exit(1);
if (_config.RestartOnCrash)
{
Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000);
var exe = typeof(Program).Assembly.Location;
_config.WaitForPID = Environment.ProcessId.ToString();
Process.Start(exe, _config.ToString());
}
else
{
MyVRage.Platform.Windows.MessageBox(
"Torch encountered a fatal error and needs to close. Please check the logs for details.",
"Fatal exception", MessageBoxOptions.OkOnly);
}
Environment.Exit(1);
}
}

View File

@@ -12,6 +12,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AD02A71-1D4C-48F9-A8C1-789A5512424F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AD02A71-1D4C-48F9-A8C1-789A5512424F}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
NLog.config = NLog.config NLog.config = NLog.config
Dockerfile = Dockerfile
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Tests", "Torch.Tests\Torch.Tests.csproj", "{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Tests", "Torch.Tests\Torch.Tests.csproj", "{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}"

View File

@@ -46,6 +46,9 @@ namespace Torch.Patches
[ReflectedMethodInfo(typeof(MyLog), nameof(MyLog.Init))] [ReflectedMethodInfo(typeof(MyLog), nameof(MyLog.Init))]
private static MethodInfo _logInit; private static MethodInfo _logInit;
[ReflectedMethodInfo(typeof(MyLog), nameof(MyLog.Close))]
private static MethodInfo _logClose;
#pragma warning restore 649 #pragma warning restore 649
@@ -64,6 +67,7 @@ namespace Torch.Patches
context.GetPattern(_logWriteLineOptions).AddPrefix(nameof(PrefixWriteLineOptions)); context.GetPattern(_logWriteLineOptions).AddPrefix(nameof(PrefixWriteLineOptions));
context.GetPattern(_logInit).AddPrefix(nameof(PrefixInit)); context.GetPattern(_logInit).AddPrefix(nameof(PrefixInit));
context.GetPattern(_logClose).AddPrefix(nameof(PrefixClose));
} }
[ReflectedMethod(Name = "GetIdentByThread")] [ReflectedMethod(Name = "GetIdentByThread")]
@@ -81,6 +85,8 @@ namespace Torch.Patches
return _getIndentByThread(MyLog.Default, Environment.CurrentManagedThreadId); return _getIndentByThread(MyLog.Default, Environment.CurrentManagedThreadId);
} }
private static bool PrefixClose() => false;
private static bool PrefixInit(MyLog __instance, StringBuilder appVersionString) private static bool PrefixInit(MyLog __instance, StringBuilder appVersionString)
{ {
__instance.WriteLine("Log Started"); __instance.WriteLine("Log Started");

View File

@@ -26,7 +26,7 @@ namespace Torch
public T Data public T Data
{ {
get => _data; get => _data;
private set private init
{ {
if (_data is INotifyPropertyChanged npc1) if (_data is INotifyPropertyChanged npc1)
npc1.PropertyChanged -= OnPropertyChanged; npc1.PropertyChanged -= OnPropertyChanged;

View File

@@ -6,7 +6,7 @@
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWpf>True</UseWpf> <UseWpf>True</UseWpf>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>

View File

@@ -1,17 +0,0 @@
$buildSalt = $Env:BUILD_NUMBER
$branchName = $Env:BRANCH_NAME
$gitSimpleVersion = git describe --tags --abbrev=0
$simpleVersionStandard = echo $gitSimpleVersion | Select-String -Pattern "([0-9]+)\.([0-9]+)\.([0-9]+)" | % {$_.Matches} | %{$_.Groups[1].Value+"."+$_.Groups[2].Value+"."+$_.Groups[3].Value}
$dotNetVersion = "$simpleVersionStandard.$buildSalt"
$infoVersion = -join(("$gitSimpleVersion" -replace "([0-9]+)\.([0-9]+)\.([0-9]+)","$dotNetVersion"), "-", "$branchName")
$fileContent = @"
using System.Reflection;
[assembly: AssemblyVersion("$dotNetVersion")]
[assembly: AssemblyInformationalVersion("$infoVersion")]
"@
echo $fileContent | Set-Content "$PSScriptRoot/AssemblyVersion.cs"
echo "$infoVersion"