
fixed most of issues with world creating/loading fixed log window lags fixed some compiler warnings fixed empty log files creating fixed logging performance added better logging of load process commands autocomplete in torch GUI chat in torch GUI now has entered history (like console, use up/down arrows) abstraction of instance manager session name now correctly displaying in torchSessionManager messages TorchSession now has loaded world property (as torch now has more control about world load process) now only dedicated config locks after session start
259 lines
8.6 KiB
C#
259 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Threading;
|
|
using NLog;
|
|
using NLog.Targets;
|
|
using Sandbox.Engine.Utils;
|
|
using Torch.Utils;
|
|
using VRage.FileSystem;
|
|
|
|
namespace Torch.Server
|
|
{
|
|
public class Initializer
|
|
{
|
|
[Obsolete("It's hack. Do not use it!")]
|
|
internal static Initializer Instance { get; private set; }
|
|
|
|
private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer));
|
|
private bool _init;
|
|
private const string STEAMCMD_DIR = "steamcmd";
|
|
private const string STEAMCMD_ZIP = "temp.zip";
|
|
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
|
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
|
|
|
private const string RUNSCRIPT = @"force_install_dir ../
|
|
login anonymous
|
|
app_update 298740
|
|
quit";
|
|
private TorchServer _server;
|
|
private string _basePath;
|
|
|
|
internal Persistent<TorchConfig> ConfigPersistent { get; private set; }
|
|
public TorchConfig Config => ConfigPersistent?.Data;
|
|
public TorchServer Server => _server;
|
|
|
|
public Initializer(string basePath)
|
|
{
|
|
_basePath = basePath;
|
|
Instance = this;
|
|
}
|
|
|
|
public bool Initialize(string[] args)
|
|
{
|
|
if (_init)
|
|
return false;
|
|
|
|
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
|
|
|
#if DEBUG
|
|
//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, "console");
|
|
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "wpf");
|
|
LogManager.ReconfigExistingLoggers();
|
|
Log.Debug("Debug logging enabled.");
|
|
#endif
|
|
|
|
// This is what happens when Keen is bad and puts extensions into the System namespace.
|
|
if (!Enumerable.Contains(args, "-noupdate"))
|
|
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))
|
|
{
|
|
try
|
|
{
|
|
var pid = int.Parse(Config.WaitForPID);
|
|
var waitProc = Process.GetProcessById(pid);
|
|
Log.Info("Continuing in 5 seconds.");
|
|
Log.Warn($"Waiting for process {pid} to close");
|
|
while (!waitProc.HasExited)
|
|
{
|
|
Console.Write(".");
|
|
Thread.Sleep(1000);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
_init = true;
|
|
return true;
|
|
}
|
|
|
|
public void Run()
|
|
{
|
|
_server = new TorchServer(Config);
|
|
|
|
if (Config.NoGui)
|
|
{
|
|
_server.Init();
|
|
_server.Start();
|
|
}
|
|
else
|
|
{
|
|
#if !DEBUG
|
|
if (!Config.IndependentConsole)
|
|
{
|
|
Console.SetOut(TextWriter.Null);
|
|
NativeMethods.FreeConsole();
|
|
}
|
|
#endif
|
|
|
|
var gameThread = new Thread(() =>
|
|
{
|
|
_server.Init();
|
|
|
|
if (Config.Autostart || Config.TempAutostart)
|
|
{
|
|
Config.TempAutostart = false;
|
|
_server.Start();
|
|
}
|
|
});
|
|
|
|
gameThread.Start();
|
|
|
|
var ui = new TorchUI(_server);
|
|
|
|
SynchronizationContext.SetSynchronizationContext(
|
|
new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
|
|
|
|
ui.ShowDialog();
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
var log = LogManager.GetLogger("SteamCMD");
|
|
|
|
if (!Directory.Exists(STEAMCMD_DIR))
|
|
{
|
|
Directory.CreateDirectory(STEAMCMD_DIR);
|
|
}
|
|
|
|
if (!File.Exists(RUNSCRIPT_PATH))
|
|
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
|
|
|
if (!File.Exists(STEAMCMD_PATH))
|
|
{
|
|
try
|
|
{
|
|
log.Info("Downloading SteamCMD.");
|
|
using (var client = new HttpClient())
|
|
using (var file = File.Create(STEAMCMD_ZIP))
|
|
client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file);
|
|
|
|
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
|
File.Delete(STEAMCMD_ZIP);
|
|
log.Info("SteamCMD downloaded successfully!");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
|
log.Error(e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
log.Info("Checking for DS updates.");
|
|
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
|
{
|
|
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = true,
|
|
StandardOutputEncoding = Encoding.ASCII
|
|
};
|
|
var cmd = Process.Start(steamCmdProc);
|
|
|
|
// ReSharper disable once PossibleNullReferenceException
|
|
while (!cmd.HasExited)
|
|
{
|
|
log.Info(cmd.StandardOutput.ReadLine());
|
|
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);
|
|
}
|
|
}
|
|
}
|