MAJOR CHANGE: Torch can install and update its own DS installation! Full changelog: https://pastebin.com/ybqDM4HP

This commit is contained in:
John Gross
2017-06-16 22:56:41 -07:00
parent 3760c4a9ea
commit 2723973673
37 changed files with 915 additions and 314 deletions

View File

@@ -0,0 +1,13 @@
:: This script creates a symlink to the game binaries to account for different installation directories on different systems.
@echo off
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
cd %~dp0
mklink /D GameBinaries %path%
if errorlevel 1 goto Error
echo Done! You can now open the Torch solution without issue.
goto End
:Error
echo An error occured creating the symlink.
:End
pause

View File

@@ -2,13 +2,11 @@
{ {
public interface ITorchConfig public interface ITorchConfig
{ {
//bool AutoRestart { get; set; }
//int Autosave { get; set; }
string InstanceName { get; set; } string InstanceName { get; set; }
string InstancePath { get; set; } string InstancePath { get; set; }
//bool LogChat { get; set; }
bool RedownloadPlugins { get; set; } bool RedownloadPlugins { get; set; }
bool EnableAutomaticUpdates { get; set; } bool AutomaticUpdates { get; set; }
bool RestartOnCrash { get; set; }
bool Save(string path = null); bool Save(string path = null);
} }

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.ModAPI
{
public static class TorchAPI
{
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.ModAPI
{
/* TODO: this without causing a circular dependency
public static class TorchAPIGateway
{
public static Version Version => TorchBase.Instance.TorchVersion;
public static IMultiplayer Multiplayer => TorchBase.Instance.Multiplayer;
public static IPluginManager Plugins => TorchBase.Instance.Plugins;
}*/
}

View File

@@ -83,9 +83,6 @@
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Torch">
<HintPath>..\Torch.Server\bin\x64\Release\Torch.dll</HintPath>
</Reference>
<Reference Include="VRage"> <Reference Include="VRage">
<HintPath>..\GameBinaries\VRage.dll</HintPath> <HintPath>..\GameBinaries\VRage.dll</HintPath>
</Reference> </Reference>
@@ -147,7 +144,7 @@
<Compile Include="ITorchBase.cs" /> <Compile Include="ITorchBase.cs" />
<Compile Include="Plugins\IWpfPlugin.cs" /> <Compile Include="Plugins\IWpfPlugin.cs" />
<Compile Include="ModAPI\Ingame\GridExtensions.cs" /> <Compile Include="ModAPI\Ingame\GridExtensions.cs" />
<Compile Include="ModAPI\TorchAPI.cs" /> <Compile Include="ModAPI\TorchAPIGateway.cs" />
<Compile Include="Plugins\PluginAttribute.cs" /> <Compile Include="Plugins\PluginAttribute.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerState.cs" /> <Compile Include="ServerState.cs" />

View File

@@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("1.0.153.575")] [assembly: AssemblyVersion("1.0.167.670")]
[assembly: AssemblyFileVersion("1.0.153.575")] [assembly: AssemblyFileVersion("1.0.167.670")]

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Utils;
using Torch.Server.ViewModels;
using VRage.Game;
namespace Torch.Server.Managers
{
public class DSConfigManager
{
public ConfigDedicatedViewModel Config { get; set; }
private ConfigDedicatedViewModel _viewModel;
public DSConfigManager()
{
//Config.
}
/// <summary>
/// Creates a skeleton of a DS instance folder at the given directory.
/// </summary>
/// <param name="path"></param>
public void CreateInstance(string path)
{
if (Directory.Exists(path))
return;
Directory.CreateDirectory(path);
var saves = Path.Combine(path, "Saves");
Directory.CreateDirectory(saves);
var mods = Path.Combine(path, "Mods");
Directory.CreateDirectory(mods);
}
}
}

View File

@@ -19,7 +19,12 @@ using Sandbox.Game.World;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using Torch; using Torch;
using Torch.API; using Torch.API;
using Torch.Server.Views;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
using System.IO.Compression;
using System.Net;
using Torch.Server.Managers;
using VRage.FileSystem;
namespace Torch.Server namespace Torch.Server
{ {
@@ -27,10 +32,22 @@ namespace Torch.Server
{ {
private static ITorchServer _server; private static ITorchServer _server;
private static Logger _log = LogManager.GetLogger("Torch"); private static Logger _log = LogManager.GetLogger("Torch");
private static bool _restartOnCrash;
public static bool IsManualInstall;
private static TorchCli _cli;
/// <summary>
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
/// </summary>
[STAThread] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
IsManualInstall = Directory.GetCurrentDirectory().Contains("DedicatedServer64");
if (!IsManualInstall)
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (!Environment.UserInteractive) if (!Environment.UserInteractive)
{ {
using (var service = new TorchService()) using (var service = new TorchService())
@@ -40,7 +57,7 @@ namespace Torch.Server
return; return;
} }
var configName = /*args.FirstOrDefault() ??*/ "TorchConfig.xml"; var configName = "TorchConfig.xml";
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName); var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
TorchConfig options; TorchConfig options;
if (File.Exists(configName)) if (File.Exists(configName))
@@ -52,23 +69,123 @@ namespace Torch.Server
{ {
_log.Info($"Generating default config at {configPath}"); _log.Info($"Generating default config at {configPath}");
options = new TorchConfig(); options = new TorchConfig();
if (!IsManualInstall)
{
new DSConfigManager().CreateInstance("Instance");
options.InstancePath = Path.GetFullPath("Instance");
_log.Warn("Would you like to enable automatic updates? (Y/n):");
var input = Console.ReadLine() ?? "";
var autoUpdate = !input.Equals("n", StringComparison.InvariantCultureIgnoreCase);
options.AutomaticUpdates = autoUpdate;
if (autoUpdate)
_log.Info("Automatic updates enabled.");
}
//var setupDialog = new FirstTimeSetup { DataContext = options };
//setupDialog.ShowDialog();
options.Save(configPath); options.Save(configPath);
} }
bool gui = true; _cli = new TorchCli { Config = options };
foreach (var arg in args) if (!_cli.Parse(args))
return;
_log.Debug(_cli.ToString());
if (!string.IsNullOrEmpty(_cli.WaitForPID))
{ {
switch (arg) try
{ {
case "-noupdate": var pid = int.Parse(_cli.WaitForPID);
options.EnableAutomaticUpdates = false; var waitProc = Process.GetProcessById(pid);
break; _log.Warn($"Waiting for process {pid} to exit.");
case "-nogui": waitProc.WaitForExit();
gui = false; }
break; catch
{
// ignored
} }
} }
_restartOnCrash = _cli.RestartOnCrash;
if (options.AutomaticUpdates || _cli.Update)
{
if (IsManualInstall)
_log.Warn("Detected manual install, won't attempt to update DS");
else
{
RunSteamCmd();
}
}
RunServer(options, _cli);
}
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";
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 WebClient())
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
File.Delete(STEAMCMD_ZIP);
log.Info("SteamCMD downloaded successfully!");
}
catch
{
log.Error("Failed to download SteamCMD, unable to update the DS.");
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);
}
}
public static void RunServer(TorchConfig options, TorchCli cli)
{
/* /*
if (!parser.ParseArguments(args, options)) if (!parser.ParseArguments(args, options))
{ {
@@ -132,16 +249,57 @@ namespace Torch.Server
_server = new TorchServer(options); _server = new TorchServer(options);
_server.Init(); _server.Init();
if (gui)
if (!cli.NoGui)
{ {
var ui = new TorchUI((TorchServer)_server); var ui = new TorchUI((TorchServer)_server);
ui.LoadConfig(options); ui.LoadConfig(options);
ui.ShowDialog(); ui.ShowDialog();
} }
else
if (cli.NoGui || cli.Autostart)
{ {
_server.Start(); _server.Start();
} }
} }
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? AppDomain.CurrentDomain.BaseDirectory;
string asmPath = Path.Combine(basePath, "DedicatedServer64", new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(asmPath))
return Assembly.LoadFrom(asmPath);
}
catch
{
// ignored
}
return null;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
_log.Fatal(ex);
if (_restartOnCrash)
{
/* Throws an exception somehow and I'm too lazy to debug it.
try
{
if (MySession.Static != null && MySession.Static.AutoSaveInMinutes > 0)
MySession.Static.Save();
}
catch { }*/
var exe = typeof(Program).Assembly.Location;
_cli.WaitForPID = Process.GetCurrentProcess().Id.ToString();
Process.Start(exe, _cli.ToString());
}
Environment.Exit(-1);
}
} }
} }

View File

@@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("1.0.153.575")] [assembly: AssemblyVersion("1.0.167.670")]
[assembly: AssemblyFileVersion("1.0.153.575")] [assembly: AssemblyFileVersion("1.0.167.670")]

View File

