Early initialization for Torch Client.

- Assembly resolution
- SE installation directory locating
Dependency manager with automatic sorting and resolution
- Drop in replacement for the system currently in TorchBase
Shared binary directory for all Torch projects
This commit is contained in:
Westin Miller
2017-08-17 17:32:08 -07:00
parent c6a6363163
commit dbd98a09c5
11 changed files with 613 additions and 84 deletions

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">

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 static readonly string[] _steamInstallDirectories = new[] {
@"C:\Program Files\Steam\", @"C:\Program Files (x86)\Steam\"
};
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;
foreach (string steamDir in _steamInstallDirectories)
{
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
{
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
break;
}
_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()
@@ -81,19 +82,46 @@ namespace Torch.Client
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen); MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
} }
public override void Start() public override void Start()
{ {
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

@@ -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

@@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NLog;
using Torch.API.Managers;
namespace Torch.Managers
{
public sealed class DependencyManager
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private class DependencyInfo
{
internal Type DependencyType => Field.FieldType;
internal FieldInfo Field { get; private set; }
internal bool Optional { get; private set; }
public DependencyInfo(FieldInfo field)
{
Field = field;
Optional = field.GetCustomAttribute<Manager.DependencyAttribute>().Optional;
}
}
/// <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;
public DependencyManager()
{
_dependencySatisfaction = new Dictionary<Type, ManagerInstance>();
_registeredManagers = new List<ManagerInstance>();
_orderedManagers = new List<ManagerInstance>();
}
/// <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>
/// <returns>true if added, false if not</returns>
public bool AddManager(IManager 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.
if (_registeredManagers.Any(x => manager.GetType().IsInstanceOfType(x.Instance)))
return false;
var instance = new ManagerInstance(manager);
_registeredManagers.Add(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);
return true;
}
private void Sort()
{
// Resets the previous sort results
#region Reset
_orderedManagers.Clear();
foreach (ManagerInstance manager in _registeredManagers)
{
manager.Dependents.Clear();
foreach (DependencyInfo dependency in manager.Dependencies)
dependency.Field.SetValue(manager.Instance, null);
}
#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))
{
inFactor++;
dependencyInstance.Dependents.Add(manager);
}
else if (!dependency.Optional)
_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);
}
#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, _dependencySatisfaction.GetValueOrDefault(dependency.DependencyType));
}
#endregion
}
private bool _initiated = false;
/// <summary>
/// Initializes the dependency manager, and all its registered managers.
/// </summary>
public void Init()
{
if (_initiated)
throw new InvalidOperationException("Can't start the dependency manager more than once");
_initiated = true;
Sort();
foreach (ManagerInstance manager in _orderedManagers)
manager.Instance.Init();
}
/// <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 T GetManager<T>() where T : class, IManager
{
return (T)_dependencySatisfaction.GetValueOrDefault(typeof(T))?.Instance;
}
}
}

View File

@@ -16,6 +16,24 @@ 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
{
public bool Optional { get; set; } = false;
}
protected ITorchBase Torch { get; } protected ITorchBase Torch { get; }
protected Manager(ITorchBase torchInstance) protected Manager(ITorchBase torchInstance)

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" />
@@ -199,7 +199,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,9 @@ 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;
private readonly DependencyManager _managers;
private bool _init; private bool _init;
/// <summary> /// <summary>
@@ -89,38 +91,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 +249,7 @@ namespace Torch
MySession.OnUnloading += OnSessionUnloading; MySession.OnUnloading += OnSessionUnloading;
MySession.OnUnloaded += OnSessionUnloaded; MySession.OnUnloaded += OnSessionUnloaded;
RegisterVRagePlugin(); RegisterVRagePlugin();
foreach (var manager in _managers) _managers.Init();
manager.Init();
_init = true; _init = true;
} }