Refactor instance management, assorted bugfixes/tweaks
This commit is contained in:
@@ -4,44 +4,21 @@ namespace Torch
|
||||
{
|
||||
public interface ITorchConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// (server) Name of the instance.
|
||||
/// </summary>
|
||||
string InstanceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// (server) Dedicated instance path.
|
||||
/// </summary>
|
||||
string InstancePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable automatic Torch updates.
|
||||
/// </summary>
|
||||
bool GetTorchUpdates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable automatic Torch updates.
|
||||
/// </summary>
|
||||
bool Autostart { get; set; }
|
||||
bool ForceUpdate { get; set; }
|
||||
bool GetPluginUpdates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Restart Torch automatically if it crashes.
|
||||
/// </summary>
|
||||
bool GetTorchUpdates { get; set; }
|
||||
string InstanceName { get; set; }
|
||||
string InstancePath { get; set; }
|
||||
bool NoGui { get; set; }
|
||||
bool NoUpdate { get; set; }
|
||||
List<string> Plugins { get; set; }
|
||||
bool RestartOnCrash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time-out in seconds for the Torch watchdog (to detect a hung session).
|
||||
/// </summary>
|
||||
bool ShouldUpdatePlugins { get; }
|
||||
bool ShouldUpdateTorch { get; }
|
||||
int TickTimeout { get; set; }
|
||||
string WaitForPID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of plugins that should be installed.
|
||||
/// </summary>
|
||||
List<string> Plugins { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Saves the config.
|
||||
/// </summary>
|
||||
bool Save(string path = null);
|
||||
}
|
||||
}
|
@@ -158,12 +158,12 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="ConnectionState.cs" />
|
||||
<Compile Include="IChatMessage.cs" />
|
||||
<Compile Include="ITorchConfig.cs" />
|
||||
<Compile Include="Managers\IManager.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManager.cs" />
|
||||
<Compile Include="IPlayer.cs" />
|
||||
<Compile Include="Managers\INetworkManager.cs" />
|
||||
<Compile Include="Managers\IPluginManager.cs" />
|
||||
<Compile Include="ITorchConfig.cs" />
|
||||
<Compile Include="Plugins\ITorchPlugin.cs" />
|
||||
<Compile Include="IServerControls.cs" />
|
||||
<Compile Include="ITorchBase.cs" />
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.198.562")]
|
||||
[assembly: AssemblyFileVersion("1.0.198.562")]
|
||||
[assembly: AssemblyVersion("1.0.203.595")]
|
||||
[assembly: AssemblyFileVersion("1.0.203.595")]
|
@@ -1,64 +0,0 @@
|
||||
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.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
using VRage.Game;
|
||||
|
||||
namespace Torch.Server.Managers
|
||||
{
|
||||
//TODO
|
||||
public class ConfigManager : Manager
|
||||
{
|
||||
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
||||
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
||||
public TorchConfig TorchConfig { get; set; }
|
||||
|
||||
public ConfigManager(ITorchBase torchInstance) : base(torchInstance)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
LoadInstance(Torch.Config.InstancePath);
|
||||
}
|
||||
|
||||
public void LoadInstance(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
throw new FileNotFoundException($"Instance directory not found at '{path}'");
|
||||
|
||||
var configPath = Path.Combine(path, CONFIG_NAME);
|
||||
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
||||
config.Load();
|
||||
DedicatedConfig = new ConfigDedicatedViewModel(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 savesPath = Path.Combine(path, "Saves");
|
||||
Directory.CreateDirectory(savesPath);
|
||||
var modsPath = Path.Combine(path, "Mods");
|
||||
Directory.CreateDirectory(modsPath);
|
||||
var configPath = Path.Combine(path, "SpaceEngineers-Dedicated.cfg");
|
||||
new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath).Save();
|
||||
LoadInstance(path);
|
||||
}
|
||||
}
|
||||
}
|
158
Torch.Server/Managers/InstanceManager.cs
Normal file
158
Torch.Server/Managers/InstanceManager.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Havok;
|
||||
using NLog;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.ObjectBuilders;
|
||||
|
||||
namespace Torch.Server.Managers
|
||||
{
|
||||
public class InstanceManager : Manager
|
||||
{
|
||||
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
||||
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
||||
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
|
||||
|
||||
public InstanceManager(ITorchBase torchInstance) : base(torchInstance)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
LoadInstance(Torch.Config.InstancePath);
|
||||
}
|
||||
|
||||
public void LoadInstance(string path, bool validate = true)
|
||||
{
|
||||
if (validate)
|
||||
ValidateInstance(path);
|
||||
|
||||
MyFileSystem.Reset();
|
||||
MyFileSystem.ExePath = Path.Combine(Torch.GetManager<FilesystemManager>().TorchDirectory, "DedicatedServer64");
|
||||
MyFileSystem.Init("Content", path);
|
||||
|
||||
var configPath = Path.Combine(path, CONFIG_NAME);
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
Log.Error($"Failed to load dedicated config at {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
||||
config.Load(configPath);
|
||||
|
||||
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
|
||||
|
||||
foreach (var f in worldFolders)
|
||||
DedicatedConfig.WorldPaths.Add(f);
|
||||
|
||||
if (DedicatedConfig.WorldPaths.Count == 0)
|
||||
{
|
||||
Log.Warn($"No worlds found in the current instance {path}.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
|
||||
{
|
||||
Log.Warn("No world specified, importing first available world.");
|
||||
SelectWorld(DedicatedConfig.WorldPaths[0], false);
|
||||
}*/
|
||||
}
|
||||
|
||||
public void SelectWorld(string worldPath, bool modsOnly = true)
|
||||
{
|
||||
DedicatedConfig.LoadWorld = worldPath;
|
||||
LoadWorldMods(modsOnly);
|
||||
}
|
||||
|
||||
|
||||
private void LoadWorldMods(bool modsOnly = true)
|
||||
{
|
||||
if (DedicatedConfig.LoadWorld == null)
|
||||
return;
|
||||
|
||||
var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc");
|
||||
|
||||
if (!File.Exists(sandboxPath))
|
||||
return;
|
||||
|
||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var mod in checkpoint.Mods)
|
||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
||||
|
||||
DedicatedConfig.Mods = sb.ToString();
|
||||
|
||||
Log.Info("Loaded mod list from world");
|
||||
|
||||
if (!modsOnly)
|
||||
DedicatedConfig.SessionSettings = new SessionSettingsViewModel(checkpoint.Settings);
|
||||
}
|
||||
|
||||
public void SaveConfig()
|
||||
{
|
||||
DedicatedConfig.Model.Save();
|
||||
Log.Info("Saved dedicated config.");
|
||||
|
||||
try
|
||||
{
|
||||
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
checkpoint.Settings = DedicatedConfig.SessionSettings;
|
||||
checkpoint.Mods.Clear();
|
||||
foreach (var modId in DedicatedConfig.Model.Mods)
|
||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||
|
||||
MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
|
||||
Log.Info("Saved world config.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the given path is a valid server instance.
|
||||
/// </summary>
|
||||
private void ValidateInstance(string path)
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(path, "Saves"));
|
||||
Directory.CreateDirectory(Path.Combine(path, "Mods"));
|
||||
var configPath = Path.Combine(path, CONFIG_NAME);
|
||||
if (File.Exists(configPath))
|
||||
return;
|
||||
|
||||
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
||||
config.Save(configPath);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.1.198.562")]
|
||||
[assembly: AssemblyFileVersion("1.1.198.562")]
|
||||
[assembly: AssemblyVersion("1.1.203.596")]
|
||||
[assembly: AssemblyFileVersion("1.1.203.596")]
|
@@ -185,7 +185,7 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Managers\ConfigManager.cs" />
|
||||
<Compile Include="Managers\InstanceManager.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
@@ -366,7 +366,9 @@
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
|
||||
<PostBuildEvent>cd "$(TargetDir)"
|
||||
copy "$(SolutionDir)NLog.config" "$(TargetDir)"
|
||||
"Torch Server Release.bat"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
@@ -12,17 +12,24 @@ namespace Torch.Server
|
||||
{
|
||||
private static Logger _log = LogManager.GetLogger("Config");
|
||||
|
||||
public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate;
|
||||
public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Arg("instancename", "The name of the Torch instance.")]
|
||||
public string InstanceName { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Arg("instancepath", "Server data folder where saves and mods are stored.")]
|
||||
public string InstancePath { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
|
||||
public bool NoUpdate { get => false; set => GetTorchUpdates = GetPluginUpdates = !value; }
|
||||
public bool NoUpdate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore, Arg("update", "Manually check for and install updates.")]
|
||||
public bool Update { get; set; }
|
||||
[JsonIgnore, Arg("forceupdate", "Manually check for and install updates.")]
|
||||
public bool ForceUpdate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Arg("autostart", "Start the server immediately.")]
|
||||
@@ -40,10 +47,6 @@ namespace Torch.Server
|
||||
[JsonIgnore, Arg("waitforpid", "Makes Torch wait for another process to exit.")]
|
||||
public string WaitForPID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Arg("instancename", "The name of the Torch instance.")]
|
||||
public string InstanceName { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool GetTorchUpdates { get; set; } = true;
|
||||
|
||||
@@ -54,7 +57,7 @@ namespace Torch.Server
|
||||
public int TickTimeout { get; set; } = 60;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<string> Plugins { get; set; } = new List<string> {"TorchAPI/Concealment", "TorchAPI/Essentials"};
|
||||
public List<string> Plugins { get; set; } = new List<string>();
|
||||
|
||||
internal Point WindowSize { get; set; } = new Point(800, 600);
|
||||
internal Point WindowPosition { get; set; } = new Point();
|
||||
@@ -73,9 +76,13 @@ namespace Torch.Server
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = JsonConvert.DeserializeObject<TorchConfig>(File.ReadAllText(path));
|
||||
config._path = path;
|
||||
return config;
|
||||
var ser = new XmlSerializer(typeof(TorchConfig));
|
||||
using (var f = File.OpenRead(path))
|
||||
{
|
||||
var config = (TorchConfig)ser.Deserialize(f);
|
||||
config._path = path;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -93,8 +100,9 @@ namespace Torch.Server
|
||||
|
||||
try
|
||||
{
|
||||
var str = JsonConvert.SerializeObject(this);
|
||||
File.WriteAllText(path, str);
|
||||
var ser = new XmlSerializer(typeof(TorchConfig));
|
||||
using (var f = File.Create(path))
|
||||
ser.Serialize(f, this);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@@ -41,6 +41,7 @@ namespace Torch.Server
|
||||
public Thread GameThread { get; private set; }
|
||||
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
||||
public bool IsRunning { get => _isRunning; set { _isRunning = value; OnPropertyChanged(); } }
|
||||
public InstanceManager DedicatedInstance { get; }
|
||||
/// <inheritdoc />
|
||||
public string InstanceName => Config?.InstanceName;
|
||||
/// <inheritdoc />
|
||||
@@ -56,7 +57,8 @@ namespace Torch.Server
|
||||
|
||||
public TorchServer(TorchConfig config = null)
|
||||
{
|
||||
AddManager(new ConfigManager(this));
|
||||
DedicatedInstance = new InstanceManager(this);
|
||||
AddManager(DedicatedInstance);
|
||||
Config = config ?? new TorchConfig();
|
||||
MyFakes.ENABLE_INFINARIO = false;
|
||||
}
|
||||
@@ -64,9 +66,8 @@ namespace Torch.Server
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||
base.Init();
|
||||
|
||||
MyPerGameSettings.SendLogToKeen = false;
|
||||
MyPerServerSettings.GameName = MyPerGameSettings.GameName;
|
||||
@@ -78,7 +79,6 @@ namespace Torch.Server
|
||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
||||
|
||||
MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
||||
|
||||
InvokeBeforeRun();
|
||||
|
||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
||||
@@ -88,26 +88,11 @@ namespace Torch.Server
|
||||
MyPlugins.Load();
|
||||
MyGlobalTypeMetadata.Static.Init();
|
||||
|
||||
if (!Directory.Exists(Config.InstancePath))
|
||||
GetManager<ConfigManager>().CreateInstance(Config.InstancePath);
|
||||
|
||||
Plugins.LoadPlugins();
|
||||
}
|
||||
|
||||
private 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
|
||||
{
|
||||
MyFileSystem.ExePath = Path.Combine(GetManager<FilesystemManager>().TorchDirectory, "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);
|
||||
@@ -142,6 +127,7 @@ namespace Torch.Server
|
||||
if (State != ServerState.Stopped)
|
||||
return;
|
||||
|
||||
DedicatedInstance.SaveConfig();
|
||||
_uptime = Stopwatch.StartNew();
|
||||
IsRunning = true;
|
||||
GameThread = Thread.CurrentThread;
|
||||
@@ -179,10 +165,10 @@ namespace Torch.Server
|
||||
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
|
||||
ElapsedPlayTime = elapsed;
|
||||
|
||||
if (_watchdog == null && Instance.Config.TickTimeout > 0)
|
||||
if (_watchdog == null && Config.TickTimeout > 0)
|
||||
{
|
||||
Log.Info("Starting server watchdog.");
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Instance.Config.TickTimeout));
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Config.TickTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +181,7 @@ namespace Torch.Server
|
||||
var mainThread = MySandboxGame.Static.UpdateThread;
|
||||
mainThread.Suspend();
|
||||
var stackTrace = new StackTrace(mainThread, true);
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {Instance.Config.TickTimeout} seconds.\n{stackTrace}");
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.\n{stackTrace}");
|
||||
}
|
||||
|
||||
Log.Debug("Server watchdog responded");
|
||||
|
@@ -15,6 +15,7 @@ namespace Torch.Server.ViewModels
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetLogger("Config");
|
||||
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
|
||||
|
||||
public ConfigDedicatedViewModel() : this(new MyConfigDedicated<MyObjectBuilder_SessionSettings>(""))
|
||||
{
|
||||
@@ -54,7 +55,8 @@ namespace Torch.Server.ViewModels
|
||||
_config.Save(path);
|
||||
}
|
||||
|
||||
public SessionSettingsViewModel SessionSettings { get; }
|
||||
private SessionSettingsViewModel _sessionSettings;
|
||||
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
|
||||
|
||||
public ObservableList<string> WorldPaths { get; } = new ObservableList<string>();
|
||||
private string _administrators;
|
||||
|
@@ -21,6 +21,7 @@ using NLog;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
using Torch.Views;
|
||||
using VRage;
|
||||
@@ -36,109 +37,29 @@ namespace Torch.Server.Views
|
||||
/// </summary>
|
||||
public partial class ConfigControl : UserControl
|
||||
{
|
||||
private readonly Logger Log = LogManager.GetLogger("Config");
|
||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
|
||||
private ConfigDedicatedViewModel _viewModel;
|
||||
private string _configPath;
|
||||
private TorchConfig _torchConfig;
|
||||
private InstanceManager _instanceManager;
|
||||
|
||||
public ConfigControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void SaveConfig()
|
||||
{
|
||||
_viewModel.Save(_configPath);
|
||||
Log.Info("Saved DS config.");
|
||||
try
|
||||
{
|
||||
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(Config.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {Config.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
checkpoint.Settings = Config.SessionSettings;
|
||||
checkpoint.Mods.Clear();
|
||||
foreach (var modId in Config.Mods)
|
||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||
|
||||
MyLocalCache.SaveCheckpoint(checkpoint, Config.LoadWorld);
|
||||
Log.Info("Saved world config.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadDedicatedConfig(TorchConfig torchConfig)
|
||||
{
|
||||
_torchConfig = torchConfig;
|
||||
DataContext = null;
|
||||
MySandboxGame.Config = new MyConfig(MyPerServerSettings.GameNameSafe + ".cfg");
|
||||
var path = Path.Combine(torchConfig.InstancePath, "SpaceEngineers-Dedicated.cfg");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
Log.Error($"Failed to load dedicated config at {path}");
|
||||
DataContext = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(path);
|
||||
Config.Load(path);
|
||||
_configPath = path;
|
||||
|
||||
_viewModel = new ConfigDedicatedViewModel(Config);
|
||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
|
||||
|
||||
foreach (var f in worldFolders)
|
||||
_viewModel.WorldPaths.Add(f);
|
||||
|
||||
LoadWorldMods();
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
private void LoadWorldMods()
|
||||
{
|
||||
var sandboxPath = Path.Combine(Config.LoadWorld, "Sandbox.sbc");
|
||||
|
||||
if (!File.Exists(sandboxPath))
|
||||
return;
|
||||
|
||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {Config.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var mod in checkpoint.Mods)
|
||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
||||
|
||||
_viewModel.Mods = sb.ToString();
|
||||
|
||||
Log.Info("Loaded mod list from world");
|
||||
_instanceManager = TorchBase.Instance.GetManager<InstanceManager>();
|
||||
DataContext = _instanceManager.DedicatedConfig;
|
||||
}
|
||||
|
||||
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SaveConfig();
|
||||
_instanceManager.SaveConfig();
|
||||
}
|
||||
|
||||
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
|
||||
_viewModel.SessionSettings.BlockLimits.Remove(vm);
|
||||
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Remove(vm);
|
||||
}
|
||||
|
||||
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_viewModel.SessionSettings, "", 0));
|
||||
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_instanceManager.DedicatedConfig.SessionSettings, "", 0));
|
||||
}
|
||||
|
||||
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
||||
@@ -151,8 +72,7 @@ namespace Torch.Server.Views
|
||||
//The control doesn't update the binding before firing the event.
|
||||
if (e.AddedItems.Count > 0)
|
||||
{
|
||||
Config.LoadWorld = (string)e.AddedItems[0];
|
||||
LoadWorldMods();
|
||||
_instanceManager.SelectWorld((string)e.AddedItems[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" 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" IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"/>
|
||||
HorizontalAlignment="Left" Click="BtnStart_Click" IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"/>
|
||||
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
|
||||
Click="BtnStop_Click" IsEnabled="{Binding IsRunning}" />
|
||||
<Label>
|
||||
@@ -51,7 +51,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Content="Instance Path: " Margin="3" />
|
||||
<TextBox Grid.Column="1" x:Name="InstancePathBox" Margin="3" Height="20"
|
||||
TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False" />
|
||||
LostKeyboardFocus="InstancePathBox_OnLostKeyboardFocus" />
|
||||
</Grid>
|
||||
<views:ConfigControl Grid.Row="1" x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"/>
|
||||
</Grid>
|
||||
|
@@ -19,6 +19,7 @@ using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Sandbox;
|
||||
using Torch.API;
|
||||
using Torch.Server.Managers;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Torch.Server
|
||||
@@ -58,7 +59,6 @@ namespace Torch.Server
|
||||
_config = config;
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ConfigControl.LoadDedicatedConfig(config);
|
||||
InstancePathBox.Text = config.InstancePath;
|
||||
});
|
||||
}
|
||||
@@ -66,7 +66,6 @@ namespace Torch.Server
|
||||
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_config.Save();
|
||||
ConfigControl.SaveConfig();
|
||||
new Thread(_server.Start).Start();
|
||||
}
|
||||
|
||||
@@ -92,13 +91,15 @@ namespace Torch.Server
|
||||
//MySandboxGame.Static.Invoke(MySandboxGame.ReloadDedicatedServerSession); use i
|
||||
}
|
||||
|
||||
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
private void InstancePathBox_OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
||||
{
|
||||
var name = ((TextBox)sender).Text;
|
||||
|
||||
_config.InstancePath = name;
|
||||
if (!Directory.Exists(name))
|
||||
return;
|
||||
|
||||
LoadConfig(_config);
|
||||
_config.InstancePath = name;
|
||||
_server.GetManager<InstanceManager>().LoadInstance(_config.InstancePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,8 +40,7 @@ namespace Torch.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
var topNodeNames = commandManager.Commands.Root.Select(x => x.Key);
|
||||
Context.Respond($"Top level commands: {string.Join(", ", topNodeNames)}");
|
||||
Context.Respond($"Use the {commandManager.Prefix}longhelp command and check your Comms menu for a full list of commands.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -71,7 +71,7 @@ namespace Torch.Managers
|
||||
}
|
||||
|
||||
var manifest = PluginManifest.Load(manifestPath);
|
||||
toDownload.Remove(manifest.Repository);
|
||||
toDownload.RemoveAll(x => string.Compare(manifest.Repository, x, StringComparison.InvariantCultureIgnoreCase) == 0);
|
||||
taskList.Add(_updateManager.CheckAndUpdatePlugin(manifest));
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Torch.Managers
|
||||
_updateManager = Torch.GetManager<UpdateManager>();
|
||||
var commands = Torch.GetManager<CommandManager>();
|
||||
|
||||
if (Torch.Config.GetPluginUpdates)
|
||||
if (Torch.Config.ShouldUpdatePlugins)
|
||||
DownloadPlugins();
|
||||
else
|
||||
_log.Warn("Automatic plugin updates are disabled.");
|
||||
|
@@ -54,9 +54,10 @@ namespace Torch.Managers
|
||||
var zip = latest.Assets.FirstOrDefault(x => x.Name.Contains(".zip"));
|
||||
return new Tuple<Version, string>(new Version(latest.TagName ?? "0"), zip?.BrowserDownloadUrl);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error($"An error occurred getting release information for '{owner}/{name}'");
|
||||
_log.Error(e);
|
||||
return new Tuple<Version, string>(new Version(), null);
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,12 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Torch
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple class that manages saving <see cref="Persistent{T}.Data"/> to disk using JSON serialization.
|
||||
/// Simple class that manages saving <see cref="Persistent{T}.Data"/> to disk using XML serialization.
|
||||
/// Can automatically save on changes by implementing <see cref="INotifyPropertyChanged"/> in the data class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data class type</typeparam>
|
||||
@@ -19,7 +19,6 @@ namespace Torch
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public T Data { get; private set; }
|
||||
private Timer _saveTimer;
|
||||
|
||||
~Persistent()
|
||||
{
|
||||
@@ -28,7 +27,6 @@ namespace Torch
|
||||
|
||||
public Persistent(string path, T data = default(T))
|
||||
{
|
||||
_saveTimer = new Timer(Callback);
|
||||
Path = path;
|
||||
Data = data;
|
||||
if (Data is INotifyPropertyChanged npc)
|
||||
@@ -36,11 +34,6 @@ namespace Torch
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
_saveTimer.Change(5000, -1);
|
||||
}
|
||||
|
||||
private void Callback(object state)
|
||||
{
|
||||
Save();
|
||||
}
|
||||
@@ -50,11 +43,10 @@ namespace Torch
|
||||
if (path == null)
|
||||
path = Path;
|
||||
|
||||
var ser = new XmlSerializer(typeof(T));
|
||||
using (var f = File.Create(path))
|
||||
{
|
||||
var writer = new StreamWriter(f);
|
||||
writer.Write(JsonConvert.SerializeObject(Data, Formatting.Indented));
|
||||
writer.Flush();
|
||||
ser.Serialize(f, Data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +56,10 @@ namespace Torch
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var ser = new XmlSerializer(typeof(T));
|
||||
using (var f = File.OpenRead(path))
|
||||
{
|
||||
var reader = new StreamReader(f);
|
||||
config.Data = JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
|
||||
config.Data = (T)ser.Deserialize(f);
|
||||
}
|
||||
}
|
||||
else if (saveIfNew)
|
||||
@@ -84,7 +76,6 @@ namespace Torch
|
||||
{
|
||||
if (Data is INotifyPropertyChanged npc)
|
||||
npc.PropertyChanged -= OnPropertyChanged;
|
||||
_saveTimer.Dispose();
|
||||
Save();
|
||||
}
|
||||
catch
|
||||
|
@@ -228,7 +228,7 @@ namespace Torch
|
||||
|
||||
TorchVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
||||
var verInfo = $"Torch {TorchVersion}, SE {GameVersion}";
|
||||
var verInfo = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}";
|
||||
Console.Title = verInfo;
|
||||
#if DEBUG
|
||||
Log.Info("DEBUG");
|
||||
|
Reference in New Issue
Block a user