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
|
public interface ITorchConfig
|
||||||
{
|
{
|
||||||
//bool AutoRestart { get; set; }
|
|
||||||
//int Autosave { get; set; }
|
|
||||||
string InstanceName { get; set; }
|
string InstanceName { get; set; }
|
||||||
string InstancePath { get; set; }
|
string InstancePath { get; set; }
|
||||||
//bool LogChat { get; set; }
|
|
||||||
bool RedownloadPlugins { get; set; }
|
bool RedownloadPlugins { get; set; }
|
||||||
bool EnableAutomaticUpdates { get; set; }
|
bool AutomaticUpdates { get; set; }
|
||||||
|
bool RestartOnCrash { get; set; }
|
||||||
|
|
||||||
bool Save(string path = null);
|
bool Save(string path = null);
|
||||||
}
|
}
|
||||||
|
@@ -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.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Torch">
|
|
||||||
<HintPath>..\Torch.Server\bin\x64\Release\Torch.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="VRage">
|
<Reference Include="VRage">
|
||||||
<HintPath>..\GameBinaries\VRage.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@@ -147,7 +144,7 @@
|
|||||||
<Compile Include="ITorchBase.cs" />
|
<Compile Include="ITorchBase.cs" />
|
||||||
<Compile Include="Plugins\IWpfPlugin.cs" />
|
<Compile Include="Plugins\IWpfPlugin.cs" />
|
||||||
<Compile Include="ModAPI\Ingame\GridExtensions.cs" />
|
<Compile Include="ModAPI\Ingame\GridExtensions.cs" />
|
||||||
<Compile Include="ModAPI\TorchAPI.cs" />
|
<Compile Include="ModAPI\TorchAPIGateway.cs" />
|
||||||
<Compile Include="Plugins\PluginAttribute.cs" />
|
<Compile Include="Plugins\PluginAttribute.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerState.cs" />
|
<Compile Include="ServerState.cs" />
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.153.575")]
|
[assembly: AssemblyVersion("1.0.167.670")]
|
||||||
[assembly: AssemblyFileVersion("1.0.153.575")]
|
[assembly: AssemblyFileVersion("1.0.167.670")]
|
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 Sandbox.ModAPI;
|
||||||
using Torch;
|
using Torch;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.Server.Views;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Net;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using VRage.FileSystem;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
@@ -27,10 +32,22 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
private static ITorchServer _server;
|
private static ITorchServer _server;
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
private static Logger _log = LogManager.GetLogger("Torch");
|
||||||
|
private static bool _restartOnCrash;
|
||||||
|
public static bool IsManualInstall;
|
||||||
|
private static TorchCli _cli;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
|
||||||
|
/// </summary>
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
IsManualInstall = Directory.GetCurrentDirectory().Contains("DedicatedServer64");
|
||||||
|
if (!IsManualInstall)
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||||
|
|
||||||
if (!Environment.UserInteractive)
|
if (!Environment.UserInteractive)
|
||||||
{
|
{
|
||||||
using (var service = new TorchService())
|
using (var service = new TorchService())
|
||||||
@@ -40,7 +57,7 @@ namespace Torch.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configName = /*args.FirstOrDefault() ??*/ "TorchConfig.xml";
|
var configName = "TorchConfig.xml";
|
||||||
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
||||||
TorchConfig options;
|
TorchConfig options;
|
||||||
if (File.Exists(configName))
|
if (File.Exists(configName))
|
||||||
@@ -52,23 +69,123 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
_log.Info($"Generating default config at {configPath}");
|
_log.Info($"Generating default config at {configPath}");
|
||||||
options = new TorchConfig();
|
options = new TorchConfig();
|
||||||
|
|
||||||
|
if (!IsManualInstall)
|
||||||
|
{
|
||||||
|
new DSConfigManager().CreateInstance("Instance");
|
||||||
|
options.InstancePath = Path.GetFullPath("Instance");
|
||||||
|
|
||||||
|
_log.Warn("Would you like to enable automatic updates? (Y/n):");
|
||||||
|
|
||||||
|
var input = Console.ReadLine() ?? "";
|
||||||
|
var autoUpdate = !input.Equals("n", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
options.AutomaticUpdates = autoUpdate;
|
||||||
|
if (autoUpdate)
|
||||||
|
_log.Info("Automatic updates enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//var setupDialog = new FirstTimeSetup { DataContext = options };
|
||||||
|
//setupDialog.ShowDialog();
|
||||||
options.Save(configPath);
|
options.Save(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gui = true;
|
_cli = new TorchCli { Config = options };
|
||||||
foreach (var arg in args)
|
if (!_cli.Parse(args))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_log.Debug(_cli.ToString());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_cli.WaitForPID))
|
||||||
{
|
{
|
||||||
switch (arg)
|
try
|
||||||
{
|
{
|
||||||
case "-noupdate":
|
var pid = int.Parse(_cli.WaitForPID);
|
||||||
options.EnableAutomaticUpdates = false;
|
var waitProc = Process.GetProcessById(pid);
|
||||||
break;
|
_log.Warn($"Waiting for process {pid} to exit.");
|
||||||
case "-nogui":
|
waitProc.WaitForExit();
|
||||||
gui = false;
|
}
|
||||||
break;
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_restartOnCrash = _cli.RestartOnCrash;
|
||||||
|
|
||||||
|
if (options.AutomaticUpdates || _cli.Update)
|
||||||
|
{
|
||||||
|
if (IsManualInstall)
|
||||||
|
_log.Warn("Detected manual install, won't attempt to update DS");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RunSteamCmd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RunServer(options, _cli);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string STEAMCMD_DIR = "steamcmd";
|
||||||
|
private const string STEAMCMD_ZIP = "temp.zip";
|
||||||
|
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
||||||
|
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
||||||
|
private const string RUNSCRIPT = @"force_install_dir ../
|
||||||
|
login anonymous
|
||||||
|
app_update 298740
|
||||||
|
quit";
|
||||||
|
|
||||||
|
public static void RunSteamCmd()
|
||||||
|
{
|
||||||
|
var log = LogManager.GetLogger("SteamCMD");
|
||||||
|
|
||||||
|
if (!Directory.Exists(STEAMCMD_DIR))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(STEAMCMD_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(RUNSCRIPT_PATH))
|
||||||
|
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
||||||
|
|
||||||
|
if (!File.Exists(STEAMCMD_PATH))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.Info("Downloading SteamCMD.");
|
||||||
|
using (var client = new WebClient())
|
||||||
|
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
|
||||||
|
|
||||||
|
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
||||||
|
File.Delete(STEAMCMD_ZIP);
|
||||||
|
log.Info("SteamCMD downloaded successfully!");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Checking for DS updates.");
|
||||||
|
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
||||||
|
{
|
||||||
|
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
StandardOutputEncoding = Encoding.ASCII
|
||||||
|
};
|
||||||
|
var cmd = Process.Start(steamCmdProc);
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
while (!cmd.HasExited)
|
||||||
|
{
|
||||||
|
log.Info(cmd.StandardOutput.ReadLine());
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RunServer(TorchConfig options, TorchCli cli)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (!parser.ParseArguments(args, options))
|
if (!parser.ParseArguments(args, options))
|
||||||
{
|
{
|
||||||
@@ -132,16 +249,57 @@ namespace Torch.Server
|
|||||||
|
|
||||||
_server = new TorchServer(options);
|
_server = new TorchServer(options);
|
||||||
_server.Init();
|
_server.Init();
|
||||||
if (gui)
|
|
||||||
|
if (!cli.NoGui)
|
||||||
{
|
{
|
||||||
var ui = new TorchUI((TorchServer)_server);
|
var ui = new TorchUI((TorchServer)_server);
|
||||||
ui.LoadConfig(options);
|
ui.LoadConfig(options);
|
||||||
ui.ShowDialog();
|
ui.ShowDialog();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (cli.NoGui || cli.Autostart)
|
||||||
{
|
{
|
||||||
_server.Start();
|
_server.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
string asmPath = Path.Combine(basePath, "DedicatedServer64", new AssemblyName(args.Name).Name + ".dll");
|
||||||
|
if (File.Exists(asmPath))
|
||||||
|
return Assembly.LoadFrom(asmPath);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
var ex = (Exception)e.ExceptionObject;
|
||||||
|
_log.Fatal(ex);
|
||||||
|
if (_restartOnCrash)
|
||||||
|
{
|
||||||
|
/* Throws an exception somehow and I'm too lazy to debug it.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (MySession.Static != null && MySession.Static.AutoSaveInMinutes > 0)
|
||||||
|
MySession.Static.Save();
|
||||||
|
}
|
||||||
|
catch { }*/
|
||||||
|
|
||||||
|
var exe = typeof(Program).Assembly.Location;
|
||||||
|
_cli.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Process.Start(exe, _cli.ToString());
|
||||||
|
}
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.153.575")]
|
[assembly: AssemblyVersion("1.0.167.670")]
|
||||||
[assembly: AssemblyFileVersion("1.0.153.575")]
|
[assembly: AssemblyFileVersion("1.0.167.670")]
|
@@ -92,6 +92,7 @@
|
|||||||
<Reference Include="System.Configuration.Install" />
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
<Reference Include="System.ServiceProcess" />
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
@@ -160,7 +161,8 @@
|
|||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CommandLine.cs" />
|
<Compile Include="Managers\DSConfigManager.cs" />
|
||||||
|
<Compile Include="TorchCli.cs" />
|
||||||
<Compile Include="NativeMethods.cs" />
|
<Compile Include="NativeMethods.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
@@ -175,6 +177,7 @@
|
|||||||
<Compile Include="TorchServiceInstaller.cs">
|
<Compile Include="TorchServiceInstaller.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
||||||
@@ -188,6 +191,7 @@
|
|||||||
<Compile Include="ViewModels\PluginViewModel.cs" />
|
<Compile Include="ViewModels\PluginViewModel.cs" />
|
||||||
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\SteamUserViewModel.cs" />
|
||||||
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
||||||
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
|
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -215,6 +219,9 @@
|
|||||||
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
||||||
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Views\FirstTimeSetup.xaml.cs">
|
||||||
|
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\ModsControl.xaml.cs">
|
<Compile Include="Views\ModsControl.xaml.cs">
|
||||||
<DependentUpon>ModsControl.xaml</DependentUpon>
|
<DependentUpon>ModsControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -297,6 +304,10 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\FirstTimeSetup.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\ModsControl.xaml">
|
<Page Include="Views\ModsControl.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
41
Torch.Server/TorchCli.cs
Normal file
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;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml.Serialization;
|
using Havok;
|
||||||
|
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
||||||
|
using Sandbox.Game.Entities.Cube;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
using SteamSDK;
|
using SteamSDK;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.Server.ViewModels.Blocks;
|
|
||||||
using VRage.Dedicated;
|
using VRage.Dedicated;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.ObjectBuilder;
|
using VRage.Game.ObjectBuilder;
|
||||||
using VRage.Game.SessionComponents;
|
using VRage.Game.SessionComponents;
|
||||||
|
using VRage.Library;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
|
using VRage.Trace;
|
||||||
|
using VRage.Utils;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
public class TorchServer : TorchBase, ITorchServer
|
public class TorchServer : TorchBase, ITorchServer
|
||||||
{
|
{
|
||||||
|
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
||||||
|
public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } }
|
||||||
|
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
|
||||||
public Thread GameThread { get; private set; }
|
public Thread GameThread { get; private set; }
|
||||||
public ServerState State { get; private set; }
|
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
||||||
public string InstanceName => Config?.InstanceName;
|
public string InstanceName => Config?.InstanceName;
|
||||||
public string InstancePath => Config?.InstancePath;
|
public string InstancePath => Config?.InstancePath;
|
||||||
|
|
||||||
|
private ServerState _state;
|
||||||
|
private TimeSpan _elapsedPlayTime;
|
||||||
|
private float _simRatio;
|
||||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
||||||
|
|
||||||
public TorchServer(TorchConfig config = null)
|
public TorchServer(TorchConfig config = null)
|
||||||
@@ -49,18 +66,68 @@ namespace Torch.Server
|
|||||||
MyPerServerSettings.AppId = 244850;
|
MyPerServerSettings.AppId = 244850;
|
||||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
||||||
|
|
||||||
|
MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
||||||
|
|
||||||
|
InvokeBeforeRun();
|
||||||
|
|
||||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
||||||
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
||||||
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
||||||
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
||||||
MyPlugins.Load();
|
MyPlugins.Load();
|
||||||
|
|
||||||
MyGlobalTypeMetadata.Static.Init();
|
MyGlobalTypeMetadata.Static.Init();
|
||||||
MyInitializer.InvokeBeforeRun(
|
RuntimeHelpers.RunClassConstructor(typeof(MyObjectBuilder_Base).TypeHandle);
|
||||||
MyPerServerSettings.AppId,
|
}
|
||||||
MyPerServerSettings.GameDSName,
|
|
||||||
InstancePath, DedicatedServer.AddDateToLog);
|
|
||||||
|
|
||||||
|
public void InvokeBeforeRun()
|
||||||
|
{
|
||||||
|
|
||||||
|
var contentPath = "Content";
|
||||||
|
|
||||||
|
var privateContentPath = typeof(MyFileSystem).GetField("m_contentPath", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
|
||||||
|
if (privateContentPath != null)
|
||||||
|
Log.Debug("MyFileSystem already initialized");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Program.IsManualInstall)
|
||||||
|
{
|
||||||
|
var rootPath = new FileInfo(MyFileSystem.ExePath).Directory.FullName;
|
||||||
|
contentPath = Path.Combine(rootPath, "Content");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MyFileSystem.ExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DedicatedServer64");
|
||||||
|
}
|
||||||
|
|
||||||
|
MyFileSystem.Init(contentPath, InstancePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
|
||||||
|
MySandboxGame.Log.WriteLine("Steam build: Always true");
|
||||||
|
MySandboxGame.Log.WriteLine("Environment.ProcessorCount: " + MyEnvironment.ProcessorCount);
|
||||||
|
//MySandboxGame.Log.WriteLine("Environment.OSVersion: " + GetOsName());
|
||||||
|
MySandboxGame.Log.WriteLine("Environment.CommandLine: " + Environment.CommandLine);
|
||||||
|
MySandboxGame.Log.WriteLine("Environment.Is64BitProcess: " + MyEnvironment.Is64BitProcess);
|
||||||
|
MySandboxGame.Log.WriteLine("Environment.Is64BitOperatingSystem: " + Environment.Is64BitOperatingSystem);
|
||||||
|
//MySandboxGame.Log.WriteLine("Environment.Version: " + GetNETFromRegistry());
|
||||||
|
MySandboxGame.Log.WriteLine("Environment.CurrentDirectory: " + Environment.CurrentDirectory);
|
||||||
|
MySandboxGame.Log.WriteLine("MainAssembly.ProcessorArchitecture: " + Assembly.GetExecutingAssembly().GetArchitecture());
|
||||||
|
MySandboxGame.Log.WriteLine("ExecutingAssembly.ProcessorArchitecture: " + MyFileSystem.MainAssembly.GetArchitecture());
|
||||||
|
MySandboxGame.Log.WriteLine("IntPtr.Size: " + IntPtr.Size.ToString());
|
||||||
|
MySandboxGame.Log.WriteLine("Default Culture: " + CultureInfo.CurrentCulture.Name);
|
||||||
|
MySandboxGame.Log.WriteLine("Default UI Culture: " + CultureInfo.CurrentUICulture.Name);
|
||||||
|
MySandboxGame.Log.WriteLine("IsAdmin: " + new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator));
|
||||||
|
|
||||||
|
MyLog.Default = MySandboxGame.Log;
|
||||||
|
|
||||||
|
Thread.CurrentThread.Name = "Main thread";
|
||||||
|
|
||||||
|
//Because we want exceptions from users to be in english
|
||||||
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
MySandboxGame.Config = new MyConfig("SpaceEngineers-Dedicated.cfg");
|
||||||
|
MySandboxGame.Config.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,8 +136,9 @@ namespace Torch.Server
|
|||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
if (State != ServerState.Stopped)
|
if (State != ServerState.Stopped)
|
||||||
throw new InvalidOperationException("Server is already running.");
|
return;
|
||||||
|
|
||||||
|
GameThread = Thread.CurrentThread;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
State = ServerState.Starting;
|
State = ServerState.Starting;
|
||||||
Log.Info("Starting server.");
|
Log.Info("Starting server.");
|
||||||
@@ -82,12 +150,9 @@ namespace Torch.Server
|
|||||||
|
|
||||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
||||||
|
|
||||||
do
|
runInternal.Invoke(null, null);
|
||||||
{
|
|
||||||
runInternal.Invoke(null, null);
|
|
||||||
} while (MySandboxGame.IsReloading);
|
|
||||||
|
|
||||||
MyInitializer.InvokeAfterRun();
|
MySandboxGame.Log.Close();
|
||||||
State = ServerState.Stopped;
|
State = ServerState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +164,14 @@ namespace Torch.Server
|
|||||||
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
SimulationRatio = Sync.ServerSimulationRatio;
|
||||||
|
ElapsedPlayTime = MySession.Static?.ElapsedPlayTime ?? default(TimeSpan);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop the server.
|
/// Stop the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -107,21 +180,18 @@ namespace Torch.Server
|
|||||||
if (State == ServerState.Stopped)
|
if (State == ServerState.Stopped)
|
||||||
Log.Error("Server is already stopped");
|
Log.Error("Server is already stopped");
|
||||||
|
|
||||||
if (Thread.CurrentThread.ManagedThreadId != GameThread?.ManagedThreadId && MySandboxGame.Static.IsRunning)
|
if (Thread.CurrentThread != MySandboxGame.Static.UpdateThread)
|
||||||
{
|
{
|
||||||
|
Log.Debug("Invoking server stop on game thread.");
|
||||||
Invoke(Stop);
|
Invoke(Stop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info("Stopping server.");
|
Log.Info("Stopping server.");
|
||||||
MySession.Static.Save();
|
|
||||||
MySession.Static.Unload();
|
|
||||||
|
|
||||||
//Unload all the static junk.
|
//Unload all the static junk.
|
||||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
//TODO: Finish unloading all server data so it's in a completely clean state.
|
||||||
MyFileSystem.Reset();
|
MySandboxGame.Static.Exit();
|
||||||
VRage.Input.MyGuiGameControlsHelpers.Reset();
|
|
||||||
VRage.Input.MyInput.UnloadData();
|
|
||||||
|
|
||||||
Log.Info("Server stopped.");
|
Log.Info("Server stopped.");
|
||||||
_stopHandle.Set();
|
_stopHandle.Set();
|
||||||
|
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;
|
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
||||||
|
|
||||||
public ConfigDedicatedViewModel()
|
public ConfigDedicatedViewModel() : this(new MyConfigDedicated<MyObjectBuilder_SessionSettings>(""))
|
||||||
{
|
{
|
||||||
_config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>("");
|
|
||||||
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
|
||||||
Administrators = new ObservableCollection<string>(_config.Administrators);
|
|
||||||
Banned = new ObservableCollection<ulong>(_config.Banned);
|
|
||||||
Mods = new ObservableCollection<ulong>(_config.Mods);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
|
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
|
||||||
{
|
{
|
||||||
_config = configDedicated;
|
_config = configDedicated;
|
||||||
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
||||||
Administrators = new ObservableCollection<string>(_config.Administrators);
|
Administrators = string.Join("\r\n", _config.Administrators);
|
||||||
Banned = new ObservableCollection<ulong>(_config.Banned);
|
Banned = string.Join("\r\n", _config.Banned);
|
||||||
Mods = new ObservableCollection<ulong>(_config.Mods);
|
Mods = string.Join("\r\n", _config.Mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(string path = null)
|
||||||
|
{
|
||||||
|
_config.Administrators.Clear();
|
||||||
|
foreach (var admin in Administrators.Split('\r', '\n'))
|
||||||
|
if (!string.IsNullOrEmpty(admin))
|
||||||
|
_config.Administrators.Add(admin);
|
||||||
|
|
||||||
|
_config.Banned.Clear();
|
||||||
|
foreach (var banned in Banned.Split('\r', '\n'))
|
||||||
|
if (!string.IsNullOrEmpty(banned))
|
||||||
|
_config.Banned.Add(ulong.Parse(banned));
|
||||||
|
|
||||||
|
_config.Mods.Clear();
|
||||||
|
foreach (var mod in Mods.Split('\r', '\n'))
|
||||||
|
if (!string.IsNullOrEmpty(mod))
|
||||||
|
_config.Mods.Add(ulong.Parse(mod));
|
||||||
|
|
||||||
|
_config.Save(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionSettingsViewModel SessionSettings { get; }
|
public SessionSettingsViewModel SessionSettings { get; }
|
||||||
|
|
||||||
public ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>();
|
public ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>();
|
||||||
public ObservableCollection<string> Administrators { get; }
|
public string Administrators { get; set; }
|
||||||
public ObservableCollection<ulong> Banned { get; }
|
public string Banned { get; set; }
|
||||||
public ObservableCollection<ulong> Mods { get; }
|
public string Mods { get; set; }
|
||||||
|
|
||||||
public int AsteroidAmount
|
public int AsteroidAmount
|
||||||
{
|
{
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using SharpDX.Toolkit.Collections;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Library.Utils;
|
using VRage.Library.Utils;
|
||||||
|
|
||||||
@@ -12,135 +14,120 @@ namespace Torch.Server.ViewModels
|
|||||||
{
|
{
|
||||||
private MyObjectBuilder_SessionSettings _settings;
|
private MyObjectBuilder_SessionSettings _settings;
|
||||||
|
|
||||||
public SessionSettingsViewModel()
|
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
||||||
{
|
{
|
||||||
_settings = new MyObjectBuilder_SessionSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
|
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
foreach (var limit in settings.BlockTypeLimits.Dictionary)
|
||||||
|
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MTObservableCollection<BlockLimitViewModel> BlockLimits { get; } = new MTObservableCollection<BlockLimitViewModel>();
|
||||||
|
|
||||||
#region Multipliers
|
#region Multipliers
|
||||||
public float InventorySizeMultiplier
|
public float InventorySizeMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.InventorySizeMultiplier; }
|
get => _settings.InventorySizeMultiplier; set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float RefinerySpeedMultiplier
|
public float RefinerySpeedMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.RefinerySpeedMultiplier; }
|
get => _settings.RefinerySpeedMultiplier; set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float AssemblerEfficiencyMultiplier
|
public float AssemblerEfficiencyMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.AssemblerEfficiencyMultiplier; }
|
get => _settings.AssemblerEfficiencyMultiplier; set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float AssemblerSpeedMultiplier
|
public float AssemblerSpeedMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.AssemblerSpeedMultiplier; }
|
get => _settings.AssemblerSpeedMultiplier; set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GrinderSpeedMultiplier
|
public float GrinderSpeedMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.GrinderSpeedMultiplier; }
|
get => _settings.GrinderSpeedMultiplier; set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float HackSpeedMultiplier
|
public float HackSpeedMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.HackSpeedMultiplier; }
|
get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region NPCs
|
#region NPCs
|
||||||
public bool EnableDrones
|
public bool EnableDrones
|
||||||
{
|
{
|
||||||
get { return _settings.EnableDrones; }
|
get => _settings.EnableDrones; set { _settings.EnableDrones = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableDrones = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableEncounters
|
public bool EnableEncounters
|
||||||
{
|
{
|
||||||
get { return _settings.EnableEncounters; }
|
get => _settings.EnableEncounters; set { _settings.EnableEncounters = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableEncounters = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableSpiders
|
public bool EnableSpiders
|
||||||
{
|
{
|
||||||
get { return _settings.EnableSpiders; }
|
get => _settings.EnableSpiders; set { _settings.EnableSpiders = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableSpiders = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableWolves
|
public bool EnableWolves
|
||||||
{
|
{
|
||||||
get { return _settings.EnableWolfs; }
|
get => _settings.EnableWolfs; set { _settings.EnableWolfs = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableWolfs = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableCargoShips
|
public bool EnableCargoShips
|
||||||
{
|
{
|
||||||
get { return _settings.CargoShipsEnabled; }
|
get => _settings.CargoShipsEnabled; set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
|
||||||
set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Environment
|
#region Environment
|
||||||
public bool EnableSunRotation
|
public bool EnableSunRotation
|
||||||
{
|
{
|
||||||
get { return _settings.EnableSunRotation; }
|
get => _settings.EnableSunRotation; set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableAirtightness
|
public bool EnableAirtightness
|
||||||
{
|
{
|
||||||
get { return _settings.EnableOxygenPressurization; }
|
get => _settings.EnableOxygenPressurization; set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableOxygen
|
public bool EnableOxygen
|
||||||
{
|
{
|
||||||
get { return _settings.EnableOxygen; }
|
get => _settings.EnableOxygen; set { _settings.EnableOxygen = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableOxygen = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableDestructibleBlocks
|
public bool EnableDestructibleBlocks
|
||||||
{
|
{
|
||||||
get { return _settings.DestructibleBlocks; }
|
get => _settings.DestructibleBlocks; set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
|
||||||
set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableToolShake
|
public bool EnableToolShake
|
||||||
{
|
{
|
||||||
get { return _settings.EnableToolShake; }
|
get => _settings.EnableToolShake; set { _settings.EnableToolShake = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableToolShake = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableVoxelDestruction
|
public bool EnableVoxelDestruction
|
||||||
{
|
{
|
||||||
get { return _settings.EnableVoxelDestruction; }
|
get => _settings.EnableVoxelDestruction; set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> EnvironmentHostilityValues => Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
|
public List<string> EnvironmentHostilityValues => Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
|
||||||
|
|
||||||
public string EnvironmentHostility
|
public string EnvironmentHostility
|
||||||
{
|
{
|
||||||
get { return _settings.EnvironmentHostility.ToString(); }
|
get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
|
||||||
set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableFlora
|
public bool EnableFlora
|
||||||
{
|
{
|
||||||
get { return _settings.EnableFlora; }
|
get => _settings.EnableFlora; set { _settings.EnableFlora = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableFlora = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -148,192 +135,164 @@ namespace Torch.Server.ViewModels
|
|||||||
|
|
||||||
public string GameMode
|
public string GameMode
|
||||||
{
|
{
|
||||||
get { return _settings.GameMode.ToString(); }
|
get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
|
||||||
set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableAutoHealing
|
public bool EnableAutoHealing
|
||||||
{
|
{
|
||||||
get { return _settings.AutoHealing; }
|
get => _settings.AutoHealing; set { _settings.AutoHealing = value; OnPropertyChanged(); }
|
||||||
set { _settings.AutoHealing = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableCopyPaste
|
public bool EnableCopyPaste
|
||||||
{
|
{
|
||||||
get { return _settings.EnableCopyPaste; }
|
get => _settings.EnableCopyPaste; set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowPlayerNamesOnHud
|
public bool ShowPlayerNamesOnHud
|
||||||
{
|
{
|
||||||
get { return _settings.ShowPlayerNamesOnHud; }
|
get => _settings.ShowPlayerNamesOnHud; set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
|
||||||
set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableThirdPerson
|
public bool EnableThirdPerson
|
||||||
{
|
{
|
||||||
get { return _settings.Enable3rdPersonView; }
|
get => _settings.Enable3rdPersonView; set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
|
||||||
set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableSpectator
|
public bool EnableSpectator
|
||||||
{
|
{
|
||||||
get { return _settings.EnableSpectator; }
|
get => _settings.EnableSpectator; set { _settings.EnableSpectator = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableSpectator = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SpawnWithTools
|
public bool SpawnWithTools
|
||||||
{
|
{
|
||||||
get { return _settings.SpawnWithTools; }
|
get => _settings.SpawnWithTools; set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
|
||||||
set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableConvertToStation
|
public bool EnableConvertToStation
|
||||||
{
|
{
|
||||||
get { return _settings.EnableConvertToStation; }
|
get => _settings.EnableConvertToStation; set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableJetpack
|
public bool EnableJetpack
|
||||||
{
|
{
|
||||||
get { return _settings.EnableJetpack; }
|
get => _settings.EnableJetpack; set { _settings.EnableJetpack = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableJetpack = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableRemoteOwnerRemoval
|
public bool EnableRemoteOwnerRemoval
|
||||||
{
|
{
|
||||||
get { return _settings.EnableRemoteBlockRemoval; }
|
get => _settings.EnableRemoteBlockRemoval; set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableRespawnShips
|
public bool EnableRespawnShips
|
||||||
{
|
{
|
||||||
get { return _settings.EnableRespawnShips; }
|
get => _settings.EnableRespawnShips; set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableScripterRole
|
public bool EnableScripterRole
|
||||||
{
|
{
|
||||||
get { return _settings.EnableScripterRole; }
|
get => _settings.EnableScripterRole; set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableRealisticSound
|
public bool EnableRealisticSound
|
||||||
{
|
{
|
||||||
get { return _settings.RealisticSound; }
|
get => _settings.RealisticSound; set { _settings.RealisticSound = value; OnPropertyChanged(); }
|
||||||
set { _settings.RealisticSound = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ResetOwnership
|
public bool ResetOwnership
|
||||||
{
|
{
|
||||||
get { return _settings.ResetOwnership; }
|
get => _settings.ResetOwnership; set { _settings.ResetOwnership = value; OnPropertyChanged(); }
|
||||||
set { _settings.ResetOwnership = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DeleteRespawnShips
|
public bool DeleteRespawnShips
|
||||||
{
|
{
|
||||||
get { return _settings.RespawnShipDelete; }
|
get => _settings.RespawnShipDelete; set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
|
||||||
set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableThrusterDamage
|
public bool EnableThrusterDamage
|
||||||
{
|
{
|
||||||
get { return _settings.ThrusterDamage; }
|
get => _settings.ThrusterDamage; set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
|
||||||
set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableWeapons
|
public bool EnableWeapons
|
||||||
{
|
{
|
||||||
get { return _settings.WeaponsEnabled; }
|
get => _settings.WeaponsEnabled; set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
|
||||||
set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableIngameScripts
|
public bool EnableIngameScripts
|
||||||
{
|
{
|
||||||
get { return _settings.EnableIngameScripts; }
|
get => _settings.EnableIngameScripts; set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
|
||||||
set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint AutosaveInterval
|
public uint AutosaveInterval
|
||||||
{
|
{
|
||||||
get { return _settings.AutoSaveInMinutes; }
|
get => _settings.AutoSaveInMinutes; set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
|
||||||
set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FloraDensity
|
public int FloraDensity
|
||||||
{
|
{
|
||||||
get { return _settings.FloraDensity; }
|
get => _settings.FloraDensity; set { _settings.FloraDensity = value; OnPropertyChanged(); }
|
||||||
set { _settings.FloraDensity = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float FloraDensityMultiplier
|
public float FloraDensityMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.FloraDensityMultiplier; }
|
get => _settings.FloraDensityMultiplier; set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short MaxBackupSaves
|
public short MaxBackupSaves
|
||||||
{
|
{
|
||||||
get { return _settings.MaxBackupSaves; }
|
get => _settings.MaxBackupSaves; set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
|
||||||
set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int MaxBlocksPerPlayer
|
public int MaxBlocksPerPlayer
|
||||||
{
|
{
|
||||||
get { return _settings.MaxBlocksPerPlayer; }
|
get => _settings.MaxBlocksPerPlayer; set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
|
||||||
set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short MaxFloatingObjects
|
public short MaxFloatingObjects
|
||||||
{
|
{
|
||||||
get { return _settings.MaxFloatingObjects; }
|
get => _settings.MaxFloatingObjects; set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
|
||||||
set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int MaxGridSize
|
public int MaxGridSize
|
||||||
{
|
{
|
||||||
get { return _settings.MaxGridSize; }
|
get => _settings.MaxGridSize; set { _settings.MaxGridSize = value; OnPropertyChanged(); }
|
||||||
set { _settings.MaxGridSize = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short MaxPlayers
|
public short MaxPlayers
|
||||||
{
|
{
|
||||||
get { return _settings.MaxPlayers; }
|
get => _settings.MaxPlayers; set { _settings.MaxPlayers = value; OnPropertyChanged(); }
|
||||||
set { _settings.MaxPlayers = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int PhysicsIterations
|
public int PhysicsIterations
|
||||||
{
|
{
|
||||||
get { return _settings.PhysicsIterations; }
|
get => _settings.PhysicsIterations; set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
|
||||||
set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float SpawnTimeMultiplier
|
public float SpawnTimeMultiplier
|
||||||
{
|
{
|
||||||
get { return _settings.SpawnShipTimeMultiplier; }
|
get => _settings.SpawnShipTimeMultiplier; set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
|
||||||
set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float SunRotationInterval
|
public float SunRotationInterval
|
||||||
{
|
{
|
||||||
get { return _settings.SunRotationIntervalMinutes; }
|
get => _settings.SunRotationIntervalMinutes; set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
|
||||||
set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ViewDistance
|
public int ViewDistance
|
||||||
{
|
{
|
||||||
get { return _settings.ViewDistance; }
|
get => _settings.ViewDistance; set { _settings.ViewDistance = value; OnPropertyChanged(); }
|
||||||
set { _settings.ViewDistance = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int WorldSize
|
public int WorldSize
|
||||||
{
|
{
|
||||||
get { return _settings.ViewDistance; }
|
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
||||||
set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
||||||
{
|
{
|
||||||
|
viewModel._settings.BlockTypeLimits.Dictionary.Clear();
|
||||||
|
foreach (var limit in viewModel.BlockLimits)
|
||||||
|
viewModel._settings.BlockTypeLimits.Dictionary.Add(limit.BlockType, limit.Limit);
|
||||||
return viewModel._settings;
|
return viewModel._settings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,10 +3,34 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using SteamSDK;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
class SteamUserViewModel
|
public class SteamUserViewModel : ViewModel
|
||||||
{
|
{
|
||||||
|
private string _name;
|
||||||
|
public string Name { get => _name; set { } }
|
||||||
|
|
||||||
|
private ulong _id;
|
||||||
|
|
||||||
|
public ulong SteamId
|
||||||
|
{
|
||||||
|
get => _id;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_id = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
//TODO: resolve user name
|
||||||
|
OnPropertyChanged(nameof(Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SteamUserViewModel(ulong id)
|
||||||
|
{
|
||||||
|
SteamId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SteamUserViewModel() : this(0) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -64,8 +64,25 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
//Can't use Message.Text directly because of object ownership in WPF.
|
//Can't use Message.Text directly because of object ownership in WPF.
|
||||||
var text = Message.Text;
|
var text = Message.Text;
|
||||||
_server.Multiplayer.SendMessage(text);
|
var commands = ((TorchBase)_server).Commands;
|
||||||
|
string response = null;
|
||||||
|
if (commands.IsCommand(text))
|
||||||
|
{
|
||||||
|
_server.Multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", text));
|
||||||
|
_server.InvokeBlocking(() =>
|
||||||
|
{
|
||||||
|
response = commands.HandleCommandFromServer(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_server.Multiplayer.SendMessage(text);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(response))
|
||||||
|
_server.Multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", response));
|
||||||
Message.Text = "";
|
Message.Text = "";
|
||||||
|
var sto = false;
|
||||||
|
Refresh(null, ref sto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
<DockPanel>
|
<DockPanel>
|
||||||
<DockPanel DockPanel.Dock="Top">
|
<DockPanel DockPanel.Dock="Top">
|
||||||
<Label Content="World:" DockPanel.Dock="Left" />
|
<Label Content="World:" DockPanel.Dock="Left" />
|
||||||
|
<Button Content="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
|
||||||
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" />
|
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel DockPanel.Dock="Bottom">
|
<DockPanel DockPanel.Dock="Bottom">
|
||||||
@@ -36,15 +37,30 @@
|
|||||||
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="3">
|
<StackPanel Margin="3">
|
||||||
<Label Content="Banned Players" />
|
<!--
|
||||||
<ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100"
|
<ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100"
|
||||||
MouseDoubleClick="Banned_OnMouseDoubleClick" />
|
MouseDoubleClick="Banned_OnMouseDoubleClick" /> -->
|
||||||
<Label Content="Administrators" />
|
<!--
|
||||||
<ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100"
|
<ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100"
|
||||||
MouseDoubleClick="Administrators_OnMouseDoubleClick" />
|
MouseDoubleClick="Administrators_OnMouseDoubleClick">
|
||||||
<Label Content="Mods" />
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox Text="{Binding SteamId}" Width="100"/>
|
||||||
|
<Label Content="{Binding Name}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox> -->
|
||||||
|
<!--
|
||||||
<ListBox ItemsSource="{Binding Mods}" Width="130" Margin="3,0,3,3" Height="100"
|
<ListBox ItemsSource="{Binding Mods}" Width="130" Margin="3,0,3,3" Height="100"
|
||||||
MouseDoubleClick="Mods_OnMouseDoubleClick" />
|
MouseDoubleClick="Mods_OnMouseDoubleClick" /> -->
|
||||||
|
<Label Content="Mods" />
|
||||||
|
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||||
|
<Label Content="Administrators" />
|
||||||
|
<TextBox Text="{Binding Administrators}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||||
|
<Label Content="Banned Players" />
|
||||||
|
<TextBox Text="{Binding Banned}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
@@ -52,6 +68,30 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
|
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
|
||||||
<StackPanel DataContext="{Binding SessionSettings}">
|
<StackPanel DataContext="{Binding SessionSettings}">
|
||||||
|
<Expander Header="Block Limits">
|
||||||
|
<StackPanel Margin="10,0,0,0">
|
||||||
|
<DockPanel>
|
||||||
|
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Max Blocks Per Player" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Max Grid Size" />
|
||||||
|
</DockPanel>
|
||||||
|
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
|
||||||
|
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
|
||||||
|
<TextBox Text="{Binding Limit}" Width="50" Margin="3" />
|
||||||
|
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackPanel>
|
||||||
|
</Expander>
|
||||||
<Expander Header="Multipliers">
|
<Expander Header="Multipliers">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
@@ -91,6 +131,14 @@
|
|||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Environment">
|
<Expander Header="Environment">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
|
<DockPanel ToolTip="Increases physics precision at the cost of performance.">
|
||||||
|
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Physics Iterations" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel>
|
||||||
|
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Max Floating Objects" />
|
||||||
|
</DockPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
|
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
||||||
@@ -136,6 +184,9 @@
|
|||||||
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Players" />
|
<Label Content="Max Players" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
||||||
|
Margin="3" />
|
||||||
|
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
||||||
@@ -144,6 +195,8 @@
|
|||||||
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Respawn Time Multiplier" />
|
<Label Content="Respawn Time Multiplier" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
||||||
|
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Miscellaneous">
|
<Expander Header="Miscellaneous">
|
||||||
@@ -152,11 +205,9 @@
|
|||||||
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
||||||
<Label Content="Autosave Interval (minutes)" />
|
<Label Content="Autosave Interval (minutes)" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
|
||||||
Margin="3" />
|
|
||||||
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
|
||||||
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
|
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
|
||||||
Content="Enable Remote Ownership Removal" Margin="3" />
|
Content="Enable Remote Ownership Removal" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
|
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
|
||||||
@@ -165,7 +216,6 @@
|
|||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
|
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
|
||||||
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
|
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
|
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
|
||||||
@@ -173,7 +223,6 @@
|
|||||||
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
||||||
Margin="3" Width="100" DockPanel.Dock="Left" />
|
Margin="3" Width="100" DockPanel.Dock="Left" />
|
||||||
@@ -183,24 +232,6 @@
|
|||||||
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Backup Saves" />
|
<Label Content="Max Backup Saves" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel>
|
|
||||||
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
|
|
||||||
<Label Content="Max Blocks Per Player" />
|
|
||||||
</DockPanel>
|
|
||||||
<DockPanel>
|
|
||||||
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
|
|
||||||
<Label Content="Max Floating Objects" />
|
|
||||||
</DockPanel>
|
|
||||||
<DockPanel>
|
|
||||||
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
|
|
||||||
<Label Content="Max Grid Size" />
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<DockPanel ToolTip="Increases physics precision at the cost of performance.">
|
|
||||||
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
|
|
||||||
<Label Content="Physics Iterations" />
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -15,14 +16,17 @@ using System.Windows.Media;
|
|||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
|
using System.Xml.Serialization;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Utils;
|
using Sandbox.Engine.Utils;
|
||||||
using Torch.Server.ViewModels;
|
using Torch.Server.ViewModels;
|
||||||
using Torch.Views;
|
using Torch.Views;
|
||||||
|
using VRage;
|
||||||
using VRage.Dedicated;
|
using VRage.Dedicated;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Torch.Server.Views
|
namespace Torch.Server.Views
|
||||||
@@ -32,6 +36,7 @@ namespace Torch.Server.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ConfigControl : UserControl
|
public partial class ConfigControl : UserControl
|
||||||
{
|
{
|
||||||
|
private readonly Logger Log = LogManager.GetLogger("Config");
|
||||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
|
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
|
||||||
private ConfigDedicatedViewModel _viewModel;
|
private ConfigDedicatedViewModel _viewModel;
|
||||||
private string _configPath;
|
private string _configPath;
|
||||||
@@ -43,22 +48,23 @@ namespace Torch.Server.Views
|
|||||||
|
|
||||||
public void SaveConfig()
|
public void SaveConfig()
|
||||||
{
|
{
|
||||||
Config.Save(_configPath);
|
_viewModel.Save(_configPath);
|
||||||
|
Log.Info("Saved DS config.");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out ulong size);
|
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out _);
|
||||||
checkpoint.Settings = _viewModel.SessionSettings;
|
checkpoint.Settings = _viewModel.SessionSettings;
|
||||||
checkpoint.Mods.Clear();
|
checkpoint.Mods.Clear();
|
||||||
foreach (var modId in _viewModel.Mods)
|
foreach (var modId in _viewModel.Mods)
|
||||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||||
|
|
||||||
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
|
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
|
||||||
|
Log.Info("Saved world config.");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
var log = LogManager.GetLogger("Torch");
|
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
||||||
log.Error("Failed to overwrite sandbox config, changes will not appear on server");
|
Log.Error(e);
|
||||||
log.Error(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +83,18 @@ namespace Torch.Server.Views
|
|||||||
Config.Load(path);
|
Config.Load(path);
|
||||||
_configPath = path;
|
_configPath = path;
|
||||||
|
|
||||||
|
var checkpoint = MyLocalCache.LoadCheckpoint(Config.LoadWorld, out ulong _);
|
||||||
|
if (checkpoint == null)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to load checkpoint when loading DS config.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Config.Mods.Clear();
|
||||||
|
foreach (var mod in checkpoint.Mods)
|
||||||
|
Config.Mods.Add(mod.PublishedFileId);
|
||||||
|
}
|
||||||
|
|
||||||
_viewModel = new ConfigDedicatedViewModel(Config);
|
_viewModel = new ConfigDedicatedViewModel(Config);
|
||||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
|
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
|
||||||
|
|
||||||
@@ -86,6 +104,7 @@ namespace Torch.Server.Views
|
|||||||
DataContext = _viewModel;
|
DataContext = _viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
private void Banned_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
private void Banned_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
var editor = new CollectionEditor {Owner = Window.GetWindow(this)};
|
var editor = new CollectionEditor {Owner = Window.GetWindow(this)};
|
||||||
@@ -102,11 +121,27 @@ namespace Torch.Server.Views
|
|||||||
{
|
{
|
||||||
var editor = new CollectionEditor { Owner = Window.GetWindow(this) };
|
var editor = new CollectionEditor { Owner = Window.GetWindow(this) };
|
||||||
editor.Edit(_viewModel.Mods, "Mods");
|
editor.Edit(_viewModel.Mods, "Mods");
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private void Save_OnClick(object sender, RoutedEventArgs e)
|
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
|
||||||
|
_viewModel.SessionSettings.BlockLimits.Remove(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_viewModel.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_viewModel.SessionSettings, "", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Feature coming soon :)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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">
|
Title="Torch">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<StackPanel DockPanel.Dock="Top" Margin="5,5,5,5" Orientation="Horizontal">
|
<StackPanel DockPanel.Dock="Top" Margin="5,5,5,5" Orientation="Horizontal">
|
||||||
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True"/>
|
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0"
|
||||||
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStop_Click" IsEnabled="False"/>
|
HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True" />
|
||||||
<Button x:Name="BtnRestart" Content="Restart" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnRestart_Click"/>
|
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
|
||||||
<Label x:Name="LabelSimulation" Content="Sim Ratio: 0.00"/>
|
Click="BtnStop_Click" IsEnabled="False" />
|
||||||
<Label x:Name="LabelUptime" Content="Uptime: 0d 0h 0m"/>
|
<Label>
|
||||||
|
<Label.Content>
|
||||||
|
<TextBlock Text="{Binding State, StringFormat=Status: {0}}"></TextBlock>
|
||||||
|
</Label.Content>
|
||||||
|
</Label>
|
||||||
|
<Label x:Name="LabelSimulation">
|
||||||
|
<Label.Content>
|
||||||
|
<TextBlock Text="{Binding SimulationRatio, StringFormat=Simulation: {0:0.00}}" />
|
||||||
|
</Label.Content>
|
||||||
|
</Label>
|
||||||
|
<Label x:Name="LabelUptime">
|
||||||
|
<Label.Content>
|
||||||
|
<TextBlock Text="{Binding ElapsedPlayTime, StringFormat=Uptime: {0:g}}"/>
|
||||||
|
</Label.Content>
|
||||||
|
</Label>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5">
|
<TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5">
|
||||||
<TabItem Header="Configuration">
|
<TabItem Header="Configuration">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<DockPanel DockPanel.Dock="Top">
|
<DockPanel DockPanel.Dock="Top">
|
||||||
<Label Content="Instance Path: " Margin="3"/>
|
<Label Content="Instance Path: " Margin="3" />
|
||||||
<TextBox x:Name="InstancePathBox" Margin="3" Height="20" TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False"/>
|
<TextBox x:Name="InstancePathBox" Margin="3" Height="20"
|
||||||
|
TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom"/>
|
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Chat/Players">
|
<TabItem Header="Chat/Players">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False"/>
|
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False" />
|
||||||
<local:ChatControl x:Name="Chat" IsEnabled="False"/>
|
<local:ChatControl x:Name="Chat" IsEnabled="False" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Entity Manager">
|
<TabItem Header="Entity Manager">
|
||||||
<views:EntitiesControl/>
|
<views:EntitiesControl />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Plugins">
|
<TabItem Header="Plugins">
|
||||||
<views:PluginsControl x:Name="Plugins"/>
|
<views:PluginsControl x:Name="Plugins" />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
|
@@ -19,7 +19,6 @@ using System.Windows.Navigation;
|
|||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using VRageMath;
|
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
@@ -31,26 +30,20 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
private TorchServer _server;
|
private TorchServer _server;
|
||||||
private TorchConfig _config;
|
private TorchConfig _config;
|
||||||
private DateTime _startTime;
|
|
||||||
private readonly Timer _uiUpdate = new Timer
|
|
||||||
{
|
|
||||||
Interval = 1000,
|
|
||||||
AutoReset = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
public TorchUI(TorchServer server)
|
public TorchUI(TorchServer server)
|
||||||
{
|
{
|
||||||
_config = (TorchConfig)server.Config;
|
_config = (TorchConfig)server.Config;
|
||||||
_server = server;
|
_server = server;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_startTime = DateTime.Now;
|
|
||||||
_uiUpdate.Elapsed += UiUpdate_Elapsed;
|
|
||||||
|
|
||||||
Left = _config.WindowPosition.X;
|
Left = _config.WindowPosition.X;
|
||||||
Top = _config.WindowPosition.Y;
|
Top = _config.WindowPosition.Y;
|
||||||
Width = _config.WindowSize.X;
|
Width = _config.WindowSize.X;
|
||||||
Height = _config.WindowSize.Y;
|
Height = _config.WindowSize.Y;
|
||||||
|
|
||||||
|
//TODO: data binding for whole server
|
||||||
|
DataContext = server;
|
||||||
Chat.BindServer(server);
|
Chat.BindServer(server);
|
||||||
PlayerList.BindServer(server);
|
PlayerList.BindServer(server);
|
||||||
Plugins.BindServer(server);
|
Plugins.BindServer(server);
|
||||||
@@ -69,47 +62,33 @@ namespace Torch.Server
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UiUpdate_Elapsed(object sender, ElapsedEventArgs e)
|
|
||||||
{
|
|
||||||
UpdateUptime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUptime()
|
|
||||||
{
|
|
||||||
var currentTime = DateTime.Now;
|
|
||||||
var uptime = currentTime - _startTime;
|
|
||||||
|
|
||||||
Dispatcher.Invoke(() => LabelUptime.Content = $"Uptime: {uptime.Days}d {uptime.Hours}h {uptime.Minutes}m");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_startTime = DateTime.Now;
|
_config.Save();
|
||||||
Chat.IsEnabled = true;
|
Chat.IsEnabled = true;
|
||||||
PlayerList.IsEnabled = true;
|
PlayerList.IsEnabled = true;
|
||||||
((Button) sender).IsEnabled = false;
|
((Button) sender).IsEnabled = false;
|
||||||
BtnStop.IsEnabled = true;
|
BtnStop.IsEnabled = true;
|
||||||
_uiUpdate.Start();
|
|
||||||
ConfigControl.SaveConfig();
|
ConfigControl.SaveConfig();
|
||||||
new Thread(_server.Start).Start();
|
new Thread(_server.Start).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
_config.Save();
|
||||||
Chat.IsEnabled = false;
|
Chat.IsEnabled = false;
|
||||||
PlayerList.IsEnabled = false;
|
PlayerList.IsEnabled = false;
|
||||||
((Button) sender).IsEnabled = false;
|
((Button) sender).IsEnabled = false;
|
||||||
//HACK: Uncomment when restarting is possible.
|
//HACK: Uncomment when restarting is possible.
|
||||||
//BtnStart.IsEnabled = true;
|
//BtnStart.IsEnabled = true;
|
||||||
_uiUpdate.Stop();
|
|
||||||
_server.Stop();
|
_server.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClosing(CancelEventArgs e)
|
protected override void OnClosing(CancelEventArgs e)
|
||||||
{
|
{
|
||||||
var newSize = new Vector2I((int)Width, (int)Height);
|
var newSize = new Point((int)Width, (int)Height);
|
||||||
_config.WindowSize = newSize;
|
_config.WindowSize = newSize;
|
||||||
var newPos = new Vector2I((int)Left, (int)Top);
|
var newPos = new Point((int)Left, (int)Top);
|
||||||
_config.WindowPosition = newPos;
|
_config.WindowPosition = newPos;
|
||||||
_config.Save();
|
_config.Save();
|
||||||
|
|
||||||
@@ -124,7 +103,7 @@ namespace Torch.Server
|
|||||||
|
|
||||||
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var name = (sender as TextBox).Text;
|
var name = ((TextBox)sender).Text;
|
||||||
|
|
||||||
_config.InstancePath = name;
|
_config.InstancePath = name;
|
||||||
|
|
||||||
|
@@ -5,25 +5,20 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch
|
||||||
{
|
{
|
||||||
public class CommandLine
|
public class CommandLine
|
||||||
{
|
{
|
||||||
public TorchConfig Config { get; }
|
private readonly string _argPrefix;
|
||||||
private string _argPrefix;
|
|
||||||
|
|
||||||
[Arg("instancepath", "Server data folder where saves and mods are stored")]
|
public CommandLine(string argPrefix = "-")
|
||||||
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
|
|
||||||
|
|
||||||
public CommandLine(TorchConfig config, string argPrefix)
|
|
||||||
{
|
{
|
||||||
Config = config;
|
|
||||||
_argPrefix = argPrefix;
|
_argPrefix = argPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyInfo[] GetArgs()
|
public PropertyInfo[] GetArgs()
|
||||||
{
|
{
|
||||||
return typeof(CommandLine).GetProperties().Where(p => p.HasAttribute<ArgAttribute>()).ToArray();
|
return GetType().GetProperties().Where(p => p.HasAttribute<ArgAttribute>()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetHelp()
|
public string GetHelp()
|
||||||
@@ -33,18 +28,43 @@ namespace Torch.Server
|
|||||||
foreach (var property in GetArgs())
|
foreach (var property in GetArgs())
|
||||||
{
|
{
|
||||||
var attr = property.GetCustomAttribute<ArgAttribute>();
|
var attr = property.GetCustomAttribute<ArgAttribute>();
|
||||||
sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(20)}{attr.Description}");
|
sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(24)}{attr.Description}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run(string[] args)
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
var args = new List<string>();
|
||||||
|
foreach (var prop in GetArgs())
|
||||||
|
{
|
||||||
|
var attr = prop.GetCustomAttribute<ArgAttribute>();
|
||||||
|
if (prop.PropertyType == typeof(bool) && (bool)prop.GetValue(this))
|
||||||
|
{
|
||||||
|
args.Add($"{_argPrefix}{attr.Name}");
|
||||||
|
}
|
||||||
|
else if (prop.PropertyType == typeof(string))
|
||||||
|
{
|
||||||
|
var str = (string)prop.GetValue(this);
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
continue;
|
||||||
|
args.Add($"{_argPrefix}{attr.Name} \"{str}\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(" ", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Parse(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (args[0] == $"{_argPrefix}help")
|
if (args[0] == $"{_argPrefix}help")
|
||||||
{
|
{
|
||||||
Console.WriteLine(GetHelp());
|
Console.WriteLine(GetHelp());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var properties = GetArgs();
|
var properties = GetArgs();
|
||||||
@@ -71,16 +91,18 @@ namespace Torch.Server
|
|||||||
property.SetValue(this, args[++i]);
|
property.SetValue(this, args[++i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error parsing arg {argName}");
|
Console.WriteLine($"Error parsing arg {argName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArgAttribute : Attribute
|
public class ArgAttribute : Attribute
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
@@ -31,6 +31,8 @@ namespace Torch.Commands
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string RawArgs { get; }
|
public string RawArgs { get; }
|
||||||
|
|
||||||
|
public string Response { get; private set; }
|
||||||
|
|
||||||
public CommandContext(ITorchBase torch, ITorchPlugin plugin, IMyPlayer player, string rawArgs = null, List<string> args = null)
|
public CommandContext(ITorchBase torch, ITorchPlugin plugin, IMyPlayer player, string rawArgs = null, List<string> args = null)
|
||||||
{
|
{
|
||||||
Torch = torch;
|
Torch = torch;
|
||||||
@@ -42,7 +44,10 @@ namespace Torch.Commands
|
|||||||
|
|
||||||
public void Respond(string message, string sender = "Server", string font = MyFontEnum.Blue)
|
public void Respond(string message, string sender = "Server", string font = MyFontEnum.Blue)
|
||||||
{
|
{
|
||||||
Torch.Multiplayer.SendMessage(message, sender, Player.IdentityId, font);
|
Response = message;
|
||||||
|
|
||||||
|
if (Player != null)
|
||||||
|
Torch.Multiplayer.SendMessage(message, sender, Player.IdentityId, font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -69,28 +69,51 @@ namespace Torch.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string HandleCommandFromServer(string message)
|
||||||
|
{
|
||||||
|
var cmdText = new string(message.Skip(1).ToArray());
|
||||||
|
var command = Commands.GetCommand(cmdText, out string argText);
|
||||||
|
var cmdPath = string.Join(".", command.Path);
|
||||||
|
|
||||||
|
var splitArgs = Regex.Matches(argText, "(\"[^\"]+\"|\\S+)").Cast<Match>().Select(x => x.ToString().Replace("\"", "")).ToList();
|
||||||
|
_log.Trace($"Invoking {cmdPath} for server.");
|
||||||
|
var context = new CommandContext(_torch, command.Plugin, null, argText, splitArgs);
|
||||||
|
if (command.TryInvoke(context))
|
||||||
|
_log.Info($"Server ran command '{message}'");
|
||||||
|
else
|
||||||
|
context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
|
||||||
|
|
||||||
|
return context.Response;
|
||||||
|
}
|
||||||
|
|
||||||
public void HandleCommand(ChatMsg msg, ref bool sendToOthers)
|
public void HandleCommand(ChatMsg msg, ref bool sendToOthers)
|
||||||
{
|
{
|
||||||
if (msg.Text.Length < 1 || msg.Text[0] != Prefix)
|
HandleCommand(msg.Text, msg.Author, ref sendToOthers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleCommand(string message, ulong steamId, ref bool sendToOthers, bool serverConsole = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (message.Length < 1 || message[0] != Prefix)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sendToOthers = false;
|
sendToOthers = false;
|
||||||
|
|
||||||
var player = _torch.Multiplayer.GetPlayerBySteamId(msg.Author);
|
var player = _torch.Multiplayer.GetPlayerBySteamId(steamId);
|
||||||
if (player == null)
|
if (player == null)
|
||||||
{
|
{
|
||||||
_log.Error($"Command {msg.Text} invoked by nonexistant player");
|
_log.Error($"Command {message} invoked by nonexistant player");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdText = new string(msg.Text.Skip(1).ToArray());
|
var cmdText = new string(message.Skip(1).ToArray());
|
||||||
var command = Commands.GetCommand(cmdText, out string argText);
|
var command = Commands.GetCommand(cmdText, out string argText);
|
||||||
|
|
||||||
if (command != null)
|
if (command != null)
|
||||||
{
|
{
|
||||||
var cmdPath = string.Join(".", command.Path);
|
var cmdPath = string.Join(".", command.Path);
|
||||||
|
|
||||||
if (!HasPermission(msg.Author, command))
|
if (!HasPermission(steamId, command))
|
||||||
{
|
{
|
||||||
_log.Info($"{player.DisplayName} tried to use command {cmdPath} without permission");
|
_log.Info($"{player.DisplayName} tried to use command {cmdPath} without permission");
|
||||||
_torch.Multiplayer.SendMessage($"You need to be a {command.MinimumPromoteLevel} or higher to use that command.", playerId: player.IdentityId);
|
_torch.Multiplayer.SendMessage($"You need to be a {command.MinimumPromoteLevel} or higher to use that command.", playerId: player.IdentityId);
|
||||||
@@ -103,7 +126,7 @@ namespace Torch.Commands
|
|||||||
_torch.Invoke(() =>
|
_torch.Invoke(() =>
|
||||||
{
|
{
|
||||||
if (command.TryInvoke(context))
|
if (command.TryInvoke(context))
|
||||||
_log.Info($"Player {player.DisplayName} ran command '{msg.Text}'");
|
_log.Info($"Player {player.DisplayName} ran command '{message}'");
|
||||||
else
|
else
|
||||||
context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
|
context.Respond($"Invalid Syntax: {command.SyntaxHelp}");
|
||||||
});
|
});
|
||||||
|
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
|
using Torch;
|
||||||
using Torch.Commands.Permissions;
|
using Torch.Commands.Permissions;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
@@ -26,10 +27,17 @@ namespace Torch.Commands
|
|||||||
Console.WriteLine(commandManager.Commands.GetTreeString());
|
Console.WriteLine(commandManager.Commands.GetTreeString());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
[Command("crash", "Causes the server to crash for testing purposes")]
|
||||||
|
[Permission(MyPromoteLevel.Admin)]
|
||||||
|
public void Crash()
|
||||||
|
{
|
||||||
|
throw new Exception("Crash triggered by Torch command");
|
||||||
|
}
|
||||||
|
|
||||||
[Command("help", "Displays help for a command")]
|
[Command("help", "Displays help for a command")]
|
||||||
public void Help()
|
public void Help()
|
||||||
{
|
{
|
||||||
var commandManager = ((PluginManager)Context.Torch.Plugins).Commands;
|
var commandManager = ((TorchBase)Context.Torch).Commands;
|
||||||
commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node);
|
commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node);
|
||||||
|
|
||||||
if (node != null)
|
if (node != null)
|
||||||
@@ -42,10 +50,11 @@ namespace Torch.Commands
|
|||||||
if (command != null)
|
if (command != null)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"Syntax: {command.SyntaxHelp}");
|
sb.AppendLine($"Syntax: {command.SyntaxHelp}");
|
||||||
sb.AppendLine(command.HelpText);
|
sb.Append(command.HelpText);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine($"Subcommands: {string.Join(", ", children)}");
|
if (node.Subcommands.Count() != 0)
|
||||||
|
sb.Append($"\nSubcommands: {string.Join(", ", children)}");
|
||||||
|
|
||||||
Context.Respond(sb.ToString());
|
Context.Respond(sb.ToString());
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,15 @@ namespace Torch.Managers
|
|||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
NetworkManager.Instance.RegisterNetworkHandlers(new ChatIntercept());
|
try
|
||||||
|
{
|
||||||
|
NetworkManager.Instance.RegisterNetworkHandlers(new ChatIntercept());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Error("Failed to initialize network intercept, command hiding will not work! Falling back to another method.");
|
||||||
|
MyMultiplayer.Static.ChatMessageReceived += Static_ChatMessageReceived;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Static_ChatMessageReceived(ulong arg1, string arg2, SteamSDK.ChatEntryTypeEnum arg3)
|
private void Static_ChatMessageReceived(ulong arg1, string arg2, SteamSDK.ChatEntryTypeEnum arg3)
|
||||||
|
@@ -96,6 +96,7 @@ namespace Torch.Managers
|
|||||||
{
|
{
|
||||||
var msg = new ScriptedChatMsg {Author = author, Font = font, Target = playerId, Text = message};
|
var msg = new ScriptedChatMsg {Author = author, Font = font, Target = playerId, Text = message};
|
||||||
MyMultiplayerBase.SendScriptedChatMessage(ref msg);
|
MyMultiplayerBase.SendScriptedChatMessage(ref msg);
|
||||||
|
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSessionLoaded()
|
private void OnSessionLoaded()
|
||||||
|
@@ -30,7 +30,6 @@ namespace Torch.Managers
|
|||||||
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
|
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
|
||||||
|
|
||||||
public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>();
|
public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>();
|
||||||
public CommandManager Commands { get; private set; }
|
|
||||||
|
|
||||||
public float LastUpdateMs => _lastUpdateMs;
|
public float LastUpdateMs => _lastUpdateMs;
|
||||||
private volatile float _lastUpdateMs;
|
private volatile float _lastUpdateMs;
|
||||||
@@ -51,7 +50,8 @@ namespace Torch.Managers
|
|||||||
public void UpdatePlugins()
|
public void UpdatePlugins()
|
||||||
{
|
{
|
||||||
var s = Stopwatch.StartNew();
|
var s = Stopwatch.StartNew();
|
||||||
Parallel.ForEach(Plugins, p => p.Update());
|
foreach (var plugin in Plugins)
|
||||||
|
plugin.Update();
|
||||||
s.Stop();
|
s.Stop();
|
||||||
_lastUpdateMs = (float)s.Elapsed.TotalMilliseconds;
|
_lastUpdateMs = (float)s.Elapsed.TotalMilliseconds;
|
||||||
}
|
}
|
||||||
@@ -100,9 +100,9 @@ namespace Torch.Managers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
Commands = new CommandManager(_torch);
|
var commands = ((TorchBase)_torch).Commands;
|
||||||
|
|
||||||
if (_torch.Config.EnableAutomaticUpdates)
|
if (_torch.Config.AutomaticUpdates)
|
||||||
DownloadPlugins();
|
DownloadPlugins();
|
||||||
else
|
else
|
||||||
_log.Warn("Automatic plugin updates are disabled.");
|
_log.Warn("Automatic plugin updates are disabled.");
|
||||||
@@ -111,6 +111,7 @@ namespace Torch.Managers
|
|||||||
var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories);
|
var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories);
|
||||||
foreach (var dllPath in dlls)
|
foreach (var dllPath in dlls)
|
||||||
{
|
{
|
||||||
|
_log.Debug($"Loading plugin {dllPath}");
|
||||||
var asm = Assembly.UnsafeLoadFrom(dllPath);
|
var asm = Assembly.UnsafeLoadFrom(dllPath);
|
||||||
|
|
||||||
foreach (var type in asm.GetExportedTypes())
|
foreach (var type in asm.GetExportedTypes())
|
||||||
@@ -119,14 +120,15 @@ namespace Torch.Managers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var plugin = (ITorchPlugin)Activator.CreateInstance(type);
|
var plugin = (TorchPluginBase)Activator.CreateInstance(type);
|
||||||
if (plugin.Id == default(Guid))
|
if (plugin.Id == default(Guid))
|
||||||
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
|
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
|
||||||
|
|
||||||
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
|
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
|
||||||
|
plugin.StoragePath = new FileInfo(asm.Location).Directory.FullName;
|
||||||
Plugins.Add(plugin);
|
Plugins.Add(plugin);
|
||||||
|
|
||||||
Commands.RegisterPluginCommands(plugin);
|
commands.RegisterPluginCommands(plugin);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@@ -3,7 +3,9 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.ModAPI;
|
||||||
using Torch.API.ModAPI.Ingame;
|
using Torch.API.ModAPI.Ingame;
|
||||||
using VRage.Scripting;
|
using VRage.Scripting;
|
||||||
|
|
||||||
@@ -20,6 +22,12 @@ namespace Torch.Managers
|
|||||||
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location);
|
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location);
|
||||||
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(typeof(GridExtensions));
|
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(typeof(GridExtensions));
|
||||||
|
|
||||||
|
using (var whitelist = _whitelist.OpenBatch())
|
||||||
|
{
|
||||||
|
//whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.ModApi, typeof(TorchAPIGateway));
|
||||||
|
whitelist.AllowNamespaceOfTypes(MyWhitelistTarget.Both, typeof(GridExtensions));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
//dump whitelist
|
//dump whitelist
|
||||||
var whitelist = new StringBuilder();
|
var whitelist = new StringBuilder();
|
||||||
|
@@ -16,13 +16,19 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public T Data { get; private set; } = new T();
|
public T Data { get; private set; }
|
||||||
|
|
||||||
~Persistent()
|
~Persistent()
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Persistent(string path, T data = default(T))
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
public void Save(string path = null)
|
public void Save(string path = null)
|
||||||
{
|
{
|
||||||
if (path == null)
|
if (path == null)
|
||||||
@@ -38,7 +44,7 @@ namespace Torch
|
|||||||
|
|
||||||
public static Persistent<T> Load(string path, bool saveIfNew = true)
|
public static Persistent<T> Load(string path, bool saveIfNew = true)
|
||||||
{
|
{
|
||||||
var config = new Persistent<T> { Path = path };
|
var config = new Persistent<T>(path, new T());
|
||||||
|
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
|
@@ -124,6 +124,7 @@
|
|||||||
<Compile Include="ChatMessage.cs" />
|
<Compile Include="ChatMessage.cs" />
|
||||||
<Compile Include="Collections\KeyTree.cs" />
|
<Compile Include="Collections\KeyTree.cs" />
|
||||||
<Compile Include="Collections\RollingAverage.cs" />
|
<Compile Include="Collections\RollingAverage.cs" />
|
||||||
|
<Compile Include="CommandLine.cs" />
|
||||||
<Compile Include="Commands\CategoryAttribute.cs" />
|
<Compile Include="Commands\CategoryAttribute.cs" />
|
||||||
<Compile Include="Commands\Command.cs" />
|
<Compile Include="Commands\Command.cs" />
|
||||||
<Compile Include="Commands\CommandAttribute.cs" />
|
<Compile Include="Commands\CommandAttribute.cs" />
|
||||||
|
@@ -4,6 +4,7 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -16,8 +17,11 @@ using Sandbox.Game.World;
|
|||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SpaceEngineers.Game;
|
using SpaceEngineers.Game;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.Commands;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
|
using VRage.Game.ObjectBuilder;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Scripting;
|
using VRage.Scripting;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
@@ -40,6 +44,7 @@ namespace Torch
|
|||||||
public IMultiplayer Multiplayer { get; protected set; }
|
public IMultiplayer Multiplayer { get; protected set; }
|
||||||
public EntityManager Entities { get; protected set; }
|
public EntityManager Entities { get; protected set; }
|
||||||
public NetworkManager Network { get; protected set; }
|
public NetworkManager Network { get; protected set; }
|
||||||
|
public CommandManager Commands { get; protected set; }
|
||||||
public event Action SessionLoading;
|
public event Action SessionLoading;
|
||||||
public event Action SessionLoaded;
|
public event Action SessionLoaded;
|
||||||
public event Action SessionUnloading;
|
public event Action SessionUnloading;
|
||||||
@@ -60,6 +65,7 @@ namespace Torch
|
|||||||
Multiplayer = new MultiplayerManager(this);
|
Multiplayer = new MultiplayerManager(this);
|
||||||
Entities = new EntityManager(this);
|
Entities = new EntityManager(this);
|
||||||
Network = NetworkManager.Instance;
|
Network = NetworkManager.Instance;
|
||||||
|
Commands = new CommandManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsOnGameThread()
|
public bool IsOnGameThread()
|
||||||
@@ -147,10 +153,6 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
action.Invoke();
|
action.Invoke();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
//log
|
|
||||||
}
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
e.Set();
|
e.Set();
|
||||||
@@ -167,10 +169,19 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
Debug.Assert(!_init, "Torch instance is already initialized.");
|
Debug.Assert(!_init, "Torch instance is already initialized.");
|
||||||
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
|
||||||
|
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (Directory.Exists("DedicatedServer64"))
|
||||||
|
{
|
||||||
|
Log.Debug("Inserting DedicatedServer64 before MyPerGameSettings assembly paths");
|
||||||
|
MyPerGameSettings.GameModAssembly = $"DedicatedServer64\\{MyPerGameSettings.GameModAssembly}";
|
||||||
|
MyPerGameSettings.GameModObjBuildersAssembly = $"DedicatedServer64\\{MyPerGameSettings.GameModObjBuildersAssembly}";
|
||||||
|
MyPerGameSettings.SandboxAssembly = $"DedicatedServer64\\{MyPerGameSettings.SandboxAssembly}";
|
||||||
|
MyPerGameSettings.SandboxGameAssembly = $"DedicatedServer64\\{MyPerGameSettings.SandboxGameAssembly}";
|
||||||
|
}*/
|
||||||
|
|
||||||
TorchVersion = Assembly.GetEntryAssembly().GetName().Version;
|
TorchVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||||
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
||||||
var verInfo = $"Torch {TorchVersion}, SE {GameVersion}";
|
var verInfo = $"Torch {TorchVersion}, SE {GameVersion}";
|
||||||
@@ -188,7 +199,7 @@ namespace Torch
|
|||||||
MySession.AfterLoading += () => SessionLoaded?.Invoke();
|
MySession.AfterLoading += () => SessionLoaded?.Invoke();
|
||||||
MySession.OnUnloading += () => SessionUnloading?.Invoke();
|
MySession.OnUnloading += () => SessionUnloading?.Invoke();
|
||||||
MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
|
MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
|
||||||
InitUpdater();
|
RegisterVRagePlugin();
|
||||||
|
|
||||||
_init = true;
|
_init = true;
|
||||||
}
|
}
|
||||||
@@ -196,7 +207,7 @@ namespace Torch
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hook into the VRage plugin system for updates.
|
/// Hook into the VRage plugin system for updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitUpdater()
|
private void RegisterVRagePlugin()
|
||||||
{
|
{
|
||||||
var fieldName = "m_plugins";
|
var fieldName = "m_plugins";
|
||||||
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
||||||
@@ -206,20 +217,6 @@ namespace Torch
|
|||||||
pluginList.Add(this);
|
pluginList.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
var ex = (Exception)e.ExceptionObject;
|
|
||||||
Log.Fatal(ex);
|
|
||||||
if (ex is ReflectionTypeLoadException rex)
|
|
||||||
{
|
|
||||||
foreach (var x in rex.LoaderExceptions)
|
|
||||||
Log.Fatal(x);
|
|
||||||
}
|
|
||||||
Console.ReadLine();
|
|
||||||
Environment.Exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Start();
|
public abstract void Start();
|
||||||
public abstract void Stop();
|
public abstract void Stop();
|
||||||
|
|
||||||
|
@@ -4,10 +4,10 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Sandbox.ModAPI.Ingame;
|
using Sandbox.ModAPI.Ingame;
|
||||||
using VRageMath;
|
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
@@ -17,14 +17,17 @@ namespace Torch
|
|||||||
|
|
||||||
public string InstancePath { get; set; }
|
public string InstancePath { get; set; }
|
||||||
public string InstanceName { get; set; }
|
public string InstanceName { get; set; }
|
||||||
|
#warning World Path not implemented
|
||||||
|
public string WorldPath { get; set; }
|
||||||
//public int Autosave { get; set; }
|
//public int Autosave { get; set; }
|
||||||
//public bool AutoRestart { get; set; }
|
//public bool AutoRestart { get; set; }
|
||||||
//public bool LogChat { get; set; }
|
//public bool LogChat { get; set; }
|
||||||
public bool EnableAutomaticUpdates { get; set; } = true;
|
public bool AutomaticUpdates { get; set; } = true;
|
||||||
public bool RedownloadPlugins { get; set; }
|
public bool RedownloadPlugins { get; set; }
|
||||||
|
public bool RestartOnCrash { get; set; }
|
||||||
public List<string> Plugins { get; set; } = new List<string>();
|
public List<string> Plugins { get; set; } = new List<string>();
|
||||||
public Vector2I WindowSize { get; set; } = new Vector2I(800, 600);
|
public Point WindowSize { get; set; } = new Point(800, 600);
|
||||||
public Vector2I WindowPosition { get; set; } = new Vector2I();
|
public Point WindowPosition { get; set; } = new Point();
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private string _path;
|
private string _path;
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ namespace Torch
|
|||||||
public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false)
|
public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false)
|
||||||
{
|
{
|
||||||
InstanceName = instanceName;
|
InstanceName = instanceName;
|
||||||
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Torch", InstanceName);
|
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SpaceEngineersDedicated");
|
||||||
//Autosave = autosaveInterval;
|
//Autosave = autosaveInterval;
|
||||||
//AutoRestart = autoRestart;
|
//AutoRestart = autoRestart;
|
||||||
}
|
}
|
||||||
@@ -68,7 +71,7 @@ namespace Torch
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var serializer = new XmlSerializer(typeof(TorchConfig));
|
var serializer = new XmlSerializer(typeof(TorchConfig));
|
||||||
using (var f = File.OpenWrite(path))
|
using (var f = File.Create(path))
|
||||||
{
|
{
|
||||||
serializer.Serialize(f, this);
|
serializer.Serialize(f, this);
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
public abstract class TorchPluginBase : ITorchPlugin
|
public abstract class TorchPluginBase : ITorchPlugin
|
||||||
{
|
{
|
||||||
|
public string StoragePath { get; internal set; }
|
||||||
public Guid Id { get; }
|
public Guid Id { get; }
|
||||||
public Version Version { get; }
|
public Version Version { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
@@ -53,7 +53,7 @@ namespace Torch.Updater
|
|||||||
currentVersion = new Version(manifest.Version);
|
currentVersion = new Version(manifest.Version);
|
||||||
latestVersion = new Version(releases[0].TagName);
|
latestVersion = new Version(releases[0].TagName);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
Log.Warn("Invalid version number on manifest or GitHub release");
|
Log.Warn("Invalid version number on manifest or GitHub release");
|
||||||
return;
|
return;
|
||||||
|
Reference in New Issue
Block a user