Rewrite plugin system to update plugins in parallel after each game tick.
This commit is contained in:
@@ -7,6 +7,7 @@ namespace Torch.API
|
|||||||
{
|
{
|
||||||
public interface IPluginManager : IEnumerable<ITorchPlugin>
|
public interface IPluginManager : IEnumerable<ITorchPlugin>
|
||||||
{
|
{
|
||||||
|
void UpdatePlugins();
|
||||||
|
void LoadPlugins();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -12,7 +12,6 @@ namespace Torch.API
|
|||||||
Guid Id { get; }
|
Guid Id { get; }
|
||||||
Version Version { get; }
|
Version Version { get; }
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
bool Enabled { get; set; }
|
|
||||||
|
|
||||||
void Init(ITorchBase torchBase);
|
void Init(ITorchBase torchBase);
|
||||||
void Update();
|
void Update();
|
||||||
|
@@ -23,7 +23,7 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
public class TorchServer : TorchBase, ITorchServer
|
public class TorchServer : TorchBase, ITorchServer
|
||||||
{
|
{
|
||||||
public Thread ServerThread { get; private set; }
|
public Thread GameThread { get; private set; }
|
||||||
public bool IsRunning { get; private set; }
|
public bool IsRunning { get; private set; }
|
||||||
|
|
||||||
public event Action SessionLoading;
|
public event Action SessionLoading;
|
||||||
@@ -66,7 +66,7 @@ namespace Torch.Server
|
|||||||
|
|
||||||
private void OnSessionReady()
|
private void OnSessionReady()
|
||||||
{
|
{
|
||||||
Plugins.LoadAllPlugins();
|
Plugins.LoadPlugins();
|
||||||
InvokeSessionLoaded();
|
InvokeSessionLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ namespace Torch.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Stop()
|
public override void Stop()
|
||||||
{
|
{
|
||||||
if (Thread.CurrentThread.ManagedThreadId != ServerThread?.ManagedThreadId)
|
if (Thread.CurrentThread.ManagedThreadId != GameThread?.ManagedThreadId)
|
||||||
{
|
{
|
||||||
Log.Write("Requesting server stop.");
|
Log.Write("Requesting server stop.");
|
||||||
MySandboxGame.Static.Invoke(Stop);
|
MySandboxGame.Static.Invoke(Stop);
|
||||||
|
@@ -261,7 +261,7 @@ namespace Torch.Managers
|
|||||||
//array to hold arguments to pass into DispatchEvent
|
//array to hold arguments to pass into DispatchEvent
|
||||||
object[] arguments = new object[11];
|
object[] arguments = new object[11];
|
||||||
|
|
||||||
arguments[0] = (obj == null ? TryGetStaticCallSite(method) : TryGetCallSite(method, obj));
|
arguments[0] = obj == null ? TryGetStaticCallSite(method) : TryGetCallSite(method, obj);
|
||||||
arguments[1] = endpoint;
|
arguments[1] = endpoint;
|
||||||
arguments[2] = 1f;
|
arguments[2] = 1f;
|
||||||
arguments[3] = owner;
|
arguments[3] = owner;
|
||||||
|
@@ -10,6 +10,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Collections;
|
using VRage.Collections;
|
||||||
@@ -17,19 +18,63 @@ using VRage.Library.Collections;
|
|||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
|
internal class TorchPluginUpdater : IPlugin
|
||||||
|
{
|
||||||
|
private readonly IPluginManager _manager;
|
||||||
|
public TorchPluginUpdater(IPluginManager manager)
|
||||||
|
{
|
||||||
|
_manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(object obj) { }
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
_manager.UpdatePlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
|
|
||||||
public class PluginManager : IPluginManager
|
public class PluginManager : IPluginManager
|
||||||
{
|
{
|
||||||
private readonly ITorchBase _torch;
|
private readonly ITorchBase _torch;
|
||||||
public const string PluginDir = "Plugins";
|
public const string PluginDir = "Plugins";
|
||||||
|
|
||||||
private readonly List<TorchPluginBase> _plugins = new List<TorchPluginBase>();
|
private readonly List<ITorchPlugin> _plugins = new List<ITorchPlugin>();
|
||||||
|
private readonly TorchPluginUpdater _updater;
|
||||||
|
|
||||||
public PluginManager(ITorchBase torch)
|
public PluginManager(ITorchBase torch)
|
||||||
{
|
{
|
||||||
_torch = torch;
|
_torch = torch;
|
||||||
|
_updater = new TorchPluginUpdater(this);
|
||||||
|
|
||||||
if (!Directory.Exists(PluginDir))
|
if (!Directory.Exists(PluginDir))
|
||||||
Directory.CreateDirectory(PluginDir);
|
Directory.CreateDirectory(PluginDir);
|
||||||
|
|
||||||
|
InitUpdater();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitUpdater()
|
||||||
|
{
|
||||||
|
var pluginList = typeof(MyPlugins).GetField("m_plugins", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
||||||
|
if (pluginList == null)
|
||||||
|
throw new TypeLoadException($"m_plugins field not found in {nameof(MyPlugins)}");
|
||||||
|
|
||||||
|
pluginList.Add(_updater);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePlugins()
|
||||||
|
{
|
||||||
|
Parallel.ForEach(_plugins, p => p.Update());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnloadPlugins()
|
||||||
|
{
|
||||||
|
foreach (var plugin in _plugins)
|
||||||
|
plugin.Unload();
|
||||||
|
|
||||||
|
_plugins.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -46,41 +91,12 @@ namespace Torch
|
|||||||
|
|
||||||
foreach (var type in asm.GetExportedTypes())
|
foreach (var type in asm.GetExportedTypes())
|
||||||
{
|
{
|
||||||
if (type.IsSubclassOf(typeof(TorchPluginBase)))
|
if (type.GetInterfaces().Contains(typeof(ITorchPlugin)))
|
||||||
_plugins.Add((TorchPluginBase)Activator.CreateInstance(type));
|
_plugins.Add((ITorchPlugin)Activator.CreateInstance(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ReloadPluginAsync(ITorchPlugin plugin)
|
|
||||||
{
|
|
||||||
var p = plugin as TorchPluginBase;
|
|
||||||
if (p == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var newPlugin = (TorchPluginBase)Activator.CreateInstance(p.GetType());
|
|
||||||
_plugins.Add(newPlugin);
|
|
||||||
|
|
||||||
await p.StopAsync();
|
|
||||||
_plugins.Remove(p);
|
|
||||||
|
|
||||||
newPlugin.Run(_torch, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartEnabledPlugins()
|
|
||||||
{
|
|
||||||
foreach (var plugin in _plugins)
|
|
||||||
{
|
|
||||||
if (plugin.Enabled)
|
|
||||||
plugin.Run(_torch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UnblockDll(string fileName)
|
|
||||||
{
|
|
||||||
return DeleteFile(fileName + ":Zone.Identifier");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<ITorchPlugin> GetEnumerator()
|
public IEnumerator<ITorchPlugin> GetEnumerator()
|
||||||
{
|
{
|
||||||
return _plugins.GetEnumerator();
|
return _plugins.GetEnumerator();
|
||||||
@@ -91,6 +107,15 @@ namespace Torch
|
|||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the lock on a DLL downloaded from the internet.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool UnblockDll(string fileName)
|
||||||
|
{
|
||||||
|
return DeleteFile(fileName + ":Zone.Identifier");
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
|
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool DeleteFile(string name);
|
private static extern bool DeleteFile(string name);
|
||||||
|
@@ -10,13 +10,8 @@ namespace Torch
|
|||||||
public static class Reflection
|
public static class Reflection
|
||||||
{
|
{
|
||||||
//private static readonly Logger Log = LogManager.GetLogger( "BaseLog" );
|
//private static readonly Logger Log = LogManager.GetLogger( "BaseLog" );
|
||||||
public static bool HasMethod(Type objectType, string methodName)
|
|
||||||
{
|
|
||||||
return HasMethod(objectType, methodName, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public static bool HasMethod(Type objectType, string methodName, Type[] argTypes = null)
|
||||||
public static bool HasMethod(Type objectType, string methodName, Type[] argTypes)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -16,8 +16,6 @@ namespace Torch
|
|||||||
public Guid Id { get; }
|
public Guid Id { get; }
|
||||||
public Version Version { get; }
|
public Version Version { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public bool IsRunning => !Loop.IsCompleted;
|
|
||||||
public ITorchBase Torch { get; private set; }
|
public ITorchBase Torch { get; private set; }
|
||||||
|
|
||||||
protected TorchPluginBase()
|
protected TorchPluginBase()
|
||||||
@@ -50,57 +48,5 @@ namespace Torch
|
|||||||
|
|
||||||
public abstract void Update();
|
public abstract void Update();
|
||||||
public abstract void Unload();
|
public abstract void Unload();
|
||||||
|
|
||||||
#region Internal Loop Code
|
|
||||||
|
|
||||||
internal CancellationTokenSource ctSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
internal Task Loop { get; private set; } = Task.CompletedTask;
|
|
||||||
private readonly TimeSpan _loopInterval = TimeSpan.FromSeconds(1d / 60d);
|
|
||||||
private bool _runLoop;
|
|
||||||
internal Task Run(ITorchBase torch, bool enable = false)
|
|
||||||
{
|
|
||||||
if (IsRunning)
|
|
||||||
throw new InvalidOperationException($"Plugin {Name} is already running.");
|
|
||||||
|
|
||||||
if (!Enabled)
|
|
||||||
return Loop = Task.CompletedTask;
|
|
||||||
|
|
||||||
_runLoop = true;
|
|
||||||
return Loop = Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Init(torch);
|
|
||||||
|
|
||||||
while (Enabled && !ctSource.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
ctSource.Token.ThrowIfCancellationRequested();
|
|
||||||
var ts = Stopwatch.GetTimestamp();
|
|
||||||
Update();
|
|
||||||
var time = TimeSpan.FromTicks(Stopwatch.GetTimestamp() - ts);
|
|
||||||
|
|
||||||
if (time < _loopInterval)
|
|
||||||
Task.Delay(_loopInterval - time);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unload();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
torch.Log.Write($"Plugin {Name} threw an exception.");
|
|
||||||
torch.Log.WriteException(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task StopAsync()
|
|
||||||
{
|
|
||||||
ctSource.Cancel();
|
|
||||||
await Loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user