Add GitHub plugin updater, refactor game update hook to TorchBase instead of PluginManager, fix world config not applying
This commit is contained in:
@@ -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
15
Torch.API/ITorchConfig.cs
Normal 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
16
Torch.API/ServerState.cs
Normal 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
|
||||
}
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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")]
|
@@ -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;
|
||||
|
@@ -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();
|
||||
if (gui)
|
||||
{
|
||||
var ui = new TorchUI((TorchServer)_server);
|
||||
ui.LoadConfig(options);
|
||||
ui.ShowDialog();
|
||||
}
|
||||
else
|
||||
{
|
||||
_server.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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")]
|
14
Torch.Server/ServerStatistics.cs
Normal file
14
Torch.Server/ServerStatistics.cs
Normal 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);
|
||||
}
|
||||
}
|
@@ -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>
|
||||
|
@@ -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;
|
||||
runInternal.Invoke(null, null);
|
||||
} while (MySandboxGame.IsReloading);
|
||||
|
||||
MyInitializer.InvokeAfterRun();
|
||||
State = ServerState.Stopped;
|
||||
}
|
||||
Log.Trace("RunMain completed");
|
||||
IsRunning = false;
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/*
|
||||
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();
|
||||
}*/
|
||||
State = ServerState.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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))
|
||||
|
@@ -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>
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
56
Torch/Collections/RollingAverage.cs
Normal file
56
Torch/Collections/RollingAverage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
{
|
||||
|
@@ -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" />
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
34
Torch/Updater/PluginManifest.cs
Normal file
34
Torch/Updater/PluginManifest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user