Merge pull request #70 from Equinox-/master

Dependency Resolution and Sorting
This commit is contained in:
John Michael Gross
2017-08-18 16:24:34 -07:00
committed by GitHub
28 changed files with 921 additions and 125 deletions

View File

@@ -44,6 +44,9 @@ namespace Torch.API
/// <inheritdoc cref="IPluginManager"/>
IPluginManager Plugins { get; }
/// <inheritdoc cref="IDependencyManager"/>
IDependencyManager Managers { get; }
/// <summary>
/// The binary version of the current instance.
/// </summary>
@@ -90,12 +93,6 @@ namespace Torch.API
/// Initialize the Torch instance.
/// </summary>
void Init();
/// <summary>
/// Get an <see cref="IManager"/> that is part of the Torch instance.
/// </summary>
/// <typeparam name="T">Manager type</typeparam>
T GetManager<T>() where T : class, IManager;
}
/// <summary>

View File

@@ -0,0 +1,30 @@
using System;
namespace Torch.API.Managers
{
public static class DependencyManagerExtensions
{
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <param name="managerType">The dependency type to remove</param>
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
public static IManager RemoveManager(this IDependencyManager depManager, Type managerType)
{
IManager mgr = depManager.GetManager(managerType);
return depManager.RemoveManager(mgr) ? mgr : null;
}
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <typeparam name="T">The dependency type to remove</typeparam>
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
public static IManager RemoveManager<T>(this IDependencyManager depManager)
{
return depManager.RemoveManager(typeof(T));
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Torch.API.Managers
{
public static class DependencyProviderExtensions
{
/// <summary>
/// Gets the manager that provides the given type. If there is no such manager, returns null.
/// </summary>
/// <typeparam name="T">Type of manager</typeparam>
/// <returns>manager, or null if none exists</returns>
public static T GetManager<T>(this IDependencyProvider depProvider) where T : class, IManager
{
return (T)depProvider.GetManager(typeof(T));
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Managers
{
/// <summary>
/// Manages a set of <see cref="IManager"/> and the dependencies between them.
/// </summary>
public interface IDependencyManager : IDependencyProvider
{
/// <summary>
/// Registers the given manager into the dependency system.
/// </summary>
/// <remarks>
/// This method only returns false when there is already a manager registered with a type derived from this given manager,
/// or when the given manager is derived from an already existing manager.
/// </remarks>
/// <param name="manager">Manager to register</param>
/// <exception cref="InvalidOperationException">When adding a new manager to an initialized dependency manager</exception>
/// <returns>true if added, false if not</returns>
bool AddManager(IManager manager);
/// <summary>
/// Clears all managers registered with this dependency manager
/// </summary>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
void ClearManagers();
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <param name="manager">The manager to remove</param>
/// <returns>true if successful, false if the manager wasn't found</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
bool RemoveManager(IManager manager);
/// <summary>
/// Sorts the dependency manager, then attaches all its registered managers in <see cref="AttachOrder" />
/// </summary>
void Attach();
/// <summary>
/// Detaches all registered managers in <see cref="DetachOrder"/>
/// </summary>
void Detach();
/// <summary>
/// The order that managers should be attached in. (Dependencies, then dependents)
/// </summary>
/// <exception cref="InvalidOperationException">When trying to determine load order before this dependency manager is initialized</exception>
IEnumerable<IManager> AttachOrder { get; }
/// <summary>
/// The order that managers should be detached in. (Dependents, then dependencies)
/// </summary>
/// <exception cref="InvalidOperationException">When trying to determine unload order before this dependency manager is initialized</exception>
IEnumerable<IManager> DetachOrder { get; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Managers
{
public interface IDependencyProvider
{
/// <summary>
/// Gets the manager that provides the given type. If there is no such manager, returns null.
/// </summary>
/// <param name="type">Type of manager</param>
/// <returns>manager, or null if none exists</returns>
IManager GetManager(Type type);
}
}

View File

@@ -12,8 +12,13 @@ namespace Torch.API.Managers
public interface IManager
{
/// <summary>
/// Initializes the manager. Called after Torch is initialized.
/// Attaches the manager to the session. Called once this manager's dependencies have been attached.
/// </summary>
void Init();
void Attach();
/// <summary>
/// Detaches the manager from the session. Called before this manager's dependencies are detached.
/// </summary>
void Detach();
}
}

View File

@@ -26,11 +26,6 @@ namespace Torch.API.Managers
/// </summary>
void UpdatePlugins();
/// <summary>
/// Disposes all loaded plugins.
/// </summary>
void DisposePlugins();
/// <summary>
/// Load plugins.
/// </summary>

View File

@@ -2,8 +2,6 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" 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>{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
@@ -15,7 +13,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
@@ -23,14 +21,14 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\x64\Release\Torch.API.xml</DocumentationFile>
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.API.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
@@ -159,6 +157,10 @@
<Compile Include="ConnectionState.cs" />
<Compile Include="IChatMessage.cs" />
<Compile Include="ITorchConfig.cs" />
<Compile Include="Managers\DependencyManagerExtensions.cs" />
<Compile Include="Managers\DependencyProviderExtensions.cs" />
<Compile Include="Managers\IDependencyManager.cs" />
<Compile Include="Managers\IDependencyProvider.cs" />
<Compile Include="Managers\IManager.cs" />
<Compile Include="Managers\IMultiplayerManager.cs" />
<Compile Include="IPlayer.cs" />

View File

@@ -1,17 +1,166 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using NLog;
using MessageBox = System.Windows.MessageBox;
namespace Torch.Client
{
public static class Program
{
public const string SpaceEngineersBinaries = "Bin64";
private static string _spaceEngInstallAlias = null;
public static string SpaceEngineersInstallAlias
{
get
{
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
if (_spaceEngInstallAlias == null)
{
// ReSharper disable once AssignNullToNotNullAttribute
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "SpaceEngineersAlias");
}
return _spaceEngInstallAlias;
}
}
private const string _steamSpaceEngineersDirectory = @"steamapps\common\SpaceEngineers\";
private const string _spaceEngineersVerifyFile = SpaceEngineersBinaries + @"\SpaceEngineers.exe";
public const string ConfigName = "Torch.cfg";
private static Logger _log = LogManager.GetLogger("Torch");
#if DEBUG
[DllImport("kernel32.dll")]
private static extern void AllocConsole();
[DllImport("kernel32.dll")]
private static extern void FreeConsole();
#endif
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
#if DEBUG
try
{
AllocConsole();
#endif
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// Early config: Resolve SE install directory.
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
SetupSpaceEngInstallAlias();
using (new TorchAssemblyResolver(Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries)))
{
RunClient();
}
#if DEBUG
}
finally
{
FreeConsole();
}
#endif
}
private static void SetupSpaceEngInstallAlias()
{
string spaceEngineersDirectory = null;
// TODO look at Steam/config/Config.VDF? Has alternate directories.
var steamDir =
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath", null) as string;
if (steamDir != null)
{
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
else
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
}
if (spaceEngineersDirectory == null)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog
{
Description = "Please select the SpaceEngineers installation folder"
};
do
{
if (dialog.ShowDialog() != DialogResult.OK)
{
var ex = new FileNotFoundException("Unable to find the Space Engineers install directory, aborting");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
spaceEngineersDirectory = dialog.SelectedPath;
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
break;
if (MessageBox.Show(
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
break;
} while (true); // Repeat until they confirm.
}
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
{
var ex = new IOException($"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
if (!File.Exists(junctionVerify))
{
var ex = new FileNotFoundException($"Junction link is not working. File {junctionVerify} does not exist");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
}
private static bool JunctionLink(string linkName, string targetDir)
{
var junctionLinkProc = new ProcessStartInfo("cmd.exe", $"/c mklink /J \"{linkName}\" \"{targetDir}\"")
{
WorkingDirectory = Directory.GetCurrentDirectory(),
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
};
Process cmd = Process.Start(junctionLinkProc);
// ReSharper disable once PossibleNullReferenceException
while (!cmd.HasExited)
{
string line = cmd.StandardOutput.ReadLine();
if (!string.IsNullOrWhiteSpace(line))
_log.Info(line);
Thread.Sleep(100);
}
if (cmd.ExitCode != 0)
_log.Error("Unable to create junction link {0} => {1}", linkName, targetDir);
return cmd.ExitCode == 0;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
_log.Error(ex);
LogManager.Flush();
MessageBox.Show(ex.StackTrace, ex.Message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void RunClient()
{
var client = new TorchClient();
try
@@ -27,11 +176,5 @@ namespace Torch.Client
client.Start();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
MessageBox.Show(ex.StackTrace, ex.Message);
}
}
}

View File

@@ -2,8 +2,6 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" 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>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
@@ -18,7 +16,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
@@ -27,7 +25,7 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
@@ -35,7 +33,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<DocumentationFile>bin\x64\Release\Torch.Client.xml</DocumentationFile>
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.Client.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>torchicon.ico</ApplicationIcon>
@@ -48,6 +46,7 @@
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Sandbox.Game">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
@@ -64,6 +63,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
@@ -81,6 +81,10 @@
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game.XmlSerializers">
<HintPath>..\GameBinaries\VRage.Game.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
@@ -104,7 +108,8 @@
<Private>False</Private>
</Reference>
<Reference Include="VRage.Steam">
<HintPath>..\..\..\..\..\..\..\steamcmd\steamapps\common\SpaceEngineersDedicatedServer\DedicatedServer64\VRage.Steam.dll</HintPath>
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
@@ -117,7 +122,9 @@
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="Properties\AssemblyInfo1.cs" />
<Compile Include="TorchAssemblyResolver.cs" />
<Compile Include="TorchClient.cs" />
<Compile Include="TorchClientConfig.cs" />
<Compile Include="TorchConsoleScreen.cs" />
<Compile Include="TorchMainMenuScreen.cs" />
<Compile Include="TorchSettingsScreen.cs" />
@@ -147,12 +154,12 @@
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
<Name>Torch.API</Name>
<Private>True</Private>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\Torch\Torch.csproj">
<Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project>
<Name>Torch</Name>
<Private>True</Private>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
@@ -169,7 +176,8 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using NLog;
namespace Torch.Client
{
public class TorchAssemblyResolver : IDisposable
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();
private readonly string[] _paths;
private readonly string _removablePathPrefix;
public TorchAssemblyResolver(params string[] paths)
{
string location = Assembly.GetEntryAssembly().Location;
location = location != null ? Path.GetDirectoryName(location) + Path.DirectorySeparatorChar : null;
_removablePathPrefix = location ?? "";
_paths = paths;
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
private string SimplifyPath(string path)
{
return path.StartsWith(_removablePathPrefix) ? path.Substring(_removablePathPrefix.Length) : path;
}
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyName = new AssemblyName(args.Name).Name;
lock (_assemblies)
{
if (_assemblies.TryGetValue(assemblyName, out Assembly asm))
return asm;
}
lock (AppDomain.CurrentDomain)
{
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
if (asm.GetName().Name.Equals(assemblyName))
{
lock (this)
{
_assemblies.Add(assemblyName, asm);
return asm;
}
}
}
lock (this)
{
foreach (string path in _paths)
{
try
{
string assemblyPath = Path.Combine(path, assemblyName + ".dll");
if (!File.Exists(assemblyPath))
continue;
_log.Debug("Loading {0} from {1}", assemblyName, SimplifyPath(assemblyPath));
LogManager.Flush();
Assembly asm = Assembly.LoadFrom(assemblyPath);
_assemblies.Add(assemblyName, asm);
// Recursively load SE dependencies since they don't trigger AssemblyResolve.
// This trades some performance on load for actually working code.
foreach (AssemblyName dependency in asm.GetReferencedAssemblies())
CurrentDomainOnAssemblyResolve(sender, new ResolveEventArgs(dependency.Name, asm));
return asm;
}
catch
{
// Ignored
}
}
}
return null;
}
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainOnAssemblyResolve;
_assemblies.Clear();
}
}
}

View File

@@ -1,20 +1,19 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Windows;
using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Platform;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.ModAPI;
using SpaceEngineers.Game;
using VRage.Steam;
using Torch.API;
using VRage;
using VRage.FileSystem;
using VRageRender;
using VRageRender.ExternalApp;
namespace Torch.Client
{
@@ -24,47 +23,49 @@ namespace Torch.Client
private IMyRender _renderer;
private const uint APP_ID = 244850;
public TorchClient()
{
Config = new TorchClientConfig();
}
public override void Init()
{
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
Log.Info("Initializing Torch Client");
base.Init();
if (!File.Exists("steam_appid.txt"))
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
}
SpaceEngineersGame.SetupBasicGameInfo();
_startup = new MyCommonProgramStartup(RunArgs);
if (_startup.PerformReporting())
return;
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
_startup.PerformAutoconnect();
if (!_startup.CheckSingleInstance())
return;
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
var appDataPath = _startup.GetAppDataPath();
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
MyInitializer.InitCheckSum();
_startup.InitSplashScreen();
if (!_startup.Check64Bit())
return;
throw new InvalidOperationException("Torch requires a 64bit operating system");
_startup.DetectSharpDxLeaksBeforeRun();
using (var mySteamService = new SteamService(Game.IsDedicated, APP_ID))
{
_renderer = null;
SpaceEngineersGame.SetupPerGameSettings();
var steamService = new SteamService(Game.IsDedicated, APP_ID);
MyServiceManager.Instance.AddService(steamService);
_renderer = null;
SpaceEngineersGame.SetupPerGameSettings();
// I'm sorry, but it's what Keen does in SpaceEngineers.MyProgram
#pragma warning disable 612
SpaceEngineersGame.SetupRender();
#pragma warning restore 612
InitializeRender();
if (!_startup.CheckSteamRunning())
throw new InvalidOperationException("Space Engineers requires steam to be running");
OverrideMenus();
InitializeRender();
if (!Game.IsDedicated)
MyFileSystem.InitUserSpecific(mySteamService.UserId.ToString());
}
_startup.DetectSharpDxLeaksAfterRun();
MyInitializer.InvokeAfterRun();
if (!Game.IsDedicated)
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
}
private void OverrideMenus()
@@ -81,19 +82,46 @@ namespace Torch.Client
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
}
public override void Start()
{
using (var spaceEngineersGame = new SpaceEngineersGame(RunArgs))
{
Log.Info("Starting client");
OverrideMenus();
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded;
spaceEngineersGame.Run();
spaceEngineersGame.OnGameExit += Dispose;
spaceEngineersGame.Run(false, _startup.DisposeSplashScreen);
}
}
private void SetRenderWindowTitle(string title)
{
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
if (renderThread == null)
return;
FieldInfo renderWindowField = typeof(MyRenderThread).GetField("m_renderWindow",
BindingFlags.Instance | BindingFlags.NonPublic);
if (renderWindowField == null)
return;
var window = renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as System.Windows.Forms.Form;
if (window != null)
renderThread.Invoke(() =>
{
window.Text = title;
});
}
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
{
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
}
public override void Dispose()
{
MyGameService.ShutDown();
_startup.DetectSharpDxLeaksAfterRun();
MyInitializer.InvokeAfterRun();
}
public override void Stop()

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Client
{
public class TorchClientConfig : ITorchConfig
{
// How do we want to handle client side config? It's radically different than the server.
public bool GetPluginUpdates { get; set; } = false;
public bool GetTorchUpdates { get; set; } = false;
public string InstanceName { get; set; } = "TorchClient";
public string InstancePath { get; set; }
public bool NoUpdate { get; set; } = true;
public List<string> Plugins { get; set; }
public bool ShouldUpdatePlugins { get; } = false;
public bool ShouldUpdateTorch { get; } = false;
public int TickTimeout { get; set; }
public bool Autostart { get; set; } = false;
public bool ForceUpdate { get; set; } = false;
public bool NoGui { get; set; } = false;
public bool RestartOnCrash { get; set; } = false;
public string WaitForPID { get; set; } = null;
public bool Save(string path = null)
{
return true;
}
}
}

View File

@@ -25,6 +25,8 @@ namespace Torch.Server.Managers
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
[Dependency]
private FilesystemManager _filesystemManager;
public InstanceManager(ITorchBase torchInstance) : base(torchInstance)
{
@@ -32,9 +34,9 @@ namespace Torch.Server.Managers
}
/// <inheritdoc />
public override void Init()
public override void Attach()
{
MyFileSystem.ExePath = Path.Combine(Torch.GetManager<FilesystemManager>().TorchDirectory, "DedicatedServer64");
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
MyFileSystem.Init("Content", Torch.Config.InstancePath);
//Initializes saves path. Why this isn't in Init() we may never know.
MyFileSystem.InitUserSpecific(null);
@@ -46,7 +48,7 @@ namespace Torch.Server.Managers
ValidateInstance(path);
MyFileSystem.Reset();
MyFileSystem.ExePath = Path.Combine(Torch.GetManager<FilesystemManager>().TorchDirectory, "DedicatedServer64");
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
MyFileSystem.Init("Content", path);
//Initializes saves path. Why this isn't in Init() we may never know.
MyFileSystem.InitUserSpecific(null);

View File

@@ -2,8 +2,6 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" 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>{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
@@ -18,7 +16,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
@@ -27,7 +25,7 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
@@ -35,7 +33,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<DocumentationFile>bin\x64\Release\Torch.Server.xml</DocumentationFile>
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.Server.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Torch.Server.Program</StartupObject>
@@ -124,6 +122,7 @@
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Dedicated, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
@@ -290,12 +289,12 @@
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
<Name>Torch.API</Name>
<Private>True</Private>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\Torch\Torch.csproj">
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
<Name>Torch</Name>
<Private>True</Private>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
@@ -367,9 +366,7 @@
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>cd "$(TargetDir)"
copy "$(SolutionDir)NLog.config" "$(TargetDir)"
"Torch Server Release.bat"</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -1,5 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using Torch.API.Managers;
using Torch.Server.Managers;
using Torch.Server.ViewModels;
@@ -15,7 +16,7 @@ namespace Torch.Server.Views
public ConfigControl()
{
InitializeComponent();
_instanceManager = TorchBase.Instance.GetManager<InstanceManager>();
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
DataContext = _instanceManager.DedicatedConfig;
}

View File

@@ -20,16 +20,18 @@ namespace Torch.Commands
public CommandTree Commands { get; set; } = new CommandTree();
private Logger _log = LogManager.GetLogger(nameof(CommandManager));
[Dependency]
private ChatManager _chatManager;
public CommandManager(ITorchBase torch, char prefix = '!') : base(torch)
{
Prefix = prefix;
}
public override void Init()
public override void Attach()
{
RegisterCommandModule(typeof(TorchCommands));
Torch.GetManager<ChatManager>().MessageRecieved += HandleCommand;
_chatManager.MessageRecieved += HandleCommand;
}
public bool HasPermission(ulong steamId, Command command)

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using System.Timers;
using Sandbox.ModAPI;
using Torch;
using Torch.API.Managers;
using Torch.Commands.Permissions;
using Torch.Managers;
using VRage.Game.ModAPI;
@@ -50,7 +51,7 @@ namespace Torch.Commands
[Command("longhelp", "Get verbose help. Will send a long message, check the Comms tab.")]
public void LongHelp()
{
var commandManager = Context.Torch.GetManager<CommandManager>();
var commandManager = Context.Torch.Managers.GetManager<CommandManager>();
commandManager.Commands.GetNode(Context.Args, out CommandTree.CommandNode node);
if (node != null)

View File

@@ -27,16 +27,19 @@ namespace Torch.Managers
internal void RaiseMessageRecieved(ChatMsg msg, ref bool sendToOthers) =>
MessageRecieved?.Invoke(msg, ref sendToOthers);
[Dependency]
private INetworkManager _networkManager;
public ChatManager(ITorchBase torchInstance) : base(torchInstance)
{
}
public override void Init()
public override void Attach()
{
try
{
Torch.GetManager<INetworkManager>().RegisterNetworkHandler(new ChatIntercept(this));
_networkManager.RegisterNetworkHandler(new ChatIntercept(this));
}
catch
{

View File

@@ -0,0 +1,309 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NLog;
using Torch.API.Managers;
namespace Torch.Managers
{
public sealed class DependencyManager : IDependencyManager
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private class DependencyInfo
{
private readonly Manager.DependencyAttribute _attribute;
internal Type DependencyType => Field.FieldType;
internal FieldInfo Field { get; private set; }
internal bool Optional => _attribute.Optional;
internal bool Ordered => _attribute.Ordered;
public DependencyInfo(FieldInfo field)
{
Field = field;
_attribute = field.GetCustomAttribute<Manager.DependencyAttribute>();
}
}
/// <summary>
/// Represents a registered instance of a manager.
/// </summary>
private class ManagerInstance
{
public IManager Instance { get; private set; }
internal readonly List<DependencyInfo> Dependencies;
internal readonly HashSet<Type> SuppliedManagers;
internal readonly HashSet<ManagerInstance> Dependents;
public ManagerInstance(IManager manager)
{
Instance = manager;
SuppliedManagers = new HashSet<Type>();
Dependencies = new List<DependencyInfo>();
Dependents = new HashSet<ManagerInstance>();
var openBases = new Queue<Type>();
openBases.Enqueue(manager.GetType());
while (openBases.TryDequeue(out Type type))
{
if (!SuppliedManagers.Add(type))
continue;
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
if (field.HasAttribute<Manager.DependencyAttribute>())
Dependencies.Add(new DependencyInfo(field));
foreach (Type parent in type.GetInterfaces())
openBases.Enqueue(parent);
if (type.BaseType != null)
openBases.Enqueue(type.BaseType);
}
}
/// <summary>
/// Used by <see cref="DependencyManager"/> internally to topologically sort the dependency list.
/// </summary>
public int UnsolvedDependencies { get; set; }
}
private readonly Dictionary<Type, ManagerInstance> _dependencySatisfaction;
private readonly List<ManagerInstance> _registeredManagers;
private readonly List<ManagerInstance> _orderedManagers;
private readonly IDependencyProvider[] _parentProviders;
public DependencyManager(params IDependencyProvider[] parents)
{
_dependencySatisfaction = new Dictionary<Type, ManagerInstance>();
_registeredManagers = new List<ManagerInstance>();
_orderedManagers = new List<ManagerInstance>();
_parentProviders = parents.Distinct().ToArray();
}
private void AddDependencySatisfaction(ManagerInstance instance)
{
foreach (Type supplied in instance.SuppliedManagers)
if (_dependencySatisfaction.ContainsKey(supplied))
// When we already have a manager supplying this component we have to unregister it.
_dependencySatisfaction[supplied] = null;
else
_dependencySatisfaction.Add(supplied, instance);
}
private void RebuildDependencySatisfaction()
{
_dependencySatisfaction.Clear();
foreach (ManagerInstance manager in _registeredManagers)
AddDependencySatisfaction(manager);
}
/// <inheritdoc/>
public bool AddManager(IManager manager)
{
if (_initialized)
throw new InvalidOperationException("Can't add new managers to an initialized dependency manager");
// Protect against adding a manager derived from an existing manager
if (_registeredManagers.Any(x => x.Instance.GetType().IsInstanceOfType(manager)))
return false;
// Protect against adding a manager when an existing manager derives from it.
// ReSharper disable once ConvertIfStatementToReturnStatement
if (_registeredManagers.Any(x => manager.GetType().IsInstanceOfType(x.Instance)))
return false;
ManagerInstance instance = new ManagerInstance(manager);
_registeredManagers.Add(instance);
AddDependencySatisfaction(instance);
return true;
}
/// <inheritdoc/>
public void ClearManagers()
{
if (_initialized)
throw new InvalidOperationException("Can't remove managers from an initialized dependency manager");
_registeredManagers.Clear();
_dependencySatisfaction.Clear();
}
/// <inheritdoc/>
public bool RemoveManager(IManager manager)
{
if (_initialized)
throw new InvalidOperationException("Can't remove managers from an initialized dependency manager");
if (manager == null)
return false;
for (int i = 0; i < _registeredManagers.Count; i++)
if (_registeredManagers[i].Instance == manager)
{
_registeredManagers.RemoveAtFast(i);
RebuildDependencySatisfaction();
return true;
}
return false;
}
private void Sort()
{
// Resets the previous sort results
#region Reset
_orderedManagers.Clear();
foreach (ManagerInstance manager in _registeredManagers)
manager.Dependents.Clear();
#endregion
// Creates the dependency graph
#region Prepare
var dagQueue = new List<ManagerInstance>();
foreach (ManagerInstance manager in _registeredManagers)
{
var inFactor = 0;
foreach (DependencyInfo dependency in manager.Dependencies)
{
if (_dependencySatisfaction.TryGetValue(dependency.DependencyType, out var dependencyInstance))
{
if (dependency.Ordered)
{
inFactor++;
dependencyInstance.Dependents.Add(manager);
}
}
else if (!dependency.Optional && _parentProviders.All(x => x.GetManager(dependency.DependencyType) == null))
_log.Warn("Unable to satisfy dependency {0} ({1}) of {2}.", dependency.DependencyType.Name,
dependency.Field.Name, manager.Instance.GetType().Name);
}
manager.UnsolvedDependencies = inFactor;
dagQueue.Add(manager);
}
#endregion
// Sorts the dependency graph into _orderedManagers
#region Sort
var tmpQueue = new List<ManagerInstance>();
while (dagQueue.Any())
{
tmpQueue.Clear();
for (var i = 0; i < dagQueue.Count; i++)
{
if (dagQueue[i].UnsolvedDependencies == 0)
tmpQueue.Add(dagQueue[i]);
else
dagQueue[i - tmpQueue.Count] = dagQueue[i];
}
dagQueue.RemoveRange(dagQueue.Count - tmpQueue.Count, tmpQueue.Count);
if (tmpQueue.Count == 0)
{
_log.Fatal("Dependency loop detected in the following managers:");
foreach (ManagerInstance manager in dagQueue)
{
_log.Fatal(" + {0} has {1} unsolved dependencies.", manager.Instance.GetType().FullName, manager.UnsolvedDependencies);
_log.Fatal(" - Dependencies: {0}",
string.Join(", ", manager.Dependencies.Select(x => x.DependencyType.Name + (x.Optional ? " (Optional)" : ""))));
_log.Fatal(" - Dependents: {0}",
string.Join(", ", manager.Dependents.Select(x => x.Instance.GetType().Name)));
}
throw new InvalidOperationException("Unable to satisfy all required manager dependencies");
}
// Update the number of unsolved dependencies
foreach (ManagerInstance manager in tmpQueue)
foreach (ManagerInstance dependent in manager.Dependents)
dependent.UnsolvedDependencies--;
// tmpQueue.Sort(); If we have priorities this is where to sort them.
_orderedManagers.AddRange(tmpQueue);
}
_log.Debug("Dependency tree satisfied. Load order is:");
foreach (ManagerInstance manager in _orderedManagers)
{
_log.Debug(" - {0}", manager.Instance.GetType().FullName);
_log.Debug(" - Dependencies: {0}",
string.Join(", ", manager.Dependencies.Select(x => x.DependencyType.Name + (x.Optional ? " (Optional)" : ""))));
_log.Debug(" - Dependents: {0}",
string.Join(", ", manager.Dependents.Select(x => x.Instance.GetType().Name)));
}
#endregion
// Updates the dependency fields with the correct manager instances
#region Satisfy
foreach (ManagerInstance manager in _registeredManagers)
{
manager.Dependents.Clear();
foreach (DependencyInfo dependency in manager.Dependencies)
dependency.Field.SetValue(manager.Instance, GetManager(dependency.DependencyType));
}
#endregion
}
private bool _initialized = false;
/// <summary>
/// Initializes the dependency manager, and all its registered managers.
/// </summary>
public void Attach()
{
if (_initialized)
throw new InvalidOperationException("Can't start the dependency manager more than once");
_initialized = true;
Sort();
foreach (ManagerInstance manager in _orderedManagers)
manager.Instance.Attach();
}
/// <summary>
/// Disposes the dependency manager, and all its registered managers.
/// </summary>
public void Detach()
{
if (!_initialized)
throw new InvalidOperationException("Can't dispose an uninitialized dependency manager");
for (int i = _orderedManagers.Count - 1; i >= 0; i--)
{
_orderedManagers[i].Instance.Detach();
foreach (DependencyInfo field in _orderedManagers[i].Dependencies)
field.Field.SetValue(_orderedManagers[i].Instance, null);
}
_initialized = false;
}
/// <inheritdoc/>
public IEnumerable<IManager> AttachOrder
{
get
{
if (!_initialized)
throw new InvalidOperationException("Can't determine dependency load order when uninitialized");
foreach (ManagerInstance k in _orderedManagers)
yield return k.Instance;
}
}
/// <inheritdoc/>
public IEnumerable<IManager> DetachOrder
{
get
{
if (!_initialized)
throw new InvalidOperationException("Can't determine dependency load order when uninitialized");
for (int i = _orderedManagers.Count - 1; i >= 0; i--)
yield return _orderedManagers[i].Instance;
}
}
/// <inheritdoc/>
public IManager GetManager(Type type)
{
// ReSharper disable once ConvertIfStatementToReturnStatement
if (_dependencySatisfaction.TryGetValue(type, out ManagerInstance mgr))
return mgr.Instance;
foreach (IDependencyProvider provider in _parentProviders)
{
IManager entry = provider.GetManager(type);
if (entry != null)
return entry;
}
return null;
}
}
}

View File

@@ -16,6 +16,46 @@ namespace Torch.Managers
public abstract class Manager : IManager
{
/// <summary>
/// Indicates a field is a dependency of this parent manager.
/// </summary>
/// <example>
/// <code>
/// public class NetworkManager : Manager { }
/// public class ChatManager : Manager {
/// [Dependency(Optional = false)]
/// private NetworkManager _network;
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Field)]
public class DependencyAttribute : Attribute
{
/// <summary>
/// If this dependency isn't required.
/// </summary>
/// <remarks>
/// The tagged field can be null if, and only if, this is true.
/// </remarks>
public bool Optional { get; set; } = false;
/// <summary>
/// Dependency must be loaded before and unloaded after the containing manager.
/// </summary>
/// <example>
/// <code>
/// public class NetworkManager : Manager { }
/// public class ChatManager : Manager {
/// [Dependency(Ordered = true)]
/// private NetworkManager _network;
/// }
/// </code>
/// Load order will be NetworkManager, then ChatManager.
/// Unload order will be ChatManager, then NetworkManager
/// </example>
public bool Ordered { get; set; } = true;
}
protected ITorchBase Torch { get; }
protected Manager(ITorchBase torchInstance)
@@ -23,7 +63,12 @@ namespace Torch.Managers
Torch = torchInstance;
}
public virtual void Init()
public virtual void Attach()
{
}
public virtual void Detach()
{
}

View File

@@ -52,16 +52,23 @@ namespace Torch.Managers
private static readonly Logger ChatLog = LogManager.GetLogger("Chat");
private Dictionary<MyPlayer.PlayerId, MyPlayer> _onlinePlayers;
[Dependency]
private ChatManager _chatManager;
[Dependency]
private CommandManager _commandManager;
[Dependency]
private NetworkManager _networkManager;
internal MultiplayerManager(ITorchBase torch) : base(torch)
{
}
/// <inheritdoc />
public override void Init()
public override void Attach()
{
Torch.SessionLoaded += OnSessionLoaded;
Torch.GetManager<ChatManager>().MessageRecieved += Instance_MessageRecieved;
_chatManager.MessageRecieved += Instance_MessageRecieved;
}
private void Instance_MessageRecieved(ChatMsg msg, ref bool sendToOthers)
@@ -125,10 +132,9 @@ namespace Torch.Managers
return;
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message));
var commands = Torch.GetManager<CommandManager>();
if (commands.IsCommand(message))
if (_commandManager.IsCommand(message))
{
var response = commands.HandleCommandFromServer(message);
var response = _commandManager.HandleCommandFromServer(message);
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, response));
}
else
@@ -141,7 +147,7 @@ namespace Torch.Managers
return;
var addToGlobalHistoryMethod = typeof(MyCharacter).GetMethod("OnGlobalMessageSuccess", BindingFlags.Instance | BindingFlags.NonPublic);
Torch.GetManager<NetworkManager>().RaiseEvent(addToGlobalHistoryMethod, character, steamId, steamId, message);
_networkManager.RaiseEvent(addToGlobalHistoryMethod, character, steamId, steamId, message);
}
}

View File

@@ -63,7 +63,7 @@ namespace Torch.Managers
/// <summary>
/// Loads the network intercept system
/// </summary>
public override void Init()
public override void Attach()
{
Torch.SessionLoaded += OnSessionLoaded;
}

View File

@@ -20,7 +20,10 @@ namespace Torch.Managers
{
private static Logger _log = LogManager.GetLogger(nameof(PluginManager));
public readonly string PluginDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
[Dependency]
private UpdateManager _updateManager;
[Dependency]
private CommandManager _commandManager;
/// <inheritdoc />
public IList<ITorchPlugin> Plugins { get; } = new ObservableList<ITorchPlugin>();
@@ -45,7 +48,7 @@ namespace Torch.Managers
/// <summary>
/// Unloads all plugins.
/// </summary>
public void DisposePlugins()
public override void Detach()
{
foreach (var plugin in Plugins)
plugin.Dispose();
@@ -87,9 +90,6 @@ namespace Torch.Managers
/// <inheritdoc />
public void LoadPlugins()
{
_updateManager = Torch.GetManager<UpdateManager>();
var commands = Torch.GetManager<CommandManager>();
if (Torch.Config.ShouldUpdatePlugins)
DownloadPlugins();
else
@@ -119,7 +119,7 @@ namespace Torch.Managers
plugin.StoragePath = Torch.Config.InstancePath;
Plugins.Add(plugin);
commands.RegisterPluginCommands(plugin);
_commandManager.RegisterPluginCommands(plugin);
}
catch (Exception e)
{

View File

@@ -16,7 +16,7 @@ namespace Torch.Managers
{
private MyScriptWhitelist _whitelist;
public void Init()
public void Attach()
{
_whitelist = MyScriptCompiler.Static.Whitelist;
MyScriptCompiler.Static.AddConditionalCompilationSymbols("TORCH");
@@ -41,6 +41,11 @@ namespace Torch.Managers
Log.Info(whitelist);*/
}
public void Detach()
{
// TODO unregister whitelist patches
}
public void UnwhitelistType(Type t)
{
throw new NotImplementedException();

View File

@@ -19,12 +19,13 @@ namespace Torch.Managers
/// <summary>
/// Handles updating of the DS and Torch plugins.
/// </summary>
public class UpdateManager : Manager, IDisposable
public class UpdateManager : Manager
{
private Timer _updatePollTimer;
private GitHubClient _gitClient = new GitHubClient(new ProductHeaderValue("Torch"));
private string _torchDir = new FileInfo(typeof(UpdateManager).Assembly.Location).DirectoryName;
private Logger _log = LogManager.GetLogger(nameof(UpdateManager));
[Dependency]
private FilesystemManager _fsManager;
public UpdateManager(ITorchBase torchInstance) : base(torchInstance)
@@ -33,9 +34,8 @@ namespace Torch.Managers
}
/// <inheritdoc />
public override void Init()
public override void Attach()
{
_fsManager = Torch.GetManager<FilesystemManager>();
CheckAndUpdateTorch();
}
@@ -146,7 +146,7 @@ namespace Torch.Managers
}
/// <inheritdoc />
public void Dispose()
public override void Detach()
{
_updatePollTimer?.Dispose();
}

View File

@@ -2,8 +2,6 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" 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>{7E01635C-3B67-472E-BCD6-C5539564F214}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
@@ -15,7 +13,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
@@ -23,14 +21,14 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\x64\Release\Torch.xml</DocumentationFile>
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
@@ -143,7 +141,8 @@
<Private>False</Private>
</Reference>
<Reference Include="VRage.Steam">
<HintPath>..\..\..\..\..\..\..\steamcmd\steamapps\common\SpaceEngineersDedicatedServer\DedicatedServer64\VRage.Steam.dll</HintPath>
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
@@ -151,6 +150,7 @@
<Compile Include="ChatMessage.cs" />
<Compile Include="Collections\ObservableList.cs" />
<Compile Include="DispatcherExtensions.cs" />
<Compile Include="Managers\DependencyManager.cs" />
<Compile Include="SaveGameStatus.cs" />
<Compile Include="Collections\KeyTree.cs" />
<Compile Include="Collections\ObservableDictionary.cs" />
@@ -181,6 +181,7 @@
<Compile Include="TorchBase.cs" />
<Compile Include="SteamService.cs" />
<Compile Include="TorchPluginBase.cs" />
<Compile Include="TorchSession.cs" />
<Compile Include="ViewModels\ModViewModel.cs" />
<Compile Include="Collections\MTObservableCollection.cs" />
<Compile Include="Extensions\MyPlayerCollectionExtensions.cs" />
@@ -199,7 +200,7 @@
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
<Name>Torch.API</Name>
<Private>True</Private>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>

View File

@@ -72,7 +72,10 @@ namespace Torch
/// Common log for the Torch instance.
/// </summary>
protected static Logger Log { get; } = LogManager.GetLogger("Torch");
private readonly List<IManager> _managers;
/// <inheritdoc/>
public IDependencyManager Managers { get; }
private bool _init;
/// <summary>
@@ -89,38 +92,37 @@ namespace Torch
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
RunArgs = new string[0];
Managers = new DependencyManager();
Plugins = new PluginManager(this);
Multiplayer = new MultiplayerManager(this);
Entities = new EntityManager(this);
Network = new NetworkManager(this);
Commands = new CommandManager(this);
_managers = new List<IManager> { new FilesystemManager(this), new UpdateManager(this), Network, Commands, Plugins, Multiplayer, Entities, new ChatManager(this), };
Managers.AddManager(new FilesystemManager(this));
Managers.AddManager(new UpdateManager(this));
Managers.AddManager(Network);
Managers.AddManager(Commands);
Managers.AddManager(Plugins);
Managers.AddManager(Multiplayer);
Managers.AddManager(Entities);
Managers.AddManager(new ChatManager(this));
TorchAPI.Instance = this;
}
/// <inheritdoc />
public ListReader<IManager> GetManagers()
{
return new ListReader<IManager>(_managers);
}
/// <inheritdoc />
public T GetManager<T>() where T : class, IManager
{
return _managers.FirstOrDefault(m => m is T) as T;
return Managers.GetManager<T>();
}
/// <inheritdoc />
public bool AddManager<T>(T manager) where T : class, IManager
{
if (_managers.Any(x => x is T))
return false;
_managers.Add(manager);
return true;
return Managers.AddManager(manager);
}
public bool IsOnGameThread()
@@ -248,8 +250,7 @@ namespace Torch
MySession.OnUnloading += OnSessionUnloading;
MySession.OnUnloaded += OnSessionUnloaded;
RegisterVRagePlugin();
foreach (var manager in _managers)
manager.Init();
Managers.Attach();
_init = true;
}
@@ -317,7 +318,7 @@ namespace Torch
/// <inheritdoc />
public virtual void Dispose()
{
Plugins.DisposePlugins();
Managers.Detach();
}
/// <inheritdoc />