@@ -92,6 +92,7 @@
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@@ -160,7 +161,8 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CommandLine.cs" /> <Compile Include="Managers\DSConfigManager.cs" />
<Compile Include="TorchCli.cs" />
<Compile Include="NativeMethods.cs" /> <Compile Include="NativeMethods.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
@@ -175,6 +177,7 @@
<Compile Include="TorchServiceInstaller.cs"> <Compile Include="TorchServiceInstaller.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" /> <Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" /> <Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" /> <Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
@@ -188,6 +191,7 @@
<Compile Include="ViewModels\PluginViewModel.cs" /> <Compile Include="ViewModels\PluginViewModel.cs" />
<Compile Include="ViewModels\SessionSettingsViewModel.cs" /> <Compile Include="ViewModels\SessionSettingsViewModel.cs" />
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" /> <Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
<Compile Include="ViewModels\SteamUserViewModel.cs" />
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs"> <Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon> <DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
</Compile> </Compile>
@@ -215,6 +219,9 @@
<Compile Include="Views\Entities\VoxelMapView.xaml.cs"> <Compile Include="Views\Entities\VoxelMapView.xaml.cs">
<DependentUpon>VoxelMapView.xaml</DependentUpon> <DependentUpon>VoxelMapView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\FirstTimeSetup.xaml.cs">
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModsControl.xaml.cs"> <Compile Include="Views\ModsControl.xaml.cs">
<DependentUpon>ModsControl.xaml</DependentUpon> <DependentUpon>ModsControl.xaml</DependentUpon>
</Compile> </Compile>
@@ -297,6 +304,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\FirstTimeSetup.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModsControl.xaml"> <Page Include="Views\ModsControl.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

41
Torch.Server/TorchCli.cs Normal file
View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Server
{
public class TorchCli : CommandLine
{
public TorchConfig Config { get; set; }
[Arg("instancepath", "Server data folder where saves and mods are stored.")]
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
[Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
public bool NoUpdate { get => !Config.AutomaticUpdates; set => Config.AutomaticUpdates = !value; }
[Arg("update", "Manually check for and install updates.")]
public bool Update { get; set; }
//TODO: backend code for this
//[Arg("worldpath", "Path to the game world folder to load.")]
public string WorldPath { get; set; }
[Arg("autostart", "Start the server immediately.")]
public bool Autostart { get; set; }
[Arg("restartoncrash", "Automatically restart the server if it crashes.")]
public bool RestartOnCrash { get => Config.RestartOnCrash; set => Config.RestartOnCrash = value; }
[Arg("nogui", "Do not show the Torch UI.")]
public bool NoGui { get; set; }
[Arg("silent", "Do not show the Torch UI or the command line.")]
public bool Silent { get; set; }
[Arg("waitforpid", "Makes Torch wait for another process to exit.")]
public string WaitForPID { get; set; }
}
}

View File

@@ -3,29 +3,46 @@ using Sandbox.Engine.Utils;
using Sandbox.Game; using Sandbox.Game;
using Sandbox.Game.World; using Sandbox.Game.World;
using System; using System;
using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Principal;
using System.Text;
using System.Threading; using System.Threading;
using System.Xml.Serialization; using Havok;
using Microsoft.Xml.Serialization.GeneratedAssembly;
using Sandbox.Game.Entities.Cube;
using Sandbox.Game.Multiplayer;
using SteamSDK; using SteamSDK;
using Torch.API; using Torch.API;
using Torch.Server.ViewModels.Blocks;
using VRage.Dedicated; using VRage.Dedicated;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.Game; using VRage.Game;
using VRage.Game.ObjectBuilder; using VRage.Game.ObjectBuilder;
using VRage.Game.SessionComponents; using VRage.Game.SessionComponents;
using VRage.Library;
using VRage.ObjectBuilders;
using VRage.Plugins; using VRage.Plugins;
using VRage.Trace;
using VRage.Utils;
namespace Torch.Server namespace Torch.Server
{ {
public class TorchServer : TorchBase, ITorchServer public class TorchServer : TorchBase, ITorchServer
{ {
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } }
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
public Thread GameThread { get; private set; } public Thread GameThread { get; private set; }
public ServerState State { get; private set; } public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
public string InstanceName => Config?.InstanceName; public string InstanceName => Config?.InstanceName;
public string InstancePath => Config?.InstancePath; public string InstancePath => Config?.InstancePath;
private ServerState _state;
private TimeSpan _elapsedPlayTime;
private float _simRatio;
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false); private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
public TorchServer(TorchConfig config = null) public TorchServer(TorchConfig config = null)
@@ -49,18 +66,68 @@ namespace Torch.Server
MyPerServerSettings.AppId = 244850; MyPerServerSettings.AppId = 244850;
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion; MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
InvokeBeforeRun();
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly); MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly); MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly); MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly); MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
MyPlugins.Load(); MyPlugins.Load();
MyGlobalTypeMetadata.Static.Init(); MyGlobalTypeMetadata.Static.Init();
MyInitializer.InvokeBeforeRun( RuntimeHelpers.RunClassConstructor(typeof(MyObjectBuilder_Base).TypeHandle);
MyPerServerSettings.AppId, }
MyPerServerSettings.GameDSName,
InstancePath, DedicatedServer.AddDateToLog);
public void InvokeBeforeRun()
{
var contentPath = "Content";
var privateContentPath = typeof(MyFileSystem).GetField("m_contentPath", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
if (privateContentPath != null)
Log.Debug("MyFileSystem already initialized");
else
{
if (Program.IsManualInstall)
{
var rootPath = new FileInfo(MyFileSystem.ExePath).Directory.FullName;
contentPath = Path.Combine(rootPath, "Content");
}
else
{
MyFileSystem.ExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DedicatedServer64");
}
MyFileSystem.Init(contentPath, InstancePath);
}
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
MySandboxGame.Log.WriteLine("Steam build: Always true");
MySandboxGame.Log.WriteLine("Environment.ProcessorCount: " + MyEnvironment.ProcessorCount);
//MySandboxGame.Log.WriteLine("Environment.OSVersion: " + GetOsName());
MySandboxGame.Log.WriteLine("Environment.CommandLine: " + Environment.CommandLine);
MySandboxGame.Log.WriteLine("Environment.Is64BitProcess: " + MyEnvironment.Is64BitProcess);
MySandboxGame.Log.WriteLine("Environment.Is64BitOperatingSystem: " + Environment.Is64BitOperatingSystem);
//MySandboxGame.Log.WriteLine("Environment.Version: " + GetNETFromRegistry());
MySandboxGame.Log.WriteLine("Environment.CurrentDirectory: " + Environment.CurrentDirectory);
MySandboxGame.Log.WriteLine("MainAssembly.ProcessorArchitecture: " + Assembly.GetExecutingAssembly().GetArchitecture());
MySandboxGame.Log.WriteLine("ExecutingAssembly.ProcessorArchitecture: " + MyFileSystem.MainAssembly.GetArchitecture());
MySandboxGame.Log.WriteLine("IntPtr.Size: " + IntPtr.Size.ToString());
MySandboxGame.Log.WriteLine("Default Culture: " + CultureInfo.CurrentCulture.Name);
MySandboxGame.Log.WriteLine("Default UI Culture: " + CultureInfo.CurrentUICulture.Name);
MySandboxGame.Log.WriteLine("IsAdmin: " + new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator));
MyLog.Default = MySandboxGame.Log;
Thread.CurrentThread.Name = "Main thread";
//Because we want exceptions from users to be in english
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
MySandboxGame.Config = new MyConfig("SpaceEngineers-Dedicated.cfg");
MySandboxGame.Config.Load();
} }
/// <summary> /// <summary>
@@ -69,8 +136,9 @@ namespace Torch.Server
public override void Start() public override void Start()
{ {
if (State != ServerState.Stopped) if (State != ServerState.Stopped)
throw new InvalidOperationException("Server is already running."); return;
GameThread = Thread.CurrentThread;
Config.Save(); Config.Save();
State = ServerState.Starting; State = ServerState.Starting;
Log.Info("Starting server."); Log.Info("Starting server.");
@@ -82,12 +150,9 @@ namespace Torch.Server
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); }; VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
do runInternal.Invoke(null, null);
{
runInternal.Invoke(null, null);
} while (MySandboxGame.IsReloading);
MyInitializer.InvokeAfterRun(); MySandboxGame.Log.Close();
State = ServerState.Stopped; State = ServerState.Stopped;
} }
@@ -99,6 +164,14 @@ namespace Torch.Server
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch"); SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
} }
/// <inheritdoc />
public override void Update()
{
base.Update();
SimulationRatio = Sync.ServerSimulationRatio;
ElapsedPlayTime = MySession.Static?.ElapsedPlayTime ?? default(TimeSpan);
}
/// <summary> /// <summary>
/// Stop the server. /// Stop the server.
/// </summary> /// </summary>
@@ -107,21 +180,18 @@ namespace Torch.Server
if (State == ServerState.Stopped) if (State == ServerState.Stopped)
Log.Error("Server is already stopped"); Log.Error("Server is already stopped");
if (Thread.CurrentThread.ManagedThreadId != GameThread?.ManagedThreadId && MySandboxGame.Static.IsRunning) if (Thread.CurrentThread != MySandboxGame.Static.UpdateThread)
{ {
Log.Debug("Invoking server stop on game thread.");
Invoke(Stop); Invoke(Stop);
return; return;
} }
Log.Info("Stopping server."); Log.Info("Stopping server.");
MySession.Static.Save();
MySession.Static.Unload();
//Unload all the static junk. //Unload all the static junk.
//TODO: Finish unloading all server data so it's in a completely clean state. //TODO: Finish unloading all server data so it's in a completely clean state.
MyFileSystem.Reset(); MySandboxGame.Static.Exit();
VRage.Input.MyGuiGameControlsHelpers.Reset();
VRage.Input.MyInput.UnloadData();
Log.Info("Server stopped."); Log.Info("Server stopped.");
_stopHandle.Set(); _stopHandle.Set();

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using VRage.Game;
namespace Torch.Server.ViewModels
{
public class BlockLimitViewModel : ViewModel
{
private SessionSettingsViewModel _sessionSettings;
private string _blockType;
private short _limit;
public string BlockType { get => _blockType; set { _blockType = value; OnPropertyChanged(); } }
public short Limit { get => _limit; set { _limit = value; OnPropertyChanged(); } }
public CommandBinding Delete { get; } = new CommandBinding(new DeleteCommand());
public BlockLimitViewModel(SessionSettingsViewModel sessionSettings, string blockType, short limit)
{
_sessionSettings = sessionSettings;
_blockType = blockType;
_limit = limit;
}
public class DeleteCommand : ICommand
{
/// <inheritdoc />
public bool CanExecute(object parameter)
{
return ((BlockLimitViewModel)parameter)._sessionSettings.BlockLimits.Contains(parameter);
}
/// <inheritdoc />
public void Execute(object parameter)
{
((BlockLimitViewModel)parameter)._sessionSettings.BlockLimits.Remove((BlockLimitViewModel)parameter);
}
/// <inheritdoc />
public event EventHandler CanExecuteChanged;
}
}
}

