MAJOR CHANGE: Torch can install and update its own DS installation! Full changelog: https://pastebin.com/ybqDM4HP
This commit is contained in:
13
Setup (run before opening solution).bat
Normal file
13
Setup (run before opening solution).bat
Normal 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
|
@@ -2,13 +2,11 @@
|
||||
{
|
||||
public interface ITorchConfig
|
||||
{
|
||||
//bool AutoRestart { get; set; }
|
||||
//int Autosave { get; set; }
|
||||
string InstanceName { get; set; }
|
||||
string InstancePath { get; set; }
|
||||
//bool LogChat { get; set; }
|
||||
bool RedownloadPlugins { get; set; }
|
||||
bool EnableAutomaticUpdates { get; set; }
|
||||
bool AutomaticUpdates { get; set; }
|
||||
bool RestartOnCrash { get; set; }
|
||||
|
||||
bool Save(string path = null);
|
||||
}
|
||||
|
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
18
Torch.API/ModAPI/TorchAPIGateway.cs
Normal file
18
Torch.API/ModAPI/TorchAPIGateway.cs
Normal 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;
|
||||
}*/
|
||||
}
|
@@ -83,9 +83,6 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Torch">
|
||||
<HintPath>..\Torch.Server\bin\x64\Release\Torch.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="VRage">
|
||||
<HintPath>..\GameBinaries\VRage.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -147,7 +144,7 @@
|
||||
<Compile Include="ITorchBase.cs" />
|
||||
<Compile Include="Plugins\IWpfPlugin.cs" />
|
||||
<Compile Include="ModAPI\Ingame\GridExtensions.cs" />
|
||||
<Compile Include="ModAPI\TorchAPI.cs" />
|
||||
<Compile Include="ModAPI\TorchAPIGateway.cs" />
|
||||
<Compile Include="Plugins\PluginAttribute.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServerState.cs" />
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.153.575")]
|
||||
[assembly: AssemblyFileVersion("1.0.153.575")]
|
||||
[assembly: AssemblyVersion("1.0.167.670")]
|
||||
[assembly: AssemblyFileVersion("1.0.167.670")]
|
39
Torch.Server/Managers/DSConfigManager.cs
Normal file
39
Torch.Server/Managers/DSConfigManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,7 +19,12 @@ using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch;
|
||||
using Torch.API;
|
||||
using Torch.Server.Views;
|
||||
using VRage.Game.ModAPI;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using Torch.Server.Managers;
|
||||
using VRage.FileSystem;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
@@ -27,10 +32,22 @@ namespace Torch.Server
|
||||
{
|
||||
private static ITorchServer _server;
|
||||
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]
|
||||
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)
|
||||
{
|
||||
using (var service = new TorchService())
|
||||
@@ -40,7 +57,7 @@ namespace Torch.Server
|
||||
return;
|
||||
}
|
||||
|
||||
var configName = /*args.FirstOrDefault() ??*/ "TorchConfig.xml";
|
||||
var configName = "TorchConfig.xml";
|
||||
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
||||
TorchConfig options;
|
||||
if (File.Exists(configName))
|
||||
@@ -52,23 +69,123 @@ namespace Torch.Server
|
||||
{
|
||||
_log.Info($"Generating default config at {configPath}");
|
||||
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);
|
||||
}
|
||||
|
||||
bool gui = true;
|
||||
foreach (var arg in args)
|
||||
_cli = new TorchCli { Config = options };
|
||||
if (!_cli.Parse(args))
|
||||
return;
|
||||
|
||||
_log.Debug(_cli.ToString());
|
||||
|
||||
if (!string.IsNullOrEmpty(_cli.WaitForPID))
|
||||
{
|
||||
switch (arg)
|
||||
try
|
||||
{
|
||||
case "-noupdate":
|
||||
options.EnableAutomaticUpdates = false;
|
||||
break;
|
||||
case "-nogui":
|
||||
gui = false;
|
||||
break;
|
||||
var pid = int.Parse(_cli.WaitForPID);
|
||||
var waitProc = Process.GetProcessById(pid);
|
||||
_log.Warn($"Waiting for process {pid} to exit.");
|
||||
waitProc.WaitForExit();
|
||||
}
|
||||
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))
|
||||
{
|
||||
@@ -132,16 +249,57 @@ namespace Torch.Server
|
||||
|
||||
_server = new TorchServer(options);
|
||||
_server.Init();
|
||||
if (gui)
|
||||
|
||||
if (!cli.NoGui)
|
||||
{
|
||||
var ui = new TorchUI((TorchServer)_server);
|
||||
ui.LoadConfig(options);
|
||||
ui.ShowDialog();
|
||||
}
|
||||
else
|
||||
|
||||
if (cli.NoGui || cli.Autostart)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.153.575")]
|
||||
[assembly: AssemblyFileVersion("1.0.153.575")]
|
||||
[assembly: AssemblyVersion("1.0.167.670")]
|
||||
[assembly: AssemblyFileVersion("1.0.167.670")]
|
@@ -92,6 +92,7 @@
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
@@ -160,7 +161,8 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CommandLine.cs" />
|
||||
<Compile Include="Managers\DSConfigManager.cs" />
|
||||
<Compile Include="TorchCli.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
@@ -175,6 +177,7 @@
|
||||
<Compile Include="TorchServiceInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
||||
@@ -188,6 +191,7 @@
|
||||
<Compile Include="ViewModels\PluginViewModel.cs" />
|
||||
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
||||
<Compile Include="ViewModels\SteamUserViewModel.cs" />
|
||||
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
||||
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -215,6 +219,9 @@
|
||||
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
||||
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\FirstTimeSetup.xaml.cs">
|
||||
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\ModsControl.xaml.cs">
|
||||
<DependentUpon>ModsControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -297,6 +304,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\FirstTimeSetup.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\ModsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
41
Torch.Server/TorchCli.cs
Normal file
41
Torch.Server/TorchCli.cs
Normal 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; }
|
||||
}
|
||||
}
|
@@ -3,29 +3,46 @@ using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.World;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
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 Torch.API;
|
||||
using Torch.Server.ViewModels.Blocks;
|
||||
using VRage.Dedicated;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ObjectBuilder;
|
||||
using VRage.Game.SessionComponents;
|
||||
using VRage.Library;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.Plugins;
|
||||
using VRage.Trace;
|
||||
using VRage.Utils;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
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 ServerState State { get; private set; }
|
||||
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
||||
public string InstanceName => Config?.InstanceName;
|
||||
public string InstancePath => Config?.InstancePath;
|
||||
|
||||
private ServerState _state;
|
||||
private TimeSpan _elapsedPlayTime;
|
||||
private float _simRatio;
|
||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
||||
|
||||
public TorchServer(TorchConfig config = null)
|
||||
@@ -49,18 +66,68 @@ namespace Torch.Server
|
||||
MyPerServerSettings.AppId = 244850;
|
||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
||||
|
||||
MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
||||
|
||||
InvokeBeforeRun();
|
||||
|
||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
||||
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
||||
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
||||
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
||||
MyPlugins.Load();
|
||||
|
||||
MyGlobalTypeMetadata.Static.Init();
|
||||
MyInitializer.InvokeBeforeRun(
|
||||
MyPerServerSettings.AppId,
|
||||
MyPerServerSettings.GameDSName,
|
||||
InstancePath, DedicatedServer.AddDateToLog);
|
||||
RuntimeHelpers.RunClassConstructor(typeof(MyObjectBuilder_Base).TypeHandle);
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -69,8 +136,9 @@ namespace Torch.Server
|
||||
public override void Start()
|
||||
{
|
||||
if (State != ServerState.Stopped)
|
||||
throw new InvalidOperationException("Server is already running.");
|
||||
return;
|
||||
|
||||
GameThread = Thread.CurrentThread;
|
||||
Config.Save();
|
||||
State = ServerState.Starting;
|
||||
Log.Info("Starting server.");
|
||||
@@ -82,12 +150,9 @@ namespace Torch.Server
|
||||
|
||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
||||
|
||||
do
|
||||
{
|
||||
runInternal.Invoke(null, null);
|
||||
} while (MySandboxGame.IsReloading);
|
||||
runInternal.Invoke(null, null);
|
||||
|
||||
MyInitializer.InvokeAfterRun();
|
||||
MySandboxGame.Log.Close();
|
||||
State = ServerState.Stopped;
|
||||
}
|
||||
|
||||
@@ -99,6 +164,14 @@ namespace Torch.Server
|
||||
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
SimulationRatio = Sync.ServerSimulationRatio;
|
||||
ElapsedPlayTime = MySession.Static?.ElapsedPlayTime ?? default(TimeSpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the server.
|
||||
/// </summary>
|
||||
@@ -107,21 +180,18 @@ namespace Torch.Server
|
||||
if (State == ServerState.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);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Stopping server.");
|
||||
MySession.Static.Save();
|
||||
MySession.Static.Unload();
|
||||
|
||||
//Unload all the static junk.
|
||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
||||
MyFileSystem.Reset();
|
||||
VRage.Input.MyGuiGameControlsHelpers.Reset();
|
||||
VRage.Input.MyInput.UnloadData();
|
||||
MySandboxGame.Static.Exit();
|
||||
|
||||
Log.Info("Server stopped.");
|
||||
_stopHandle.Set();
|
||||
|
47
Torch.Server/ViewModels/BlockLimitViewModel.cs
Normal file
47
Torch.Server/ViewModels/BlockLimitViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,30 +14,46 @@ namespace Torch.Server.ViewModels
|
||||
{
|
||||
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)
|
||||
{
|
||||
_config = configDedicated;
|
||||
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
||||
Administrators = new ObservableCollection<string>(_config.Administrators);
|
||||
Banned = new ObservableCollection<ulong>(_config.Banned);
|
||||
Mods = new ObservableCollection<ulong>(_config.Mods);
|
||||
Administrators = string.Join("\r\n", _config.Administrators);
|
||||
Banned = string.Join("\r\n", _config.Banned);
|
||||
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 ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>();
|
||||
public ObservableCollection<string> Administrators { get; }
|
||||
public ObservableCollection<ulong> Banned { get; }
|
||||
public ObservableCollection<ulong> Mods { get; }
|
||||
public string Administrators { get; set; }
|
||||
public string Banned { get; set; }
|
||||
public string Mods { get; set; }
|
||||
|
||||
public int AsteroidAmount
|
||||
{
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpDX.Toolkit.Collections;
|
||||
using VRage.Game;
|
||||
using VRage.Library.Utils;
|
||||
|
||||
@@ -12,135 +14,120 @@ namespace Torch.Server.ViewModels
|
||||
{
|
||||
private MyObjectBuilder_SessionSettings _settings;
|
||||
|
||||
public SessionSettingsViewModel()
|
||||
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
||||
{
|
||||
_settings = new MyObjectBuilder_SessionSettings();
|
||||
|
||||
}
|
||||
|
||||
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings 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
|
||||
public float InventorySizeMultiplier
|
||||
{
|
||||
get { return _settings.InventorySizeMultiplier; }
|
||||
set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.InventorySizeMultiplier; set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float RefinerySpeedMultiplier
|
||||
{
|
||||
get { return _settings.RefinerySpeedMultiplier; }
|
||||
set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.RefinerySpeedMultiplier; set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float AssemblerEfficiencyMultiplier
|
||||
{
|
||||
get { return _settings.AssemblerEfficiencyMultiplier; }
|
||||
set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.AssemblerEfficiencyMultiplier; set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float AssemblerSpeedMultiplier
|
||||
{
|
||||
get { return _settings.AssemblerSpeedMultiplier; }
|
||||
set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.AssemblerSpeedMultiplier; set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float GrinderSpeedMultiplier
|
||||
{
|
||||
get { return _settings.GrinderSpeedMultiplier; }
|
||||
set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.GrinderSpeedMultiplier; set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float HackSpeedMultiplier
|
||||
{
|
||||
get { return _settings.HackSpeedMultiplier; }
|
||||
set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region NPCs
|
||||
public bool EnableDrones
|
||||
{
|
||||
get { return _settings.EnableDrones; }
|
||||
set { _settings.EnableDrones = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableDrones; set { _settings.EnableDrones = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableEncounters
|
||||
{
|
||||
get { return _settings.EnableEncounters; }
|
||||
set { _settings.EnableEncounters = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableEncounters; set { _settings.EnableEncounters = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableSpiders
|
||||
{
|
||||
get { return _settings.EnableSpiders; }
|
||||
set { _settings.EnableSpiders = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableSpiders; set { _settings.EnableSpiders = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableWolves
|
||||
{
|
||||
get { return _settings.EnableWolfs; }
|
||||
set { _settings.EnableWolfs = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableWolfs; set { _settings.EnableWolfs = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableCargoShips
|
||||
{
|
||||
get { return _settings.CargoShipsEnabled; }
|
||||
set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
|
||||
get => _settings.CargoShipsEnabled; set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Environment
|
||||
public bool EnableSunRotation
|
||||
{
|
||||
get { return _settings.EnableSunRotation; }
|
||||
set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableSunRotation; set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableAirtightness
|
||||
{
|
||||
get { return _settings.EnableOxygenPressurization; }
|
||||
set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableOxygenPressurization; set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableOxygen
|
||||
{
|
||||
get { return _settings.EnableOxygen; }
|
||||
set { _settings.EnableOxygen = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableOxygen; set { _settings.EnableOxygen = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableDestructibleBlocks
|
||||
{
|
||||
get { return _settings.DestructibleBlocks; }
|
||||
set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
|
||||
get => _settings.DestructibleBlocks; set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableToolShake
|
||||
{
|
||||
get { return _settings.EnableToolShake; }
|
||||
set { _settings.EnableToolShake = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableToolShake; set { _settings.EnableToolShake = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableVoxelDestruction
|
||||
{
|
||||
get { return _settings.EnableVoxelDestruction; }
|
||||
set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableVoxelDestruction; set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public List<string> EnvironmentHostilityValues => Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
|
||||
|
||||
public string EnvironmentHostility
|
||||
{
|
||||
get { return _settings.EnvironmentHostility.ToString(); }
|
||||
set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
|
||||
get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableFlora
|
||||
{
|
||||
get { return _settings.EnableFlora; }
|
||||
set { _settings.EnableFlora = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableFlora; set { _settings.EnableFlora = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -148,192 +135,164 @@ namespace Torch.Server.ViewModels
|
||||
|
||||
public string GameMode
|
||||
{
|
||||
get { return _settings.GameMode.ToString(); }
|
||||
set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
|
||||
get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableAutoHealing
|
||||
{
|
||||
get { return _settings.AutoHealing; }
|
||||
set { _settings.AutoHealing = value; OnPropertyChanged(); }
|
||||
get => _settings.AutoHealing; set { _settings.AutoHealing = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableCopyPaste
|
||||
{
|
||||
get { return _settings.EnableCopyPaste; }
|
||||
set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableCopyPaste; set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool ShowPlayerNamesOnHud
|
||||
{
|
||||
get { return _settings.ShowPlayerNamesOnHud; }
|
||||
set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
|
||||
get => _settings.ShowPlayerNamesOnHud; set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableThirdPerson
|
||||
{
|
||||
get { return _settings.Enable3rdPersonView; }
|
||||
set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
|
||||
get => _settings.Enable3rdPersonView; set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableSpectator
|
||||
{
|
||||
get { return _settings.EnableSpectator; }
|
||||
set { _settings.EnableSpectator = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableSpectator; set { _settings.EnableSpectator = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool SpawnWithTools
|
||||
{
|
||||
get { return _settings.SpawnWithTools; }
|
||||
set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
|
||||
get => _settings.SpawnWithTools; set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableConvertToStation
|
||||
{
|
||||
get { return _settings.EnableConvertToStation; }
|
||||
set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableConvertToStation; set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableJetpack
|
||||
{
|
||||
get { return _settings.EnableJetpack; }
|
||||
set { _settings.EnableJetpack = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableJetpack; set { _settings.EnableJetpack = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableRemoteOwnerRemoval
|
||||
{
|
||||
get { return _settings.EnableRemoteBlockRemoval; }
|
||||
set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableRemoteBlockRemoval; set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableRespawnShips
|
||||
{
|
||||
get { return _settings.EnableRespawnShips; }
|
||||
set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableRespawnShips; set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableScripterRole
|
||||
{
|
||||
get { return _settings.EnableScripterRole; }
|
||||
set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableScripterRole; set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableRealisticSound
|
||||
{
|
||||
get { return _settings.RealisticSound; }
|
||||
set { _settings.RealisticSound = value; OnPropertyChanged(); }
|
||||
get => _settings.RealisticSound; set { _settings.RealisticSound = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool ResetOwnership
|
||||
{
|
||||
get { return _settings.ResetOwnership; }
|
||||
set { _settings.ResetOwnership = value; OnPropertyChanged(); }
|
||||
get => _settings.ResetOwnership; set { _settings.ResetOwnership = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool DeleteRespawnShips
|
||||
{
|
||||
get { return _settings.RespawnShipDelete; }
|
||||
set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
|
||||
get => _settings.RespawnShipDelete; set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableThrusterDamage
|
||||
{
|
||||
get { return _settings.ThrusterDamage; }
|
||||
set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
|
||||
get => _settings.ThrusterDamage; set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableWeapons
|
||||
{
|
||||
get { return _settings.WeaponsEnabled; }
|
||||
set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
|
||||
get => _settings.WeaponsEnabled; set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public bool EnableIngameScripts
|
||||
{
|
||||
get { return _settings.EnableIngameScripts; }
|
||||
set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
|
||||
get => _settings.EnableIngameScripts; set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public uint AutosaveInterval
|
||||
{
|
||||
get { return _settings.AutoSaveInMinutes; }
|
||||
set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
|
||||
get => _settings.AutoSaveInMinutes; set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int FloraDensity
|
||||
{
|
||||
get { return _settings.FloraDensity; }
|
||||
set { _settings.FloraDensity = value; OnPropertyChanged(); }
|
||||
get => _settings.FloraDensity; set { _settings.FloraDensity = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float FloraDensityMultiplier
|
||||
{
|
||||
get { return _settings.FloraDensityMultiplier; }
|
||||
set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.FloraDensityMultiplier; set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public short MaxBackupSaves
|
||||
{
|
||||
get { return _settings.MaxBackupSaves; }
|
||||
set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
|
||||
get => _settings.MaxBackupSaves; set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int MaxBlocksPerPlayer
|
||||
{
|
||||
get { return _settings.MaxBlocksPerPlayer; }
|
||||
set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
|
||||
get => _settings.MaxBlocksPerPlayer; set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public short MaxFloatingObjects
|
||||
{
|
||||
get { return _settings.MaxFloatingObjects; }
|
||||
set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
|
||||
get => _settings.MaxFloatingObjects; set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int MaxGridSize
|
||||
{
|
||||
get { return _settings.MaxGridSize; }
|
||||
set { _settings.MaxGridSize = value; OnPropertyChanged(); }
|
||||
get => _settings.MaxGridSize; set { _settings.MaxGridSize = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public short MaxPlayers
|
||||
{
|
||||
get { return _settings.MaxPlayers; }
|
||||
set { _settings.MaxPlayers = value; OnPropertyChanged(); }
|
||||
get => _settings.MaxPlayers; set { _settings.MaxPlayers = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int PhysicsIterations
|
||||
{
|
||||
get { return _settings.PhysicsIterations; }
|
||||
set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
|
||||
get => _settings.PhysicsIterations; set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float SpawnTimeMultiplier
|
||||
{
|
||||
get { return _settings.SpawnShipTimeMultiplier; }
|
||||
set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
|
||||
get => _settings.SpawnShipTimeMultiplier; set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public float SunRotationInterval
|
||||
{
|
||||
get { return _settings.SunRotationIntervalMinutes; }
|
||||
set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
|
||||
get => _settings.SunRotationIntervalMinutes; set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int ViewDistance
|
||||
{
|
||||
get { return _settings.ViewDistance; }
|
||||
set { _settings.ViewDistance = value; OnPropertyChanged(); }
|
||||
get => _settings.ViewDistance; set { _settings.ViewDistance = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public int WorldSize
|
||||
{
|
||||
get { return _settings.ViewDistance; }
|
||||
set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
||||
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -3,10 +3,34 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SteamSDK;
|
||||
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
|
@@ -64,8 +64,25 @@ namespace Torch.Server
|
||||
{
|
||||
//Can't use Message.Text directly because of object ownership in WPF.
|
||||
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 = "";
|
||||
var sto = false;
|
||||
Refresh(null, ref sto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top">
|
||||
<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" />
|
||||
</DockPanel>
|
||||
<DockPanel DockPanel.Dock="Bottom">
|
||||
@@ -36,15 +37,30 @@
|
||||
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="3">
|
||||
<Label Content="Banned Players" />
|
||||
<!--
|
||||
<ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100"
|
||||
MouseDoubleClick="Banned_OnMouseDoubleClick" />
|
||||
<Label Content="Administrators" />
|
||||
MouseDoubleClick="Banned_OnMouseDoubleClick" /> -->
|
||||
<!--
|
||||
<ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100"
|
||||
MouseDoubleClick="Administrators_OnMouseDoubleClick" />
|
||||
<Label Content="Mods" />
|
||||
MouseDoubleClick="Administrators_OnMouseDoubleClick">
|
||||
<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"
|
||||
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>
|
||||
</ScrollViewer>
|
||||
@@ -52,6 +68,30 @@
|
||||
</StackPanel>
|
||||
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
|
||||
<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">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<DockPanel>
|
||||
@@ -91,6 +131,14 @@
|
||||
</Expander>
|
||||
<Expander Header="Environment">
|
||||
<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"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
||||
@@ -136,6 +184,9 @@
|
||||
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
||||
<Label Content="Max Players" />
|
||||
</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 EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
||||
@@ -144,6 +195,8 @@
|
||||
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Respawn Time Multiplier" />
|
||||
</DockPanel>
|
||||
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Miscellaneous">
|
||||
@@ -152,11 +205,9 @@
|
||||
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
||||
<Label Content="Autosave Interval (minutes)" />
|
||||
</DockPanel>
|
||||
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
||||
|
||||
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
|
||||
Content="Enable Remote Ownership Removal" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
|
||||
@@ -165,7 +216,6 @@
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
|
||||
@@ -173,7 +223,6 @@
|
||||
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
||||
<DockPanel>
|
||||
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
||||
Margin="3" Width="100" DockPanel.Dock="Left" />
|
||||
@@ -183,24 +232,6 @@
|
||||
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
||||
<Label Content="Max Backup Saves" />
|
||||
</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>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -15,14 +16,17 @@ using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Xml.Serialization;
|
||||
using NLog;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Torch.Server.ViewModels;
|
||||
using Torch.Views;
|
||||
using VRage;
|
||||
using VRage.Dedicated;
|
||||
using VRage.Game;
|
||||
using VRage.ObjectBuilders;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Torch.Server.Views
|
||||
@@ -32,6 +36,7 @@ namespace Torch.Server.Views
|
||||
/// </summary>
|
||||
public partial class ConfigControl : UserControl
|
||||
{
|
||||
private readonly Logger Log = LogManager.GetLogger("Config");
|
||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
|
||||
private ConfigDedicatedViewModel _viewModel;
|
||||
private string _configPath;
|
||||
@@ -43,22 +48,23 @@ namespace Torch.Server.Views
|
||||
|
||||
public void SaveConfig()
|
||||
{
|
||||
Config.Save(_configPath);
|
||||
_viewModel.Save(_configPath);
|
||||
Log.Info("Saved DS config.");
|
||||
try
|
||||
{
|
||||
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out ulong size);
|
||||
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out _);
|
||||
checkpoint.Settings = _viewModel.SessionSettings;
|
||||
checkpoint.Mods.Clear();
|
||||
foreach (var modId in _viewModel.Mods)
|
||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||
|
||||
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
|
||||
Log.Info("Saved world config.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var log = LogManager.GetLogger("Torch");
|
||||
log.Error("Failed to overwrite sandbox config, changes will not appear on server");
|
||||
log.Error(e);
|
||||
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +83,18 @@ namespace Torch.Server.Views
|
||||
Config.Load(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);
|
||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
|
||||
|
||||
@@ -86,6 +104,7 @@ namespace Torch.Server.Views
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
/*
|
||||
private void Banned_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var editor = new CollectionEditor {Owner = Window.GetWindow(this)};
|
||||
@@ -102,11 +121,27 @@ namespace Torch.Server.Views
|
||||
{
|
||||
var editor = new CollectionEditor { Owner = Window.GetWindow(this) };
|
||||
editor.Edit(_viewModel.Mods, "Mods");
|
||||
}
|
||||
}*/
|
||||
|
||||
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
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 :)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
Torch.Server/Views/FirstTimeSetup.xaml
Normal file
29
Torch.Server/Views/FirstTimeSetup.xaml
Normal 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>
|
32
Torch.Server/Views/FirstTimeSetup.xaml.cs
Normal file
32
Torch.Server/Views/FirstTimeSetup.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,35 +9,49 @@
|
||||
Title="Torch">
|
||||
<DockPanel>
|
||||
<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="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStop_Click" IsEnabled="False"/>
|
||||
<Button x:Name="BtnRestart" Content="Restart" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnRestart_Click"/>
|
||||
<Label x:Name="LabelSimulation" Content="Sim Ratio: 0.00"/>
|
||||
<Label x:Name="LabelUptime" Content="Uptime: 0d 0h 0m"/>
|
||||
<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="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
|
||||
Click="BtnStop_Click" IsEnabled="False" />
|
||||
<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>
|
||||
<TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5">
|
||||
<TabItem Header="Configuration">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top">
|
||||
<Label Content="Instance Path: " Margin="3"/>
|
||||
<TextBox x:Name="InstancePathBox" Margin="3" Height="20" TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False"/>
|
||||
<Label Content="Instance Path: " Margin="3" />
|
||||
<TextBox x:Name="InstancePathBox" Margin="3" Height="20"
|
||||
TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False" />
|
||||
</DockPanel>
|
||||
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom"/>
|
||||
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Chat/Players">
|
||||
<DockPanel>
|
||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False"/>
|
||||
<local:ChatControl x:Name="Chat" IsEnabled="False"/>
|
||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False" />
|
||||
<local:ChatControl x:Name="Chat" IsEnabled="False" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Entity Manager">
|
||||
<views:EntitiesControl/>
|
||||
<views:EntitiesControl />
|
||||
</TabItem>
|
||||
<TabItem Header="Plugins">
|
||||
<views:PluginsControl x:Name="Plugins"/>
|
||||
<views:PluginsControl x:Name="Plugins" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
</Window>
|
@@ -19,7 +19,6 @@ using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Sandbox;
|
||||
using Torch.API;
|
||||
using VRageMath;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Torch.Server
|
||||
@@ -31,26 +30,20 @@ namespace Torch.Server
|
||||
{
|
||||
private TorchServer _server;
|
||||
private TorchConfig _config;
|
||||
private DateTime _startTime;
|
||||
private readonly Timer _uiUpdate = new Timer
|
||||
{
|
||||
Interval = 1000,
|
||||
AutoReset = true,
|
||||
};
|
||||
|
||||
public TorchUI(TorchServer server)
|
||||
{
|
||||
_config = (TorchConfig)server.Config;
|
||||
_server = server;
|
||||
InitializeComponent();
|
||||
_startTime = DateTime.Now;
|
||||
_uiUpdate.Elapsed += UiUpdate_Elapsed;
|
||||
|
||||
Left = _config.WindowPosition.X;
|
||||
Top = _config.WindowPosition.Y;
|
||||
Width = _config.WindowSize.X;
|
||||
Height = _config.WindowSize.Y;
|
||||
|
||||
//TODO: data binding for whole server
|
||||
DataContext = server;
|
||||
Chat.BindServer(server);
|
||||
PlayerList.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)
|
||||
{
|
||||
_startTime = DateTime.Now;
|
||||
_config.Save();
|
||||
Chat.IsEnabled = true;
|
||||
PlayerList.IsEnabled = true;
|
||||
((Button) sender).IsEnabled = false;
|
||||
BtnStop.IsEnabled = true;
|
||||
_uiUpdate.Start();
|
||||
ConfigControl.SaveConfig();
|
||||
new Thread(_server.Start).Start();
|
||||
}
|
||||
|
||||
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_config.Save();
|
||||
Chat.IsEnabled = false;
|
||||
PlayerList.IsEnabled = false;
|
||||
((Button) sender).IsEnabled = false;
|
||||
//HACK: Uncomment when restarting is possible.
|
||||
//BtnStart.IsEnabled = true;
|
||||
_uiUpdate.Stop();
|
||||
_server.Stop();
|
||||
}
|
||||
|
||||
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;
|
||||
var newPos = new Vector2I((int)Left, (int)Top);
|
||||
var newPos = new Point((int)Left, (int)Top);
|
||||
_config.WindowPosition = newPos;
|
||||
_config.Save();
|
||||
|
||||
@@ -124,7 +103,7 @@ namespace Torch.Server
|
||||
|
||||
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
var name = (sender as TextBox).Text;
|
||||
var name = ((TextBox)sender).Text;
|
||||
|
||||
_config.InstancePath = name;
|
||||
|
||||
|
@@ -5,25 +5,20 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.Server
|
||||
namespace Torch
|
||||
{
|
||||
public class CommandLine
|
||||
{
|
||||
public TorchConfig Config { get; }
|
||||
private string _argPrefix;
|
||||
private readonly string _argPrefix;
|
||||
|
||||
[Arg("instancepath", "Server data folder where saves and mods are stored")]
|
||||
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
|
||||
|
||||
public CommandLine(TorchConfig config, string argPrefix)
|
||||
public CommandLine(string argPrefix = "-")
|
||||
{
|
||||
Config = config;
|
||||
_argPrefix = argPrefix;
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -33,18 +28,43 @@ namespace Torch.Server
|
||||
foreach (var property in GetArgs())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
Console.WriteLine(GetHelp());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var properties = GetArgs();
|
||||
@@ -71,16 +91,18 @@ namespace Torch.Server
|
||||
property.SetValue(this, args[++i]);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Error parsing arg {argName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private class ArgAttribute : Attribute
|
||||
public class ArgAttribute : Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
@@ -31,6 +31,8 @@ namespace Torch.Commands
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
Torch = torch;
|
||||
@@ -42,7 +44,10 @@ namespace Torch.Commands
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
{
|
||||
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;
|
||||
|
||||
sendToOthers = false;
|
||||
|
||||
var player = _torch.Multiplayer.GetPlayerBySteamId(msg.Author);
|
||||
var player = _torch.Multiplayer.GetPlayerBySteamId(steamId);
|
||||
if (player == null)
|
||||
{
|
||||
_log.Error($"Command {msg.Text} invoked by nonexistant player");
|
||||
_log.Error($"Command {message} invoked by nonexistant player");
|
||||
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);
|
||||
|
||||
if (command != null)
|
||||
{
|
||||
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");
|
||||
_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(() =>
|
||||
{
|
||||
if (command.TryInvoke(context))
|
||||
_log.Info($"Player {player.DisplayName} ran command '{msg.Text}'");
|
||||
_log.Info($"Player {player.DisplayName} ran command '{message}'");
|
||||
else
|
||||
context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
|
||||
});
|
||||
|
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch;
|
||||
using Torch.Commands.Permissions;
|
||||
using Torch.Managers;
|
||||
using VRage.Game.ModAPI;
|
||||
@@ -26,10 +27,17 @@ namespace Torch.Commands
|
||||
Console.WriteLine(commandManager.Commands.GetTreeString());
|
||||
}
|
||||
#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")]
|
||||
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);
|
||||
|
||||
if (node != null)
|
||||
@@ -42,10 +50,11 @@ namespace Torch.Commands
|
||||
if (command != null)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
@@ -27,7 +27,15 @@ namespace Torch.Managers
|
||||
|
||||
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)
|
||||
|
@@ -96,6 +96,7 @@ namespace Torch.Managers
|
||||
{
|
||||
var msg = new ScriptedChatMsg {Author = author, Font = font, Target = playerId, Text = message};
|
||||
MyMultiplayerBase.SendScriptedChatMessage(ref msg);
|
||||
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message));
|
||||
}
|
||||
|
||||
private void OnSessionLoaded()
|
||||
|
@@ -30,7 +30,6 @@ namespace Torch.Managers
|
||||
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
|
||||
|
||||
public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>();
|
||||
public CommandManager Commands { get; private set; }
|
||||
|
||||
public float LastUpdateMs => _lastUpdateMs;
|
||||
private volatile float _lastUpdateMs;
|
||||
@@ -51,7 +50,8 @@ namespace Torch.Managers
|
||||
public void UpdatePlugins()
|
||||
{
|
||||
var s = Stopwatch.StartNew();
|
||||
Parallel.ForEach(Plugins, p => p.Update());
|
||||
foreach (var plugin in Plugins)
|
||||
plugin.Update();
|
||||
s.Stop();
|
||||
_lastUpdateMs = (float)s.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
@@ -100,9 +100,9 @@ namespace Torch.Managers
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
Commands = new CommandManager(_torch);
|
||||
var commands = ((TorchBase)_torch).Commands;
|
||||
|
||||
if (_torch.Config.EnableAutomaticUpdates)
|
||||
if (_torch.Config.AutomaticUpdates)
|
||||
DownloadPlugins();
|
||||
else
|
||||
_log.Warn("Automatic plugin updates are disabled.");
|
||||
@@ -111,6 +111,7 @@ namespace Torch.Managers
|
||||
var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories);
|
||||
foreach (var dllPath in dlls)
|
||||
{
|
||||
_log.Debug($"Loading plugin {dllPath}");
|
||||
var asm = Assembly.UnsafeLoadFrom(dllPath);
|
||||
|
||||
foreach (var type in asm.GetExportedTypes())
|
||||
@@ -119,14 +120,15 @@ namespace Torch.Managers
|
||||
{
|
||||
try
|
||||
{
|
||||
var plugin = (ITorchPlugin)Activator.CreateInstance(type);
|
||||
var plugin = (TorchPluginBase)Activator.CreateInstance(type);
|
||||
if (plugin.Id == default(Guid))
|
||||
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
|
||||
|
||||
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
|
||||
plugin.StoragePath = new FileInfo(asm.Location).Directory.FullName;
|
||||
Plugins.Add(plugin);
|
||||
|
||||
Commands.RegisterPluginCommands(plugin);
|
||||
commands.RegisterPluginCommands(plugin);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@@ -3,7 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch.API;
|
||||
using Torch.API.ModAPI;
|
||||
using Torch.API.ModAPI.Ingame;
|
||||
using VRage.Scripting;
|
||||
|
||||
@@ -20,6 +22,12 @@ namespace Torch.Managers
|
||||
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location);
|
||||
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(typeof(GridExtensions));
|
||||
|
||||
using (var whitelist = _whitelist.OpenBatch())
|
||||
{
|
||||
//whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.ModApi, typeof(TorchAPIGateway));
|
||||
whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.Both, typeof(GridExtensions));
|
||||
}
|
||||
|
||||
/*
|
||||
//dump whitelist
|
||||
var whitelist = new StringBuilder();
|
||||
|
@@ -16,13 +16,19 @@ namespace Torch
|
||||
{
|
||||
[JsonIgnore]
|
||||
public string Path { get; set; }
|
||||
public T Data { get; private set; } = new T();
|
||||
public T Data { get; private set; }
|
||||
|
||||
~Persistent()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public Persistent(string path, T data = default(T))
|
||||
{
|
||||
Path = path;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public void Save(string path = null)
|
||||
{
|
||||
if (path == null)
|
||||
@@ -38,7 +44,7 @@ namespace Torch
|
||||
|
||||
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))
|
||||
{
|
||||
|
@@ -124,6 +124,7 @@
|
||||
<Compile Include="ChatMessage.cs" />
|
||||
<Compile Include="Collections\KeyTree.cs" />
|
||||
<Compile Include="Collections\RollingAverage.cs" />
|
||||
<Compile Include="CommandLine.cs" />
|
||||
<Compile Include="Commands\CategoryAttribute.cs" />
|
||||
<Compile Include="Commands\Command.cs" />
|
||||
<Compile Include="Commands\CommandAttribute.cs" />
|
||||
|
@@ -4,6 +4,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,8 +17,11 @@ using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using SpaceEngineers.Game;
|
||||
using Torch.API;
|
||||
using Torch.Commands;
|
||||
using Torch.Managers;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game.ObjectBuilder;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.Plugins;
|
||||
using VRage.Scripting;
|
||||
using VRage.Utils;
|
||||
@@ -40,6 +44,7 @@ namespace Torch
|
||||
public IMultiplayer Multiplayer { get; protected set; }
|
||||
public EntityManager Entities { get; protected set; }
|
||||
public NetworkManager Network { get; protected set; }
|
||||
public CommandManager Commands { get; protected set; }
|
||||
public event Action SessionLoading;
|
||||
public event Action SessionLoaded;
|
||||
public event Action SessionUnloading;
|
||||
@@ -60,6 +65,7 @@ namespace Torch
|
||||
Multiplayer = new MultiplayerManager(this);
|
||||
Entities = new EntityManager(this);
|
||||
Network = NetworkManager.Instance;
|
||||
Commands = new CommandManager(this);
|
||||
}
|
||||
|
||||
public bool IsOnGameThread()
|
||||
@@ -147,10 +153,6 @@ namespace Torch
|
||||
{
|
||||
action.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//log
|
||||
}
|
||||
finally
|
||||
{
|
||||
e.Set();
|
||||
@@ -167,10 +169,19 @@ namespace Torch
|
||||
{
|
||||
Debug.Assert(!_init, "Torch instance is already initialized.");
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
SpaceEngineersGame.SetupBasicGameInfo();
|
||||
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;
|
||||
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
||||
var verInfo = $"Torch {TorchVersion}, SE {GameVersion}";
|
||||
@@ -188,7 +199,7 @@ namespace Torch
|
||||
MySession.AfterLoading += () => SessionLoaded?.Invoke();
|
||||
MySession.OnUnloading += () => SessionUnloading?.Invoke();
|
||||
MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
|
||||
InitUpdater();
|
||||
RegisterVRagePlugin();
|
||||
|
||||
_init = true;
|
||||
}
|
||||
@@ -196,7 +207,7 @@ namespace Torch
|
||||
/// <summary>
|
||||
/// Hook into the VRage plugin system for updates.
|
||||
/// </summary>
|
||||
private void InitUpdater()
|
||||
private void RegisterVRagePlugin()
|
||||
{
|
||||
var fieldName = "m_plugins";
|
||||
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
||||
@@ -206,20 +217,6 @@ namespace Torch
|
||||
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 Stop();
|
||||
|
||||
|
@@ -4,10 +4,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Xml.Serialization;
|
||||
using NLog;
|
||||
using Sandbox.ModAPI.Ingame;
|
||||
using VRageMath;
|
||||
|
||||
namespace Torch
|
||||
{
|
||||
@@ -17,14 +17,17 @@ namespace Torch
|
||||
|
||||
public string InstancePath { get; set; }
|
||||
public string InstanceName { get; set; }
|
||||
#warning World Path not implemented
|
||||
public string WorldPath { get; set; }
|
||||
//public int Autosave { get; set; }
|
||||
//public bool AutoRestart { 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 RestartOnCrash { get; set; }
|
||||
public List<string> Plugins { get; set; } = new List<string>();
|
||||
public Vector2I WindowSize { get; set; } = new Vector2I(800, 600);
|
||||
public Vector2I WindowPosition { get; set; } = new Vector2I();
|
||||
public Point WindowSize { get; set; } = new Point(800, 600);
|
||||
public Point WindowPosition { get; set; } = new Point();
|
||||
[NonSerialized]
|
||||
private string _path;
|
||||
|
||||
@@ -33,7 +36,7 @@ namespace Torch
|
||||
public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false)
|
||||
{
|
||||
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;
|
||||
//AutoRestart = autoRestart;
|
||||
}
|
||||
@@ -68,7 +71,7 @@ namespace Torch
|
||||
try
|
||||
{
|
||||
var serializer = new XmlSerializer(typeof(TorchConfig));
|
||||
using (var f = File.OpenWrite(path))
|
||||
using (var f = File.Create(path))
|
||||
{
|
||||
serializer.Serialize(f, this);
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ namespace Torch
|
||||
{
|
||||
public abstract class TorchPluginBase : ITorchPlugin
|
||||
{
|
||||
public string StoragePath { get; internal set; }
|
||||
public Guid Id { get; }
|
||||
public Version Version { get; }
|
||||
public string Name { get; }
|
||||
|
@@ -53,7 +53,7 @@ namespace Torch.Updater
|
||||
currentVersion = new Version(manifest.Version);
|
||||
latestVersion = new Version(releases[0].TagName);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch
|
||||
{
|
||||
Log.Warn("Invalid version number on manifest or GitHub release");
|
||||
return;
|
||||
|
Reference in New Issue
Block a user