Add GitHub plugin updater, refactor game update hook to TorchBase instead of PluginManager, fix world config not applying

This commit is contained in:
John Gross
2017-04-29 13:28:24 -07:00
parent 03a22851af
commit 135d1f4be8
28 changed files with 419 additions and 189 deletions

View File

@@ -13,6 +13,7 @@ namespace Torch.API
event Action SessionLoaded;
event Action SessionUnloading;
event Action SessionUnloaded;
ITorchConfig Config { get; }
IMultiplayer Multiplayer { get; }
IPluginManager Plugins { get; }
Version TorchVersion { get; }
@@ -28,9 +29,7 @@ namespace Torch.API
public interface ITorchServer : ITorchBase
{
bool IsRunning { get; }
string InstancePath { get; }
void Start(IMyConfigDedicated config);
}
public interface ITorchClient : ITorchBase

15
Torch.API/ITorchConfig.cs Normal file
View File

@@ -0,0 +1,15 @@
namespace Torch
{
public interface ITorchConfig
{
//bool AutoRestart { get; set; }
//int Autosave { get; set; }
string InstanceName { get; set; }
string InstancePath { get; set; }
//bool LogChat { get; set; }
bool RedownloadPlugins { get; set; }
bool EnableAutomaticUpdates { get; set; }
bool Save(string path = null);
}
}

16
Torch.API/ServerState.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API
{
public enum ServerState
{
Stopped,
Starting,
Running,
Error
}
}

View File

@@ -141,6 +141,7 @@
<Compile Include="IChatMessage.cs" />
<Compile Include="IMultiplayer.cs" />
<Compile Include="IPluginManager.cs" />
<Compile Include="ITorchConfig.cs" />
<Compile Include="Plugins\ITorchPlugin.cs" />
<Compile Include="IServerControls.cs" />
<Compile Include="ITorchBase.cs" />
@@ -149,6 +150,7 @@
<Compile Include="ModAPI\TorchAPI.cs" />
<Compile Include="Plugins\PluginAttribute.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerState.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -1,4 +1,4 @@
using System.Reflection;
[assembly: AssemblyVersion("1.0.105.498")]
[assembly: AssemblyFileVersion("1.0.105.498")]
[assembly: AssemblyVersion("1.0.119.399")]
[assembly: AssemblyFileVersion("1.0.119.399")]

View File

@@ -43,7 +43,7 @@ namespace Torch.Client
return;
var appDataPath = _startup.GetAppDataPath();
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath, false);
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
MyInitializer.InitCheckSum();
if (!_startup.Check64Bit())
return;

View File