View File

@@ -14,30 +14,46 @@ namespace Torch.Server.ViewModels
{ {
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config; private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
public ConfigDedicatedViewModel() public ConfigDedicatedViewModel() : this(new MyConfigDedicated<MyObjectBuilder_SessionSettings>(""))
{ {
_config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>("");
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
Administrators = new ObservableCollection<string>(_config.Administrators);
Banned = new ObservableCollection<ulong>(_config.Banned);
Mods = new ObservableCollection<ulong>(_config.Mods);
} }
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated) public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
{ {
_config = configDedicated; _config = configDedicated;
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings); SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
Administrators = new ObservableCollection<string>(_config.Administrators); Administrators = string.Join("\r\n", _config.Administrators);
Banned = new ObservableCollection<ulong>(_config.Banned); Banned = string.Join("\r\n", _config.Banned);
Mods = new ObservableCollection<ulong>(_config.Mods); Mods = string.Join("\r\n", _config.Mods);
}
public void Save(string path = null)
{
_config.Administrators.Clear();
foreach (var admin in Administrators.Split('\r', '\n'))
if (!string.IsNullOrEmpty(admin))
_config.Administrators.Add(admin);
_config.Banned.Clear();
foreach (var banned in Banned.Split('\r', '\n'))
if (!string.IsNullOrEmpty(banned))
_config.Banned.Add(ulong.Parse(banned));
_config.Mods.Clear();
foreach (var mod in Mods.Split('\r', '\n'))
if (!string.IsNullOrEmpty(mod))
_config.Mods.Add(ulong.Parse(mod));
_config.Save(path);
} }
public SessionSettingsViewModel SessionSettings { get; } public SessionSettingsViewModel SessionSettings { get; }
public ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>(); public ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>();
public ObservableCollection<string> Administrators { get; } public string Administrators { get; set; }
public ObservableCollection<ulong> Banned { get; } public string Banned { get; set; }
public ObservableCollection<ulong> Mods { get; } public string Mods { get; set; }
public int AsteroidAmount public int AsteroidAmount
{ {

View File

@@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SharpDX.Toolkit.Collections;
using VRage.Game; using VRage.Game;
using VRage.Library.Utils; using VRage.Library.Utils;
@@ -12,135 +14,120 @@ namespace Torch.Server.ViewModels
{ {
private MyObjectBuilder_SessionSettings _settings; private MyObjectBuilder_SessionSettings _settings;
public SessionSettingsViewModel() public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
{ {
_settings = new MyObjectBuilder_SessionSettings();
} }
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings) public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
{ {
_settings = settings; _settings = settings;
foreach (var limit in settings.BlockTypeLimits.Dictionary)
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
} }
public MTObservableCollection<BlockLimitViewModel> BlockLimits { get; } = new MTObservableCollection<BlockLimitViewModel>();
#region Multipliers #region Multipliers
public float InventorySizeMultiplier public float InventorySizeMultiplier
{ {
get { return _settings.InventorySizeMultiplier; } get => _settings.InventorySizeMultiplier; set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
} }
public float RefinerySpeedMultiplier public float RefinerySpeedMultiplier
{ {
get { return _settings.RefinerySpeedMultiplier; } get => _settings.RefinerySpeedMultiplier; set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
} }
public float AssemblerEfficiencyMultiplier public float AssemblerEfficiencyMultiplier
{ {
get { return _settings.AssemblerEfficiencyMultiplier; } get => _settings.AssemblerEfficiencyMultiplier; set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
} }
public float AssemblerSpeedMultiplier public float AssemblerSpeedMultiplier
{ {
get { return _settings.AssemblerSpeedMultiplier; } get => _settings.AssemblerSpeedMultiplier; set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
} }
public float GrinderSpeedMultiplier public float GrinderSpeedMultiplier
{ {
get { return _settings.GrinderSpeedMultiplier; } get => _settings.GrinderSpeedMultiplier; set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
} }
public float HackSpeedMultiplier public float HackSpeedMultiplier
{ {
get { return _settings.HackSpeedMultiplier; } get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
} }
#endregion #endregion
#region NPCs #region NPCs
public bool EnableDrones public bool EnableDrones
{ {
get { return _settings.EnableDrones; } get => _settings.EnableDrones; set { _settings.EnableDrones = value; OnPropertyChanged(); }
set { _settings.EnableDrones = value; OnPropertyChanged(); }
} }
public bool EnableEncounters public bool EnableEncounters
{ {
get { return _settings.EnableEncounters; } get => _settings.EnableEncounters; set { _settings.EnableEncounters = value; OnPropertyChanged(); }
set { _settings.EnableEncounters = value; OnPropertyChanged(); }
} }
public bool EnableSpiders public bool EnableSpiders
{ {
get { return _settings.EnableSpiders; } get => _settings.EnableSpiders; set { _settings.EnableSpiders = value; OnPropertyChanged(); }
set { _settings.EnableSpiders = value; OnPropertyChanged(); }
} }
public bool EnableWolves public bool EnableWolves
{ {
get { return _settings.EnableWolfs; } get => _settings.EnableWolfs; set { _settings.EnableWolfs = value; OnPropertyChanged(); }
set { _settings.EnableWolfs = value; OnPropertyChanged(); }
} }
public bool EnableCargoShips public bool EnableCargoShips
{ {
get { return _settings.CargoShipsEnabled; } get => _settings.CargoShipsEnabled; set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
} }
#endregion #endregion
#region Environment #region Environment
public bool EnableSunRotation public bool EnableSunRotation
{ {
get { return _settings.EnableSunRotation; } get => _settings.EnableSunRotation; set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
} }
public bool EnableAirtightness public bool EnableAirtightness
{ {
get { return _settings.EnableOxygenPressurization; } get => _settings.EnableOxygenPressurization; set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
} }
public bool EnableOxygen public bool EnableOxygen
{ {
get { return _settings.EnableOxygen; } get => _settings.EnableOxygen; set { _settings.EnableOxygen = value; OnPropertyChanged(); }
set { _settings.EnableOxygen = value; OnPropertyChanged(); }
} }
public bool EnableDestructibleBlocks public bool EnableDestructibleBlocks
{ {
get { return _settings.DestructibleBlocks; } get => _settings.DestructibleBlocks; set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
} }
public bool EnableToolShake public bool EnableToolShake
{ {
get { return _settings.EnableToolShake; } get => _settings.EnableToolShake; set { _settings.EnableToolShake = value; OnPropertyChanged(); }
set { _settings.EnableToolShake = value; OnPropertyChanged(); }
} }
public bool EnableVoxelDestruction public bool EnableVoxelDestruction
{ {
get { return _settings.EnableVoxelDestruction; } get => _settings.EnableVoxelDestruction; set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
} }
public List<string> EnvironmentHostilityValues => Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList(); public List<string> EnvironmentHostilityValues => Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
public string EnvironmentHostility public string EnvironmentHostility
{ {
get { return _settings.EnvironmentHostility.ToString(); } get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
} }
public bool EnableFlora public bool EnableFlora
{ {
get { return _settings.EnableFlora; } get => _settings.EnableFlora; set { _settings.EnableFlora = value; OnPropertyChanged(); }
set { _settings.EnableFlora = value; OnPropertyChanged(); }
} }
#endregion #endregion
@@ -148,192 +135,164 @@ namespace Torch.Server.ViewModels
public string GameMode public string GameMode
{ {
get { return _settings.GameMode.ToString(); } get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
} }
public bool EnableAutoHealing public bool EnableAutoHealing
{ {
get { return _settings.AutoHealing; } get => _settings.AutoHealing; set { _settings.AutoHealing = value; OnPropertyChanged(); }
set { _settings.AutoHealing = value; OnPropertyChanged(); }
} }
public bool EnableCopyPaste public bool EnableCopyPaste
{ {
get { return _settings.EnableCopyPaste; } get => _settings.EnableCopyPaste; set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
} }
public bool ShowPlayerNamesOnHud public bool ShowPlayerNamesOnHud
{ {
get { return _settings.ShowPlayerNamesOnHud; } get => _settings.ShowPlayerNamesOnHud; set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
} }
public bool EnableThirdPerson public bool EnableThirdPerson
{ {
get { return _settings.Enable3rdPersonView; } get => _settings.Enable3rdPersonView; set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
} }
public bool EnableSpectator public bool EnableSpectator
{ {
get { return _settings.EnableSpectator; } get => _settings.EnableSpectator; set { _settings.EnableSpectator = value; OnPropertyChanged(); }
set { _settings.EnableSpectator = value; OnPropertyChanged(); }
} }
public bool SpawnWithTools public bool SpawnWithTools
{ {
get { return _settings.SpawnWithTools; } get => _settings.SpawnWithTools; set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
} }
public bool EnableConvertToStation public bool EnableConvertToStation
{ {
get { return _settings.EnableConvertToStation; } get => _settings.EnableConvertToStation; set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
} }
public bool EnableJetpack public bool EnableJetpack
{ {
get { return _settings.EnableJetpack; } get => _settings.EnableJetpack; set { _settings.EnableJetpack = value; OnPropertyChanged(); }
set { _settings.EnableJetpack = value; OnPropertyChanged(); }
} }
public bool EnableRemoteOwnerRemoval public bool EnableRemoteOwnerRemoval
{ {
get { return _settings.EnableRemoteBlockRemoval; } get => _settings.EnableRemoteBlockRemoval; set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
} }
public bool EnableRespawnShips public bool EnableRespawnShips
{ {
get { return _settings.EnableRespawnShips; } get => _settings.EnableRespawnShips; set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
} }
public bool EnableScripterRole public bool EnableScripterRole
{ {
get { return _settings.EnableScripterRole; } get => _settings.EnableScripterRole; set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
} }
public bool EnableRealisticSound public bool EnableRealisticSound
{ {
get { return _settings.RealisticSound; } get => _settings.RealisticSound; set { _settings.RealisticSound = value; OnPropertyChanged(); }
set { _settings.RealisticSound = value; OnPropertyChanged(); }
} }
public bool ResetOwnership public bool ResetOwnership
{ {
get { return _settings.ResetOwnership; } get => _settings.ResetOwnership; set { _settings.ResetOwnership = value; OnPropertyChanged(); }
set { _settings.ResetOwnership = value; OnPropertyChanged(); }
} }
public bool DeleteRespawnShips public bool DeleteRespawnShips
{ {
get { return _settings.RespawnShipDelete; } get => _settings.RespawnShipDelete; set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
} }
public bool EnableThrusterDamage public bool EnableThrusterDamage
{ {
get { return _settings.ThrusterDamage; } get => _settings.ThrusterDamage; set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
} }
public bool EnableWeapons public bool EnableWeapons
{ {
get { return _settings.WeaponsEnabled; } get => _settings.WeaponsEnabled; set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
} }
public bool EnableIngameScripts public bool EnableIngameScripts
{ {
get { return _settings.EnableIngameScripts; } get => _settings.EnableIngameScripts; set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
} }
public uint AutosaveInterval public uint AutosaveInterval
{ {
get { return _settings.AutoSaveInMinutes; } get => _settings.AutoSaveInMinutes; set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
} }
public int FloraDensity public int FloraDensity
{ {
get { return _settings.FloraDensity; } get => _settings.FloraDensity; set { _settings.FloraDensity = value; OnPropertyChanged(); }
set { _settings.FloraDensity = value; OnPropertyChanged(); }
} }
public float FloraDensityMultiplier public float FloraDensityMultiplier
{ {
get { return _settings.FloraDensityMultiplier; } get => _settings.FloraDensityMultiplier; set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
} }
public short MaxBackupSaves public short MaxBackupSaves
{ {
get { return _settings.MaxBackupSaves; } get => _settings.MaxBackupSaves; set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
} }
public int MaxBlocksPerPlayer public int MaxBlocksPerPlayer
{ {
get { return _settings.MaxBlocksPerPlayer; } get => _settings.MaxBlocksPerPlayer; set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
} }
public short MaxFloatingObjects public short MaxFloatingObjects
{ {
get { return _settings.MaxFloatingObjects; } get => _settings.MaxFloatingObjects; set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
} }
public int MaxGridSize public int MaxGridSize
{ {
get { return _settings.MaxGridSize; } get => _settings.MaxGridSize; set { _settings.MaxGridSize = value; OnPropertyChanged(); }
set { _settings.MaxGridSize = value; OnPropertyChanged(); }
} }
public short MaxPlayers public short MaxPlayers
{ {
get { return _settings.MaxPlayers; } get => _settings.MaxPlayers; set { _settings.MaxPlayers = value; OnPropertyChanged(); }
set { _settings.MaxPlayers = value; OnPropertyChanged(); }
} }
public int PhysicsIterations public int PhysicsIterations
{ {
get { return _settings.PhysicsIterations; } get => _settings.PhysicsIterations; set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
} }
public float SpawnTimeMultiplier public float SpawnTimeMultiplier
{ {
get { return _settings.SpawnShipTimeMultiplier; } get => _settings.SpawnShipTimeMultiplier; set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
} }
public float SunRotationInterval public float SunRotationInterval
{ {
get { return _settings.SunRotationIntervalMinutes; } get => _settings.SunRotationIntervalMinutes; set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
} }
public int ViewDistance public int ViewDistance
{ {
get { return _settings.ViewDistance; } get => _settings.ViewDistance; set { _settings.ViewDistance = value; OnPropertyChanged(); }
set { _settings.ViewDistance = value; OnPropertyChanged(); }
} }
public int WorldSize public int WorldSize
{ {
get { return _settings.ViewDistance; } get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
} }
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel) public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
{ {
viewModel._settings.BlockTypeLimits.Dictionary.Clear();
foreach (var limit in viewModel.BlockLimits)
viewModel._settings.BlockTypeLimits.Dictionary.Add(limit.BlockType, limit.Limit);
return viewModel._settings; return viewModel._settings;
} }
} }

