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"/> /// <inheritdoc cref="IPluginManager"/>
IPluginManager Plugins { get; } IPluginManager Plugins { get; }
/// <inheritdoc cref="IDependencyManager"/>
IDependencyManager Managers { get; }
/// <summary> /// <summary>
/// The binary version of the current instance. /// The binary version of the current instance.
/// </summary> /// </summary>
@@ -90,12 +93,6 @@ namespace Torch.API
/// Initialize the Torch instance. /// Initialize the Torch instance.
/// </summary> /// </summary>
void Init(); 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> /// <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 public interface IManager
{ {
/// <summary> /// <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> /// </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> /// </summary>
void UpdatePlugins(); void UpdatePlugins();
/// <summary>
/// Disposes all loaded plugins.
/// </summary>
void DisposePlugins();
/// <summary> /// <summary>
/// Load plugins. /// Load plugins.
/// </summary> /// </summary>

View File

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

View File

@@ -1,17 +1,166 @@
using System; 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;
using System.Windows.Forms;
using NLog; using NLog;
using MessageBox = System.Windows.MessageBox;
namespace Torch.Client namespace Torch.Client
{ {
public static class Program 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"); 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) 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(); var client = new TorchClient();
try try
@@ -27,11 +176,5 @@ namespace Torch.Client
client.Start(); 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"> <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')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid> <ProjectGuid>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
@@ -18,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath> <OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
@@ -27,7 +25,7 @@
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath> <OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@@ -35,7 +33,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<DocumentationFile>bin\x64\Release\Torch.Client.xml</DocumentationFile> <DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.Client.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>torchicon.ico</ApplicationIcon> <ApplicationIcon>torchicon.ico</ApplicationIcon>
@@ -48,6 +46,7 @@
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="Sandbox.Game"> <Reference Include="Sandbox.Game">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
@@ -64,6 +63,7 @@
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@@ -81,6 +81,10 @@
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath> <HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </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"> <Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath> <HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
@@ -104,7 +108,8 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Steam"> <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>
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
@@ -117,7 +122,9 @@
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
</Compile> </Compile>
<Compile Include="Properties\AssemblyInfo1.cs" /> <Compile Include="Properties\AssemblyInfo1.cs" />
<Compile Include="TorchAssemblyResolver.cs" />
<Compile Include="TorchClient.cs" /> <Compile Include="TorchClient.cs" />
<Compile Include="TorchClientConfig.cs" />
<Compile Include="TorchConsoleScreen.cs" /> <Compile Include="TorchConsoleScreen.cs" />
<Compile Include="TorchMainMenuScreen.cs" /> <Compile Include="TorchMainMenuScreen.cs" />
<Compile Include="TorchSettingsScreen.cs" /> <Compile Include="TorchSettingsScreen.cs" />
@@ -147,12 +154,12 @@
<ProjectReference Include="..\Torch.API\Torch.API.csproj"> <ProjectReference Include="..\Torch.API\Torch.API.csproj">
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project> <Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
<Name>Torch.API</Name> <Name>Torch.API</Name>
<Private>True</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Torch\Torch.csproj"> <ProjectReference Include="..\Torch\Torch.csproj">
<Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project> <Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project>
<Name>Torch</Name> <Name>Torch</Name>
<Private>True</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -169,7 +176,8 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent> <PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Sandbox; using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Platform; using Sandbox.Engine.Platform;
using Sandbox.Engine.Utils;
using Sandbox.Game; using Sandbox.Game;
using Sandbox.ModAPI;
using SpaceEngineers.Game; using SpaceEngineers.Game;
using VRage.Steam; using VRage.Steam;
using Torch.API; using Torch.API;
using VRage;
using VRage.FileSystem; using VRage.FileSystem;
using VRageRender; using VRageRender;
using VRageRender.ExternalApp;
namespace Torch.Client namespace Torch.Client
{ {
@@ -24,47 +23,49 @@ namespace Torch.Client
private IMyRender _renderer; private IMyRender _renderer;
private const uint APP_ID = 244850; private const uint APP_ID = 244850;
public TorchClient()
{
Config = new TorchClientConfig();
}
public override void Init() public override void Init()
{ {
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
Log.Info("Initializing Torch Client"); Log.Info("Initializing Torch Client");
base.Init(); base.Init();
if (!File.Exists("steam_appid.txt"))
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
}
SpaceEngineersGame.SetupBasicGameInfo(); SpaceEngineersGame.SetupBasicGameInfo();
_startup = new MyCommonProgramStartup(RunArgs); _startup = new MyCommonProgramStartup(RunArgs);
if (_startup.PerformReporting()) if (_startup.PerformReporting())
return; throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
_startup.PerformAutoconnect(); _startup.PerformAutoconnect();
if (!_startup.CheckSingleInstance()) if (!_startup.CheckSingleInstance())
return; throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
var appDataPath = _startup.GetAppDataPath(); var appDataPath = _startup.GetAppDataPath();
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath); MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
MyInitializer.InitCheckSum(); MyInitializer.InitCheckSum();
_startup.InitSplashScreen();
if (!_startup.Check64Bit()) if (!_startup.Check64Bit())
return; throw new InvalidOperationException("Torch requires a 64bit operating system");
_startup.DetectSharpDxLeaksBeforeRun(); _startup.DetectSharpDxLeaksBeforeRun();
using (var mySteamService = new SteamService(Game.IsDedicated, APP_ID)) var steamService = new SteamService(Game.IsDedicated, APP_ID);
{ MyServiceManager.Instance.AddService(steamService);
_renderer = null; _renderer = null;
SpaceEngineersGame.SetupPerGameSettings(); 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(); if (!Game.IsDedicated)
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
InitializeRender();
if (!Game.IsDedicated)
MyFileSystem.InitUserSpecific(mySteamService.UserId.ToString());
}
_startup.DetectSharpDxLeaksAfterRun();
MyInitializer.InvokeAfterRun();
} }
private void OverrideMenus() private void OverrideMenus()
@@ -87,13 +88,40 @@ namespace Torch.Client
using (var spaceEngineersGame = new SpaceEngineersGame(RunArgs)) using (var spaceEngineersGame = new SpaceEngineersGame(RunArgs))
{ {
Log.Info("Starting client"); Log.Info("Starting client");
OverrideMenus();
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded; 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) 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() 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"; private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
public ConfigDedicatedViewModel DedicatedConfig { get; set; } public ConfigDedicatedViewModel DedicatedConfig { get; set; }
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager)); private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
[Dependency]
private FilesystemManager _filesystemManager;
public InstanceManager(ITorchBase torchInstance) : base(torchInstance) public InstanceManager(ITorchBase torchInstance) : base(torchInstance)
{ {
@@ -32,9 +34,9 @@ namespace Torch.Server.Managers
} }
/// <inheritdoc /> /// <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); MyFileSystem.Init("Content", Torch.Config.InstancePath);
//Initializes saves path. Why this isn't in Init() we may never know. //Initializes saves path. Why this isn't in Init() we may never know.
MyFileSystem.InitUserSpecific(null); MyFileSystem.InitUserSpecific(null);
@@ -46,7 +48,7 @@ namespace Torch.Server.Managers
ValidateInstance(path); ValidateInstance(path);
MyFileSystem.Reset(); MyFileSystem.Reset();
MyFileSystem.ExePath = Path.Combine(Torch.GetManager<FilesystemManager>().TorchDirectory, "DedicatedServer64"); MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
MyFileSystem.Init("Content", path); MyFileSystem.Init("Content", path);
//Initializes saves path. Why this isn't in Init() we may never know. //Initializes saves path. Why this isn't in Init() we may never know.
MyFileSystem.InitUserSpecific(null); MyFileSystem.InitUserSpecific(null);

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,16 +27,19 @@ namespace Torch.Managers
internal void RaiseMessageRecieved(ChatMsg msg, ref bool sendToOthers) => internal void RaiseMessageRecieved(ChatMsg msg, ref bool sendToOthers) =>
MessageRecieved?.Invoke(msg, ref sendToOthers); MessageRecieved?.Invoke(msg, ref sendToOthers);
[Dependency]
private INetworkManager _networkManager;
public ChatManager(ITorchBase torchInstance) : base(torchInstance) public ChatManager(ITorchBase torchInstance) : base(torchInstance)
{ {
} }
public override void Init() public override void Attach()
{ {
try try
{ {
Torch.GetManager<INetworkManager>().RegisterNetworkHandler(new ChatIntercept(this)); _networkManager.RegisterNetworkHandler(new ChatIntercept(this));
} }
catch 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 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 ITorchBase Torch { get; }
protected Manager(ITorchBase torchInstance) protected Manager(ITorchBase torchInstance)
@@ -23,7 +63,12 @@ namespace Torch.Managers
Torch = torchInstance; 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 static readonly Logger ChatLog = LogManager.GetLogger("Chat");
private Dictionary<MyPlayer.PlayerId, MyPlayer> _onlinePlayers; private Dictionary<MyPlayer.PlayerId, MyPlayer> _onlinePlayers;
[Dependency]
private ChatManager _chatManager;
[Dependency]
private CommandManager _commandManager;
[Dependency]
private NetworkManager _networkManager;
internal MultiplayerManager(ITorchBase torch) : base(torch) internal MultiplayerManager(ITorchBase torch) : base(torch)
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Init() public override void Attach()
{ {
Torch.SessionLoaded += OnSessionLoaded; Torch.SessionLoaded += OnSessionLoaded;
Torch.GetManager<ChatManager>().MessageRecieved += Instance_MessageRecieved; _chatManager.MessageRecieved += Instance_MessageRecieved;
} }
private void Instance_MessageRecieved(ChatMsg msg, ref bool sendToOthers) private void Instance_MessageRecieved(ChatMsg msg, ref bool sendToOthers)
@@ -125,10 +132,9 @@ namespace Torch.Managers
return; return;
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message)); ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, message));
var commands = Torch.GetManager<CommandManager>(); if (_commandManager.IsCommand(message))
if (commands.IsCommand(message))
{ {
var response = commands.HandleCommandFromServer(message); var response = _commandManager.HandleCommandFromServer(message);
ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, response)); ChatHistory.Add(new ChatMessage(DateTime.Now, 0, author, response));
} }
else else
@@ -141,7 +147,7 @@ namespace Torch.Managers
return; return;
var addToGlobalHistoryMethod = typeof(MyCharacter).GetMethod("OnGlobalMessageSuccess", BindingFlags.Instance | BindingFlags.NonPublic); 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> /// <summary>
/// Loads the network intercept system /// Loads the network intercept system
/// </summary> /// </summary>
public override void Init() public override void Attach()
{ {
Torch.SessionLoaded += OnSessionLoaded; Torch.SessionLoaded += OnSessionLoaded;
} }

View File

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

View File

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

View File

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

View File

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

View File

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