Refactor instance management, assorted bugfixes/tweaks

This commit is contained in:
John Gross
2017-07-22 23:11:16 -07:00
parent 3ece4baba6
commit 1fcfe6fb5f
18 changed files with 238 additions and 257 deletions

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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