View File

@@ -3,10 +3,34 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SteamSDK;
namespace Torch.Server.ViewModels namespace Torch.Server.ViewModels
{ {
class SteamUserViewModel public class SteamUserViewModel : ViewModel
{ {
private string _name;
public string Name { get => _name; set { } }
private ulong _id;
public ulong SteamId
{
get => _id;
set
{
_id = value;
OnPropertyChanged();
//TODO: resolve user name
OnPropertyChanged(nameof(Name));
}
}
public SteamUserViewModel(ulong id)
{
SteamId = id;
}
public SteamUserViewModel() : this(0) { }
} }
} }

View File

@@ -64,8 +64,25 @@ namespace Torch.Server
{ {
//Can't use Message.Text directly because of object ownership in WPF. //Can't use Message.Text directly because of object ownership in WPF.
var text = Message.Text; var text = Message.Text;
_server.Multiplayer.SendMessage(text); var commands = ((TorchBase)_server).Commands;
string response = null;
if (commands.IsCommand(text))
{
_server.Multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", text));
_server.InvokeBlocking(() =>
{
response = commands.HandleCommandFromServer(text);
});
}
else
{
_server.Multiplayer.SendMessage(text);
}
if (!string.IsNullOrEmpty(response))
_server.Multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", response));
Message.Text = ""; Message.Text = "";
var sto = false;
Refresh(null, ref sto);
} }
} }
} }

View File

