My New Year's resolution is to stop making commits like these
This commit is contained in:
33
TestPlugin/Class1.cs
Normal file
33
TestPlugin/Class1.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch;
|
||||||
|
using Torch.API;
|
||||||
|
using VRage.Plugins;
|
||||||
|
|
||||||
|
namespace TestPlugin
|
||||||
|
{
|
||||||
|
public class Plugin : TorchPluginBase
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Init(ITorchBase torch)
|
||||||
|
{
|
||||||
|
base.Init(torch);
|
||||||
|
Torch.Log.Write($"Plugin init {Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
Torch.Log.Write($"Plugin update {Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Unload()
|
||||||
|
{
|
||||||
|
Torch.Log.Write($"Plugin unload {Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
TestPlugin/Properties/AssemblyInfo.cs
Normal file
36
TestPlugin/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TestPlugin")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TestPlugin")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("abd18a6c-f638-44e9-8e55-dedea321c600")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
61
TestPlugin/TestPlugin.csproj
Normal file
61
TestPlugin/TestPlugin.csproj
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{ABD18A6C-F638-44E9-8E55-DEDEA321C600}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>TestPlugin</RootNamespace>
|
||||||
|
<AssemblyName>TestPlugin</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="VRage">
|
||||||
|
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Class1.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
@@ -4,10 +4,11 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
public class TorchPlugin
|
public interface ILogger
|
||||||
{
|
{
|
||||||
|
void Write(string message);
|
||||||
|
void WriteException(Exception e);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,21 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using VRage.Collections;
|
using VRage.Collections;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
|
|
||||||
namespace Torch.API
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
public interface IPluginManager
|
public interface IPluginManager : IEnumerable<ITorchPlugin>
|
||||||
{
|
{
|
||||||
ListReader<IPlugin> Plugins { get; }
|
|
||||||
|
|
||||||
string[] GetPluginFolders();
|
|
||||||
string GetPluginName(Type pluginType);
|
|
||||||
void LoadAllPlugins();
|
|
||||||
void LoadPlugin(IPlugin plugin);
|
|
||||||
void LoadPluginFolder(string folderName);
|
|
||||||
void ReloadAll();
|
|
||||||
void ReloadPlugin(IPlugin plugin, bool forceNonPiston = false);
|
|
||||||
bool UnblockDll(string fileName);
|
|
||||||
void UnloadPlugin(IPlugin plugin);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,8 +11,10 @@ namespace Torch.API
|
|||||||
event Action SessionLoaded;
|
event Action SessionLoaded;
|
||||||
IMultiplayer Multiplayer { get; }
|
IMultiplayer Multiplayer { get; }
|
||||||
IPluginManager Plugins { get; }
|
IPluginManager Plugins { get; }
|
||||||
void DoGameAction(Action action);
|
ILogger Log { get; set; }
|
||||||
Task DoGameActionAsync(Action action);
|
void Invoke(Action action);
|
||||||
|
void InvokeBlocking(Action action);
|
||||||
|
Task InvokeAsync(Action action);
|
||||||
string[] RunArgs { get; set; }
|
string[] RunArgs { get; set; }
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
@@ -4,13 +4,18 @@ using System.Linq;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using VRage.Plugins;
|
|
||||||
|
|
||||||
namespace Torch.API
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
public interface ITorchPlugin : IPlugin
|
public interface ITorchPlugin
|
||||||
{
|
{
|
||||||
void Init(ITorchBase torch);
|
Guid Id { get; }
|
||||||
void Reload();
|
Version Version { get; }
|
||||||
|
string Name { get; }
|
||||||
|
bool Enabled { get; set; }
|
||||||
|
|
||||||
|
void Init(ITorchBase torchBase);
|
||||||
|
void Update();
|
||||||
|
void Unload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace PistonAPI
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
public class PluginAttribute : Attribute
|
public class PluginAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
@@ -53,6 +53,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ConnectionState.cs" />
|
<Compile Include="ConnectionState.cs" />
|
||||||
<Compile Include="IChatItem.cs" />
|
<Compile Include="IChatItem.cs" />
|
||||||
|
<Compile Include="ILogger.cs" />
|
||||||
<Compile Include="IMultiplayer.cs" />
|
<Compile Include="IMultiplayer.cs" />
|
||||||
<Compile Include="IPlayer.cs" />
|
<Compile Include="IPlayer.cs" />
|
||||||
<Compile Include="IPluginManager.cs" />
|
<Compile Include="IPluginManager.cs" />
|
||||||
|
@@ -15,7 +15,7 @@ using VRageRender;
|
|||||||
|
|
||||||
namespace Torch.Client
|
namespace Torch.Client
|
||||||
{
|
{
|
||||||
class TorchClient : TorchBase, ITorchClient
|
public class TorchClient : TorchBase, ITorchClient
|
||||||
{
|
{
|
||||||
private MyCommonProgramStartup _startup;
|
private MyCommonProgramStartup _startup;
|
||||||
private IMyRender _renderer;
|
private IMyRender _renderer;
|
||||||
@@ -24,6 +24,8 @@ namespace Torch.Client
|
|||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
|
base.Init();
|
||||||
|
|
||||||
if (!File.Exists("steam_appid.txt"))
|
if (!File.Exists("steam_appid.txt"))
|
||||||
{
|
{
|
||||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
|
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
|
||||||
@@ -83,11 +85,18 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
using (var spaceEngineersGame = new SpaceEngineersGame(_services, RunArgs))
|
using (var spaceEngineersGame = new SpaceEngineersGame(_services, RunArgs))
|
||||||
{
|
{
|
||||||
Logger.Write("Starting client...");
|
Log.Write("Starting client...");
|
||||||
|
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded;
|
||||||
spaceEngineersGame.Run();
|
spaceEngineersGame.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Log.Write("Loading plugins");
|
||||||
|
Plugins.LoadAllPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Stop()
|
public override void Stop()
|
||||||
{
|
{
|
||||||
MySandboxGame.ExitThreadSafe();
|
MySandboxGame.ExitThreadSafe();
|
||||||
|
@@ -23,9 +23,16 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
base.RecreateControls(constructor);
|
base.RecreateControls(constructor);
|
||||||
AddCaption(MyStringId.GetOrCompute("Torch Settings"), null, new Vector2(0, 0), 1.2f);
|
AddCaption(MyStringId.GetOrCompute("Torch Settings"), null, new Vector2(0, 0), 1.2f);
|
||||||
var pluginList = new MyGuiControlListbox {VisibleRowsCount = 20};
|
var pluginList = new MyGuiControlListbox
|
||||||
foreach (var name in TorchBase.Instance.Plugins.GetPluginFolders())
|
{
|
||||||
|
VisibleRowsCount = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var plugin in TorchBase.Instance.Plugins.Plugins)
|
||||||
|
{
|
||||||
|
var name = TorchBase.Instance.Plugins.GetPluginName(plugin.GetType());
|
||||||
pluginList.Items.Add(new MyGuiControlListbox.Item(new StringBuilder(name)));
|
pluginList.Items.Add(new MyGuiControlListbox.Item(new StringBuilder(name)));
|
||||||
|
}
|
||||||
Controls.Add(pluginList);
|
Controls.Add(pluginList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -15,38 +16,23 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private static readonly ITorchServer _server = new TorchServer();
|
private static ITorchServer _server;
|
||||||
private static TorchUI _ui;
|
|
||||||
|
|
||||||
[STAThread]
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
_server.Init();
|
if (!Environment.UserInteractive)
|
||||||
_server.RunArgs = new[] { "-console" };
|
{
|
||||||
_ui = new TorchUI(_server);
|
using (var service = new TorchService())
|
||||||
|
{
|
||||||
if (args.Contains("-nogui"))
|
ServiceBase.Run(service);
|
||||||
_server.Start();
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
StartUI();
|
|
||||||
|
|
||||||
if (args.Contains("-autostart") && !_server.IsRunning)
|
|
||||||
new Thread(() => _server.Start()).Start();
|
|
||||||
|
|
||||||
Dispatcher.Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void StartUI()
|
|
||||||
{
|
{
|
||||||
Thread.CurrentThread.Name = "UI Thread";
|
_server = new TorchServer();
|
||||||
_ui.Show();
|
_server.Init();
|
||||||
}
|
_server.Start();
|
||||||
|
}
|
||||||
public static void FullRestart()
|
|
||||||
{
|
|
||||||
_server.Stop();
|
|
||||||
Process.Start("TorchServer.exe", "-autostart");
|
|
||||||
Environment.Exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,8 +67,10 @@
|
|||||||
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\SteamSDK.dll</HintPath>
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\SteamSDK.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -104,6 +106,12 @@
|
|||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="TorchService.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="TorchServiceInstaller.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="ViewModels\ConfigDedicatedViewModel.cs" />
|
<Compile Include="ViewModels\ConfigDedicatedViewModel.cs" />
|
||||||
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
||||||
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
|
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
|
||||||
|
@@ -33,11 +33,12 @@ namespace Torch.Server
|
|||||||
internal TorchServer()
|
internal TorchServer()
|
||||||
{
|
{
|
||||||
MySession.OnLoading += OnSessionLoading;
|
MySession.OnLoading += OnSessionLoading;
|
||||||
Plugins = new PluginManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
|
base.Init();
|
||||||
|
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
MyPerGameSettings.SendLogToKeen = false;
|
MyPerGameSettings.SendLogToKeen = false;
|
||||||
@@ -78,7 +79,7 @@ namespace Torch.Server
|
|||||||
throw new InvalidOperationException("Server is already running.");
|
throw new InvalidOperationException("Server is already running.");
|
||||||
|
|
||||||
IsRunning = true;
|
IsRunning = true;
|
||||||
Logger.Write("Starting server.");
|
Log.Write("Starting server.");
|
||||||
|
|
||||||
if (MySandboxGame.Log.LogEnabled)
|
if (MySandboxGame.Log.LogEnabled)
|
||||||
MySandboxGame.Log.Close();
|
MySandboxGame.Log.Close();
|
||||||
@@ -93,13 +94,13 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
if (Thread.CurrentThread.ManagedThreadId != ServerThread?.ManagedThreadId)
|
if (Thread.CurrentThread.ManagedThreadId != ServerThread?.ManagedThreadId)
|
||||||
{
|
{
|
||||||
Logger.Write("Requesting server stop.");
|
Log.Write("Requesting server stop.");
|
||||||
MySandboxGame.Static.Invoke(Stop);
|
MySandboxGame.Static.Invoke(Stop);
|
||||||
_stopHandle.WaitOne();
|
_stopHandle.WaitOne();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Write("Stopping server.");
|
Log.Write("Stopping server.");
|
||||||
MySession.Static.Save();
|
MySession.Static.Save();
|
||||||
MySession.Static.Unload();
|
MySession.Static.Unload();
|
||||||
MySandboxGame.Static.Exit();
|
MySandboxGame.Static.Exit();
|
||||||
@@ -111,7 +112,7 @@ namespace Torch.Server
|
|||||||
VRage.Input.MyInput.UnloadData();
|
VRage.Input.MyInput.UnloadData();
|
||||||
CleanupProfilers();
|
CleanupProfilers();
|
||||||
|
|
||||||
Logger.Write("Server stopped.");
|
Log.Write("Server stopped.");
|
||||||
_stopHandle.Set();
|
_stopHandle.Set();
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
}
|
}
|
||||||
|
54
Torch.Server/TorchService.cs
Normal file
54
Torch.Server/TorchService.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using Torch.API;
|
||||||
|
|
||||||
|
namespace Torch.Server
|
||||||
|
{
|
||||||
|
class TorchService : ServiceBase
|
||||||
|
{
|
||||||
|
public const string Name = "Torch (SEDS)";
|
||||||
|
private readonly ITorchServer _server = new TorchServer();
|
||||||
|
|
||||||
|
public TorchService()
|
||||||
|
{
|
||||||
|
ServiceName = Name;
|
||||||
|
EventLog.Log = "Application";
|
||||||
|
|
||||||
|
CanHandlePowerEvent = true;
|
||||||
|
CanHandleSessionChangeEvent = false;
|
||||||
|
CanPauseAndContinue = false;
|
||||||
|
CanStop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnStart(string[] args)
|
||||||
|
{
|
||||||
|
base.OnStart(args);
|
||||||
|
_server.Init();
|
||||||
|
_server.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnStop()
|
||||||
|
{
|
||||||
|
_server.Stop();
|
||||||
|
base.OnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnShutdown()
|
||||||
|
{
|
||||||
|
base.OnShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
|
||||||
|
{
|
||||||
|
return base.OnPowerEvent(powerStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
Torch.Server/TorchServiceInstaller.cs
Normal file
61
Torch.Server/TorchServiceInstaller.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Torch.Server
|
||||||
|
{
|
||||||
|
[RunInstaller(true)]
|
||||||
|
class TorchServiceInstaller : Installer
|
||||||
|
{
|
||||||
|
private ServiceInstaller _serviceInstaller;
|
||||||
|
|
||||||
|
public TorchServiceInstaller()
|
||||||
|
{
|
||||||
|
var serviceProcessInstaller = new ServiceProcessInstaller();
|
||||||
|
_serviceInstaller = new ServiceInstaller();
|
||||||
|
|
||||||
|
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
|
||||||
|
serviceProcessInstaller.Username = null;
|
||||||
|
serviceProcessInstaller.Password = null;
|
||||||
|
|
||||||
|
_serviceInstaller.DisplayName = "Torch (SEDS)";
|
||||||
|
_serviceInstaller.Description = "Service for Torch (SE Dedicated Server)";
|
||||||
|
_serviceInstaller.StartType = ServiceStartMode.Manual;
|
||||||
|
|
||||||
|
_serviceInstaller.ServiceName = TorchService.Name;
|
||||||
|
|
||||||
|
Installers.Add(serviceProcessInstaller);
|
||||||
|
Installers.Add(_serviceInstaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Install(IDictionary stateSaver)
|
||||||
|
{
|
||||||
|
GetServiceName();
|
||||||
|
base.Install(stateSaver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Uninstall(IDictionary savedState)
|
||||||
|
{
|
||||||
|
GetServiceName();
|
||||||
|
base.Uninstall(savedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetServiceName()
|
||||||
|
{
|
||||||
|
var name = Context.Parameters["name"];
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_serviceInstaller.DisplayName = name;
|
||||||
|
_serviceInstaller.ServiceName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -88,7 +88,7 @@ namespace Torch.Server
|
|||||||
|
|
||||||
private void BtnRestart_Click(object sender, RoutedEventArgs e)
|
private void BtnRestart_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Program.FullRestart();
|
//Program.FullRestart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
Torch.sln
20
Torch.sln
@@ -13,32 +13,52 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Server", "Torch.Serve
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Launcher", "Torch.Launcher\Torch.Launcher.csproj", "{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Launcher", "Torch.Launcher\Torch.Launcher.csproj", "{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{ABD18A6C-F638-44E9-8E55-DEDEA321C600}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
Release|x64 = Release|x64
|
Release|x64 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.ActiveCfg = Debug|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.Build.0 = Debug|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.ActiveCfg = Release|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.ActiveCfg = Release|x64
|
||||||
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.Build.0 = Release|x64
|
{7E01635C-3B67-472E-BCD6-C5539564F214}.Release|x64.Build.0 = Release|x64
|
||||||
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.ActiveCfg = Debug|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.Build.0 = Debug|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.ActiveCfg = Release|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.ActiveCfg = Release|x64
|
||||||
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.Build.0 = Release|x64
|
{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}.Release|x64.Build.0 = Release|x64
|
||||||
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.ActiveCfg = Debug|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.Build.0 = Debug|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.ActiveCfg = Release|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.ActiveCfg = Release|x64
|
||||||
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.Build.0 = Release|x64
|
{E36DF745-260B-4956-A2E8-09F08B2E7161}.Release|x64.Build.0 = Release|x64
|
||||||
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.ActiveCfg = Debug|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Debug|x64.ActiveCfg = Debug|x64
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Debug|x64.Build.0 = Debug|x64
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Release|x64.ActiveCfg = Release|x64
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Release|x64.ActiveCfg = Release|x64
|
||||||
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Release|x64.Build.0 = Release|x64
|
{19292801-5B9C-4EE0-961F-0FA37B3A6C3D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{ABD18A6C-F638-44E9-8E55-DEDEA321C600}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
22
Torch/Commands/CategoryAttribute.cs
Normal file
22
Torch/Commands/CategoryAttribute.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Torch.Commands
|
||||||
|
{
|
||||||
|
public class CategoryAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string[] Path { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies where to add the class's commands in the command tree.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Command path, e.g. "/admin config" -> "admin, config"</param>
|
||||||
|
public CategoryAttribute(params string[] path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Torch.Commands
|
|
||||||
{
|
|
||||||
public class ChatCommandAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
public ChatCommandAttribute(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
namespace Torch.Commands
|
namespace Torch.Commands
|
||||||
{
|
{
|
||||||
public class ChatCommand
|
public class Command
|
||||||
{
|
{
|
||||||
public ChatCommandModule Module;
|
public CommandModule Module;
|
||||||
public string Name;
|
public string Name;
|
||||||
|
public string[] Path;
|
||||||
public Action<CommandContext> Invoke;
|
public Action<CommandContext> Invoke;
|
||||||
}
|
}
|
||||||
}
|
}
|
18
Torch/Commands/CommandAttribute.cs
Normal file
18
Torch/Commands/CommandAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Torch.Commands
|
||||||
|
{
|
||||||
|
public class CommandAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies a command to add to the command tree.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public CommandAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -5,22 +5,26 @@ namespace Torch.Commands
|
|||||||
{
|
{
|
||||||
public struct CommandContext
|
public struct CommandContext
|
||||||
{
|
{
|
||||||
public string Argument;
|
public string[] Args;
|
||||||
public ulong SteamId;
|
public ulong SteamId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits the argument by single words and quoted blocks.
|
/// Splits the argument by single words and quoted blocks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string[] SplitArgument()
|
public CommandContext(ulong steamId, params string[] args)
|
||||||
{
|
{
|
||||||
var split = Regex.Split(Argument, "(\"[^\"]+\"|\\S+)");
|
SteamId = steamId;
|
||||||
|
Args = args;
|
||||||
|
|
||||||
|
/*
|
||||||
|
var split = Regex.Split(Args, "(\"[^\"]+\"|\\S+)");
|
||||||
for (var i = 0; i < split.Length; i++)
|
for (var i = 0; i < split.Length; i++)
|
||||||
{
|
{
|
||||||
split[i] = Regex.Replace(split[i], "\"", "");
|
split[i] = Regex.Replace(split[i], "\"", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
return split;
|
return split;*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Torch.Commands
|
namespace Torch.Commands
|
||||||
{
|
{
|
||||||
public class ChatCommandModule
|
public class CommandModule
|
||||||
{
|
{
|
||||||
public ITorchPlugin Plugin { get; set; }
|
public ITorchPlugin Plugin { get; set; }
|
||||||
public ITorchBase Server { get; set; }
|
public ITorchBase Server { get; set; }
|
@@ -10,7 +10,7 @@ namespace Torch.Commands
|
|||||||
public ITorchBase Server { get; }
|
public ITorchBase Server { get; }
|
||||||
public char Prefix { get; set; }
|
public char Prefix { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, ChatCommand> Commands { get; } = new Dictionary<string, ChatCommand>();
|
public Dictionary<string, Command> Commands { get; } = new Dictionary<string, Command>();
|
||||||
|
|
||||||
public CommandSystem(ITorchBase server, char prefix = '/')
|
public CommandSystem(ITorchBase server, char prefix = '/')
|
||||||
{
|
{
|
||||||
@@ -28,15 +28,15 @@ namespace Torch.Commands
|
|||||||
var assembly = plugin.GetType().Assembly;
|
var assembly = plugin.GetType().Assembly;
|
||||||
foreach (var type in assembly.ExportedTypes)
|
foreach (var type in assembly.ExportedTypes)
|
||||||
{
|
{
|
||||||
if (!type.IsSubclassOf(typeof(ChatCommandModule)))
|
if (!type.IsSubclassOf(typeof(CommandModule)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var module = (ChatCommandModule)Activator.CreateInstance(type);
|
var module = (CommandModule)Activator.CreateInstance(type);
|
||||||
module.Server = Server;
|
module.Server = Server;
|
||||||
module.Plugin = plugin;
|
module.Plugin = plugin;
|
||||||
foreach (var method in type.GetMethods())
|
foreach (var method in type.GetMethods())
|
||||||
{
|
{
|
||||||
var commandAttrib = method.GetCustomAttribute<ChatCommandAttribute>();
|
var commandAttrib = method.GetCustomAttribute<CommandAttribute>();
|
||||||
if (commandAttrib == null)
|
if (commandAttrib == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ namespace Torch.Commands
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var command = new ChatCommand
|
var command = new Command
|
||||||
{
|
{
|
||||||
Module = module,
|
Module = module,
|
||||||
Name = commandAttrib.Name,
|
Name = commandAttrib.Name,
|
||||||
@@ -82,7 +82,7 @@ namespace Torch.Commands
|
|||||||
|
|
||||||
var context = new CommandContext
|
var context = new CommandContext
|
||||||
{
|
{
|
||||||
Argument = arg,
|
Args = arg,
|
||||||
SteamId = steamId
|
SteamId = steamId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
99
Torch/Commands/CommandTree.cs
Normal file
99
Torch/Commands/CommandTree.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Library.Collections;
|
||||||
|
|
||||||
|
namespace Torch.Commands
|
||||||
|
{
|
||||||
|
public class CommandTree
|
||||||
|
{
|
||||||
|
private Dictionary<string, Node> RootNodes { get; } = new Dictionary<string, Node>();
|
||||||
|
|
||||||
|
public bool AddCommand(Command command)
|
||||||
|
{
|
||||||
|
var root = command.Path.First();
|
||||||
|
var node = RootNodes.ContainsKey(root) ? RootNodes[root] : new Node(root);
|
||||||
|
|
||||||
|
for (var i = 1; i < command.Path.Length; i++)
|
||||||
|
{
|
||||||
|
var current = command.Path[i];
|
||||||
|
if (node.Nodes.ContainsKey(current))
|
||||||
|
{
|
||||||
|
node = node.Nodes[current];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newNode = new Node(current);
|
||||||
|
node.AddNode(newNode);
|
||||||
|
node = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.IsEmpty)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
node.Command = command;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeResult Invoke(ulong steamId, string[] command)
|
||||||
|
{
|
||||||
|
var root = command.First();
|
||||||
|
if (!RootNodes.ContainsKey(root))
|
||||||
|
return InvokeResult.NoCommand;
|
||||||
|
|
||||||
|
var node = RootNodes[root];
|
||||||
|
var args = new string[0];
|
||||||
|
for (var i = 1; i < command.Length; i++)
|
||||||
|
{
|
||||||
|
var current = command[i];
|
||||||
|
if (node.Nodes.ContainsKey(current))
|
||||||
|
{
|
||||||
|
node = node.Nodes[current];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
args = new string[command.Length - i];
|
||||||
|
Array.Copy(command, i, args, 0, args.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.IsCommand)
|
||||||
|
return InvokeResult.NoCommand;
|
||||||
|
|
||||||
|
//check permission here
|
||||||
|
|
||||||
|
var context = new CommandContext(steamId, args);
|
||||||
|
node.Command.Invoke(context);
|
||||||
|
return InvokeResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Node
|
||||||
|
{
|
||||||
|
public Dictionary<string, Node> Nodes { get; } = new Dictionary<string, Node>();
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public Command Command { get; set; }
|
||||||
|
public bool IsCommand => Command != null;
|
||||||
|
public bool IsEmpty => !IsCommand && Nodes.Count == 0;
|
||||||
|
|
||||||
|
public Node(string name, Command command = null)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddNode(Node node)
|
||||||
|
{
|
||||||
|
Nodes.Add(node.Name, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum InvokeResult
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
NoCommand,
|
||||||
|
NoPermission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,21 +1,64 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Torch.API;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
public static class Logger
|
public class Logger : ILogger
|
||||||
{
|
{
|
||||||
public const string Prefix = "[TORCH]";
|
public string Prefix = "[TORCH]";
|
||||||
|
private StringBuilder _sb = new StringBuilder();
|
||||||
|
private string _path;
|
||||||
|
|
||||||
public static void Write(string message)
|
public Logger(string path)
|
||||||
{
|
{
|
||||||
var msg = $"{Prefix}: {message}";
|
_path = path;
|
||||||
MyLog.Default.WriteLineAndConsole(msg);
|
if (File.Exists(_path))
|
||||||
|
File.Delete(_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string message)
|
||||||
|
{
|
||||||
|
var msg = $"{GetInfo()}: {message}";
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
_sb.AppendLine(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteExceptionAndThrow(Exception e)
|
||||||
|
{
|
||||||
|
WriteException(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteException(Exception e)
|
||||||
|
{
|
||||||
|
_sb.AppendLine($"{GetInfo()}: {e.Message}");
|
||||||
|
|
||||||
|
foreach (var line in e.StackTrace.Split('\n'))
|
||||||
|
_sb.AppendLine($"\t{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetInfo()
|
||||||
|
{
|
||||||
|
return $"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()} {Prefix}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
File.AppendAllText(_path, _sb.ToString());
|
||||||
|
_sb.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Logger()
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,11 +50,11 @@ namespace Torch
|
|||||||
_torch.SessionLoaded += OnSessionLoaded;
|
_torch.SessionLoaded += OnSessionLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void KickPlayer(ulong steamId) => _torch.DoGameActionAsync(() => MyMultiplayer.Static.KickClient(steamId));
|
public void KickPlayer(ulong steamId) => _torch.InvokeAsync(() => MyMultiplayer.Static.KickClient(steamId));
|
||||||
|
|
||||||
public void BanPlayer(ulong steamId, bool banned = true)
|
public void BanPlayer(ulong steamId, bool banned = true)
|
||||||
{
|
{
|
||||||
_torch.DoGameActionAsync(() =>
|
_torch.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
MyMultiplayer.Static.BanClient(steamId, banned);
|
MyMultiplayer.Static.BanClient(steamId, banned);
|
||||||
if (_gameOwnerIds.ContainsKey(steamId))
|
if (_gameOwnerIds.ContainsKey(steamId))
|
||||||
@@ -118,7 +118,7 @@ namespace Torch
|
|||||||
player.SetConnectionState(ConnectionState.Connected);
|
player.SetConnectionState(ConnectionState.Connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Write($"{player.Name} connected.");
|
_torch.Log.Write($"{player.Name} connected.");
|
||||||
PlayerJoined?.Invoke(player);
|
PlayerJoined?.Invoke(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ namespace Torch
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var player = Players[steamId];
|
var player = Players[steamId];
|
||||||
Logger.Write($"{player.Name} disconnected ({(ConnectionState)stateChange}).");
|
_torch.Log.Write($"{player.Name} disconnected ({(ConnectionState)stateChange}).");
|
||||||
player.SetConnectionState((ConnectionState)stateChange);
|
player.SetConnectionState((ConnectionState)stateChange);
|
||||||
PlayerLeft?.Invoke(player);
|
PlayerLeft?.Invoke(player);
|
||||||
}
|
}
|
||||||
@@ -179,16 +179,16 @@ namespace Torch
|
|||||||
//Largely copied from SE
|
//Largely copied from SE
|
||||||
private void ValidateAuthTicketResponse(ulong steamID, AuthSessionResponseEnum response, ulong ownerSteamID)
|
private void ValidateAuthTicketResponse(ulong steamID, AuthSessionResponseEnum response, ulong ownerSteamID)
|
||||||
{
|
{
|
||||||
Logger.Write($"Server ValidateAuthTicketResponse ({response}), owner: {ownerSteamID}");
|
_torch.Log.Write($"Server ValidateAuthTicketResponse ({response}), owner: {ownerSteamID}");
|
||||||
|
|
||||||
if (steamID != ownerSteamID)
|
if (steamID != ownerSteamID)
|
||||||
{
|
{
|
||||||
Logger.Write($"User {steamID} is using a game owned by {ownerSteamID}. Tracking...");
|
_torch.Log.Write($"User {steamID} is using a game owned by {ownerSteamID}. Tracking...");
|
||||||
_gameOwnerIds[steamID] = ownerSteamID;
|
_gameOwnerIds[steamID] = ownerSteamID;
|
||||||
|
|
||||||
if (MySandboxGame.ConfigDedicated.Banned.Contains(ownerSteamID))
|
if (MySandboxGame.ConfigDedicated.Banned.Contains(ownerSteamID))
|
||||||
{
|
{
|
||||||
Logger.Write($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
|
_torch.Log.Write($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
|
||||||
UserRejected(steamID, JoinResult.BannedByAdmins);
|
UserRejected(steamID, JoinResult.BannedByAdmins);
|
||||||
BanPlayer(steamID, true);
|
BanPlayer(steamID, true);
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PistonAPI;
|
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
@@ -17,142 +19,60 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
public class PluginManager : IPluginManager
|
public class PluginManager : IPluginManager
|
||||||
{
|
{
|
||||||
//TODO: Disable reloading if the plugin has static elements because they prevent a full reload.
|
private readonly ITorchBase _torch;
|
||||||
|
|
||||||
public ListReader<IPlugin> Plugins => MyPlugins.Plugins;
|
|
||||||
|
|
||||||
private List<IPlugin> _plugins;
|
|
||||||
public const string PluginDir = "Plugins";
|
public const string PluginDir = "Plugins";
|
||||||
|
|
||||||
public PluginManager()
|
private readonly List<TorchPluginBase> _plugins = new List<TorchPluginBase>();
|
||||||
|
|
||||||
|
public PluginManager(ITorchBase torch)
|
||||||
{
|
{
|
||||||
|
_torch = torch;
|
||||||
|
|
||||||
if (!Directory.Exists(PluginDir))
|
if (!Directory.Exists(PluginDir))
|
||||||
Directory.CreateDirectory(PluginDir);
|
Directory.CreateDirectory(PluginDir);
|
||||||
|
|
||||||
GetPluginList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a reference to the internal VRage plugin list.
|
|
||||||
/// </summary>
|
|
||||||
private void GetPluginList()
|
|
||||||
{
|
|
||||||
_plugins = typeof(MyPlugins).GetField("m_plugins", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a plugin's name from its <see cref="PluginAttribute"/> or its type name.
|
|
||||||
/// </summary>
|
|
||||||
public string GetPluginName(Type pluginType)
|
|
||||||
{
|
|
||||||
var attr = pluginType.GetCustomAttribute<PluginAttribute>();
|
|
||||||
return attr?.Name ?? pluginType.Name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load all plugins in the <see cref="PluginDir"/> folder.
|
/// Load all plugins in the <see cref="PluginDir"/> folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadAllPlugins()
|
public void LoadPlugins()
|
||||||
{
|
{
|
||||||
var pluginFolders = GetPluginFolders();
|
var pluginsPath = Path.Combine(Directory.GetCurrentDirectory(), PluginDir);
|
||||||
foreach (var folder in pluginFolders)
|
var dlls = Directory.GetFiles(pluginsPath, "*.dll", SearchOption.AllDirectories);
|
||||||
|
foreach (var dllPath in dlls)
|
||||||
{
|
{
|
||||||
LoadPluginFolder(folder);
|
UnblockDll(dllPath);
|
||||||
|
var asm = Assembly.LoadFrom(dllPath);
|
||||||
|
|
||||||
|
foreach (var type in asm.GetExportedTypes())
|
||||||
|
{
|
||||||
|
if (type.IsSubclassOf(typeof(TorchPluginBase)))
|
||||||
|
_plugins.Add((TorchPluginBase)Activator.CreateInstance(type));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public async void ReloadPluginAsync(ITorchPlugin plugin)
|
||||||
/// Load a plugin into the game.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin"></param>
|
|
||||||
public void LoadPlugin(IPlugin plugin)
|
|
||||||
{
|
{
|
||||||
Logger.Write($"Loading plugin: {GetPluginName(plugin.GetType())}");
|
var p = plugin as TorchPluginBase;
|
||||||
plugin.Init(MySandboxGame.Static);
|
if (p == null)
|
||||||
_plugins.Add(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the names of all the subfolders in the Plugins directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public string[] GetPluginFolders()
|
|
||||||
{
|
|
||||||
var dirs = Directory.GetDirectories(PluginDir);
|
|
||||||
for (var i = 0; i < dirs.Length; i++)
|
|
||||||
{
|
|
||||||
dirs[i] = dirs[i].Substring(PluginDir.Length + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load all plugins in the specified folder.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folderName">Folder in the <see cref="PluginDir"/> directory</param>
|
|
||||||
public void LoadPluginFolder(string folderName)
|
|
||||||
{
|
|
||||||
var relativeDir = Path.Combine(PluginDir, folderName);
|
|
||||||
if (!Directory.Exists(relativeDir))
|
|
||||||
{
|
|
||||||
Logger.Write($"Plugin {folderName} does not exist in the Plugins folder.");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var newPlugin = (TorchPluginBase)Activator.CreateInstance(p.GetType());
|
||||||
|
_plugins.Add(newPlugin);
|
||||||
|
|
||||||
|
await p.StopAsync();
|
||||||
|
_plugins.Remove(p);
|
||||||
|
|
||||||
|
newPlugin.Run(_torch, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileNames = Directory.GetFiles(relativeDir, "*.dll");
|
public void StartEnabledPlugins()
|
||||||
|
|
||||||
foreach (var fileName in fileNames)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(Directory.GetCurrentDirectory(), fileName);
|
|
||||||
UnblockDll(fullPath);
|
|
||||||
var asm = Assembly.LoadFrom(fullPath);
|
|
||||||
|
|
||||||
foreach (var type in asm.GetTypes())
|
|
||||||
{
|
|
||||||
if (type.GetInterfaces().Contains(typeof(IPlugin)))
|
|
||||||
{
|
|
||||||
var inst = (IPlugin)Activator.CreateInstance(type);
|
|
||||||
MySandboxGame.Static.Invoke(() => LoadPlugin(inst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unload a plugin from the game.
|
|
||||||
/// </summary>
|
|
||||||
public void UnloadPlugin(IPlugin plugin)
|
|
||||||
{
|
|
||||||
_plugins.Remove(plugin);
|
|
||||||
plugin.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reload a plugin.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin"></param>
|
|
||||||
/// <param name="forceNonPiston">Reload a non-Piston plugin</param>
|
|
||||||
public void ReloadPlugin(IPlugin plugin, bool forceNonPiston = false)
|
|
||||||
{
|
|
||||||
var p = plugin as ITorchPlugin;
|
|
||||||
if (p == null && forceNonPiston)
|
|
||||||
{
|
|
||||||
plugin.Dispose();
|
|
||||||
plugin.Init(MySandboxGame.Static);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p?.Reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReloadAll()
|
|
||||||
{
|
{
|
||||||
foreach (var plugin in _plugins)
|
foreach (var plugin in _plugins)
|
||||||
{
|
{
|
||||||
var p = plugin as ITorchPlugin;
|
if (plugin.Enabled)
|
||||||
p?.Reload();
|
plugin.Run(_torch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +81,16 @@ namespace Torch
|
|||||||
return DeleteFile(fileName + ":Zone.Identifier");
|
return DeleteFile(fileName + ":Zone.Identifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerator<ITorchPlugin> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _plugins.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
[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);
|
||||||
|
@@ -8,6 +8,7 @@ using Sandbox;
|
|||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Platform;
|
using Sandbox.Engine.Platform;
|
||||||
using SteamSDK;
|
using SteamSDK;
|
||||||
|
using Torch.API;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
@@ -15,9 +16,12 @@ namespace Torch
|
|||||||
public static class SteamHelper
|
public static class SteamHelper
|
||||||
{
|
{
|
||||||
private static Thread _callbackThread;
|
private static Thread _callbackThread;
|
||||||
|
private static ILogger _log;
|
||||||
|
|
||||||
public static void Init()
|
public static void Init(ILogger log)
|
||||||
{
|
{
|
||||||
|
_log = log;
|
||||||
|
|
||||||
_callbackThread = new Thread(() =>
|
_callbackThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@@ -52,7 +56,7 @@ namespace Torch
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Write($"Failed to get item info for {itemId}");
|
_log.Write($"Failed to get item info for {itemId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
mre.Set();
|
mre.Set();
|
||||||
@@ -68,20 +72,19 @@ namespace Torch
|
|||||||
public static SteamUGCDetails GetItemDetails(ulong itemId)
|
public static SteamUGCDetails GetItemDetails(ulong itemId)
|
||||||
{
|
{
|
||||||
SteamUGCDetails details = default(SteamUGCDetails);
|
SteamUGCDetails details = default(SteamUGCDetails);
|
||||||
using (var mre = new ManualResetEvent(false))
|
using (var re = new AutoResetEvent(false))
|
||||||
{
|
{
|
||||||
SteamAPI.Instance.UGC.RequestUGCDetails(itemId, 0, (b, result) =>
|
SteamAPI.Instance.UGC.RequestUGCDetails(itemId, 0, (b, result) =>
|
||||||
{
|
{
|
||||||
if (!b && result.Details.Result == Result.OK)
|
if (!b && result.Details.Result == Result.OK)
|
||||||
details = result.Details;
|
details = result.Details;
|
||||||
else
|
else
|
||||||
Logger.Write($"Failed to get item details for {itemId}");
|
_log.Write($"Failed to get item details for {itemId}");
|
||||||
|
|
||||||
mre.Set();
|
re.Set();
|
||||||
});
|
});
|
||||||
|
|
||||||
mre.WaitOne();
|
re.WaitOne();
|
||||||
mre.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
|
@@ -121,18 +121,20 @@
|
|||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Commands\ChatCommand.cs" />
|
<Compile Include="Commands\CategoryAttribute.cs" />
|
||||||
<Compile Include="Commands\ChatCommandAttribute.cs" />
|
<Compile Include="Commands\Command.cs" />
|
||||||
<Compile Include="Commands\ChatCommandModule.cs" />
|
<Compile Include="Commands\CommandAttribute.cs" />
|
||||||
|
<Compile Include="Commands\CommandModule.cs" />
|
||||||
<Compile Include="Commands\CommandContext.cs" />
|
<Compile Include="Commands\CommandContext.cs" />
|
||||||
<Compile Include="Commands\CommandSystem.cs" />
|
<Compile Include="Commands\CommandSystem.cs" />
|
||||||
|
<Compile Include="Commands\CommandTree.cs" />
|
||||||
<Compile Include="Logger.cs" />
|
<Compile Include="Logger.cs" />
|
||||||
<Compile Include="MultiplayerManager.cs" />
|
<Compile Include="MultiplayerManager.cs" />
|
||||||
<Compile Include="TorchBase.cs" />
|
<Compile Include="TorchBase.cs" />
|
||||||
<Compile Include="TorchPlugin.cs" />
|
|
||||||
<Compile Include="Player.cs" />
|
<Compile Include="Player.cs" />
|
||||||
<Compile Include="Collections\PlayerInfoCache.cs" />
|
<Compile Include="Collections\PlayerInfoCache.cs" />
|
||||||
<Compile Include="SteamService.cs" />
|
<Compile Include="SteamService.cs" />
|
||||||
|
<Compile Include="TorchPluginBase.cs" />
|
||||||
<Compile Include="ViewModels\ChatItem.cs" />
|
<Compile Include="ViewModels\ChatItem.cs" />
|
||||||
<Compile Include="ViewModels\ModViewModel.cs" />
|
<Compile Include="ViewModels\ModViewModel.cs" />
|
||||||
<Compile Include="Collections\MTObservableCollection.cs" />
|
<Compile Include="Collections\MTObservableCollection.cs" />
|
||||||
|
@@ -1,25 +1,32 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using VRage.Scripting;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
public abstract class TorchBase : ITorchBase
|
public abstract class TorchBase : ITorchBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dirty hack because *keen*
|
||||||
|
/// Use only if absolutely necessary.
|
||||||
|
/// </summary>
|
||||||
public static ITorchBase Instance { get; private set; }
|
public static ITorchBase Instance { get; private set; }
|
||||||
|
|
||||||
public string[] RunArgs { get; set; }
|
public string[] RunArgs { get; set; }
|
||||||
public IPluginManager Plugins { get; protected set; }
|
public IPluginManager Plugins { get; protected set; }
|
||||||
public IMultiplayer Multiplayer { get; protected set; }
|
public IMultiplayer Multiplayer { get; protected set; }
|
||||||
|
public ILogger Log { get; set; }
|
||||||
public event Action SessionLoaded;
|
public event Action SessionLoaded;
|
||||||
|
|
||||||
|
private bool _init;
|
||||||
|
|
||||||
protected void InvokeSessionLoaded()
|
protected void InvokeSessionLoaded()
|
||||||
{
|
{
|
||||||
SessionLoaded?.Invoke();
|
SessionLoaded?.Invoke();
|
||||||
@@ -27,13 +34,22 @@ namespace Torch
|
|||||||
|
|
||||||
protected TorchBase()
|
protected TorchBase()
|
||||||
{
|
{
|
||||||
RunArgs = new string[0];
|
if (Instance != null)
|
||||||
|
throw new InvalidOperationException("A TorchBase instance already exists.");
|
||||||
|
|
||||||
Instance = this;
|
Instance = this;
|
||||||
Plugins = new PluginManager();
|
|
||||||
|
Log = new Logger(Path.Combine(Directory.GetCurrentDirectory(), "TorchLog.log"));
|
||||||
|
RunArgs = new string[0];
|
||||||
|
Plugins = new PluginManager(this);
|
||||||
Multiplayer = new MultiplayerManager(this);
|
Multiplayer = new MultiplayerManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoGameAction(Action action)
|
/// <summary>
|
||||||
|
/// Invokes an action on the game thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void Invoke(Action action)
|
||||||
{
|
{
|
||||||
MySandboxGame.Static.Invoke(action);
|
MySandboxGame.Static.Invoke(action);
|
||||||
}
|
}
|
||||||
@@ -42,24 +58,41 @@ namespace Torch
|
|||||||
/// Invokes an action on the game thread asynchronously.
|
/// Invokes an action on the game thread asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="action"></param>
|
/// <param name="action"></param>
|
||||||
public Task DoGameActionAsync(Action action)
|
public Task InvokeAsync(Action action)
|
||||||
{
|
{
|
||||||
if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
|
if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
|
||||||
{
|
{
|
||||||
Debug.Assert(false, $"{nameof(DoGameActionAsync)} should not be called on the game thread.");
|
Debug.Assert(false, $"{nameof(InvokeAsync)} should not be called on the game thread.");
|
||||||
action?.Invoke();
|
action?.Invoke();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.Run(() =>
|
return Task.Run(() => InvokeBlocking(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes an action on the game thread and blocks until it is completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void InvokeBlocking(Action action)
|
||||||
{
|
{
|
||||||
|
if (action == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
|
||||||
|
{
|
||||||
|
Debug.Assert(false, $"{nameof(InvokeBlocking)} should not be called on the game thread.");
|
||||||
|
action.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new AutoResetEvent(false);
|
var e = new AutoResetEvent(false);
|
||||||
|
|
||||||
MySandboxGame.Static.Invoke(() =>
|
MySandboxGame.Static.Invoke(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action?.Invoke();
|
action.Invoke();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -73,12 +106,19 @@ namespace Torch
|
|||||||
|
|
||||||
if (!e.WaitOne(60000))
|
if (!e.WaitOne(60000))
|
||||||
throw new TimeoutException("The game action timed out.");
|
throw new TimeoutException("The game action timed out.");
|
||||||
|
}
|
||||||
|
|
||||||
});
|
public virtual void Init()
|
||||||
|
{
|
||||||
|
Debug.Assert(!_init, "Torch instance is already initialized.");
|
||||||
|
|
||||||
|
_init = true;
|
||||||
|
MyScriptCompiler.Static.AddConditionalCompilationSymbols("TORCH");
|
||||||
|
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(ITorchBase).Assembly.Location);
|
||||||
|
MyScriptCompiler.Static.AddReferencedAssemblies(typeof(TorchBase).Assembly.Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Start();
|
public abstract void Start();
|
||||||
public abstract void Stop();
|
public abstract void Stop();
|
||||||
public abstract void Init();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
Torch/TorchPluginBase.cs
Normal file
106
Torch/TorchPluginBase.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch.API;
|
||||||
|
|
||||||
|
namespace Torch
|
||||||
|
{
|
||||||
|
public abstract class TorchPluginBase : ITorchPlugin
|
||||||
|
{
|
||||||
|
public Guid Id { get; }
|
||||||
|
public Version Version { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
public bool IsRunning => !Loop.IsCompleted;
|
||||||
|
public ITorchBase Torch { get; private set; }
|
||||||
|
|
||||||
|
protected TorchPluginBase()
|
||||||
|
{
|
||||||
|
var asm = Assembly.GetCallingAssembly();
|
||||||
|
|
||||||
|
var id = asm.GetCustomAttribute<GuidAttribute>()?.Value;
|
||||||
|
if (id == null)
|
||||||
|
throw new InvalidOperationException($"{asm.FullName} has no Guid attribute.");
|
||||||
|
|
||||||
|
Id = new Guid(id);
|
||||||
|
|
||||||
|
var ver = asm.GetCustomAttribute<AssemblyVersionAttribute>()?.Version;
|
||||||
|
if (ver == null)
|
||||||
|
throw new InvalidOperationException($"{asm.FullName} has no AssemblyVersion attribute.");
|
||||||
|
|
||||||
|
Version = new Version(ver);
|
||||||
|
|
||||||
|
var name = asm.GetCustomAttribute<AssemblyTitleAttribute>()?.Title;
|
||||||
|
if (name == null)
|
||||||
|
throw new InvalidOperationException($"{asm.FullName} has no AssemblyTitle attribute.");
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Init(ITorchBase torch)
|
||||||
|
{
|
||||||
|
Torch = torch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Update();
|
||||||
|
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