diff --git a/Torch.API/ITorchBase.cs b/Torch.API/ITorchBase.cs
index e83aa63..2eecdba 100644
--- a/Torch.API/ITorchBase.cs
+++ b/Torch.API/ITorchBase.cs
@@ -8,32 +8,99 @@ using VRage.Game.ModAPI;
namespace Torch.API
{
+ ///
+ /// API for Torch functions shared between client and server.
+ ///
public interface ITorchBase
{
+ ///
+ /// Fired when the session begins loading.
+ ///
event Action SessionLoading;
+
+ ///
+ /// Fired when the session finishes loading.
+ ///
event Action SessionLoaded;
+
+ ///
+ /// Fires when the session begins unloading.
+ ///
event Action SessionUnloading;
+
+ ///
+ /// Fired when the session finishes unloading.
+ ///
event Action SessionUnloaded;
+
+ ///
+ /// Configuration for the current instance.
+ ///
ITorchConfig Config { get; }
+
+ ///
IMultiplayerManager Multiplayer { get; }
+
+ ///
IPluginManager Plugins { get; }
+
+ ///
+ /// The binary version of the current instance.
+ ///
Version TorchVersion { get; }
+
+ ///
+ /// Invoke an action on the game thread.
+ ///
void Invoke(Action action);
+
+ ///
+ /// Invoke an action on the game thread and block until it has completed.
+ /// If this is called on the game thread the action will execute immediately.
+ ///
void InvokeBlocking(Action action);
+
+ ///
+ /// Invoke an action on the game thread asynchronously.
+ ///
Task InvokeAsync(Action action);
- string[] RunArgs { get; set; }
- bool IsOnGameThread();
+
+ ///
+ /// Start the Torch instance.
+ ///
void Start();
+
+ ///
+ /// Stop the Torch instance.
+ ///
void Stop();
+
+ ///
+ /// Initialize the Torch instance.
+ ///
void Init();
+
+ ///
+ /// Get an that is part of the Torch instance.
+ ///
+ /// Manager type
T GetManager() where T : class, IManager;
}
+ ///
+ /// API for the Torch server.
+ ///
public interface ITorchServer : ITorchBase
{
+ ///
+ /// Path of the dedicated instance folder.
+ ///
string InstancePath { get; }
}
+ ///
+ /// API for the Torch client.
+ ///
public interface ITorchClient : ITorchBase
{
diff --git a/Torch.API/ITorchConfig.cs b/Torch.API/ITorchConfig.cs
index 12d0328..574d837 100644
--- a/Torch.API/ITorchConfig.cs
+++ b/Torch.API/ITorchConfig.cs
@@ -1,14 +1,47 @@
-namespace Torch
+using System.Collections.Generic;
+
+namespace Torch
{
public interface ITorchConfig
{
+ ///
+ /// (server) Name of the instance.
+ ///
string InstanceName { get; set; }
+
+ ///
+ /// (server) Dedicated instance path.
+ ///
string InstancePath { get; set; }
- bool RedownloadPlugins { get; set; }
- bool AutomaticUpdates { get; set; }
+
+ ///
+ /// Enable automatic Torch updates.
+ ///
+ bool GetTorchUpdates { get; set; }
+
+ ///
+ /// Enable automatic Torch updates.
+ ///
+ bool GetPluginUpdates { get; set; }
+
+ ///
+ /// Restart Torch automatically if it crashes.
+ ///
bool RestartOnCrash { get; set; }
+
+ ///
+ /// Time-out in seconds for the Torch watchdog (to detect a hung session).
+ ///
int TickTimeout { get; set; }
+ ///
+ /// A list of plugins that should be installed.
+ ///
+ List Plugins { get; }
+
+ ///
+ /// Saves the config.
+ ///
bool Save(string path = null);
}
}
\ No newline at end of file
diff --git a/Torch.API/Managers/IManager.cs b/Torch.API/Managers/IManager.cs
index 6274262..e6e3059 100644
--- a/Torch.API/Managers/IManager.cs
+++ b/Torch.API/Managers/IManager.cs
@@ -6,8 +6,14 @@ using System.Threading.Tasks;
namespace Torch.API.Managers
{
+ ///
+ /// Base interface for Torch managers.
+ ///
public interface IManager
{
+ ///
+ /// Initializes the manager. Called after Torch is initialized.
+ ///
void Init();
}
}
diff --git a/Torch.API/Managers/IMultiplayerManager.cs b/Torch.API/Managers/IMultiplayerManager.cs
index 4a52844..509360d 100644
--- a/Torch.API/Managers/IMultiplayerManager.cs
+++ b/Torch.API/Managers/IMultiplayerManager.cs
@@ -6,17 +6,56 @@ using VRage.Game.ModAPI;
namespace Torch.API.Managers
{
+ ///
+ /// Delegate for received messages.
+ ///
+ /// Message data.
+ /// Flag to broadcast message to other players.
public delegate void MessageReceivedDel(IChatMessage message, ref bool sendToOthers);
+ ///
+ /// API for multiplayer related functions.
+ ///
public interface IMultiplayerManager : IManager
{
+ ///
+ /// Fired when a player joins.
+ ///
event Action PlayerJoined;
+
+ ///
+ /// Fired when a player disconnects.
+ ///
event Action PlayerLeft;
+
+ ///
+ /// Fired when a chat message is received.
+ ///
event MessageReceivedDel MessageReceived;
+
+ ///
+ /// Send a chat message to all or one specific player.
+ ///
void SendMessage(string message, string author = "Server", long playerId = 0, string font = MyFontEnum.Blue);
+
+ ///
+ /// Kicks the player from the game.
+ ///
void KickPlayer(ulong steamId);
+
+ ///
+ /// Bans or unbans a player from the game.
+ ///
void BanPlayer(ulong steamId, bool banned = true);
+
+ ///
+ /// Gets a player by their Steam64 ID or returns null if the player isn't found.
+ ///
IMyPlayer GetPlayerBySteamId(ulong id);
+
+ ///
+ /// Gets a player by their display name or returns null if the player isn't found.
+ ///
IMyPlayer GetPlayerByName(string name);
}
}
\ No newline at end of file
diff --git a/Torch.API/Managers/INetworkManager.cs b/Torch.API/Managers/INetworkManager.cs
index dc14f60..548ec10 100644
--- a/Torch.API/Managers/INetworkManager.cs
+++ b/Torch.API/Managers/INetworkManager.cs
@@ -9,14 +9,30 @@ using VRage.Network;
namespace Torch.API.Managers
{
+ ///
+ /// API for the network intercept.
+ ///
public interface INetworkManager : IManager
{
+ ///
+ /// Register a network handler.
+ ///
void RegisterNetworkHandler(INetworkHandler handler);
}
+ ///
+ /// Handler for multiplayer network messages.
+ ///
public interface INetworkHandler
{
+ ///
+ /// Returns if the handler can process the call site.
+ ///
bool CanHandle(CallSite callSite);
+
+ ///
+ /// Processes a network message.
+ ///
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
}
}
diff --git a/Torch.API/Managers/IPluginManager.cs b/Torch.API/Managers/IPluginManager.cs
index 7d685ba..a1ad100 100644
--- a/Torch.API/Managers/IPluginManager.cs
+++ b/Torch.API/Managers/IPluginManager.cs
@@ -6,11 +6,29 @@ using VRage.Plugins;
namespace Torch.API.Managers
{
+ ///
+ /// API for the Torch plugin manager.
+ ///
public interface IPluginManager : IManager, IEnumerable
{
+ ///
+ /// Fired when plugins are loaded.
+ ///
event Action> PluginsLoaded;
- List Plugins { get; }
+
+ ///
+ /// Collection of loaded plugins.
+ ///
+ ObservableCollection Plugins { get; }
+
+ ///
+ /// Updates all loaded plugins.
+ ///
void UpdatePlugins();
+
+ ///
+ /// Disposes all loaded plugins.
+ ///
void DisposePlugins();
}
}
\ No newline at end of file
diff --git a/Torch.API/ServerState.cs b/Torch.API/ServerState.cs
index b80e8e2..7409680 100644
--- a/Torch.API/ServerState.cs
+++ b/Torch.API/ServerState.cs
@@ -6,11 +6,29 @@ using System.Threading.Tasks;
namespace Torch.API
{
+ ///
+ /// Used to indicate the state of the dedicated server.
+ ///
public enum ServerState
{
+ ///
+ /// The server is not running.
+ ///
Stopped,
+
+ ///
+ /// The server is starting/loading the session.
+ ///
Starting,
+
+ ///
+ /// The server is running.
+ ///
Running,
+
+ ///
+ /// The server encountered an error.
+ ///
Error
}
}
diff --git a/Torch.Client/Properties/AssemblyInfo.cs b/Torch.Client/Properties/AssemblyInfo.cs
index 9e1b243..0b06134 100644
--- a/Torch.Client/Properties/AssemblyInfo.cs
+++ b/Torch.Client/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("1.0.182.329")]
-[assembly: AssemblyFileVersion("1.0.182.329")]
\ No newline at end of file
+[assembly: AssemblyVersion("1.0.186.642")]
+[assembly: AssemblyFileVersion("1.0.186.642")]
\ No newline at end of file
diff --git a/Torch.Client/Torch.Client.csproj b/Torch.Client/Torch.Client.csproj
index b56672d..f279cf8 100644
--- a/Torch.Client/Torch.Client.csproj
+++ b/Torch.Client/Torch.Client.csproj
@@ -45,6 +45,10 @@
..\packages\NLog.4.4.1\lib\net45\NLog.dll
True
+
+ False
+ ..\GameBinaries\Sandbox.Common.dll
+
..\GameBinaries\Sandbox.Game.dll
False
diff --git a/Torch.Client/TorchClient.cs b/Torch.Client/TorchClient.cs
index 92b26b6..350cb60 100644
--- a/Torch.Client/TorchClient.cs
+++ b/Torch.Client/TorchClient.cs
@@ -9,6 +9,7 @@ using Sandbox;
using Sandbox.Engine.Platform;
using Sandbox.Engine.Utils;
using Sandbox.Game;
+using Sandbox.ModAPI;
using SpaceEngineers.Game;
using Torch.API;
using VRage.FileSystem;
diff --git a/Torch.Server/Program.cs b/Torch.Server/Program.cs
index c1823d3..ecf9a3f 100644
--- a/Torch.Server/Program.cs
+++ b/Torch.Server/Program.cs
@@ -34,8 +34,8 @@ namespace Torch.Server
private static ITorchServer _server;
private static Logger _log = LogManager.GetLogger("Torch");
private static bool _restartOnCrash;
- public static bool IsManualInstall;
- private static TorchCli _cli;
+ private static TorchConfig _config;
+ private static bool _steamCmdDone;
///
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
@@ -46,10 +46,10 @@ namespace Torch.Server
//Ensures that all the files are downloaded in the Torch directory.
Directory.SetCurrentDirectory(new FileInfo(typeof(Program).Assembly.Location).Directory.ToString());
- IsManualInstall = File.Exists("SpaceEngineersDedicated.exe");
- if (!IsManualInstall)
- AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
+ foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.old"))
+ File.Delete(file);
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (!Environment.UserInteractive)
@@ -63,50 +63,40 @@ namespace Torch.Server
var configName = "TorchConfig.xml";
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
- TorchConfig options;
if (File.Exists(configName))
{
_log.Info($"Loading config {configPath}");
- options = TorchConfig.LoadFrom(configPath);
+ _config = TorchConfig.LoadFrom(configPath);
}
else
{
_log.Info($"Generating default config at {configPath}");
- options = new TorchConfig();
+ _config = new TorchConfig {InstancePath = Path.GetFullPath("Instance")};
- if (!IsManualInstall)
+ _log.Warn("Would you like to enable automatic updates? (Y/n):");
+
+ var input = Console.ReadLine() ?? "";
+ var autoUpdate = string.IsNullOrEmpty(input) || input.Equals("y", StringComparison.InvariantCultureIgnoreCase);
+ _config.GetTorchUpdates = _config.GetPluginUpdates = autoUpdate;
+ if (autoUpdate)
{
- //new ConfigManager().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, updating server.");
- RunSteamCmd();
- }
+ _log.Info("Automatic updates enabled.");
+ RunSteamCmd();
}
- //var setupDialog = new FirstTimeSetup { DataContext = options };
- //setupDialog.ShowDialog();
- options.Save(configPath);
+ _config.Save(configPath);
}
- _cli = new TorchCli { Config = options };
- if (!_cli.Parse(args))
+ if (!_config.Parse(args))
return;
- _log.Debug(_cli.ToString());
+ _log.Debug(_config.ToString());
- if (!string.IsNullOrEmpty(_cli.WaitForPID))
+ if (!string.IsNullOrEmpty(_config.WaitForPID))
{
try
{
- var pid = int.Parse(_cli.WaitForPID);
+ var pid = int.Parse(_config.WaitForPID);
var waitProc = Process.GetProcessById(pid);
_log.Warn($"Waiting for process {pid} to exit.");
waitProc.WaitForExit();
@@ -117,18 +107,13 @@ namespace Torch.Server
}
}
- _restartOnCrash = _cli.RestartOnCrash;
+ _restartOnCrash = _config.RestartOnCrash;
- if (options.AutomaticUpdates || _cli.Update)
+ if (_config.GetTorchUpdates || _config.Update)
{
- if (IsManualInstall)
- _log.Warn("Detected manual install, won't attempt to update DS");
- else
- {
- RunSteamCmd();
- }
+ RunSteamCmd();
}
- RunServer(options, _cli);
+ RunServer(_config);
}
private const string STEAMCMD_DIR = "steamcmd";
@@ -142,6 +127,9 @@ quit";
public static void RunSteamCmd()
{
+ if (_steamCmdDone)
+ return;
+
var log = LogManager.GetLogger("SteamCMD");
if (!Directory.Exists(STEAMCMD_DIR))
@@ -187,38 +175,40 @@ quit";
log.Info(cmd.StandardOutput.ReadLine());
Thread.Sleep(100);
}
+
+ _steamCmdDone = true;
}
- public static void RunServer(TorchConfig options, TorchCli cli)
+ public static void RunServer(TorchConfig config)
{
/*
- if (!parser.ParseArguments(args, options))
+ if (!parser.ParseArguments(args, config))
{
_log.Error($"Parsing arguments failed: {string.Join(" ", args)}");
return;
}
- if (!string.IsNullOrEmpty(options.Config) && File.Exists(options.Config))
+ if (!string.IsNullOrEmpty(config.Config) && File.Exists(config.Config))
{
- options = ServerConfig.LoadFrom(options.Config);
- parser.ParseArguments(args, options);
+ config = ServerConfig.LoadFrom(config.Config);
+ parser.ParseArguments(args, config);
}*/
//RestartOnCrash autostart autosave=15
//gamepath ="C:\Program Files\Space Engineers DS" instance="Hydro Survival" instancepath="C:\ProgramData\SpaceEngineersDedicated\Hydro Survival"
/*
- if (options.InstallService)
+ if (config.InstallService)
{
- var serviceName = $"\"Torch - {options.InstanceName}\"";
+ var serviceName = $"\"Torch - {config.InstanceName}\"";
// Working on installing the service properly instead of with sc.exe
_log.Info($"Installing service '{serviceName}");
var exePath = $"\"{Assembly.GetExecutingAssembly().Location}\"";
var createInfo = new ServiceCreateInfo
{
- Name = options.InstanceName,
+ Name = config.InstanceName,
BinaryPath = exePath,
};
_log.Info("Service Installed");
@@ -238,7 +228,7 @@ quit";
return;
}
- if (options.UninstallService)
+ if (config.UninstallService)
{
_log.Info("Uninstalling Torch service");
var startInfo = new ProcessStartInfo
@@ -254,18 +244,18 @@ quit";
return;
}*/
- _server = new TorchServer(options);
+ _server = new TorchServer(config);
_server.Init();
- if (cli.NoGui || cli.Autostart)
+ if (config.NoGui || config.Autostart)
{
new Thread(() => _server.Start()).Start();
}
- if (!cli.NoGui)
+ if (!config.NoGui)
{
var ui = new TorchUI((TorchServer)_server);
- ui.LoadConfig(options);
+ ui.LoadConfig(config);
ui.ShowDialog();
}
}
@@ -295,17 +285,9 @@ quit";
Thread.Sleep(5000);
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());
+ _config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
+ Process.Start(exe, _config.ToString());
}
//1627 = Function failed during execution.
Environment.Exit(1627);
diff --git a/Torch.Server/Properties/AssemblyInfo.cs b/Torch.Server/Properties/AssemblyInfo.cs
index 9e1b243..0b06134 100644
--- a/Torch.Server/Properties/AssemblyInfo.cs
+++ b/Torch.Server/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("1.0.182.329")]
-[assembly: AssemblyFileVersion("1.0.182.329")]
\ No newline at end of file
+[assembly: AssemblyVersion("1.0.186.642")]
+[assembly: AssemblyFileVersion("1.0.186.642")]
\ No newline at end of file
diff --git a/Torch.Server/Torch.Server.csproj b/Torch.Server/Torch.Server.csproj
index 5fd97a7..df199ad 100644
--- a/Torch.Server/Torch.Server.csproj
+++ b/Torch.Server/Torch.Server.csproj
@@ -184,7 +184,6 @@
-
True
diff --git a/Torch.Server/TorchCli.cs b/Torch.Server/TorchCli.cs
deleted file mode 100644
index 4112993..0000000
--- a/Torch.Server/TorchCli.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-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; }
- }
-}
diff --git a/Torch.Server/TorchConfig.cs b/Torch.Server/TorchConfig.cs
index d8b0da7..16d37a4 100644
--- a/Torch.Server/TorchConfig.cs
+++ b/Torch.Server/TorchConfig.cs
@@ -7,25 +7,54 @@ using NLog;
namespace Torch.Server
{
- public class TorchConfig : ITorchConfig
+ public class TorchConfig : CommandLine, ITorchConfig
{
private static Logger _log = LogManager.GetLogger("Config");
+ ///
+ [Arg("instancepath", "Server data folder where saves and mods are stored.")]
public string InstancePath { get; set; }
- public string InstanceName { get; set; }
-#warning World Path not implemented
- public string WorldPath { get; set; }
- public bool AutomaticUpdates { get; set; } = true;
- public bool RedownloadPlugins { get; set; }
+
+ ///
+ [XmlIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
+ public bool NoUpdate { get => false; set => GetTorchUpdates = GetPluginUpdates = !value; }
+
+ ///
+ [XmlIgnore, Arg("update", "Manually check for and install updates.")]
+ public bool Update { get; set; }
+
+ ///
+ [XmlIgnore, Arg("autostart", "Start the server immediately.")]
+ public bool Autostart { get; set; }
+
+ ///
+ [Arg("restartoncrash", "Automatically restart the server if it crashes.")]
public bool RestartOnCrash { get; set; }
- ///
- /// How long in seconds to wait before automatically resetting a frozen server.
- ///
+
+ ///
+ [XmlIgnore, Arg("nogui", "Do not show the Torch UI.")]
+ public bool NoGui { get; set; }
+
+ ///
+ [XmlIgnore, Arg("waitforpid", "Makes Torch wait for another process to exit.")]
+ public string WaitForPID { get; set; }
+
+ ///
+ [Arg("instancename", "The name of the Torch instance.")]
+ public string InstanceName { get; set; }
+
+ ///
+ public bool GetTorchUpdates { get; set; } = true;
+
+ ///
+ public bool GetPluginUpdates { get; set; } = true;
+
+ ///
public int TickTimeout { get; set; } = 60;
- ///
- /// A list of plugins to install or update. TODO
- ///
+
+ ///
public List Plugins { get; set; } = new List();
+
internal Point WindowSize { get; set; } = new Point(800, 600);
internal Point WindowPosition { get; set; } = new Point();
[NonSerialized]
diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs
index a41eed5..9429a22 100644
--- a/Torch.Server/TorchServer.cs
+++ b/Torch.Server/TorchServer.cs
@@ -16,6 +16,7 @@ using Sandbox.Game.Multiplayer;
using Sandbox.ModAPI;
using SteamSDK;
using Torch.API;
+using Torch.Managers;
using VRage.Dedicated;
using VRage.FileSystem;
using VRage.Game;
@@ -37,7 +38,9 @@ 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 string InstanceName => Config?.InstanceName;
+ ///
public string InstancePath => Config?.InstancePath;
private bool _isRunning;
@@ -53,6 +56,7 @@ namespace Torch.Server
MyFakes.ENABLE_INFINARIO = false;
}
+ ///
public override void Init()
{
base.Init();
@@ -81,7 +85,7 @@ namespace Torch.Server
RuntimeHelpers.RunClassConstructor(typeof(MyObjectBuilder_Base).TypeHandle);
}
- public void InvokeBeforeRun()
+ private void InvokeBeforeRun()
{
var contentPath = "Content";
@@ -91,16 +95,7 @@ namespace Torch.Server
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.ExePath = Path.Combine(GetManager().TorchDirectory, "DedicatedServer64");
MyFileSystem.Init(contentPath, InstancePath);
}
@@ -132,14 +127,13 @@ namespace Torch.Server
MySandboxGame.Config.Load();
}
- ///
- /// Start server on the current thread.
- ///
+ ///
public override void Start()
{
if (State != ServerState.Stopped)
return;
+ IsRunning = true;
GameThread = Thread.CurrentThread;
Config.Save();
State = ServerState.Starting;
@@ -196,9 +190,7 @@ namespace Torch.Server
Log.Debug("Server watchdog responded");
}
- ///
- /// Stop the server.
- ///
+ ///
public override void Stop()
{
if (State == ServerState.Stopped)
@@ -220,6 +212,12 @@ namespace Torch.Server
Log.Info("Server stopped.");
_stopHandle.Set();
State = ServerState.Stopped;
+ IsRunning = false;
+ }
+
+ public void Restart()
+ {
+
}
}
}
diff --git a/Torch.Server/Views/ChatControl.xaml.cs b/Torch.Server/Views/ChatControl.xaml.cs
index 5e9ab16..52d4562 100644
--- a/Torch.Server/Views/ChatControl.xaml.cs
+++ b/Torch.Server/Views/ChatControl.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -41,6 +42,17 @@ namespace Torch.Server
_server = (TorchBase)server;
_multiplayer = (MultiplayerManager)server.Multiplayer;
DataContext = _multiplayer;
+ _multiplayer.ChatHistory.CollectionChanged += ChatHistory_CollectionChanged;
+ }
+
+ private void ChatHistory_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (VisualTreeHelper.GetChildrenCount(ChatItems) > 0)
+ {
+ Border border = (Border)VisualTreeHelper.GetChild(ChatItems, 0);
+ ScrollViewer scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
+ scrollViewer.ScrollToBottom();
+ }
}
private void SendButton_Click(object sender, RoutedEventArgs e)
diff --git a/Torch.Server/Views/TorchUI.xaml b/Torch.Server/Views/TorchUI.xaml
index c26438a..39506df 100644
--- a/Torch.Server/Views/TorchUI.xaml
+++ b/Torch.Server/Views/TorchUI.xaml
@@ -5,14 +5,18 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server"
xmlns:views="clr-namespace:Torch.Server.Views"
+ xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
mc:Ignorable="d"
Title="Torch">
+
+
+
+ HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True" IsEnabled="{Binding IsRunning, Converter={StaticResource inverseBool}}"/>
+ Click="BtnStop_Click" IsEnabled="{Binding IsRunning}" />
-
+
-
+
diff --git a/Torch.Server/Views/TorchUI.xaml.cs b/Torch.Server/Views/TorchUI.xaml.cs
index 4e82884..23d1dfe 100644
--- a/Torch.Server/Views/TorchUI.xaml.cs
+++ b/Torch.Server/Views/TorchUI.xaml.cs
@@ -65,18 +65,12 @@ namespace Torch.Server
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_config.Save();
- ((Button) sender).IsEnabled = false;
- BtnStop.IsEnabled = true;
ConfigControl.SaveConfig();
new Thread(_server.Start).Start();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
- _config.Save();
- ((Button) sender).IsEnabled = false;
- //HACK: Uncomment when restarting is possible.
- //BtnStart.IsEnabled = true;
_server.Stop();
}
diff --git a/Torch/ChatMessage.cs b/Torch/ChatMessage.cs
index 390a92d..f2307ab 100644
--- a/Torch/ChatMessage.cs
+++ b/Torch/ChatMessage.cs
@@ -10,7 +10,7 @@ using VRage.Network;
namespace Torch
{
- public struct ChatMessage : IChatMessage
+ public class ChatMessage : IChatMessage
{
public DateTime Timestamp { get; }
public ulong SteamId { get; }
diff --git a/Torch/Collections/ObservableDictionary.cs b/Torch/Collections/ObservableDictionary.cs
index 958143d..2d3f955 100644
--- a/Torch/Collections/ObservableDictionary.cs
+++ b/Torch/Collections/ObservableDictionary.cs
@@ -9,6 +9,7 @@ using System.Windows.Threading;
namespace Torch.Collections
{
+ [Serializable]
public class ObservableDictionary : Dictionary, INotifyCollectionChanged, INotifyPropertyChanged
{
///
diff --git a/Torch/Managers/FilesystemManager.cs b/Torch/Managers/FilesystemManager.cs
new file mode 100644
index 0000000..b0fdcb1
--- /dev/null
+++ b/Torch/Managers/FilesystemManager.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Torch.API;
+
+namespace Torch.Managers
+{
+ public class FilesystemManager : Manager
+ {
+ ///
+ /// Temporary directory for Torch that is cleared every time the program is started.
+ ///
+ public string TempDirectory { get; }
+
+ ///
+ /// Directory that contains the current Torch assemblies.
+ ///
+ public string TorchDirectory { get; }
+
+ public FilesystemManager(ITorchBase torchInstance) : base(torchInstance)
+ {
+ var temp = Path.Combine(Path.GetTempPath(), "Torch");
+ TempDirectory = Directory.CreateDirectory(temp).FullName;
+ var torch = new FileInfo(typeof(FilesystemManager).Assembly.Location).Directory.FullName;
+ TorchDirectory = torch;
+
+ ClearTemp();
+ }
+
+ private void ClearTemp()
+ {
+ foreach (var file in Directory.GetFiles(TempDirectory, "*", SearchOption.AllDirectories))
+ File.Delete(file);
+ }
+
+ ///
+ /// Move the given file (if it exists) to a temporary directory that will be cleared the next time the application starts.
+ ///
+ public void SoftDelete(string file)
+ {
+ if (!File.Exists(file))
+ return;
+ var rand = Path.GetRandomFileName();
+ var dest = Path.Combine(TempDirectory, rand);
+ File.Move(file, dest);
+ }
+ }
+}
diff --git a/Torch/Managers/MultiplayerManager.cs b/Torch/Managers/MultiplayerManager.cs
index 08911c0..998836b 100644
--- a/Torch/Managers/MultiplayerManager.cs
+++ b/Torch/Managers/MultiplayerManager.cs
@@ -33,20 +33,21 @@ using VRage.Utils;
namespace Torch.Managers
{
- ///
- /// Provides a proxy to the game's multiplayer-related functions.
- ///
+ ///
public class MultiplayerManager : Manager, IMultiplayerManager
{
+ ///
public event Action PlayerJoined;
+ ///
public event Action PlayerLeft;
+ ///
public event MessageReceivedDel MessageReceived;
public MTObservableCollection ChatHistory { get; } = new MTObservableCollection();
public ObservableDictionary Players { get; } = new ObservableDictionary();
public IMyPlayer LocalPlayer => MySession.Static.LocalHumanPlayer;
- private static readonly Logger _log = LogManager.GetLogger(nameof(MultiplayerManager));
- private static readonly Logger _chatLog = LogManager.GetLogger("Chat");
+ private static readonly Logger Log = LogManager.GetLogger(nameof(MultiplayerManager));
+ private static readonly Logger ChatLog = LogManager.GetLogger("Chat");
private Dictionary _onlinePlayers;
internal MultiplayerManager(ITorchBase torch) : base(torch)
@@ -65,12 +66,14 @@ namespace Torch.Managers
{
var message = ChatMessage.FromChatMsg(msg);
ChatHistory.Add(message);
- _chatLog.Info($"{message.Name}: {message.Message}");
+ ChatLog.Info($"{message.Name}: {message.Message}");
MessageReceived?.Invoke(message, ref sendToOthers);
}
+ ///
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
+ ///
public void BanPlayer(ulong steamId, bool banned = true)
{
Torch.Invoke(() =>
@@ -81,12 +84,14 @@ namespace Torch.Managers
});
}
+ ///
public IMyPlayer GetPlayerByName(string name)
{
ValidateOnlinePlayersList();
return _onlinePlayers.FirstOrDefault(x => x.Value.DisplayName == name).Value;
}
+ ///
public IMyPlayer GetPlayerBySteamId(ulong steamId)
{
ValidateOnlinePlayersList();
@@ -94,14 +99,13 @@ namespace Torch.Managers
return p;
}
+ ///
public string GetSteamUsername(ulong steamId)
{
return MyMultiplayer.Static.GetMemberName(steamId);
}
- ///
- /// Send a message in chat.
- ///
+ ///
public void SendMessage(string message, string author = "Server", long playerId = 0, string font = MyFontEnum.Red)
{
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", message));
@@ -126,7 +130,7 @@ namespace Torch.Managers
private void OnSessionLoaded()
{
- _log.Info("Initializing Steam auth");
+ Log.Info("Initializing Steam auth");
MyMultiplayer.Static.ClientKicked += OnClientKicked;
MyMultiplayer.Static.ClientLeft += OnClientLeft;
@@ -138,7 +142,7 @@ namespace Torch.Managers
SteamServerAPI.Instance.GameServer.UserGroupStatus += UserGroupStatus;
_members = (List)typeof(MyDedicatedServerBase).GetField("m_members", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
_waitingForGroup = (HashSet)typeof(MyDedicatedServerBase).GetField("m_waitingForGroup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
- _log.Info("Steam auth initialized");
+ Log.Info("Steam auth initialized");
}
private void OnClientKicked(ulong steamId)
@@ -151,7 +155,7 @@ namespace Torch.Managers
Players.TryGetValue(steamId, out PlayerViewModel vm);
if (vm == null)
vm = new PlayerViewModel(steamId);
- _log.Info($"{vm.Name} ({vm.SteamId}) {(ConnectionState)stateChange}.");
+ Log.Info($"{vm.Name} ({vm.SteamId}) {(ConnectionState)stateChange}.");
PlayerLeft?.Invoke(vm);
Players.Remove(steamId);
}
@@ -178,7 +182,7 @@ namespace Torch.Managers
if (handle.Method.Name == "GameServer_ValidateAuthTicketResponse")
{
SteamServerAPI.Instance.GameServer.ValidateAuthTicketResponse -= handle as ValidateAuthTicketResponse;
- _log.Debug("Removed GameServer_ValidateAuthTicketResponse");
+ Log.Debug("Removed GameServer_ValidateAuthTicketResponse");
}
}
}
@@ -191,7 +195,7 @@ namespace Torch.Managers
if (handle.Method.Name == "GameServer_UserGroupStatus")
{
SteamServerAPI.Instance.GameServer.UserGroupStatus -= handle as UserGroupStatus;
- _log.Debug("Removed GameServer_UserGroupStatus");
+ Log.Debug("Removed GameServer_UserGroupStatus");
}
}
}
@@ -200,16 +204,16 @@ namespace Torch.Managers
//Largely copied from SE
private void ValidateAuthTicketResponse(ulong steamID, AuthSessionResponseEnum response, ulong ownerSteamID)
{
- _log.Info($"Server ValidateAuthTicketResponse ({response}), owner: {ownerSteamID}");
+ Log.Info($"Server ValidateAuthTicketResponse ({response}), owner: {ownerSteamID}");
if (steamID != ownerSteamID)
{
- _log.Info($"User {steamID} is using a game owned by {ownerSteamID}. Tracking...");
+ Log.Info($"User {steamID} is using a game owned by {ownerSteamID}. Tracking...");
_gameOwnerIds[steamID] = ownerSteamID;
if (MySandboxGame.ConfigDedicated.Banned.Contains(ownerSteamID))
{
- _log.Info($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
+ Log.Info($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
UserRejected(steamID, JoinResult.BannedByAdmins);
BanPlayer(steamID);
}
@@ -306,7 +310,7 @@ namespace Torch.Managers
{
typeof(MyDedicatedServerBase).GetMethod("UserAccepted", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(MyMultiplayer.Static, new object[] {steamId});
var vm = new PlayerViewModel(steamId) {State = ConnectionState.Connected};
- _log.Info($"Player {vm.Name} joined ({vm.SteamId})");
+ Log.Info($"Player {vm.Name} joined ({vm.SteamId})");
Players.Add(steamId, vm);
PlayerJoined?.Invoke(vm);
}
diff --git a/Torch/Managers/NetworkManager/NetworkManager.cs b/Torch/Managers/NetworkManager/NetworkManager.cs
index bed7f31..1e86254 100644
--- a/Torch/Managers/NetworkManager/NetworkManager.cs
+++ b/Torch/Managers/NetworkManager/NetworkManager.cs
@@ -64,6 +64,11 @@ namespace Torch.Managers
/// Loads the network intercept system
///
public override void Init()
+ {
+ Torch.SessionLoaded += OnSessionLoaded;
+ }
+
+ private void OnSessionLoaded()
{
if (_init)
return;
diff --git a/Torch/Managers/PluginManager.cs b/Torch/Managers/PluginManager.cs
index f796192..6171645 100644
--- a/Torch/Managers/PluginManager.cs
+++ b/Torch/Managers/PluginManager.cs
@@ -5,42 +5,30 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
using System.Threading.Tasks;
using NLog;
-using Sandbox;
-using Sandbox.ModAPI;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Plugins;
using Torch.Commands;
-using Torch.Managers;
-using Torch.Updater;
-using VRage.Plugins;
using VRage.Collections;
-using VRage.Library.Collections;
namespace Torch.Managers
{
- public class PluginManager : IPluginManager
+ ///
+ public class PluginManager : Manager, IPluginManager
{
- private readonly ITorchBase _torch;
private static Logger _log = LogManager.GetLogger(nameof(PluginManager));
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
+ private UpdateManager _updateManager;
- public List Plugins { get; } = new List();
-
- public float LastUpdateMs => _lastUpdateMs;
- private volatile float _lastUpdateMs;
+ ///
+ public ObservableCollection Plugins { get; } = new ObservableCollection();
public event Action> PluginsLoaded;
- public PluginManager(ITorchBase torch)
+ public PluginManager(ITorchBase torchInstance) : base(torchInstance)
{
- _torch = torch;
-
if (!Directory.Exists(PluginDir))
Directory.CreateDirectory(PluginDir);
}
@@ -50,11 +38,8 @@ namespace Torch.Managers
///
public void UpdatePlugins()
{
- var s = Stopwatch.StartNew();
foreach (var plugin in Plugins)
plugin.Update();
- s.Stop();
- _lastUpdateMs = (float)s.Elapsed.TotalMilliseconds;
}
///
@@ -70,40 +55,42 @@ namespace Torch.Managers
private void DownloadPlugins()
{
- _log.Info("Downloading plugins");
- var updater = new PluginUpdater(this);
-
var folders = Directory.GetDirectories(PluginDir);
var taskList = new List();
- if (_torch.Config.RedownloadPlugins)
- _log.Warn("Force downloading all plugins because the RedownloadPlugins flag is set in the config");
+
+ //Copy list because we don't want to modify the config.
+ var toDownload = Torch.Config.Plugins.ToList();
foreach (var folder in folders)
{
var manifestPath = Path.Combine(folder, "manifest.xml");
if (!File.Exists(manifestPath))
{
- _log.Info($"No manifest in {folder}, skipping");
+ _log.Debug($"No manifest in {folder}, skipping");
continue;
}
- _log.Info($"Checking for updates for {folder}");
var manifest = PluginManifest.Load(manifestPath);
- taskList.Add(updater.CheckAndUpdate(manifest, _torch.Config.RedownloadPlugins));
+ toDownload.Remove(manifest.Repository);
+ taskList.Add(_updateManager.CheckAndUpdatePlugin(manifest));
+ }
+
+ foreach (var repository in toDownload)
+ {
+ var manifest = new PluginManifest {Repository = repository, Version = "0.0"};
+ taskList.Add(_updateManager.CheckAndUpdatePlugin(manifest));
}
Task.WaitAll(taskList.ToArray());
- _torch.Config.RedownloadPlugins = false;
}
- ///
- /// Loads and creates instances of all plugins in the folder.
- ///
- public void Init()
+ ///
+ public override void Init()
{
- var commands = ((TorchBase)_torch).Commands;
+ _updateManager = Torch.GetManager();
+ var commands = Torch.GetManager();
- if (_torch.Config.AutomaticUpdates)
+ if (Torch.Config.GetPluginUpdates)
DownloadPlugins();
else
_log.Warn("Automatic plugin updates are disabled.");
@@ -129,7 +116,7 @@ namespace Torch.Managers
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
- plugin.StoragePath = _torch.Config.InstancePath;
+ plugin.StoragePath = Torch.Config.InstancePath;
Plugins.Add(plugin);
commands.RegisterPluginCommands(plugin);
@@ -143,8 +130,8 @@ namespace Torch.Managers
}
}
- Plugins.ForEach(p => p.Init(_torch));
- PluginsLoaded?.Invoke(Plugins);
+ Plugins.ForEach(p => p.Init(Torch));
+ PluginsLoaded?.Invoke(Plugins.ToList());
}
public IEnumerator GetEnumerator()
diff --git a/Torch/Managers/UpdateManager.cs b/Torch/Managers/UpdateManager.cs
index a5018a7..0e120cc 100644
--- a/Torch/Managers/UpdateManager.cs
+++ b/Torch/Managers/UpdateManager.cs
@@ -1,28 +1,128 @@
using System;
using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.IO.Packaging;
using System.Linq;
+using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using NLog;
+using Octokit;
using SteamSDK;
+using Torch.API;
namespace Torch.Managers
{
///
/// Handles updating of the DS and Torch plugins.
///
- public class UpdateManager : IDisposable
+ public class UpdateManager : Manager, IDisposable
{
private Timer _updatePollTimer;
+ private GitHubClient _gitClient = new GitHubClient(new ProductHeaderValue("Torch"));
+ private string _torchDir = new FileInfo(typeof(UpdateManager).Assembly.Location).DirectoryName;
+ private Logger _log = LogManager.GetLogger(nameof(UpdateManager));
+ private FilesystemManager _fsManager;
- public UpdateManager()
+ public UpdateManager(ITorchBase torchInstance) : base(torchInstance)
{
- _updatePollTimer = new Timer(CheckForUpdates, this, TimeSpan.Zero, TimeSpan.FromMinutes(5));
+ //_updatePollTimer = new Timer(TimerElapsed, this, TimeSpan.Zero, TimeSpan.FromMinutes(5));
}
- private void CheckForUpdates(object state)
+ ///
+ public override void Init()
{
-
+ _fsManager = Torch.GetManager();
+ CheckAndUpdateTorch();
+ }
+
+ private void TimerElapsed(object state)
+ {
+ CheckAndUpdateTorch();
+ }
+
+ private async Task> GetLatestRelease(string owner, string name)
+ {
+ try
+ {
+ var latest = await _gitClient.Repository.Release.GetLatest(owner, name).ConfigureAwait(false);
+ if (latest == null)
+ return new Tuple(new Version(), null);
+
+ var zip = latest.Assets.FirstOrDefault(x => x.Name.Contains(".zip"));
+ return new Tuple(new Version(latest.TagName ?? "0"), zip?.BrowserDownloadUrl);
+ }
+ catch (Exception)
+ {
+ _log.Error($"An error occurred getting release information for '{owner}/{name}'");
+ return new Tuple(new Version(), null);
+ }
+ }
+
+ public async Task CheckAndUpdatePlugin(PluginManifest manifest)
+ {
+ if (!Torch.Config.GetPluginUpdates)
+ return;
+
+ var name = manifest.Repository.Split('/');
+ if (name.Length != 2)
+ {
+ _log.Error($"'{manifest.Repository}' is not a valid GitHub repository.");
+ return;
+ }
+
+ var currentVersion = new Version(manifest.Version);
+ var releaseInfo = await GetLatestRelease(name[0], name[1]).ConfigureAwait(false);
+ if (releaseInfo.Item1 > currentVersion)
+ {
+ _log.Warn($"Updating {manifest.Repository} from version {currentVersion} to version {releaseInfo.Item1}");
+ var updateName = Path.Combine(_fsManager.TempDirectory, $"{name[0]}_{name[1]}.zip");
+ var updatePath = Path.Combine(_torchDir, "Plugins");
+ await new WebClient().DownloadFileTaskAsync(new Uri(releaseInfo.Item2), updateName).ConfigureAwait(false);
+ UpdateFromZip(updateName, updatePath);
+ File.Delete(updateName);
+ }
+ else
+ {
+ _log.Info($"{manifest.Repository} is up to date. ({currentVersion})");
+ }
+ }
+
+ private async void CheckAndUpdateTorch()
+ {
+ if (!Torch.Config.GetTorchUpdates)
+ return;
+
+ var releaseInfo = await GetLatestRelease("TorchAPI", "Torch").ConfigureAwait(false);
+ if (releaseInfo.Item1 > Torch.TorchVersion)
+ {
+ _log.Warn($"Updating Torch from version {Torch.TorchVersion} to version {releaseInfo.Item1}");
+ var updateName = Path.Combine(_fsManager.TempDirectory, "torchupdate.zip");
+ new WebClient().DownloadFile(new Uri(releaseInfo.Item2), updateName);
+ UpdateFromZip(updateName, _torchDir);
+ File.Delete(updateName);
+ }
+ else
+ {
+ _log.Info("Torch is up to date.");
+ }
+ }
+
+ private void UpdateFromZip(string zipFile, string extractPath)
+ {
+ using (var zip = ZipFile.OpenRead(zipFile))
+ {
+ foreach (var file in zip.Entries)
+ {
+ _log.Debug($"Unzipping {file.FullName}");
+ var targetFile = Path.Combine(extractPath, file.FullName);
+ _fsManager.SoftDelete(targetFile);
+ }
+
+ zip.ExtractToDirectory(extractPath);
+ }
}
///
diff --git a/Torch/Persistent.cs b/Torch/Persistent.cs
index 2dd3d3e..ccc5a1b 100644
--- a/Torch/Persistent.cs
+++ b/Torch/Persistent.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
@@ -10,12 +12,14 @@ namespace Torch
{
///
/// Simple class that manages saving to disk using JSON serialization.
+ /// Can automatically save on changes by implementing in the data class.
///
/// Data class type
public sealed class Persistent : IDisposable where T : new()
{
public string Path { get; set; }
public T Data { get; private set; }
+ private Timer _saveTimer;
~Persistent()
{
@@ -24,8 +28,21 @@ namespace Torch
public Persistent(string path, T data = default(T))
{
+ _saveTimer = new Timer(Callback);
Path = path;
Data = data;
+ if (Data is INotifyPropertyChanged npc)
+ npc.PropertyChanged += OnPropertyChanged;
+ }
+
+ private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ _saveTimer.Change(5000, -1);
+ }
+
+ private void Callback(object state)
+ {
+ Save();
}
public void Save(string path = null)
@@ -65,6 +82,9 @@ namespace Torch
{
try
{
+ if (Data is INotifyPropertyChanged npc)
+ npc.PropertyChanged -= OnPropertyChanged;
+ _saveTimer.Dispose();
Save();
}
catch
diff --git a/Torch/Updater/PluginManifest.cs b/Torch/PluginManifest.cs
similarity index 100%
rename from Torch/Updater/PluginManifest.cs
rename to Torch/PluginManifest.cs
diff --git a/Torch/Torch.csproj b/Torch/Torch.csproj
index 8803230..ea93410 100644
--- a/Torch/Torch.csproj
+++ b/Torch/Torch.csproj
@@ -81,6 +81,8 @@
+
+
@@ -160,19 +162,19 @@
+
-
+
-
diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs
index 7f3f471..ddb996a 100644
--- a/Torch/TorchBase.cs
+++ b/Torch/TorchBase.cs
@@ -41,22 +41,38 @@ namespace Torch
/// Use only if necessary, prefer dependency injection.
///
public static ITorchBase Instance { get; private set; }
+ ///
public ITorchConfig Config { get; protected set; }
- protected static Logger Log { get; } = LogManager.GetLogger("Torch");
+ ///
public Version TorchVersion { get; protected set; }
+ ///
public Version GameVersion { get; private set; }
+ ///
public string[] RunArgs { get; set; }
+ ///
public IPluginManager Plugins { get; protected set; }
+ ///
public IMultiplayerManager Multiplayer { get; protected set; }
+ ///
public EntityManager Entities { get; protected set; }
+ ///
public INetworkManager Network { get; protected set; }
+ ///
public CommandManager Commands { get; protected set; }
+ ///
public event Action SessionLoading;
+ ///
public event Action SessionLoaded;
+ ///
public event Action SessionUnloading;
+ ///
public event Action SessionUnloaded;
- private readonly List _managers;
+ ///
+ /// Common log for the Torch instance.
+ ///
+ protected static Logger Log { get; } = LogManager.GetLogger("Torch");
+ private readonly List _managers;
private bool _init;
///
@@ -79,22 +95,25 @@ namespace Torch
Network = new NetworkManager(this);
Commands = new CommandManager(this);
- _managers = new List {Network, Commands, Plugins, Multiplayer, Entities, new ChatManager(this)};
+ _managers = new List { new FilesystemManager(this), new UpdateManager(this), Network, Commands, Plugins, Multiplayer, Entities, new ChatManager(this), };
TorchAPI.Instance = this;
}
+ ///
public ListReader GetManagers()
{
return new ListReader(_managers);
}
+ ///
public T GetManager() where T : class, IManager
{
return _managers.FirstOrDefault(m => m is T) as T;
}
+ ///
public bool AddManager(T manager) where T : class, IManager
{
if (_managers.Any(x => x is T))
@@ -104,11 +123,6 @@ namespace Torch
return true;
}
- public bool IsOnGameThread()
- {
- return Thread.CurrentThread.ManagedThreadId == MySandboxGame.Static.UpdateThread.ManagedThreadId;
- }
-
public async Task SaveGameAsync()
{
Log.Info("Saving game");
@@ -201,6 +215,7 @@ namespace Torch
#endregion
+ ///
public virtual void Init()
{
Debug.Assert(!_init, "Torch instance is already initialized.");
@@ -236,15 +251,14 @@ namespace Torch
MySession.OnUnloading += OnSessionUnloading;
MySession.OnUnloaded += OnSessionUnloaded;
RegisterVRagePlugin();
-
+ foreach (var manager in _managers)
+ manager.Init();
_init = true;
}
private void OnSessionLoading()
{
Log.Debug("Session loading");
- foreach (var manager in _managers)
- manager.Init();
SessionLoading?.Invoke();
}
@@ -279,11 +293,13 @@ namespace Torch
pluginList.Add(this);
}
+ ///
public virtual void Start()
{
}
+ ///
public virtual void Stop() { }
///
diff --git a/Torch/Updater/PluginUpdater.cs b/Torch/Updater/PluginUpdater.cs
deleted file mode 100644
index 355bfd3..0000000
--- a/Torch/Updater/PluginUpdater.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using NLog;
-using Octokit;
-using Torch.API;
-using Torch.Managers;
-using VRage.Compression;
-
-namespace Torch.Updater
-{
- public class PluginUpdater
- {
- private readonly PluginManager _pluginManager;
- private static readonly Logger Log = LogManager.GetLogger("PluginUpdater");
-
- public PluginUpdater(PluginManager pm)
- {
- _pluginManager = pm;
- }
-
- public async Task CheckAndUpdate(PluginManifest manifest, bool force = false)
- {
- Log.Info($"Checking for update at {manifest.Repository}");
- var split = manifest.Repository.Split('/');
-
- if (split.Length != 2)
- {
- Log.Warn($"Manifest has an invalid repository name: {manifest.Repository}");
- return;
- }
-
- var gitClient = new GitHubClient(new ProductHeaderValue("Torch"));
- var releases = await gitClient.Repository.Release.GetAll(split[0], split[1]).ConfigureAwait(false);
-
- if (releases.Count == 0)
- {
- Log.Debug("No releases in repo");
- return;
- }
-
- Version currentVersion;
- Version latestVersion;
-
- try
- {
- currentVersion = new Version(manifest.Version);
- latestVersion = new Version(releases[0].TagName);
- }
- catch
- {
- Log.Warn("Invalid version number on manifest or GitHub release");
- return;
- }
-
- if (force || latestVersion > currentVersion)
- {
- var webClient = new WebClient();
- var assets = await gitClient.Repository.Release.GetAllAssets(split[0], split[1], releases[0].Id).ConfigureAwait(false);
- foreach (var asset in assets)
- {
- if (asset.Name.EndsWith(".zip"))
- {
- Log.Debug(asset.BrowserDownloadUrl);
- var localPath = Path.Combine(Path.GetTempPath(), asset.Name);
- await webClient.DownloadFileTaskAsync(new Uri(asset.BrowserDownloadUrl), localPath).ConfigureAwait(false);
- UnzipPlugin(localPath);
- Log.Info($"Downloaded update for {manifest.Repository}");
- return;
- }
- }
- }
- else
- {
- Log.Info($"{manifest.Repository} is up to date.");
- }
- }
-
- public void UnzipPlugin(string zipName)
- {
- if (!File.Exists(zipName))
- return;
-
- MyZipArchive.ExtractToDirectory(zipName, _pluginManager.PluginDir);
- }
- }
-}