@@ -13,6 +13,7 @@
<DockPanel> <DockPanel>
<DockPanel DockPanel.Dock="Top"> <DockPanel DockPanel.Dock="Top">
<Label Content="World:" DockPanel.Dock="Left" /> <Label Content="World:" DockPanel.Dock="Left" />
<Button Content="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" /> <ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" />
</DockPanel> </DockPanel>
<DockPanel DockPanel.Dock="Bottom"> <DockPanel DockPanel.Dock="Bottom">
@@ -36,15 +37,30 @@
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" /> <CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
</StackPanel> </StackPanel>
<StackPanel Margin="3"> <StackPanel Margin="3">
<Label Content="Banned Players" /> <!--
<ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100" <ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100"
MouseDoubleClick="Banned_OnMouseDoubleClick" /> MouseDoubleClick="Banned_OnMouseDoubleClick" /> -->
<Label Content="Administrators" /> <!--
<ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100" <ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100"
MouseDoubleClick="Administrators_OnMouseDoubleClick" /> MouseDoubleClick="Administrators_OnMouseDoubleClick">
<Label Content="Mods" /> <ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SteamId}" Width="100"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox> -->
<!--
<ListBox ItemsSource="{Binding Mods}" Width="130" Margin="3,0,3,3" Height="100" <ListBox ItemsSource="{Binding Mods}" Width="130" Margin="3,0,3,3" Height="100"
MouseDoubleClick="Mods_OnMouseDoubleClick" /> MouseDoubleClick="Mods_OnMouseDoubleClick" /> -->
<Label Content="Mods" />
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<Label Content="Administrators" />
<TextBox Text="{Binding Administrators}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<Label Content="Banned Players" />
<TextBox Text="{Binding Banned}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
@@ -52,6 +68,30 @@
</StackPanel> </StackPanel>
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True"> <ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
<StackPanel DataContext="{Binding SessionSettings}"> <StackPanel DataContext="{Binding SessionSettings}">
<Expander Header="Block Limits">
<StackPanel Margin="10,0,0,0">
<DockPanel>
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
<Label Content="Max Blocks Per Player" />
</DockPanel>
<DockPanel>
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
<Label Content="Max Grid Size" />
</DockPanel>
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
<TextBox Text="{Binding Limit}" Width="50" Margin="3" />
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
</StackPanel>
</Expander>
<Expander Header="Multipliers"> <Expander Header="Multipliers">
<StackPanel Margin="10,0,0,0"> <StackPanel Margin="10,0,0,0">
<DockPanel> <DockPanel>
@@ -91,6 +131,14 @@
</Expander> </Expander>
<Expander Header="Environment"> <Expander Header="Environment">
<StackPanel Margin="10,0,0,0"> <StackPanel Margin="10,0,0,0">
<DockPanel ToolTip="Increases physics precision at the cost of performance.">
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
<Label Content="Physics Iterations" />
</DockPanel>
<DockPanel>
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
<Label Content="Max Floating Objects" />
</DockPanel>
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound" <CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" /> <CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
@@ -136,6 +184,9 @@
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" /> <TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
<Label Content="Max Players" /> <Label Content="Max Players" />
</DockPanel> </DockPanel>
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
Margin="3" />
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
<CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" /> <CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" />
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" /> <CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD" <CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
@@ -144,6 +195,8 @@
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" /> <TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
<Label Content="Respawn Time Multiplier" /> <Label Content="Respawn Time Multiplier" />
</DockPanel> </DockPanel>
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
</StackPanel> </StackPanel>
</Expander> </Expander>
<Expander Header="Miscellaneous"> <Expander Header="Miscellaneous">
@@ -152,11 +205,9 @@
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" /> <TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
<Label Content="Autosave Interval (minutes)" /> <Label Content="Autosave Interval (minutes)" />
</DockPanel> </DockPanel>
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
Margin="3" />
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station" <CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}" <CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
Content="Enable Remote Ownership Removal" Margin="3" /> Content="Enable Remote Ownership Removal" Margin="3" />
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships" <CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
@@ -165,7 +216,6 @@
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera" <CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff" <CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage" <CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
@@ -173,7 +223,6 @@
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" /> <CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts" <CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
Margin="3" /> Margin="3" />
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
<DockPanel> <DockPanel>
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}" <ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
Margin="3" Width="100" DockPanel.Dock="Left" /> Margin="3" Width="100" DockPanel.Dock="Left" />
@@ -183,24 +232,6 @@
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" /> <TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
<Label Content="Max Backup Saves" /> <Label Content="Max Backup Saves" />
</DockPanel> </DockPanel>
<DockPanel>
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
<Label Content="Max Blocks Per Player" />
</DockPanel>
<DockPanel>
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
<Label Content="Max Floating Objects" />
</DockPanel>
<DockPanel>
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
<Label Content="Max Grid Size" />
</DockPanel>
<DockPanel ToolTip="Increases physics precision at the cost of performance.">
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
<Label Content="Physics Iterations" />
</DockPanel>
</StackPanel> </StackPanel>
</Expander> </Expander>
</StackPanel> </StackPanel>

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -15,14 +16,17 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using System.Xml.Serialization;
using NLog; using NLog;
using Sandbox; using Sandbox;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils; using Sandbox.Engine.Utils;
using Torch.Server.ViewModels; using Torch.Server.ViewModels;
using Torch.Views; using Torch.Views;
using VRage;
using VRage.Dedicated; using VRage.Dedicated;
using VRage.Game; using VRage.Game;
using VRage.ObjectBuilders;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Torch.Server.Views namespace Torch.Server.Views
@@ -32,6 +36,7 @@ namespace Torch.Server.Views
/// </summary> /// </summary>
public partial class ConfigControl : UserControl public partial class ConfigControl : UserControl
{ {
private readonly Logger Log = LogManager.GetLogger("Config");
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; } public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
private ConfigDedicatedViewModel _viewModel; private ConfigDedicatedViewModel _viewModel;
private string _configPath; private string _configPath;
@@ -43,22 +48,23 @@ namespace Torch.Server.Views
public void SaveConfig() public void SaveConfig()
{ {
Config.Save(_configPath); _viewModel.Save(_configPath);
Log.Info("Saved DS config.");
try try
{ {
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out ulong size); var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out _);
checkpoint.Settings = _viewModel.SessionSettings; checkpoint.Settings = _viewModel.SessionSettings;
checkpoint.Mods.Clear(); checkpoint.Mods.Clear();
foreach (var modId in _viewModel.Mods) foreach (var modId in _viewModel.Mods)
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId)); checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld); MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
Log.Info("Saved world config.");
} }
catch (Exception e) catch (Exception e)
{ {
var log = LogManager.GetLogger("Torch"); Log.Error("Failed to write sandbox config, changes will not appear on server");
log.Error("Failed to overwrite sandbox config, changes will not appear on server"); Log.Error(e);
log.Error(e);
} }
} }
@@ -77,6 +83,18 @@ namespace Torch.Server.Views
Config.Load(path); Config.Load(path);
_configPath = path; _configPath = path;
var checkpoint = MyLocalCache.LoadCheckpoint(Config.LoadWorld, out ulong _);
if (checkpoint == null)
{
Log.Error("Failed to load checkpoint when loading DS config.");
}
else
{
Config.Mods.Clear();
foreach (var mod in checkpoint.Mods)
Config.Mods.Add(mod.PublishedFileId);
}
_viewModel = new ConfigDedicatedViewModel(Config); _viewModel = new ConfigDedicatedViewModel(Config);
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves")); var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
@@ -86,6 +104,7 @@ namespace Torch.Server.Views
DataContext = _viewModel; DataContext = _viewModel;
} }
/*
private void Banned_OnMouseDoubleClick(object sender, MouseButtonEventArgs e) private void Banned_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{ {
var editor = new CollectionEditor {Owner = Window.GetWindow(this)}; var editor = new CollectionEditor {Owner = Window.GetWindow(this)};
@@ -102,11 +121,27 @@ namespace Torch.Server.Views
{ {
var editor = new CollectionEditor { Owner = Window.GetWindow(this) }; var editor = new CollectionEditor { Owner = Window.GetWindow(this) };
editor.Edit(_viewModel.Mods, "Mods"); editor.Edit(_viewModel.Mods, "Mods");
} }*/
private void Save_OnClick(object sender, RoutedEventArgs e) private void Save_OnClick(object sender, RoutedEventArgs e)
{ {
SaveConfig(); SaveConfig();
} }
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
{
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
_viewModel.SessionSettings.BlockLimits.Remove(vm);
}
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
{
_viewModel.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_viewModel.SessionSettings, "", 0));
}
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Feature coming soon :)");
}
} }
} }

View File

@@ -0,0 +1,29 @@
<Window x:Class="Torch.Server.Views.FirstTimeSetup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:torch="clr-namespace:Torch;assembly=Torch"
mc:Ignorable="d"
Title="Torch First Time Setup" Height="200" Width="500">
<Window.DataContext>
<torch:TorchConfig/>
</Window.DataContext>
<StackPanel>
<DockPanel ToolTip="This should be set to the folder that contains your mods and saves.">
<Label Content="Instance Path:" Width="150"/>
<TextBox Text="{Binding InstancePath}" Margin="3"/>
</DockPanel>
<DockPanel ToolTip="The name of your instance, this doesn't really matter.">
<Label Content="Instance Name:" Width="150"/>
<TextBox Text="{Binding InstanceName}" Margin="3"></TextBox>
</DockPanel>
<DockPanel ToolTip="This enables/disables automatic plugin updating.">
<Label Content="Automatic Plugin Updates:" Width="150"/>
<CheckBox IsChecked="{Binding EnableAutomaticUpdates}" VerticalAlignment="Center" Margin="3"/>
</DockPanel>
<Label Content="You can change these settings later by editing TorchConfig.xml"/>
<Button Content="Done" Margin="3" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Window>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Torch.Server.Views
{
/// <summary>
/// Interaction logic for FirstTimeSetup.xaml
/// </summary>
public partial class FirstTimeSetup : Window
{
public FirstTimeSetup()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Close();
}
}
}

View File

