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
{
//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);
}

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.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" />

View File

@@ -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")]

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 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);
}
}
}

View File

@@ -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")]

View File

@@ -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
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.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);
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();

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;
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
{

View File

@@ -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;
}
}

View File

@@ -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) { }
}
}

View File

@@ -64,8 +64,25 @@ namespace Torch.Server
{
//Can't use Message.Text directly because of object ownership in WPF.
var text = Message.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);
}
}
}

View File

@@ -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>

View File

@@ -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 :)");
}
}
}

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">
<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>

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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,6 +44,9 @@ namespace Torch.Commands
public void Respond(string message, string sender = "Server", string font = MyFontEnum.Blue)
{
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)
{
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}");
});

View File

@@ -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());
}

View File

@@ -26,9 +26,17 @@ namespace Torch.Managers
MessageRecieved?.Invoke(msg, ref sendToOthers);
public void Init()
{
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)
{

View File

@@ -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()

View File

@@ -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)
{

View File

@@ -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();

View File

@@ -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))
{

View File

@@ -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" />

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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; }

View File

@@ -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;