@@ -40,7 +40,7 @@ namespace Torch.Server
return;
}
var configName = args.FirstOrDefault() ?? "TorchConfig.xml";
var configName = /*args.FirstOrDefault() ??*/ "TorchConfig.xml";
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
TorchConfig options;
if (File.Exists(configName))
@@ -55,6 +55,20 @@ namespace Torch.Server
options.Save(configPath);
}
bool gui = true;
foreach (var arg in args)
{
switch (arg)
{
case "-noupdate":
options.EnableAutomaticUpdates = false;
break;
case "-nogui":
gui = false;
break;
}
}
/*
if (!parser.ParseArguments(args, options))
{
@@ -118,9 +132,16 @@ namespace Torch.Server
_server = new TorchServer(options);
_server.Init();
var ui = new TorchUI((TorchServer)_server);
ui.LoadConfig(options);
ui.ShowDialog();
if (gui)
{
var ui = new TorchUI((TorchServer)_server);
ui.LoadConfig(options);
ui.ShowDialog();
}
else
{
_server.Start();
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Reflection;
[assembly: AssemblyVersion("1.0.105.498")]
[assembly: AssemblyFileVersion("1.0.105.498")]
[assembly: AssemblyVersion("1.0.119.399")]
[assembly: AssemblyFileVersion("1.0.119.399")]

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Torch.Collections;
namespace Torch.Server
{
public class ServerStatistics
{
public RollingAverage SimSpeed { get; } = new RollingAverage(30);
}
}

View File

@@ -167,7 +167,7 @@
<DependentUpon>AssemblyInfo.tt</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo1.cs" />
<Compile Include="TorchConfig.cs" />
<Compile Include="ServerStatistics.cs" />
<Compile Include="TorchService.cs">
<SubType>Component</SubType>
</Compile>

View File

@@ -1,40 +1,27 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using NLog;
using Torch;
using Sandbox;
using Sandbox.Engine.Analytics;
using Sandbox.Engine.Multiplayer;
using Sandbox;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Sandbox.Game.World;
using SpaceEngineers.Game;
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Xml.Serialization;
using SteamSDK;
using Torch.API;
using VRage.Dedicated;
using VRage.FileSystem;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.Game.ObjectBuilder;
using VRage.Game.SessionComponents;
using VRage.Profiler;
using VRage.Plugins;
namespace Torch.Server
{
public class TorchServer : TorchBase, ITorchServer
{
public Thread GameThread { get; private set; }
public bool IsRunning { get; private set; }
public TorchConfig Config { get; }
public ServerState State { get; private set; }
public string InstanceName => Config?.InstanceName;
public string InstancePath => Config?.InstancePath;
@@ -49,7 +36,7 @@ namespace Torch.Server
{
base.Init();
Log.Info($"Init server instance '{Config.InstanceName}' at path '{Config.InstancePath}'");
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
MyFakes.ENABLE_INFINARIO = false;
MyPerGameSettings.SendLogToKeen = false;
@@ -60,21 +47,19 @@ namespace Torch.Server
MySessionComponentExtDebug.ForceDisable = true;
MyPerServerSettings.AppId = 244850;
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
//MyGlobalTypeMetadata.Static.Init();
//TODO: Allows players to filter servers for Torch in the server browser. Need to init Steam before this
//SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
}
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
MyPlugins.Load();
public void SetConfig(IMyConfigDedicated config)
{
MySandboxGame.ConfigDedicated = config;
}
MyGlobalTypeMetadata.Static.Init();
MyInitializer.InvokeBeforeRun(
MyPerServerSettings.AppId,
MyPerServerSettings.GameDSName,
InstancePath, DedicatedServer.AddDateToLog);
public void Start(IMyConfigDedicated config)
{
SetConfig(config);
Start();
}
/// <summary>
@@ -82,26 +67,35 @@ namespace Torch.Server
/// </summary>
public override void Start()
{
if (IsRunning)
if (State > 0)
throw new InvalidOperationException("Server is already running.");
Config.Save();
IsRunning = true;
State = ServerState.Starting;
Log.Info("Starting server.");
var runInternal = typeof(DedicatedServer).GetMethod("RunInternal", BindingFlags.Static | BindingFlags.NonPublic);
MySandboxGame.IsDedicated = true;
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
Log.Trace("Invoking RunMain");
try { Reflection.InvokeStaticMethod(typeof(DedicatedServer), "RunMain", Config.InstanceName, Config.InstancePath, false, true); }
catch (Exception e)
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
do
{
Log.Error("Error running server.");
Log.Error(e);
throw;
}
Log.Trace("RunMain completed");
IsRunning = false;
runInternal.Invoke(null, null);
} while (MySandboxGame.IsReloading);
MyInitializer.InvokeAfterRun();
State = ServerState.Stopped;
}
/// <inheritdoc />
public override void Init(object gameInstance)
{
base.Init(gameInstance);
State = ServerState.Running;
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
}
/// <summary>
@@ -109,7 +103,7 @@ namespace Torch.Server
/// </summary>
public override void Stop()
{
if (!IsRunning)
if (State == ServerState.Stopped)
Log.Error("Server is already stopped");
if (Thread.CurrentThread.ManagedThreadId != GameThread?.ManagedThreadId && MySandboxGame.Static.IsRunning)
@@ -125,31 +119,14 @@ namespace Torch.Server
//Unload all the static junk.
//TODO: Finish unloading all server data so it's in a completely clean state.
VRage.FileSystem.MyFileSystem.Reset();
MyFileSystem.Reset();
VRage.Input.MyGuiGameControlsHelpers.Reset();
VRage.Input.MyInput.UnloadData();
//CleanupProfilers();
Log.Info("Server stopped.");
_stopHandle.Set();
IsRunning = false;
State = ServerState.Stopped;
}
/*
private string GetInstancePath(bool isService = false, string instanceName = "Torch")
{
if (isService)
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), MyPerServerSettings.GameDSName, instanceName);
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), MyPerServerSettings.GameDSName);
}*/
/*
private void CleanupProfilers()
{
typeof(MyRenderProfiler).GetField("m_threadProfiler", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, null);
typeof(MyRenderProfiler).GetField("m_gpuProfiler", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, null);
(typeof(MyRenderProfiler).GetField("m_threadProfilers", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as List<MyProfiler>).Clear();
}*/
}
}