@@ -9,35 +9,49 @@
Title="Torch"> Title="Torch">
<DockPanel> <DockPanel>
<StackPanel DockPanel.Dock="Top" Margin="5,5,5,5" Orientation="Horizontal"> <StackPanel DockPanel.Dock="Top" Margin="5,5,5,5" Orientation="Horizontal">
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True"/> <Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0"
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStop_Click" IsEnabled="False"/> HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True" />
<Button x:Name="BtnRestart" Content="Restart" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnRestart_Click"/> <Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
<Label x:Name="LabelSimulation" Content="Sim Ratio: 0.00"/> Click="BtnStop_Click" IsEnabled="False" />
<Label x:Name="LabelUptime" Content="Uptime: 0d 0h 0m"/> <Label>
<Label.Content>
<TextBlock Text="{Binding State, StringFormat=Status: {0}}"></TextBlock>
</Label.Content>
</Label>
<Label x:Name="LabelSimulation">
<Label.Content>
<TextBlock Text="{Binding SimulationRatio, StringFormat=Simulation: {0:0.00}}" />
</Label.Content>
</Label>
<Label x:Name="LabelUptime">
<Label.Content>
<TextBlock Text="{Binding ElapsedPlayTime, StringFormat=Uptime: {0:g}}"/>
</Label.Content>
</Label>
</StackPanel> </StackPanel>
<TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5"> <TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5">
<TabItem Header="Configuration"> <TabItem Header="Configuration">
<DockPanel> <DockPanel>
<DockPanel DockPanel.Dock="Top"> <DockPanel DockPanel.Dock="Top">
<Label Content="Instance Path: " Margin="3"/> <Label Content="Instance Path: " Margin="3" />
<TextBox x:Name="InstancePathBox" Margin="3" Height="20" TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False"/> <TextBox x:Name="InstancePathBox" Margin="3" Height="20"
TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False" />
</DockPanel> </DockPanel>
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom"/> <views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" />
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="Chat/Players"> <TabItem Header="Chat/Players">
<DockPanel> <DockPanel>
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False"/> <local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False" />
<local:ChatControl x:Name="Chat" IsEnabled="False"/> <local:ChatControl x:Name="Chat" IsEnabled="False" />
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="Entity Manager"> <TabItem Header="Entity Manager">
<views:EntitiesControl/> <views:EntitiesControl />
</TabItem> </TabItem>
<TabItem Header="Plugins"> <TabItem Header="Plugins">
<views:PluginsControl x:Name="Plugins"/> <views:PluginsControl x:Name="Plugins" />
</TabItem> </TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

View File

@@ -19,7 +19,6 @@ using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using Sandbox; using Sandbox;
using Torch.API; using Torch.API;
using VRageMath;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
namespace Torch.Server namespace Torch.Server
@@ -31,26 +30,20 @@ namespace Torch.Server
{ {
private TorchServer _server; private TorchServer _server;
private TorchConfig _config; private TorchConfig _config;
private DateTime _startTime;
private readonly Timer _uiUpdate = new Timer
{
Interval = 1000,
AutoReset = true,
};
public TorchUI(TorchServer server) public TorchUI(TorchServer server)
{ {
_config = (TorchConfig)server.Config; _config = (TorchConfig)server.Config;
_server = server; _server = server;
InitializeComponent(); InitializeComponent();
_startTime = DateTime.Now;
_uiUpdate.Elapsed += UiUpdate_Elapsed;
Left = _config.WindowPosition.X; Left = _config.WindowPosition.X;
Top = _config.WindowPosition.Y; Top = _config.WindowPosition.Y;
Width = _config.WindowSize.X; Width = _config.WindowSize.X;
Height = _config.WindowSize.Y; Height = _config.WindowSize.Y;
//TODO: data binding for whole server
DataContext = server;
Chat.BindServer(server); Chat.BindServer(server);
PlayerList.BindServer(server); PlayerList.BindServer(server);
Plugins.BindServer(server); Plugins.BindServer(server);
@@ -69,47 +62,33 @@ namespace Torch.Server
}); });
} }
private void UiUpdate_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateUptime();
}
private void UpdateUptime()
{
var currentTime = DateTime.Now;
var uptime = currentTime - _startTime;
Dispatcher.Invoke(() => LabelUptime.Content = $"Uptime: {uptime.Days}d {uptime.Hours}h {uptime.Minutes}m");
}
private void BtnStart_Click(object sender, RoutedEventArgs e) private void BtnStart_Click(object sender, RoutedEventArgs e)
{ {
_startTime = DateTime.Now; _config.Save();
Chat.IsEnabled = true; Chat.IsEnabled = true;
PlayerList.IsEnabled = true; PlayerList.IsEnabled = true;
((Button) sender).IsEnabled = false; ((Button) sender).IsEnabled = false;
BtnStop.IsEnabled = true; BtnStop.IsEnabled = true;
_uiUpdate.Start();
ConfigControl.SaveConfig(); ConfigControl.SaveConfig();
new Thread(_server.Start).Start(); new Thread(_server.Start).Start();
} }
private void BtnStop_Click(object sender, RoutedEventArgs e) private void BtnStop_Click(object sender, RoutedEventArgs e)
{ {
_config.Save();
Chat.IsEnabled = false; Chat.IsEnabled = false;
PlayerList.IsEnabled = false; PlayerList.IsEnabled = false;
((Button) sender).IsEnabled = false; ((Button) sender).IsEnabled = false;
//HACK: Uncomment when restarting is possible. //HACK: Uncomment when restarting is possible.
//BtnStart.IsEnabled = true; //BtnStart.IsEnabled = true;
_uiUpdate.Stop();
_server.Stop(); _server.Stop();
} }
protected override void OnClosing(CancelEventArgs e) protected override void OnClosing(CancelEventArgs e)
{ {
var newSize = new Vector2I((int)Width, (int)Height); var newSize = new Point((int)Width, (int)Height);
_config.WindowSize = newSize; _config.WindowSize = newSize;
var newPos = new Vector2I((int)Left, (int)Top); var newPos = new Point((int)Left, (int)Top);
_config.WindowPosition = newPos; _config.WindowPosition = newPos;
_config.Save(); _config.Save();
@@ -124,7 +103,7 @@ namespace Torch.Server
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e) private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)
{ {
var name = (sender as TextBox).Text; var name = ((TextBox)sender).Text;
_config.InstancePath = name; _config.InstancePath = name;

View File

@@ -5,25 +5,20 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Torch.Server namespace Torch
{ {
public class CommandLine public class CommandLine
{ {
public TorchConfig Config { get; } private readonly string _argPrefix;
private string _argPrefix;
[Arg("instancepath", "Server data folder where saves and mods are stored")] public CommandLine(string argPrefix = "-")
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
public CommandLine(TorchConfig config, string argPrefix)
{ {
Config = config;
_argPrefix = argPrefix; _argPrefix = argPrefix;
} }
public PropertyInfo[] GetArgs() public PropertyInfo[] GetArgs()
{ {
return typeof(CommandLine).GetProperties().Where(p => p.HasAttribute<ArgAttribute>()).ToArray(); return GetType().GetProperties().Where(p => p.HasAttribute<ArgAttribute>()).ToArray();
} }
public string GetHelp() public string GetHelp()
@@ -33,18 +28,43 @@ namespace Torch.Server
foreach (var property in GetArgs()) foreach (var property in GetArgs())
{ {
var attr = property.GetCustomAttribute<ArgAttribute>(); var attr = property.GetCustomAttribute<ArgAttribute>();
sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(20)}{attr.Description}"); sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(24)}{attr.Description}");
} }
return sb.ToString(); return sb.ToString();
} }
public void Run(string[] args) public override string ToString()
{ {
var args = new List<string>();
foreach (var prop in GetArgs())
{
var attr = prop.GetCustomAttribute<ArgAttribute>();
if (prop.PropertyType == typeof(bool) && (bool)prop.GetValue(this))
{
args.Add($"{_argPrefix}{attr.Name}");
}
else if (prop.PropertyType == typeof(string))
{
var str = (string)prop.GetValue(this);
if (string.IsNullOrEmpty(str))
continue;
args.Add($"{_argPrefix}{attr.Name} \"{str}\"");
}
}
return string.Join(" ", args);
}
public bool Parse(string[] args)
{
if (args.Length == 0)
return true;
if (args[0] == $"{_argPrefix}help") if (args[0] == $"{_argPrefix}help")
{ {
Console.WriteLine(GetHelp()); Console.WriteLine(GetHelp());
return; return false;
} }
var properties = GetArgs(); var properties = GetArgs();
@@ -71,16 +91,18 @@ namespace Torch.Server
property.SetValue(this, args[++i]); property.SetValue(this, args[++i]);
} }
} }
catch (Exception e) catch
{ {
Console.WriteLine($"Error parsing arg {argName}"); Console.WriteLine($"Error parsing arg {argName}");
} }
} }
} }
return true;
} }
private class ArgAttribute : Attribute public class ArgAttribute : Attribute
{ {
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }

View File

@@ -31,6 +31,8 @@ namespace Torch.Commands
/// </summary> /// </summary>
public string RawArgs { get; } public string RawArgs { get; }
public string Response { get; private set; }
public CommandContext(ITorchBase torch, ITorchPlugin plugin, IMyPlayer player, string rawArgs = null, List<string> args = null) public CommandContext(ITorchBase torch, ITorchPlugin plugin, IMyPlayer player, string rawArgs = null, List<string> args = null)
{ {
Torch = torch; Torch = torch;
@@ -42,7 +44,10 @@ namespace Torch.Commands
public void Respond(string message, string sender = "Server", string font = MyFontEnum.Blue) public void Respond(string message, string sender = "Server", string font = MyFontEnum.Blue)
{ {
Torch.Multiplayer.SendMessage(message, sender, Player.IdentityId, font); Response = message;
if (Player != null)
Torch.Multiplayer.SendMessage(message, sender, Player.IdentityId, font);
} }
} }
} }

View File

@@ -69,28 +69,51 @@ namespace Torch.Commands
} }
} }
public string HandleCommandFromServer(string message)
{
var cmdText = new string(message.Skip(1).ToArray());
var command = Commands.GetCommand(cmdText, out string argText);
var cmdPath = string.Join(".", command.Path);
var splitArgs = Regex.Matches(argText, "(\"[^\"]+\"|\\S+)").Cast<Match>().Select(x => x.ToString().Replace("\"", "")).ToList();
_log.Trace($"Invoking {cmdPath} for server.");
var context = new CommandContext(_torch, command.Plugin, null, argText, splitArgs);
if (command.TryInvoke(context))
_log.Info($"Server ran command '{message}'");
else
context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
return context.Response;
}
public void HandleCommand(ChatMsg msg, ref bool sendToOthers) public void HandleCommand(ChatMsg msg, ref bool sendToOthers)
{ {
if (msg.Text.Length < 1 || msg.Text[0] != Prefix) HandleCommand(msg.Text, msg.Author, ref sendToOthers);
}
public void HandleCommand(string message, ulong steamId, ref bool sendToOthers, bool serverConsole = false)
{
if (message.Length < 1 || message[0] != Prefix)
return; return;
sendToOthers = false; sendToOthers = false;
var player = _torch.Multiplayer.GetPlayerBySteamId(msg.Author); var player = _torch.Multiplayer.GetPlayerBySteamId(steamId);
if (player == null) if (player == null)
{ {
_log.Error($"Command {msg.Text} invoked by nonexistant player"); _log.Error($"Command {message} invoked by nonexistant player");
return; return;
} }
var cmdText = new string(msg.Text.Skip(1).ToArray()); var cmdText = new string(message.Skip(1).ToArray());
var command = Commands.GetCommand(cmdText, out string argText); var command = Commands.GetCommand(cmdText, out string argText);
if (command != null) if (command != null)
{ {
var cmdPath = string.Join(".", command.Path); var cmdPath = string.Join(".", command.Path);
if (!HasPermission(msg.Author, command)) if (!HasPermission(steamId, command))
{ {
_log.Info($"{player.DisplayName} tried to use command {cmdPath} without permission"); _log.Info($"{player.DisplayName} tried to use command {cmdPath} without permission");
_torch.Multiplayer.SendMessage($"You need to be a {command.MinimumPromoteLevel} or higher to use that command.", playerId: player.IdentityId); _torch.Multiplayer.SendMessage($"You need to be a {command.MinimumPromoteLevel} or higher to use that command.", playerId: player.IdentityId);
@@ -103,7 +126,7 @@ namespace Torch.Commands
_torch.Invoke(() => _torch.Invoke(() =>
{ {
if (command.TryInvoke(context)) if (command.TryInvoke(context))
_log.Info($"Player {player.DisplayName} ran command '{msg.Text}'"); _log.Info($"Player {player.DisplayName} ran command '{message}'");
else else
context.Respond($"Invalid Syntax: {command.SyntaxHelp}"); context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
}); });

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using Torch;
using Torch.Commands.Permissions; using Torch.Commands.Permissions;
using Torch.Managers; using Torch.Managers;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
@@ -26,10 +27,17 @@ namespace Torch.Commands
Console.WriteLine(commandManager.Commands.GetTreeString()); Console.WriteLine(commandManager.Commands.GetTreeString());
} }
#endif #endif
[Command("crash", "Causes the server to crash for testing purposes")]
[Permission(MyPromoteLevel.Admin)]
public void Crash()
{
throw new Exception("Crash triggered by Torch command");
}
[Command("help", "Displays help for a command")] [Command("help", "Displays help for a command")]
public void Help() public void Help()
{ {
var commandManager = ((PluginManager)Context.Torch.Plugins).Commands; var commandManager = ((TorchBase)Context.Torch).Commands;
commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node); commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node);
if (node != null) if (node != null)
@@ -42,10 +50,11 @@ namespace Torch.Commands
if (command != null) if (command != null)
{ {
sb.AppendLine($"Syntax: {command.SyntaxHelp}"); sb.AppendLine($"Syntax: {command.SyntaxHelp}");
sb.AppendLine(command.HelpText); sb.Append(command.HelpText);
} }
sb.AppendLine($"Subcommands: {string.Join(", ", children)}"); if (node.Subcommands.Count() != 0)
sb.Append($"\nSubcommands: {string.Join(", ", children)}");
Context.Respond(sb.ToString()); Context.Respond(sb.ToString());
} }

View File

@@ -27,7 +27,15 @@ namespace Torch.Managers
public void Init() public void Init()
{ {
NetworkManager.Instance.RegisterNetworkHandlers(new ChatIntercept()); try
{
NetworkManager.Instance.RegisterNetworkHandlers(new ChatIntercept());
}
catch
{
_log.Error("Failed to initialize network intercept, command hiding will not work! Falling back to another method.");
MyMultiplayer.Static.ChatMessageReceived += Static_ChatMessageReceived;
}
} }
private void Static_ChatMessageReceived(ulong arg1, string arg2, SteamSDK.ChatEntryTypeEnum arg3) private void Static_ChatMessageReceived(ulong arg1, string arg2, SteamSDK.ChatEntryTypeEnum arg3)

View File

@@ -96,6 +96,7 @@ namespace Torch.Managers
{ {
var msg = new ScriptedChatMsg {Author = author, Font = font, Target = playerId, Text = message}; var msg = new ScriptedChatMsg {Author = author, Font = font, Target = playerId, Text = message};
MyMultiplayerBase.SendScriptedChatMessage(ref msg); MyMultiplayerBase.SendScriptedChatMessage(ref msg);
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message));
} }
private void OnSessionLoaded() private void OnSessionLoaded()

View File