View File

@@ -331,5 +331,10 @@ namespace Torch.Server.ViewModels
get { return _settings.ViewDistance; }
set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
}
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
{
return viewModel._settings;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -14,8 +15,12 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NLog;
using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Torch.Server.ViewModels;
using VRage.Dedicated;
using VRage.Game;
using Path = System.IO.Path;
@@ -38,10 +43,31 @@ namespace Torch.Server.Views
public void SaveConfig()
{
Config.Save(_configPath);
//TODO: make this work
try
{
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out ulong size);
checkpoint.SessionName = _viewModel.WorldName;
checkpoint.Settings = _viewModel.SessionSettings;
checkpoint.Mods.Clear();
foreach (var modId in _viewModel.Mods)
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
Debug.Assert(checkpoint != null);
Debug.Assert(_viewModel.LoadWorld != null);
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
}
catch (Exception e)
{
var log = LogManager.GetLogger("Torch");
log.Error("Failed to overwrite sandbox config, changes will not appear on server");
log.Error(e);
}
}
public void LoadDedicatedConfig(TorchConfig torchConfig)
{
MySandboxGame.Config = new MyConfig(MyPerServerSettings.GameNameSafe + ".cfg");
var path = Path.Combine(torchConfig.InstancePath, "SpaceEngineers-Dedicated.cfg");
if (!File.Exists(path))

View File

@@ -20,7 +20,7 @@
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Label Content="Instance Path: " Margin="3"/>
<TextBox x:Name="InstancePathBox" Margin="3" Height="20" TextChanged="InstancePathBox_OnTextChanged"/>
<TextBox x:Name="InstancePathBox" Margin="3" Height="20" TextChanged="InstancePathBox_OnTextChanged" IsEnabled="False"/>
</DockPanel>
<views:ConfigControl x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom"/>
</DockPanel>

View File

@@ -38,7 +38,7 @@ namespace Torch.Server
public TorchUI(TorchServer server)
{
_config = server.Config;
_config = (TorchConfig)server.Config;
_server = server;
InitializeComponent();
_startTime = DateTime.Now;
@@ -84,7 +84,7 @@ namespace Torch.Server
BtnStop.IsEnabled = true;
_uiUpdate.Start();
ConfigControl.SaveConfig();
new Thread(() => _server.Start(ConfigControl.Config)).Start();
new Thread(_server.Start).Start();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
@@ -100,7 +100,7 @@ namespace Torch.Server
protected override void OnClosing(CancelEventArgs e)
{
if (_server?.IsRunning ?? false)
if (_server?.State == ServerState.Running)
_server.Stop();
}

View File

@@ -0,0 +1,56 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Collections
{
public class RollingAverage
{
private readonly double[] _array;
private int _idx;
private bool _full;
public RollingAverage(int size)
{
_array = new double[size];
}
/// <summary>
/// Adds a new value and removes the oldest if necessary.
/// </summary>
/// <param name="value"></param>
public void Add(double value)
{
if (_idx >= _array.Length - 1)
{
_full = true;
_idx = 0;
}
_array[_idx] = value;
_idx++;
}
public double GetAverage()
{
return _array.Sum() / (_full ? _array.Length : (_idx + 1));
}
/// <summary>
/// Resets the rolling average.
/// </summary>
public void Clear()
{
_idx = 0;
_full = false;
}
public static implicit operator double(RollingAverage avg)
{
return avg.GetAverage();
}
}
}

View File

@@ -17,6 +17,7 @@ using Sandbox.ModAPI;
using SpaceEngineers.Game.Entities.Blocks;
using SpaceEngineers.Game.ModAPI;
using Torch.API;
using Torch.API.Plugins;
using VRage;
using VRage.Collections;
using VRage.Game;

View File

@@ -177,7 +177,7 @@ namespace Torch.Managers
{
_log.Info($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
UserRejected(steamID, JoinResult.BannedByAdmins);
BanPlayer(steamID, true);
BanPlayer(steamID);
}
}

View File

@@ -120,17 +120,15 @@ namespace Torch.Managers
CallSite site;
IMyNetObject sendAs;
object obj;
if (networkId.IsInvalid) // Static event
{
site = m_typeTable.StaticEventTable.Get(eventId);
sendAs = null;
obj = null;
}
else // Instance event
{
sendAs = ((MyReplicationLayer)MyMultiplayer.ReplicationLayer).GetObjectByNetworkId(networkId);
var sendAs = ((MyReplicationLayer)MyMultiplayer.ReplicationLayer).GetObjectByNetworkId(networkId);
if (sendAs == null)
{
return;
@@ -165,7 +163,7 @@ namespace Torch.Managers
//ApplicationLog.Error(ex.ToString());
_log.Error(ex);
}
};
}
//one of the handlers wants us to discard this packet
if (discard)
@@ -278,7 +276,7 @@ namespace Torch.Managers
for (var j = args.Length + 4; j < 10; j++)
arguments[j] = e;
arguments[10] = (IMyEventOwner)null;
arguments[10] = null;
//create an array of Types so we can create a generic method
var argTypes = new Type[8];
@@ -336,8 +334,7 @@ namespace Torch.Managers
private CallSite TryGetStaticCallSite(MethodInfo method)
{
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(m_typeTable.StaticEventTable);
CallSite result;
if(!methodLookup.TryGetValue(method, out result))
if (!methodLookup.TryGetValue(method, out CallSite result))
throw new MissingMemberException("Provided event target not found!");
return result;
}
@@ -346,8 +343,7 @@ namespace Torch.Managers
{
var typeInfo = m_typeTable.Get(arg.GetType());
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(typeInfo.EventTable);
CallSite result;
if (!methodLookup.TryGetValue(method, out result))
if (!methodLookup.TryGetValue(method, out CallSite result))
throw new MissingMemberException("Provided event target not found!");
return result;
}

View File

@@ -16,17 +16,18 @@ using Torch.API;
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, IPlugin
public class PluginManager : IPluginManager
{
private readonly ITorchBase _torch;
private static Logger _log = LogManager.GetLogger(nameof(PluginManager));
public const string PluginDir = "Plugins";
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
public List<ITorchPlugin> Plugins { get; } = new List<ITorchPlugin>();
public CommandManager Commands { get; private set; }
@@ -42,21 +43,6 @@ namespace Torch.Managers
if (!Directory.Exists(PluginDir))
Directory.CreateDirectory(PluginDir);
InitUpdater();
}
/// <summary>
/// Adds the plugin manager "plugin" to VRage's plugin system.
/// </summary>
private void InitUpdater()
{
var fieldName = "m_plugins";
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
if (pluginList == null)
throw new TypeLoadException($"{fieldName} field not found in {nameof(MyPlugins)}");
pluginList.Add(this);
}
/// <summary>
@@ -81,18 +67,48 @@ namespace Torch.Managers
Plugins.Clear();
}
private void DownloadPlugins()
{
_log.Info("Downloading plugins");
var updater = new PluginUpdater(this);
var folders = Directory.GetDirectories(PluginDir);
var taskList = new List<Task>();
if (_torch.Config.RedownloadPlugins)
_log.Warn("Force downloading all plugins because the RedownloadPlugins flag is set in the config");
foreach (var folder in folders)
{
var manifestPath = Path.Combine(folder, "manifest.xml");
if (!File.Exists(manifestPath))
{
_log.Info($"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));
}
Task.WaitAll(taskList.ToArray());
_torch.Config.RedownloadPlugins = false;
}
/// <summary>
/// Loads and creates instances of all plugins in the <see cref="PluginDir"/> folder.
/// </summary>
public void Init()
{
((TorchBase)_torch).Network.Init();
ChatManager.Instance.Init();
Commands = new CommandManager(_torch);
if (_torch.Config.EnableAutomaticUpdates)
DownloadPlugins();
else
_log.Warn("Automatic plugin updates are disabled.");
_log.Info("Loading plugins");
var pluginsPath = Path.Combine(Directory.GetCurrentDirectory(), PluginDir);
var dlls = Directory.GetFiles(pluginsPath, "*.dll", SearchOption.AllDirectories);
var dlls = Directory.GetFiles(PluginDir, "*.dll", SearchOption.AllDirectories);
foreach (var dllPath in dlls)
{
var asm = Assembly.UnsafeLoadFrom(dllPath);
@@ -135,20 +151,5 @@ namespace Torch.Managers
{
return GetEnumerator();
}
void IPlugin.Init(object obj)
{
Init();
}
void IPlugin.Update()
{
UpdatePlugins();
}
public void Dispose()
{
DisposePlugins();
}
}
}

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch
{
public class PluginManifest
{
public string Repository { get; set; }
public string Version { get; set; }
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch
{
public class PluginOptions
{
public virtual string Save()
{
return null;
}
public virtual void Load(string data)
{
}
}
}

View File

@@ -16,24 +16,23 @@ namespace Torch
/// </summary>
public class SteamService : MySteamService
{
public SteamService(bool isDedicated, uint appId)
: base(true, appId)
public SteamService(bool isDedicated, uint appId) : base(true, appId)
{
// TODO: Add protection for this mess... somewhere
SteamSDK.SteamServerAPI.Instance.Dispose();
var steam = typeof(Sandbox.MySteamService);
SteamServerAPI.Instance.Dispose();
var steam = typeof(MySteamService);
steam.GetField("SteamServerAPI").SetValue(this, null);
steam.GetProperty("AppId").GetSetMethod(true).Invoke(this, new object[] { appId });
if (isDedicated)
{
steam.GetField("SteamServerAPI").SetValue(this, SteamSDK.SteamServerAPI.Instance);
steam.GetField("SteamServerAPI").SetValue(this, SteamServerAPI.Instance);
}
else
{
var steamApi = SteamSDK.SteamAPI.Instance;
steam.GetField("SteamAPI").SetValue(this, SteamSDK.SteamAPI.Instance);
steam.GetProperty("IsActive").GetSetMethod(true).Invoke(this, new object[] { SteamSDK.SteamAPI.Instance != null });
var steamApi = SteamAPI.Instance;
steam.GetField("SteamAPI").SetValue(this, SteamAPI.Instance);
steam.GetProperty("IsActive").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.Instance != null });
if (steamApi != null)
{

View File

@@ -116,6 +116,7 @@
<ItemGroup>
<Compile Include="ChatMessage.cs" />
<Compile Include="Collections\KeyTree.cs" />
<Compile Include="Collections\RollingAverage.cs" />
<Compile Include="Commands\CategoryAttribute.cs" />
<Compile Include="Commands\Command.cs" />
<Compile Include="Commands\CommandAttribute.cs" />
@@ -131,8 +132,8 @@
<Compile Include="Managers\NetworkManager\NetworkHandlerBase.cs" />
<Compile Include="Managers\NetworkManager\NetworkManager.cs" />
<Compile Include="Managers\MultiplayerManager.cs" />
<Compile Include="PluginManifest.cs" />
<Compile Include="PluginOptions.cs" />
<Compile Include="TorchConfig.cs" />
<Compile Include="Updater\PluginManifest.cs" />
<Compile Include="Reflection.cs" />
<Compile Include="Managers\ScriptingManager.cs" />
<Compile Include="TorchBase.cs" />

View File

@@ -18,18 +18,20 @@ using SpaceEngineers.Game;
using Torch.API;
using Torch.Managers;
using VRage.FileSystem;
using VRage.Plugins;
using VRage.Scripting;
using VRage.Utils;
namespace Torch
{
public abstract class TorchBase : ITorchBase
public abstract class TorchBase : ViewModel, ITorchBase, IPlugin
{
/// <summary>
/// Hack because *keen*.
/// Use only if necessary, prefer dependency injection.
/// </summary>
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; }
@@ -186,10 +188,25 @@ namespace Torch
MySession.AfterLoading += () => SessionLoaded?.Invoke();
MySession.OnUnloading += () => SessionUnloading?.Invoke();
MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
InitUpdater();
_init = true;
}
/// <summary>
/// Hook into the VRage plugin system for updates.
/// </summary>
private void InitUpdater()
{
var fieldName = "m_plugins";
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
if (pluginList == null)
throw new TypeLoadException($"{fieldName} field not found in {nameof(MyPlugins)}");
pluginList.Add(this);
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
@@ -205,5 +222,25 @@ namespace Torch
public abstract void Start();
public abstract void Stop();
/// <inheritdoc />
public virtual void Dispose()
{
Plugins.DisposePlugins();
}
/// <inheritdoc />
public virtual void Init(object gameInstance)
{
Network.Init();
ChatManager.Instance.Init();
Plugins.Init();
}
/// <inheritdoc />
public virtual void Update()
{
Plugins.UpdatePlugins();
}
}
}

View File

@@ -10,15 +10,17 @@ using Sandbox.ModAPI.Ingame;
namespace Torch
{
public class TorchConfig
public class TorchConfig : ITorchConfig
{
private static Logger _log = LogManager.GetLogger("Config");
public string InstancePath { get; set; }
public string InstanceName { get; set; }
public int Autosave { get; set; }
public bool AutoRestart { get; set; }
public bool LogChat { get; set; }
//public int Autosave { get; set; }
//public bool AutoRestart { get; set; }
//public bool LogChat { get; set; }
public bool EnableAutomaticUpdates { get; set; } = true;
public bool RedownloadPlugins { get; set; }
[NonSerialized]
private string _path;
@@ -28,8 +30,8 @@ namespace Torch
{
InstanceName = instanceName;
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Torch", InstanceName);
Autosave = autosaveInterval;
AutoRestart = autoRestart;
//Autosave = autosaveInterval;
//AutoRestart = autoRestart;
}
public static TorchConfig LoadFrom(string path)

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Torch
{
public class PluginManifest
{
public string Repository { get; set; } = "Jimmacle/notarealrepo";
public string Version { get; set; } = "1.0";
public void Save(string path)
{
using (var f = File.OpenWrite(path))
{
var ser = new XmlSerializer(typeof(PluginManifest));
ser.Serialize(f, this);
}
}
public static PluginManifest Load(string path)
{
using (var f = File.OpenRead(path))
{
var ser = new XmlSerializer(typeof(PluginManifest));
return (PluginManifest)ser.Deserialize(f);
}
}
}
}

View File

@@ -1,30 +1,93 @@
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
{
public async Task CheckForUpdate(PluginManifest manifest)
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]);
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 (Exception e)
{
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);
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);
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;
var client = new GitHubClient(new ProductHeaderValue("Torch"));
var releases = await client.Repository.Release.GetAll(split[0], split[1]);
var currentVersion = new Version(manifest.Version);
var latestVersion = new Version(releases[0].TagName);
if (latestVersion > currentVersion)
{
//update
}
MyZipArchive.ExtractToDirectory(zipName, _pluginManager.PluginDir);
}
}
}