@@ -30,7 +30,6 @@ namespace Torch.Managers
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>(); public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>();
public CommandManager Commands { get; private set; }
public float LastUpdateMs => _lastUpdateMs; public float LastUpdateMs => _lastUpdateMs;
private volatile float _lastUpdateMs; private volatile float _lastUpdateMs;
@@ -51,7 +50,8 @@ namespace Torch.Managers
public void UpdatePlugins() public void UpdatePlugins()
{ {
var s = Stopwatch.StartNew(); var s = Stopwatch.StartNew();
Parallel.ForEach(Plugins, p => p.Update()); foreach (var plugin in Plugins)
plugin.Update();
s.Stop(); s.Stop();
_lastUpdateMs = (float)s.Elapsed.TotalMilliseconds; _lastUpdateMs = (float)s.Elapsed.TotalMilliseconds;
} }
@@ -100,9 +100,9 @@ namespace Torch.Managers
/// </summary> /// </summary>
public void Init() public void Init()
{ {
Commands = new CommandManager(_torch); var commands = ((TorchBase)_torch).Commands;
if (_torch.Config.EnableAutomaticUpdates) if (_torch.Config.AutomaticUpdates)
DownloadPlugins(); DownloadPlugins();
else else
_log.Warn("Automatic plugin updates are disabled."); _log.Warn("Automatic plugin updates are disabled.");
@@ -111,6 +111,7 @@ namespace Torch.Managers
var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories); var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories);
foreach (var dllPath in dlls) foreach (var dllPath in dlls)
{ {
_log.Debug($"Loading plugin {dllPath}");
var asm = Assembly.UnsafeLoadFrom(dllPath); var asm = Assembly.UnsafeLoadFrom(dllPath);
foreach (var type in asm.GetExportedTypes()) foreach (var type in asm.GetExportedTypes())
@@ -119,14 +120,15 @@ namespace Torch.Managers
{ {
try try
{ {
var plugin = (ITorchPlugin)Activator.CreateInstance(type); var plugin = (TorchPluginBase)Activator.CreateInstance(type);
if (plugin.Id == default(Guid)) if (plugin.Id == default(Guid))
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}"); throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})"); _log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
plugin.StoragePath = new FileInfo(asm.Location).Directory.FullName;
Plugins.Add(plugin); Plugins.Add(plugin);
Commands.RegisterPluginCommands(plugin); commands.RegisterPluginCommands(plugin);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Sandbox.ModAPI;
using Torch.API; using Torch.API;
using Torch.API.ModAPI;
using Torch.API.ModAPI.Ingame; using Torch.API.ModAPI.Ingame;
using VRage.Scripting; using VRage.Scripting;
@@ -20,6 +22,12 @@ namespace Torch.Managers
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location); MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location);
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(typeof(GridExtensions)); MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(typeof(GridExtensions));
using (var whitelist = _whitelist.OpenBatch())
{
//whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.ModApi, typeof(TorchAPIGateway));
whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.Both, typeof(GridExtensions));
}
/* /*
//dump whitelist //dump whitelist
var whitelist = new StringBuilder(); var whitelist = new StringBuilder();

View File

@@ -16,13 +16,19 @@ namespace Torch
{ {
[JsonIgnore] [JsonIgnore]
public string Path { get; set; } public string Path { get; set; }
public T Data { get; private set; } = new T(); public T Data { get; private set; }
~Persistent() ~Persistent()
{ {
Dispose(); Dispose();
} }
public Persistent(string path, T data = default(T))
{
Path = path;
Data = data;
}
public void Save(string path = null) public void Save(string path = null)
{ {
if (path == null) if (path == null)
@@ -38,7 +44,7 @@ namespace Torch
public static Persistent<T> Load(string path, bool saveIfNew = true) public static Persistent<T> Load(string path, bool saveIfNew = true)
{ {
var config = new Persistent<T> { Path = path }; var config = new Persistent<T>(path, new T());
if (File.Exists(path)) if (File.Exists(path))
{ {

View File

@@ -124,6 +124,7 @@
<Compile Include="ChatMessage.cs" /> <Compile Include="ChatMessage.cs" />
<Compile Include="Collections\KeyTree.cs" /> <Compile Include="Collections\KeyTree.cs" />
<Compile Include="Collections\RollingAverage.cs" /> <Compile Include="Collections\RollingAverage.cs" />
<Compile Include="CommandLine.cs" />
<Compile Include="Commands\CategoryAttribute.cs" /> <Compile Include="Commands\CategoryAttribute.cs" />
<Compile Include="Commands\Command.cs" /> <Compile Include="Commands\Command.cs" />
<Compile Include="Commands\CommandAttribute.cs" /> <Compile Include="Commands\CommandAttribute.cs" />

View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -16,8 +17,11 @@ using Sandbox.Game.World;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using SpaceEngineers.Game; using SpaceEngineers.Game;
using Torch.API; using Torch.API;
using Torch.Commands;
using Torch.Managers; using Torch.Managers;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.Game.ObjectBuilder;
using VRage.ObjectBuilders;
using VRage.Plugins; using VRage.Plugins;
using VRage.Scripting; using VRage.Scripting;
using VRage.Utils; using VRage.Utils;
@@ -40,6 +44,7 @@ namespace Torch
public IMultiplayer Multiplayer { get; protected set; } public IMultiplayer Multiplayer { get; protected set; }
public EntityManager Entities { get; protected set; } public EntityManager Entities { get; protected set; }
public NetworkManager Network { get; protected set; } public NetworkManager Network { get; protected set; }
public CommandManager Commands { get; protected set; }
public event Action SessionLoading; public event Action SessionLoading;
public event Action SessionLoaded; public event Action SessionLoaded;
public event Action SessionUnloading; public event Action SessionUnloading;
@@ -60,6 +65,7 @@ namespace Torch
Multiplayer = new MultiplayerManager(this); Multiplayer = new MultiplayerManager(this);
Entities = new EntityManager(this); Entities = new EntityManager(this);
Network = NetworkManager.Instance; Network = NetworkManager.Instance;
Commands = new CommandManager(this);
} }
public bool IsOnGameThread() public bool IsOnGameThread()
@@ -147,10 +153,6 @@ namespace Torch
{ {
action.Invoke(); action.Invoke();
} }
catch (Exception ex)
{
//log
}
finally finally
{ {
e.Set(); e.Set();
@@ -167,10 +169,19 @@ namespace Torch
{ {
Debug.Assert(!_init, "Torch instance is already initialized."); Debug.Assert(!_init, "Torch instance is already initialized.");
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
SpaceEngineersGame.SetupBasicGameInfo(); SpaceEngineersGame.SetupBasicGameInfo();
SpaceEngineersGame.SetupPerGameSettings(); SpaceEngineersGame.SetupPerGameSettings();
/*
if (Directory.Exists("DedicatedServer64"))
{
Log.Debug("Inserting DedicatedServer64 before MyPerGameSettings assembly paths");
MyPerGameSettings.GameModAssembly = $"DedicatedServer64\\{MyPerGameSettings.GameModAssembly}";
MyPerGameSettings.GameModObjBuildersAssembly = $"DedicatedServer64\\{MyPerGameSettings.GameModObjBuildersAssembly}";
MyPerGameSettings.SandboxAssembly = $"DedicatedServer64\\{MyPerGameSettings.SandboxAssembly}";
MyPerGameSettings.SandboxGameAssembly = $"DedicatedServer64\\{MyPerGameSettings.SandboxGameAssembly}";
}*/
TorchVersion = Assembly.GetEntryAssembly().GetName().Version; TorchVersion = Assembly.GetEntryAssembly().GetName().Version;
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", ".")); GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
var verInfo = $"Torch {TorchVersion}, SE {GameVersion}"; var verInfo = $"Torch {TorchVersion}, SE {GameVersion}";
@@ -188,7 +199,7 @@ namespace Torch
MySession.AfterLoading += () => SessionLoaded?.Invoke(); MySession.AfterLoading += () => SessionLoaded?.Invoke();
MySession.OnUnloading += () => SessionUnloading?.Invoke(); MySession.OnUnloading += () => SessionUnloading?.Invoke();
MySession.OnUnloaded += () => SessionUnloaded?.Invoke(); MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
InitUpdater(); RegisterVRagePlugin();
_init = true; _init = true;
} }
@@ -196,7 +207,7 @@ namespace Torch
/// <summary> /// <summary>
/// Hook into the VRage plugin system for updates. /// Hook into the VRage plugin system for updates.
/// </summary> /// </summary>
private void InitUpdater() private void RegisterVRagePlugin()
{ {
var fieldName = "m_plugins"; var fieldName = "m_plugins";
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>; var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
@@ -206,20 +217,6 @@ namespace Torch
pluginList.Add(this); pluginList.Add(this);
} }
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
Log.Fatal(ex);
if (ex is ReflectionTypeLoadException rex)
{
foreach (var x in rex.LoaderExceptions)
Log.Fatal(x);
}
Console.ReadLine();
Environment.Exit(-1);
}
public abstract void Start(); public abstract void Start();
public abstract void Stop(); public abstract void Stop();

View File

@@ -4,10 +4,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Xml.Serialization; using System.Xml.Serialization;
using NLog; using NLog;
using Sandbox.ModAPI.Ingame; using Sandbox.ModAPI.Ingame;
using VRageMath;
namespace Torch namespace Torch
{ {
@@ -17,14 +17,17 @@ namespace Torch
public string InstancePath { get; set; } public string InstancePath { get; set; }
public string InstanceName { get; set; } public string InstanceName { get; set; }
#warning World Path not implemented
public string WorldPath { get; set; }
//public int Autosave { get; set; } //public int Autosave { get; set; }
//public bool AutoRestart { get; set; } //public bool AutoRestart { get; set; }
//public bool LogChat { get; set; } //public bool LogChat { get; set; }
public bool EnableAutomaticUpdates { get; set; } = true; public bool AutomaticUpdates { get; set; } = true;
public bool RedownloadPlugins { get; set; } public bool RedownloadPlugins { get; set; }
public bool RestartOnCrash { get; set; }
public List<string> Plugins { get; set; } = new List<string>(); public List<string> Plugins { get; set; } = new List<string>();
public Vector2I WindowSize { get; set; } = new Vector2I(800, 600); public Point WindowSize { get; set; } = new Point(800, 600);
public Vector2I WindowPosition { get; set; } = new Vector2I(); public Point WindowPosition { get; set; } = new Point();
[NonSerialized] [NonSerialized]
private string _path; private string _path;
@@ -33,7 +36,7 @@ namespace Torch
public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false) public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false)
{ {
InstanceName = instanceName; InstanceName = instanceName;
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Torch", InstanceName); InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SpaceEngineersDedicated");
//Autosave = autosaveInterval; //Autosave = autosaveInterval;
//AutoRestart = autoRestart; //AutoRestart = autoRestart;
} }
@@ -68,7 +71,7 @@ namespace Torch
try try
{ {
var serializer = new XmlSerializer(typeof(TorchConfig)); var serializer = new XmlSerializer(typeof(TorchConfig));
using (var f = File.OpenWrite(path)) using (var f = File.Create(path))
{ {
serializer.Serialize(f, this); serializer.Serialize(f, this);
} }

View File

@@ -15,6 +15,7 @@ namespace Torch
{ {
public abstract class TorchPluginBase : ITorchPlugin public abstract class TorchPluginBase : ITorchPlugin
{ {
public string StoragePath { get; internal set; }
public Guid Id { get; } public Guid Id { get; }
public Version Version { get; } public Version Version { get; }
public string Name { get; } public string Name { get; }

View File

@@ -53,7 +53,7 @@ namespace Torch.Updater
currentVersion = new Version(manifest.Version); currentVersion = new Version(manifest.Version);
latestVersion = new Version(releases[0].TagName); latestVersion = new Version(releases[0].TagName);
} }
catch (Exception e) catch
{ {
Log.Warn("Invalid version number on manifest or GitHub release"); Log.Warn("Invalid version number on manifest or GitHub release");
return; return;