Compare commits
65 Commits
1.1.229.26
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bd9d549607 | ||
![]() |
3d6806b63a | ||
![]() |
22e3019610 | ||
![]() |
7937cbd122 | ||
![]() |
2bef34ee5b | ||
![]() |
efe7236d31 | ||
![]() |
67dba9c820 | ||
![]() |
52c509aba0 | ||
![]() |
437c7d293e | ||
![]() |
2b6ce4f25b | ||
![]() |
599a98bceb | ||
![]() |
2cd1b8bd4e | ||
![]() |
c0be9c25da | ||
![]() |
5cea66374f | ||
![]() |
ee1c270c68 | ||
![]() |
4ab08e2faf | ||
![]() |
dd094edb88 | ||
![]() |
56e45236d8 | ||
![]() |
be9a8c5839 | ||
![]() |
8c11baf3b9 | ||
![]() |
6ce679bd83 | ||
![]() |
a4b1b9bb96 | ||
![]() |
91ad78e6a2 | ||
![]() |
4a68d66ab0 | ||
![]() |
4cb50b556f | ||
![]() |
b5f73a99cc | ||
![]() |
e9476a59e8 | ||
![]() |
f48f23c2eb | ||
![]() |
ddf465d8c9 | ||
![]() |
7149287b8e | ||
![]() |
cc709c6bb3 | ||
![]() |
8d101c4c11 | ||
![]() |
64eef6cd8e | ||
![]() |
f8ae3c0dd1 | ||
![]() |
589205edc3 | ||
![]() |
48b212faaf | ||
![]() |
0554dbc608 | ||
![]() |
3564eb805c | ||
![]() |
62a8064edd | ||
![]() |
55ed45190b | ||
![]() |
a6ae96093f | ||
![]() |
5b1afe6d50 | ||
![]() |
60df71a74c | ||
![]() |
a6d5da861f | ||
![]() |
afc10911f7 | ||
![]() |
0686e95c72 | ||
![]() |
234754fd49 | ||
![]() |
efb8d0f226 | ||
![]() |
3f881f7d67 | ||
![]() |
64d38abc99 | ||
![]() |
4a39362702 | ||
![]() |
db2d3794ae | ||
![]() |
526ff6fff0 | ||
![]() |
eebc0e428e | ||
![]() |
80aca514b2 | ||
![]() |
3e8068e82d | ||
![]() |
601fbcd176 | ||
![]() |
40eab15d69 | ||
![]() |
ceb272c0b4 | ||
![]() |
80d4f62694 | ||
![]() |
42f58a8649 | ||
![]() |
6b9af71967 | ||
![]() |
dbd98a09c5 | ||
![]() |
c6a6363163 | ||
![]() |
0a38eb770d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -252,3 +252,6 @@ ModelManifest.xml
|
|||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
GameBinaries
|
GameBinaries
|
||||||
|
|
||||||
|
# Generated Files
|
||||||
|
**Gen.cs
|
||||||
|
22
Jenkins/jenkins-grab-se.ps1
Normal file
22
Jenkins/jenkins-grab-se.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
pushd
|
||||||
|
|
||||||
|
$steamData = "C:/Steam/Data/"
|
||||||
|
$steamCMDPath = "C:/Steam/steamcmd/"
|
||||||
|
$steamCMDZip = "C:/Steam/steamcmd.zip"
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
|
||||||
|
if (!(Test-Path $steamData)) {
|
||||||
|
mkdir "$steamData"
|
||||||
|
}
|
||||||
|
if (!(Test-Path $steamCMDPath)) {
|
||||||
|
if (!(Test-Path $steamCMDZip)) {
|
||||||
|
(New-Object System.Net.WebClient).DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", "$steamCMDZip");
|
||||||
|
}
|
||||||
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($steamCMDZip, $steamCMDPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "$steamData"
|
||||||
|
& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740" "+quit"
|
||||||
|
|
||||||
|
popd
|
52
Jenkins/release.ps1
Normal file
52
Jenkins/release.ps1
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
param([string] $ApiBase, [string]$tagName, [string]$authinfo, [string[]] $assetPaths)
|
||||||
|
Add-Type -AssemblyName "System.Web"
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
Authorization = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authinfo))
|
||||||
|
Accept = "application/vnd.github.v3+json"
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Write-Output("Checking if release with tag " + $tagName + " already exists...")
|
||||||
|
$release = Invoke-RestMethod -Uri ($ApiBase+"releases/tags/$tagName") -Method "GET" -Headers $headers
|
||||||
|
Write-Output(" Using existing release " + $release.id + " at " + $release.html_url)
|
||||||
|
} catch {
|
||||||
|
Write-Output(" Doesn't exist")
|
||||||
|
$rel_arg = @{
|
||||||
|
tag_name=$tagName
|
||||||
|
name="Generated $tagName"
|
||||||
|
body=""
|
||||||
|
draft=$FALSE
|
||||||
|
prerelease=$tagName.Contains("alpha") -or $tagName.Contains("beta")
|
||||||
|
}
|
||||||
|
Write-Output("Creating new release " + $tagName + "...")
|
||||||
|
$release = Invoke-RestMethod -Uri ($ApiBase+"releases") -Method "POST" -Headers $headers -Body (ConvertTo-Json($rel_arg))
|
||||||
|
Write-Output(" Created new release " + $tagName + " at " + $release.html_url)
|
||||||
|
}
|
||||||
|
|
||||||
|
$assetsApiBase = $release.assets_url
|
||||||
|
Write-Output("Checking for existing assets...")
|
||||||
|
$existingAssets = Invoke-RestMethod -Uri ($assetsApiBase) -Method "GET" -Headers $headers
|
||||||
|
$assetLabels = ($assetPaths | ForEach-Object {[System.IO.Path]::GetFileName($_)})
|
||||||
|
foreach ($asset in $existingAssets) {
|
||||||
|
if ($assetLabels -contains $asset.name) {
|
||||||
|
$uri = $asset.url
|
||||||
|
Write-Output(" Deleting old asset " + $asset.name + " (id " + $asset.id + "); URI=" + $uri)
|
||||||
|
$result = Invoke-RestMethod -Uri $uri -Method "DELETE" -Headers $headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Output("Uploading assets...")
|
||||||
|
$uploadUrl = $release.upload_url.Substring(0, $release.upload_url.LastIndexOf('{'))
|
||||||
|
foreach ($asset in $assetPaths) {
|
||||||
|
$assetName = [System.IO.Path]::GetFileName($asset)
|
||||||
|
$assetType = [System.Web.MimeMapping]::GetMimeMapping($asset)
|
||||||
|
$assetData = [System.IO.File]::ReadAllBytes($asset)
|
||||||
|
$headerExtra = $headers + @{
|
||||||
|
"Content-Type" = $assetType
|
||||||
|
Name = $assetName
|
||||||
|
}
|
||||||
|
$uri = $uploadUrl + "?name=" + $assetName
|
||||||
|
Write-Output(" Uploading " + $asset + " as " + $assetType + "; URI=" + $uri)
|
||||||
|
$result = Invoke-RestMethod -Uri $uri -Method "POST" -Headers $headerExtra -Body $assetData
|
||||||
|
Write-Output(" ID=" + $result.id + ", found at=" + $result.browser_download_url)
|
||||||
|
}
|
67
Jenkinsfile
vendored
Normal file
67
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
node {
|
||||||
|
stage('Checkout') {
|
||||||
|
checkout scm
|
||||||
|
bat 'git pull --tags'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Acquire SE') {
|
||||||
|
bat 'powershell -File Jenkins/jenkins-grab-se.ps1'
|
||||||
|
bat 'IF EXIST GameBinaries RMDIR GameBinaries'
|
||||||
|
bat 'mklink /J GameBinaries "C:/Steam/Data/DedicatedServer64/"'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Acquire NuGet Packages') {
|
||||||
|
bat 'nuget restore Torch.sln'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
currentBuild.description = bat(returnStdout: true, script: '@powershell -File Versioning/version.ps1').trim()
|
||||||
|
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=Release /p:Platform=x64"
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Test') {
|
||||||
|
bat 'IF NOT EXIST reports MKDIR reports'
|
||||||
|
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/Release/Torch.Tests.dll\" \"bin-test/x64/Release/Torch.Server.Tests.dll\" \"bin-test/x64/Release/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
|
||||||
|
step([
|
||||||
|
$class: 'XUnitBuilder',
|
||||||
|
thresholdMode: 1,
|
||||||
|
thresholds: [[$class: 'FailedThreshold', failureThreshold: '1']],
|
||||||
|
tools: [[
|
||||||
|
$class: 'XUnitDotNetTestType',
|
||||||
|
deleteOutputFiles: true,
|
||||||
|
failIfNotNew: true,
|
||||||
|
pattern: 'reports/*.xml',
|
||||||
|
skipNoTestFiles: false,
|
||||||
|
stopProcessingIfError: true
|
||||||
|
]]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Archive') {
|
||||||
|
bat '''IF EXIST bin\\torch-server.zip DEL bin\\torch-server.zip
|
||||||
|
IF EXIST bin\\package-server RMDIR /S /Q bin\\package-server
|
||||||
|
xcopy bin\\x64\\Release bin\\package-server\\
|
||||||
|
del bin\\package-server\\Torch.Client*'''
|
||||||
|
bat "powershell -Command \"Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\\\"\$PWD\\bin\\package-server\\\", \\\"\$PWD\\bin\\torch-server.zip\\\")\""
|
||||||
|
archiveArtifacts artifacts: 'bin/torch-server.zip', caseSensitive: false, onlyIfSuccessful: true
|
||||||
|
|
||||||
|
bat '''IF EXIST bin\\torch-client.zip DEL bin\\torch-client.zip
|
||||||
|
IF EXIST bin\\package-client RMDIR /S /Q bin\\package-client
|
||||||
|
xcopy bin\\x64\\Release bin\\package-client\\
|
||||||
|
del bin\\package-client\\Torch.Server*'''
|
||||||
|
bat "powershell -Command \"Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\\\"\$PWD\\bin\\package-client\\\", \\\"\$PWD\\bin\\torch-client.zip\\\")\""
|
||||||
|
archiveArtifacts artifacts: 'bin/torch-client.zip', caseSensitive: false, onlyIfSuccessful: true
|
||||||
|
|
||||||
|
archiveArtifacts artifacts: 'bin/x64/Release/Torch*', caseSensitive: false, fingerprint: true, onlyIfSuccessful: true
|
||||||
|
}
|
||||||
|
|
||||||
|
gitVersion = bat(returnStdout: true, script: "@git describe --tags").trim()
|
||||||
|
gitSimpleVersion = bat(returnStdout: true, script: "@git describe --tags --abbrev=0").trim()
|
||||||
|
if (gitVersion == gitSimpleVersion) {
|
||||||
|
stage('Release') {
|
||||||
|
withCredentials([usernamePassword(credentialsId: 'e771beac-b3ee-4bc9-82b7-40a6d426d508', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
|
||||||
|
powershell "./Jenkins/release.ps1 \"https://api.github.com/repos/TorchAPI/Torch/\" \"$gitSimpleVersion\" \"$USERNAME:$PASSWORD\" @(\"bin/torch-server.zip\", \"bin/torch-client.zip\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
Discord: [](https://discord.gg/8uHZykr)
|
[](https://discord.gg/8uHZykr) [](http://server.torchapi.net:8080/job/Torch/job/Torch/job/master/)
|
||||||
|
|
||||||
# What is Torch?
|
# What is Torch?
|
||||||
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
|
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
|
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
mklink /D GameBinaries %path%
|
mklink /J GameBinaries "%path%"
|
||||||
if errorlevel 1 goto Error
|
if errorlevel 1 goto Error
|
||||||
echo Done! You can now open the Torch solution without issue.
|
echo Done! You can now open the Torch solution without issue.
|
||||||
goto End
|
goto End
|
||||||
|
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
|
||||||
namespace Torch.API
|
namespace Torch.API
|
||||||
@@ -33,17 +34,27 @@ namespace Torch.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
event Action SessionUnloaded;
|
event Action SessionUnloaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently running session instance, or null if none exists.
|
||||||
|
/// </summary>
|
||||||
|
ITorchSession CurrentSession { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configuration for the current instance.
|
/// Configuration for the current instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITorchConfig Config { get; }
|
ITorchConfig Config { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IMultiplayerManager"/>
|
/// <inheritdoc cref="IMultiplayerManager"/>
|
||||||
|
[Obsolete]
|
||||||
IMultiplayerManager Multiplayer { get; }
|
IMultiplayerManager Multiplayer { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IPluginManager"/>
|
/// <inheritdoc cref="IPluginManager"/>
|
||||||
|
[Obsolete]
|
||||||
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 +101,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>
|
||||||
|
30
Torch.API/Managers/DependencyManagerExtensions.cs
Normal file
30
Torch.API/Managers/DependencyManagerExtensions.cs
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
Torch.API/Managers/DependencyProviderExtensions.cs
Normal file
15
Torch.API/Managers/DependencyProviderExtensions.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
Torch.API/Managers/IDependencyManager.cs
Normal file
62
Torch.API/Managers/IDependencyManager.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
18
Torch.API/Managers/IDependencyProvider.cs
Normal file
18
Torch.API/Managers/IDependencyProvider.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
|
@@ -1,22 +1,55 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Torch.API.Plugins
|
namespace Torch.API.Plugins
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class PluginAttribute : Attribute
|
public class PluginAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the plugin
|
||||||
|
/// </summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the plugin
|
||||||
|
/// </summary>
|
||||||
public Version Version { get; }
|
public Version Version { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The GUID of the plugin
|
||||||
|
/// </summary>
|
||||||
public Guid Guid { get; }
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new plugin attribute with the given attributes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="guid"></param>
|
||||||
public PluginAttribute(string name, string version, string guid)
|
public PluginAttribute(string name, string version, string guid)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Version = Version.Parse(version);
|
Version = Version.Parse(version);
|
||||||
Guid = Guid.Parse(guid);
|
Guid = Guid.Parse(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new plugin attribute with the given attributes. Version is computed as the version of the assembly containing the given type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="versionSupplier">Version is this type's assembly's version</param>
|
||||||
|
/// <param name="guid"></param>
|
||||||
|
public PluginAttribute(string name, Type versionSupplier, string guid)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Version = versionSupplier.Assembly.GetName().Version;
|
||||||
|
Guid = Guid.Parse(guid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
[assembly: AssemblyTitle("Torch API")]
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("TorchAPI")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("TorchAPI")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
#if DEBUG
|
||||||
[assembly: Guid("fba5d932-6254-4a1e-baf4-e229fa94e3c2")]
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
// Version information for an assembly consists of the following four values:
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
//
|
#endif
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
29
Torch.API/Session/ITorchSession.cs
Normal file
29
Torch.API/Session/ITorchSession.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
|
namespace Torch.API.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the Torch code working with a single game session
|
||||||
|
/// </summary>
|
||||||
|
public interface ITorchSession
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Torch instance this session is bound to
|
||||||
|
/// </summary>
|
||||||
|
ITorchBase Torch { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Space Engineers game session this session is bound to.
|
||||||
|
/// </summary>
|
||||||
|
MySession KeenSession { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
|
IDependencyManager Managers { get; }
|
||||||
|
}
|
||||||
|
}
|
46
Torch.API/Session/ITorchSessionManager.cs
Normal file
46
Torch.API/Session/ITorchSessionManager.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
|
namespace Torch.API.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a manager for the given session if applicable.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is for creating managers that will live inside the session, not the manager that controls sesssions.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="session">The session to construct a bound manager for</param>
|
||||||
|
/// <returns>The manager that will live in the session, or null if none.</returns>
|
||||||
|
public delegate IManager SessionManagerFactoryDel(ITorchSession session);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the creation and destruction of <see cref="ITorchSession"/> instances for each <see cref="Sandbox.Game.World.MySession"/> created by Space Engineers.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITorchSessionManager : IManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The currently running session
|
||||||
|
/// </summary>
|
||||||
|
ITorchSession CurrentSession { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given factory as a supplier for session based managers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="factory">Session based manager supplier</param>
|
||||||
|
/// <returns>true if added, false if already present</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">If the factory is null</exception>
|
||||||
|
bool AddFactory(SessionManagerFactoryDel factory);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the given factory from the suppliers for session based managers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="factory">Session based manager supplier</param>
|
||||||
|
/// <returns>true if removed, false if not present</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">If the factory is null</exception>
|
||||||
|
bool RemoveFactory(SessionManagerFactoryDel factory);
|
||||||
|
}
|
||||||
|
}
|
@@ -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>
|
||||||
@@ -12,10 +10,12 @@
|
|||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</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 +23,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">
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NLog">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
@@ -156,9 +156,16 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
<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" />
|
||||||
@@ -173,16 +180,15 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerState.cs" />
|
<Compile Include="ServerState.cs" />
|
||||||
<Compile Include="ModAPI\TorchAPI.cs" />
|
<Compile Include="ModAPI\TorchAPI.cs" />
|
||||||
|
<Compile Include="Session\ITorchSession.cs" />
|
||||||
|
<Compile Include="Session\ITorchSessionManager.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.4.1" targetFramework="net461" />
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
@@ -1,12 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Torch Server")]
|
[assembly: AssemblyTitle("Torch Client Tests")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("Torch")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
96
Torch.Client.Tests/Torch.Client.Tests.csproj
Normal file
96
Torch.Client.Tests/Torch.Client.Tests.csproj
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>{632E78C0-0DAC-4B71-B411-2F1B333CC310}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Torch.Client.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Torch.Client.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<NoWarn>1591,0649</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<DocumentationFile>$(SolutionDir)\bin-test\x64\Release\Torch.Client.Tests.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TorchClientReflectionTest.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Client\Torch.Client.csproj">
|
||||||
|
<Project>{e36df745-260b-4956-a2e8-09f08b2e7161}</Project>
|
||||||
|
<Name>Torch.Client</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj">
|
||||||
|
<Project>{c3c8b671-6ad1-44aa-a8da-e0c0dc0fedf5}</Project>
|
||||||
|
<Name>Torch.Tests</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
|
</Project>
|
67
Torch.Client.Tests/TorchClientReflectionTest.cs
Normal file
67
Torch.Client.Tests/TorchClientReflectionTest.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Client;
|
||||||
|
using Torch.Tests;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Client.Tests
|
||||||
|
{
|
||||||
|
public class TorchClientReflectionTest
|
||||||
|
{
|
||||||
|
static TorchClientReflectionTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager;
|
||||||
|
|
||||||
|
private static ReflectionTestManager Manager()
|
||||||
|
{
|
||||||
|
if (_manager != null)
|
||||||
|
return _manager;
|
||||||
|
|
||||||
|
return _manager = new ReflectionTestManager().Init(typeof(TorchClient).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Getters => Manager().Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => Manager().Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
12
Torch.Client.Tests/packages.config
Normal file
12
Torch.Client.Tests/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="xunit" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
|
||||||
|
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0" targetFramework="net461" developmentDependency="true" />
|
||||||
|
</packages>
|
@@ -1,17 +1,167 @@
|
|||||||
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 Torch.Utils;
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
#endif
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
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 +177,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.229.265")]
|
[assembly: AssemblyTitle("Torch Client")]
|
||||||
[assembly: AssemblyFileVersion("1.0.229.265")]
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
@@ -1,16 +0,0 @@
|
|||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
<# var dt = DateTime.Now;
|
|
||||||
int major = 1;
|
|
||||||
int minor = 0;
|
|
||||||
int build = dt.DayOfYear;
|
|
||||||
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
|
|
||||||
#>
|
|
||||||
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
||||||
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
@@ -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>
|
||||||
@@ -15,10 +13,12 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</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 +27,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,19 +35,20 @@
|
|||||||
<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>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<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 +65,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 +83,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,20 +110,20 @@
|
|||||||
<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" />
|
||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo1.cs" />
|
<Compile Include="Properties\AssemblyInfo.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,35 +153,24 @@
|
|||||||
<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>
|
||||||
<Resource Include="torchicon.ico" />
|
<Resource Include="torchicon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Properties\AssemblyInfo.tt">
|
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.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.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -1,20 +1,20 @@
|
|||||||
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 VRage.GameServices;
|
||||||
using VRageRender;
|
using VRageRender;
|
||||||
|
using VRageRender.ExternalApp;
|
||||||
|
|
||||||
namespace Torch.Client
|
namespace Torch.Client
|
||||||
{
|
{
|
||||||
@@ -24,47 +24,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<IMyGameService>(steamService);
|
||||||
_renderer = null;
|
_renderer = null;
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
|
// I'm sorry, but it's what Keen does in SpaceEngineers.MyProgram
|
||||||
OverrideMenus();
|
#pragma warning disable 612
|
||||||
|
SpaceEngineersGame.SetupRender();
|
||||||
|
#pragma warning restore 612
|
||||||
InitializeRender();
|
InitializeRender();
|
||||||
|
if (!_startup.CheckSteamRunning())
|
||||||
|
throw new InvalidOperationException("Space Engineers requires steam to be running");
|
||||||
|
|
||||||
if (!Game.IsDedicated)
|
if (!Game.IsDedicated)
|
||||||
MyFileSystem.InitUserSpecific(mySteamService.UserId.ToString());
|
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
|
||||||
}
|
|
||||||
|
|
||||||
_startup.DetectSharpDxLeaksAfterRun();
|
|
||||||
MyInitializer.InvokeAfterRun();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OverrideMenus()
|
private void OverrideMenus()
|
||||||
@@ -87,13 +89,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()
|
||||||
|
32
Torch.Client/TorchClientConfig.cs
Normal file
32
Torch.Client/TorchClientConfig.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.4.1" targetFramework="net461" />
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
17
Torch.Server.Tests/Properties/AssemblyInfo.cs
Normal file
17
Torch.Server.Tests/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("Torch Server Tests")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
93
Torch.Server.Tests/Torch.Server.Tests.csproj
Normal file
93
Torch.Server.Tests/Torch.Server.Tests.csproj
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Torch.Server.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Torch.Server.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<NoWarn>1591,0649</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<DocumentationFile>$(SolutionDir)\bin-test\x64\Release\Torch.Server.Tests.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TorchServerReflectionTest.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Server\Torch.Server.csproj">
|
||||||
|
<Project>{ca50886b-7b22-4cd8-93a0-c06f38d4f77d}</Project>
|
||||||
|
<Name>Torch.Server</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj">
|
||||||
|
<Project>{c3c8b671-6ad1-44aa-a8da-e0c0dc0fedf5}</Project>
|
||||||
|
<Name>Torch.Tests</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
|
</Project>
|
66
Torch.Server.Tests/TorchServerReflectionTest.cs
Normal file
66
Torch.Server.Tests/TorchServerReflectionTest.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Tests;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Server.Tests
|
||||||
|
{
|
||||||
|
public class TorchServerReflectionTest
|
||||||
|
{
|
||||||
|
static TorchServerReflectionTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager;
|
||||||
|
|
||||||
|
private static ReflectionTestManager Manager()
|
||||||
|
{
|
||||||
|
if (_manager != null)
|
||||||
|
return _manager;
|
||||||
|
|
||||||
|
return _manager = new ReflectionTestManager().Init(typeof(TorchServer).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Getters => Manager().Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => Manager().Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
12
Torch.Server.Tests/packages.config
Normal file
12
Torch.Server.Tests/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="xunit" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
|
||||||
|
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0" targetFramework="net461" developmentDependency="true" />
|
||||||
|
</packages>
|
184
Torch.Server/Initializer.cs
Normal file
184
Torch.Server/Initializer.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Torch.Utils;
|
||||||
|
|
||||||
|
namespace Torch.Server
|
||||||
|
{
|
||||||
|
public class Initializer
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer));
|
||||||
|
private bool _init;
|
||||||
|
private const string STEAMCMD_DIR = "steamcmd";
|
||||||
|
private const string STEAMCMD_ZIP = "temp.zip";
|
||||||
|
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
||||||
|
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
||||||
|
private const string RUNSCRIPT = @"force_install_dir ../
|
||||||
|
login anonymous
|
||||||
|
app_update 298740
|
||||||
|
quit";
|
||||||
|
|
||||||
|
private TorchAssemblyResolver _resolver;
|
||||||
|
private TorchConfig _config;
|
||||||
|
private TorchServer _server;
|
||||||
|
private string _basePath;
|
||||||
|
|
||||||
|
public TorchConfig Config => _config;
|
||||||
|
public TorchServer Server => _server;
|
||||||
|
|
||||||
|
public Initializer(string basePath)
|
||||||
|
{
|
||||||
|
_basePath = basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Initialize(string[] args)
|
||||||
|
{
|
||||||
|
if (_init)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
||||||
|
|
||||||
|
if (!args.Contains("-noupdate"))
|
||||||
|
RunSteamCmd();
|
||||||
|
|
||||||
|
_resolver = new TorchAssemblyResolver(Path.Combine(_basePath, "DedicatedServer64"));
|
||||||
|
_config = InitConfig();
|
||||||
|
if (!_config.Parse(args))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_config.WaitForPID))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pid = int.Parse(_config.WaitForPID);
|
||||||
|
var waitProc = Process.GetProcessById(pid);
|
||||||
|
Log.Info("Continuing in 5 seconds.");
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
if (!waitProc.HasExited)
|
||||||
|
{
|
||||||
|
Log.Warn($"Killing old process {pid}.");
|
||||||
|
waitProc.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_init = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
_server = new TorchServer(_config);
|
||||||
|
_server.Init();
|
||||||
|
|
||||||
|
if (_config.NoGui || _config.Autostart)
|
||||||
|
{
|
||||||
|
new Thread(_server.Start).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_config.NoGui)
|
||||||
|
{
|
||||||
|
new TorchUI(_server).ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolver?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TorchConfig InitConfig()
|
||||||
|
{
|
||||||
|
var configName = "Torch.cfg";
|
||||||
|
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
||||||
|
if (File.Exists(configName))
|
||||||
|
{
|
||||||
|
Log.Info($"Loading config {configPath}");
|
||||||
|
return TorchConfig.LoadFrom(configPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Info($"Generating default config at {configPath}");
|
||||||
|
var config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") };
|
||||||
|
config.Save(configPath);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunSteamCmd()
|
||||||
|
{
|
||||||
|
var log = LogManager.GetLogger("SteamCMD");
|
||||||
|
|
||||||
|
if (!Directory.Exists(STEAMCMD_DIR))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(STEAMCMD_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(RUNSCRIPT_PATH))
|
||||||
|
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
||||||
|
|
||||||
|
if (!File.Exists(STEAMCMD_PATH))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.Info("Downloading SteamCMD.");
|
||||||
|
using (var client = new WebClient())
|
||||||
|
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
|
||||||
|
|
||||||
|
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
||||||
|
File.Delete(STEAMCMD_ZIP);
|
||||||
|
log.Info("SteamCMD downloaded successfully!");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Checking for DS updates.");
|
||||||
|
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
||||||
|
{
|
||||||
|
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
StandardOutputEncoding = Encoding.ASCII
|
||||||
|
};
|
||||||
|
var cmd = Process.Start(steamCmdProc);
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
while (!cmd.HasExited)
|
||||||
|
{
|
||||||
|
log.Info(cmd.StandardOutput.ReadLine());
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void HandleException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
var ex = (Exception)e.ExceptionObject;
|
||||||
|
Log.Fatal(ex);
|
||||||
|
Console.WriteLine("Exiting in 5 seconds.");
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
if (_config.RestartOnCrash)
|
||||||
|
{
|
||||||
|
var exe = typeof(Program).Assembly.Location;
|
||||||
|
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Process.Start(exe, _config.ToString());
|
||||||
|
}
|
||||||
|
//1627 = Function failed during execution.
|
||||||
|
Environment.Exit(1627);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
@@ -25,6 +25,7 @@ using System.IO.Compression;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Policy;
|
using System.Security.Policy;
|
||||||
using Torch.Server.Managers;
|
using Torch.Server.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRageRender;
|
using VRageRender;
|
||||||
|
|
||||||
@@ -32,268 +33,32 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static ITorchServer _server;
|
/// <remarks>
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
|
||||||
private static bool _restartOnCrash;
|
|
||||||
private static TorchConfig _config;
|
|
||||||
private static bool _steamCmdDone;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
|
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
|
||||||
/// </summary>
|
/// </remarks>
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
//Ensures that all the files are downloaded in the Torch directory.
|
//Ensures that all the files are downloaded in the Torch directory.
|
||||||
Directory.SetCurrentDirectory(new FileInfo(typeof(Program).Assembly.Location).Directory.ToString());
|
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
||||||
|
var binDir = Path.Combine(workingDir, "DedicatedServer64");
|
||||||
foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.old"))
|
Directory.SetCurrentDirectory(workingDir);
|
||||||
File.Delete(file);
|
|
||||||
|
|
||||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
|
||||||
|
|
||||||
if (!Environment.UserInteractive)
|
if (!Environment.UserInteractive)
|
||||||
{
|
{
|
||||||
using (var service = new TorchService())
|
using (var service = new TorchService())
|
||||||
|
using (new TorchAssemblyResolver(binDir))
|
||||||
{
|
{
|
||||||
ServiceBase.Run(service);
|
ServiceBase.Run(service);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//CommandLine reflection triggers assembly loading, so DS update must be completely separated.
|
var initializer = new Initializer(workingDir);
|
||||||
if (!args.Contains("-noupdate"))
|
if (!initializer.Initialize(args))
|
||||||
{
|
|
||||||
if (!Directory.Exists("DedicatedServer64"))
|
|
||||||
{
|
|
||||||
_log.Error("Game libraries not found. Press the Enter key to install the dedicated server.");
|
|
||||||
Console.ReadLine();
|
|
||||||
}
|
|
||||||
RunSteamCmd();
|
|
||||||
}
|
|
||||||
|
|
||||||
InitConfig();
|
|
||||||
|
|
||||||
if (!_config.Parse(args))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_config.WaitForPID))
|
initializer.Run();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var pid = int.Parse(_config.WaitForPID);
|
|
||||||
var waitProc = Process.GetProcessById(pid);
|
|
||||||
_log.Info("Continuing in 5 seconds.");
|
|
||||||
Thread.Sleep(5000);
|
|
||||||
if (!waitProc.HasExited)
|
|
||||||
{
|
|
||||||
_log.Warn($"Killing old process {pid}.");
|
|
||||||
waitProc.Kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_restartOnCrash = _config.RestartOnCrash;
|
|
||||||
RunServer(_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InitConfig()
|
|
||||||
{
|
|
||||||
var configName = "Torch.cfg";
|
|
||||||
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
|
||||||
if (File.Exists(configName))
|
|
||||||
{
|
|
||||||
_log.Info($"Loading config {configPath}");
|
|
||||||
_config = TorchConfig.LoadFrom(configPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_log.Info($"Generating default config at {configPath}");
|
|
||||||
_config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") };
|
|
||||||
_config.Save(configPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string STEAMCMD_DIR = "steamcmd";
|
|
||||||
private const string STEAMCMD_ZIP = "temp.zip";
|
|
||||||
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
|
||||||
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
|
||||||
private const string RUNSCRIPT = @"force_install_dir ../
|
|
||||||
login anonymous
|
|
||||||
app_update 298740
|
|
||||||
quit";
|
|
||||||
|
|
||||||
public static void RunSteamCmd()
|
|
||||||
{
|
|
||||||
if (_steamCmdDone)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var log = LogManager.GetLogger("SteamCMD");
|
|
||||||
|
|
||||||
if (!Directory.Exists(STEAMCMD_DIR))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(STEAMCMD_DIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(RUNSCRIPT_PATH))
|
|
||||||
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
|
||||||
|
|
||||||
if (!File.Exists(STEAMCMD_PATH))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log.Info("Downloading SteamCMD.");
|
|
||||||
using (var client = new WebClient())
|
|
||||||
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
|
|
||||||
|
|
||||||
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
|
||||||
File.Delete(STEAMCMD_ZIP);
|
|
||||||
log.Info("SteamCMD downloaded successfully!");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Checking for DS updates.");
|
|
||||||
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
|
||||||
{
|
|
||||||
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
|
||||||
UseShellExecute = false,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
StandardOutputEncoding = Encoding.ASCII
|
|
||||||
};
|
|
||||||
var cmd = Process.Start(steamCmdProc);
|
|
||||||
|
|
||||||
// ReSharper disable once PossibleNullReferenceException
|
|
||||||
while (!cmd.HasExited)
|
|
||||||
{
|
|
||||||
log.Info(cmd.StandardOutput.ReadLine());
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
_steamCmdDone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RunServer(TorchConfig config)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!parser.ParseArguments(args, config))
|
|
||||||
{
|
|
||||||
_log.Error($"Parsing arguments failed: {string.Join(" ", args)}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(config.Config) && File.Exists(config.Config))
|
|
||||||
{
|
|
||||||
config = ServerConfig.LoadFrom(config.Config);
|
|
||||||
parser.ParseArguments(args, config);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//RestartOnCrash autostart autosave=15
|
|
||||||
//gamepath ="C:\Program Files\Space Engineers DS" instance="Hydro Survival" instancepath="C:\ProgramData\SpaceEngineersDedicated\Hydro Survival"
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (config.InstallService)
|
|
||||||
{
|
|
||||||
var serviceName = $"\"Torch - {config.InstanceName}\"";
|
|
||||||
// Working on installing the service properly instead of with sc.exe
|
|
||||||
_log.Info($"Installing service '{serviceName}");
|
|
||||||
var exePath = $"\"{Assembly.GetExecutingAssembly().Location}\"";
|
|
||||||
var createInfo = new ServiceCreateInfo
|
|
||||||
{
|
|
||||||
Name = config.InstanceName,
|
|
||||||
BinaryPath = exePath,
|
|
||||||
};
|
|
||||||
_log.Info("Service Installed");
|
|
||||||
|
|
||||||
var runArgs = string.Join(" ", args.Skip(1));
|
|
||||||
_log.Info($"Installing Torch as a service with arguments '{runArgs}'");
|
|
||||||
var startInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "sc.exe",
|
|
||||||
Arguments = $"create Torch binPath=\"{Assembly.GetExecutingAssembly().Location} {runArgs}\"",
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas"
|
|
||||||
};
|
|
||||||
Process.Start(startInfo).WaitForExit();
|
|
||||||
_log.Info("Torch service installed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.UninstallService)
|
|
||||||
{
|
|
||||||
_log.Info("Uninstalling Torch service");
|
|
||||||
var startInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "sc.exe",
|
|
||||||
Arguments = "delete Torch",
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas"
|
|
||||||
};
|
|
||||||
Process.Start(startInfo).WaitForExit();
|
|
||||||
_log.Info("Torch service uninstalled");
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
_server = new TorchServer(config);
|
|
||||||
|
|
||||||
_server.Init();
|
|
||||||
if (config.NoGui || config.Autostart)
|
|
||||||
{
|
|
||||||
new Thread(() => _server.Start()).Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.NoGui)
|
|
||||||
{
|
|
||||||
var ui = new TorchUI((TorchServer)_server);
|
|
||||||
ui.ShowDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var basePath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "DedicatedServer64");
|
|
||||||
string asmPath = Path.Combine(basePath, new AssemblyName(args.Name).Name + ".dll");
|
|
||||||
if (File.Exists(asmPath))
|
|
||||||
return Assembly.LoadFrom(asmPath);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
var ex = (Exception)e.ExceptionObject;
|
|
||||||
_log.Fatal(ex);
|
|
||||||
Console.WriteLine("Exiting in 5 seconds.");
|
|
||||||
Thread.Sleep(5000);
|
|
||||||
if (_restartOnCrash)
|
|
||||||
{
|
|
||||||
var exe = typeof(Program).Assembly.Location;
|
|
||||||
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
|
||||||
Process.Start(exe, _config.ToString());
|
|
||||||
}
|
|
||||||
//1627 = Function failed during execution.
|
|
||||||
Environment.Exit(1627);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.1.229.265")]
|
[assembly: AssemblyTitle("Torch Server")]
|
||||||
[assembly: AssemblyFileVersion("1.1.229.265")]
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
@@ -1,16 +0,0 @@
|
|||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
<# var dt = DateTime.Now;
|
|
||||||
int major = 1;
|
|
||||||
int minor = 1;
|
|
||||||
int build = dt.DayOfYear;
|
|
||||||
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
|
|
||||||
#>
|
|
||||||
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
||||||
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
@@ -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>
|
||||||
@@ -15,10 +13,12 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</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 +27,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 +35,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>
|
||||||
@@ -63,7 +63,8 @@
|
|||||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<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>
|
||||||
@@ -124,6 +125,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>
|
||||||
@@ -185,15 +187,14 @@
|
|||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="ListBoxExtensions.cs" />
|
<Compile Include="ListBoxExtensions.cs" />
|
||||||
<Compile Include="Managers\InstanceManager.cs" />
|
<Compile Include="Managers\InstanceManager.cs" />
|
||||||
<Compile Include="NativeMethods.cs" />
|
<Compile Include="NativeMethods.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Initializer.cs" />
|
||||||
<AutoGen>True</AutoGen>
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Properties\AssemblyInfo1.cs" />
|
|
||||||
<Compile Include="ServerStatistics.cs" />
|
<Compile Include="ServerStatistics.cs" />
|
||||||
<Compile Include="TorchConfig.cs" />
|
<Compile Include="TorchConfig.cs" />
|
||||||
<Compile Include="TorchService.cs">
|
<Compile Include="TorchService.cs">
|
||||||
@@ -290,12 +291,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>
|
||||||
@@ -358,24 +359,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Properties\AssemblyInfo.tt">
|
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.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.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -8,6 +8,7 @@ using NLog;
|
|||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
|
// TODO: redesign this gerbage
|
||||||
public class TorchConfig : CommandLine, ITorchConfig
|
public class TorchConfig : CommandLine, ITorchConfig
|
||||||
{
|
{
|
||||||
private static Logger _log = LogManager.GetLogger("Config");
|
private static Logger _log = LogManager.GetLogger("Config");
|
||||||
|
@@ -19,6 +19,7 @@ using SteamSDK;
|
|||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
using Torch.Server.Managers;
|
using Torch.Server.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
using VRage.Dedicated;
|
using VRage.Dedicated;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
@@ -29,6 +30,7 @@ using VRage.Library;
|
|||||||
using VRage.ObjectBuilders;
|
using VRage.ObjectBuilders;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
|
|
||||||
#pragma warning disable 618
|
#pragma warning disable 618
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
@@ -121,6 +123,9 @@ namespace Torch.Server
|
|||||||
MySandboxGame.Config.Load();
|
MySandboxGame.Config.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ReflectedStaticMethod(Type = typeof(DedicatedServer), Name = "RunInternal")]
|
||||||
|
private static Action _dsRunInternal;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
@@ -134,17 +139,23 @@ namespace Torch.Server
|
|||||||
State = ServerState.Starting;
|
State = ServerState.Starting;
|
||||||
Log.Info("Starting server.");
|
Log.Info("Starting server.");
|
||||||
|
|
||||||
var runInternal = typeof(DedicatedServer).GetMethod("RunInternal", BindingFlags.Static | BindingFlags.NonPublic);
|
|
||||||
|
|
||||||
MySandboxGame.IsDedicated = true;
|
MySandboxGame.IsDedicated = true;
|
||||||
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
||||||
|
|
||||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
||||||
|
|
||||||
base.Start();
|
base.Start();
|
||||||
//Stops RunInternal from calling MyFileSystem.InitUserSpecific(null), we call it in InstanceManager.
|
// Stops RunInternal from calling MyFileSystem.InitUserSpecific(null), we call it in InstanceManager.
|
||||||
MySandboxGame.IsReloading = true;
|
MySandboxGame.IsReloading = true;
|
||||||
runInternal.Invoke(null, null);
|
try
|
||||||
|
{
|
||||||
|
_dsRunInternal.Invoke();
|
||||||
|
}
|
||||||
|
catch (TargetInvocationException e)
|
||||||
|
{
|
||||||
|
// Makes log formatting a little nicer.
|
||||||
|
throw e.InnerException ?? e;
|
||||||
|
}
|
||||||
|
|
||||||
MySandboxGame.Log.Close();
|
MySandboxGame.Log.Close();
|
||||||
State = ServerState.Stopped;
|
State = ServerState.Stopped;
|
||||||
@@ -180,6 +191,7 @@ namespace Torch.Server
|
|||||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||||
{
|
{
|
||||||
var mainThread = MySandboxGame.Static.UpdateThread;
|
var mainThread = MySandboxGame.Static.UpdateThread;
|
||||||
|
if (mainThread.IsAlive)
|
||||||
mainThread.Suspend();
|
mainThread.Suspend();
|
||||||
var stackTrace = new StackTrace(mainThread, true);
|
var stackTrace = new StackTrace(mainThread, true);
|
||||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.\n{stackTrace}");
|
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.\n{stackTrace}");
|
||||||
|
@@ -14,13 +14,15 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
public const string Name = "Torch (SEDS)";
|
public const string Name = "Torch (SEDS)";
|
||||||
private TorchServer _server;
|
private TorchServer _server;
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
private Initializer _initializer;
|
||||||
|
|
||||||
public TorchService()
|
public TorchService()
|
||||||
{
|
{
|
||||||
ServiceName = Name;
|
var workingDir = new FileInfo(typeof(TorchService).Assembly.Location).Directory.ToString();
|
||||||
|
Directory.SetCurrentDirectory(workingDir);
|
||||||
|
_initializer = new Initializer(workingDir);
|
||||||
|
|
||||||
CanHandlePowerEvent = true;
|
ServiceName = Name;
|
||||||
CanHandleSessionChangeEvent = false;
|
CanHandleSessionChangeEvent = false;
|
||||||
CanPauseAndContinue = false;
|
CanPauseAndContinue = false;
|
||||||
CanStop = true;
|
CanStop = true;
|
||||||
@@ -31,17 +33,8 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
base.OnStart(args);
|
base.OnStart(args);
|
||||||
|
|
||||||
string configName = args.Length > 0 ? args[0] : "TorchConfig.xml";
|
_initializer.Initialize(args);
|
||||||
var options = new TorchConfig("Torch");
|
_initializer.Run();
|
||||||
if (File.Exists(configName))
|
|
||||||
options = TorchConfig.LoadFrom(configName);
|
|
||||||
else
|
|
||||||
options.Save(configName);
|
|
||||||
|
|
||||||
_server = new TorchServer(options);
|
|
||||||
_server.Init();
|
|
||||||
_server.RunArgs = args;
|
|
||||||
Task.Run(() => _server.Start());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -50,17 +43,5 @@ namespace Torch.Server
|
|||||||
_server.Stop();
|
_server.Stop();
|
||||||
base.OnStop();
|
base.OnStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnShutdown()
|
|
||||||
{
|
|
||||||
base.OnShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
|
|
||||||
{
|
|
||||||
return base.OnPowerEvent(powerStatus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||||
<package id="NLog" version="4.4.11" targetFramework="net461" />
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
@@ -1,12 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Torch Client")]
|
[assembly: AssemblyTitle("Torch Tests")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("Torch")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
221
Torch.Tests/ReflectionSystemTest.cs
Normal file
221
Torch.Tests/ReflectionSystemTest.cs
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Tests
|
||||||
|
{
|
||||||
|
public class ReflectionSystemTest
|
||||||
|
{
|
||||||
|
static ReflectionSystemTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager = new ReflectionTestManager().Init(typeof(ReflectionTestBinding));
|
||||||
|
public static IEnumerable<object[]> Getters => _manager.Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => _manager.Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => _manager.Invokers;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Results
|
||||||
|
#region Dummy
|
||||||
|
private class ReflectionTestTarget
|
||||||
|
{
|
||||||
|
public int TestField;
|
||||||
|
public int TestProperty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return true when greater or equal than 0
|
||||||
|
/// </summary>
|
||||||
|
public bool TestCall(int k)
|
||||||
|
{
|
||||||
|
return k >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int TestFieldStatic;
|
||||||
|
public static int TestPropertyStatic { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return true when greater or equal than 0
|
||||||
|
/// </summary>
|
||||||
|
public static bool TestCallStatic(int k)
|
||||||
|
{
|
||||||
|
return k >= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReflectionTestBinding
|
||||||
|
{
|
||||||
|
[ReflectedGetter(Name = "TestField")]
|
||||||
|
public static Func<ReflectionTestTarget, int> TestFieldGetter;
|
||||||
|
[ReflectedSetter(Name = "TestField")]
|
||||||
|
public static Action<ReflectionTestTarget, int> TestFieldSetter;
|
||||||
|
|
||||||
|
[ReflectedGetter(Name = "TestProperty")]
|
||||||
|
public static Func<ReflectionTestTarget, int> TestPropertyGetter;
|
||||||
|
[ReflectedSetter(Name = "TestProperty")]
|
||||||
|
public static Action<ReflectionTestTarget, int> TestPropertySetter;
|
||||||
|
|
||||||
|
[ReflectedMethod]
|
||||||
|
public static Func<ReflectionTestTarget, int, bool> TestCall;
|
||||||
|
|
||||||
|
|
||||||
|
[ReflectedGetter(Name = "TestFieldStatic", Type = typeof(ReflectionTestTarget))]
|
||||||
|
public static Func<int> TestStaticFieldGetter;
|
||||||
|
[ReflectedSetter(Name = "TestFieldStatic", Type = typeof(ReflectionTestTarget))]
|
||||||
|
public static Action<int> TestStaticFieldSetter;
|
||||||
|
|
||||||
|
[ReflectedGetter(Name = "TestPropertyStatic", Type = typeof(ReflectionTestTarget))]
|
||||||
|
public static Func<int> TestStaticPropertyGetter;
|
||||||
|
[ReflectedSetter(Name = "TestPropertyStatic", Type = typeof(ReflectionTestTarget))]
|
||||||
|
public static Action<int> TestStaticPropertySetter;
|
||||||
|
|
||||||
|
[ReflectedStaticMethod(Type = typeof(ReflectionTestTarget))]
|
||||||
|
public static Func<int, bool> TestCallStatic;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private readonly Random _rand = new Random();
|
||||||
|
private int AcquireRandomNum()
|
||||||
|
{
|
||||||
|
return _rand.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Instance
|
||||||
|
[Fact]
|
||||||
|
public void TestInstanceFieldGet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
var target = new ReflectionTestTarget
|
||||||
|
{
|
||||||
|
TestField = testNumber
|
||||||
|
};
|
||||||
|
Assert.Equal(testNumber, ReflectionTestBinding.TestFieldGetter.Invoke(target));
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TestInstanceFieldSet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
var target = new ReflectionTestTarget();
|
||||||
|
ReflectionTestBinding.TestFieldSetter.Invoke(target, testNumber);
|
||||||
|
Assert.Equal(testNumber, target.TestField);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestInstancePropertyGet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
var target = new ReflectionTestTarget
|
||||||
|
{
|
||||||
|
TestProperty = testNumber
|
||||||
|
};
|
||||||
|
Assert.Equal(testNumber, ReflectionTestBinding.TestPropertyGetter.Invoke(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestInstancePropertySet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
var target = new ReflectionTestTarget();
|
||||||
|
ReflectionTestBinding.TestPropertySetter.Invoke(target, testNumber);
|
||||||
|
Assert.Equal(testNumber, target.TestProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestInstanceInvoke()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
var target = new ReflectionTestTarget();
|
||||||
|
Assert.True(ReflectionTestBinding.TestCall.Invoke(target, 1));
|
||||||
|
Assert.False(ReflectionTestBinding.TestCall.Invoke(target, -1));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static
|
||||||
|
[Fact]
|
||||||
|
public void TestStaticFieldGet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
ReflectionTestTarget.TestFieldStatic = testNumber;
|
||||||
|
Assert.Equal(testNumber, ReflectionTestBinding.TestStaticFieldGetter.Invoke());
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TestStaticFieldSet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
ReflectionTestBinding.TestStaticFieldSetter.Invoke(testNumber);
|
||||||
|
Assert.Equal(testNumber, ReflectionTestTarget.TestFieldStatic);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestStaticPropertyGet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
ReflectionTestTarget.TestPropertyStatic = testNumber;
|
||||||
|
Assert.Equal(testNumber, ReflectionTestBinding.TestStaticPropertyGetter.Invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestStaticPropertySet()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
int testNumber = AcquireRandomNum();
|
||||||
|
ReflectionTestBinding.TestStaticPropertySetter.Invoke(testNumber);
|
||||||
|
Assert.Equal(testNumber, ReflectionTestTarget.TestPropertyStatic);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestStaticInvoke()
|
||||||
|
{
|
||||||
|
ReflectedManager.Process(typeof(ReflectionTestBinding));
|
||||||
|
Assert.True(ReflectionTestBinding.TestCallStatic.Invoke(1));
|
||||||
|
Assert.False(ReflectionTestBinding.TestCallStatic.Invoke(-1));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
72
Torch.Tests/ReflectionTestManager.cs
Normal file
72
Torch.Tests/ReflectionTestManager.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Torch.Utils;
|
||||||
|
|
||||||
|
namespace Torch.Tests
|
||||||
|
{
|
||||||
|
public class ReflectionTestManager
|
||||||
|
{
|
||||||
|
#region FieldProvider
|
||||||
|
public struct FieldRef
|
||||||
|
{
|
||||||
|
public FieldInfo Field;
|
||||||
|
|
||||||
|
public FieldRef(FieldInfo f)
|
||||||
|
{
|
||||||
|
Field = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Field == null)
|
||||||
|
return "Ignored";
|
||||||
|
return Field.DeclaringType?.FullName + "." + Field.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly HashSet<object[]> _getters = new HashSet<object[]>();
|
||||||
|
private readonly HashSet<object[]> _setters = new HashSet<object[]>();
|
||||||
|
private readonly HashSet<object[]> _invokers = new HashSet<object[]>();
|
||||||
|
|
||||||
|
public ReflectionTestManager()
|
||||||
|
{
|
||||||
|
_getters.Add(new object[] { new FieldRef(null) });
|
||||||
|
_setters.Add(new object[] { new FieldRef(null) });
|
||||||
|
_invokers.Add(new object[] { new FieldRef(null) });
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReflectionTestManager Init(Assembly asm)
|
||||||
|
{
|
||||||
|
foreach (Type type in asm.GetTypes())
|
||||||
|
Init(type);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReflectionTestManager Init(Type type)
|
||||||
|
{
|
||||||
|
foreach (FieldInfo field in type.GetFields(BindingFlags.Static |
|
||||||
|
BindingFlags.Instance |
|
||||||
|
BindingFlags.Public |
|
||||||
|
BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
if (field.GetCustomAttribute<ReflectedMethodAttribute>() != null)
|
||||||
|
_invokers.Add(new object[] { new FieldRef(field) });
|
||||||
|
if (field.GetCustomAttribute<ReflectedGetterAttribute>() != null)
|
||||||
|
_getters.Add(new object[] { new FieldRef(field) });
|
||||||
|
if (field.GetCustomAttribute<ReflectedSetterAttribute>() != null)
|
||||||
|
_setters.Add(new object[] { new FieldRef(field) });
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<object[]> Getters => _getters;
|
||||||
|
|
||||||
|
public IEnumerable<object[]> Setters => _setters;
|
||||||
|
|
||||||
|
public IEnumerable<object[]> Invokers => _invokers;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
35
Torch.Tests/TestUtils.cs
Normal file
35
Torch.Tests/TestUtils.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch.Utils;
|
||||||
|
|
||||||
|
namespace Torch.Tests
|
||||||
|
{
|
||||||
|
public sealed class TestUtils
|
||||||
|
{
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
if (_torchResolver == null)
|
||||||
|
_torchResolver = new TorchAssemblyResolver(GetGameBinaries());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetGameBinaries()
|
||||||
|
{
|
||||||
|
string dir = Environment.CurrentDirectory;
|
||||||
|
while (!string.IsNullOrWhiteSpace(dir))
|
||||||
|
{
|
||||||
|
string gameBin = Path.Combine(dir, "GameBinaries");
|
||||||
|
if (Directory.Exists(gameBin))
|
||||||
|
return gameBin;
|
||||||
|
|
||||||
|
dir = Path.GetDirectoryName(dir);
|
||||||
|
}
|
||||||
|
throw new Exception("GetGameBinaries failed to find a folder named GameBinaries in the directory tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TorchAssemblyResolver _torchResolver;
|
||||||
|
}
|
||||||
|
}
|
91
Torch.Tests/Torch.Tests.csproj
Normal file
91
Torch.Tests/Torch.Tests.csproj
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Torch.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Torch.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<NoWarn>1591,0649</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<DocumentationFile>$(SolutionDir)\bin-test\x64\Release\Torch.Tests.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="ReflectionTestManager.cs" />
|
||||||
|
<Compile Include="ReflectionSystemTest.cs" />
|
||||||
|
<Compile Include="TestUtils.cs" />
|
||||||
|
<Compile Include="TorchReflectionTest.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
|
</Project>
|
65
Torch.Tests/TorchReflectionTest.cs
Normal file
65
Torch.Tests/TorchReflectionTest.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Tests
|
||||||
|
{
|
||||||
|
public class TorchReflectionTest
|
||||||
|
{
|
||||||
|
static TorchReflectionTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager;
|
||||||
|
|
||||||
|
private static ReflectionTestManager Manager()
|
||||||
|
{
|
||||||
|
if (_manager != null)
|
||||||
|
return _manager;
|
||||||
|
|
||||||
|
return _manager = new ReflectionTestManager().Init(typeof(TorchBase).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Getters => Manager().Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => Manager().Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
12
Torch.Tests/packages.config
Normal file
12
Torch.Tests/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="xunit" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
|
||||||
|
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0" targetFramework="net461" developmentDependency="true" />
|
||||||
|
</packages>
|
31
Torch.sln
31
Torch.sln
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26430.14
|
VisualStudioVersion = 15.0.26730.8
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch", "Torch\Torch.csproj", "{7E01635C-3B67-472E-BCD6-C5539564F214}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch", "Torch\Torch.csproj", "{7E01635C-3B67-472E-BCD6-C5539564F214}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -16,6 +16,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
NLog.config = NLog.config
|
NLog.config = NLog.config
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Tests", "Torch.Tests\Torch.Tests.csproj", "{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Server.Tests", "Torch.Server.Tests\Torch.Server.Tests.csproj", "{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Client.Tests", "Torch.Client.Tests\Torch.Client.Tests.csproj", "{632E78C0-0DAC-4B71-B411-2F1B333CC310}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Versioning", "Versioning", "{762F6A0D-55EF-4173-8CDE-309D183F40C4}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
Versioning\AssemblyVersion.cs = Versioning\AssemblyVersion.cs
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
@@ -38,8 +49,26 @@ Global
|
|||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Debug|x64.Build.0 = Debug|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.ActiveCfg = Release|x64
|
||||||
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}.Release|x64.Build.0 = Release|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}.Release|x64.Build.0 = Release|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{632E78C0-0DAC-4B71-B411-2F1B333CC310}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{762F6A0D-55EF-4173-8CDE-309D183F40C4} = {7AD02A71-1D4C-48F9-A8C1-789A5512424F}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {BB51D91F-958D-4B63-A897-3C40642ACD3E}
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@@ -62,7 +62,7 @@ namespace Torch.Commands
|
|||||||
_parameters = commandMethod.GetParameters();
|
_parameters = commandMethod.GetParameters();
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append($"/{string.Join(" ", Path)} ");
|
sb.Append($"!{string.Join(" ", Path)} ");
|
||||||
for (var i = 0; i < _parameters.Length; i++)
|
for (var i = 0; i < _parameters.Length; i++)
|
||||||
{
|
{
|
||||||
var param = _parameters[i];
|
var param = _parameters[i];
|
||||||
|
@@ -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)
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
{
|
{
|
||||||
|
309
Torch/Managers/DependencyManager.cs
Normal file
309
Torch/Managers/DependencyManager.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ using NLog;
|
|||||||
using Torch;
|
using Torch;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Game.Entities.Character;
|
using Sandbox.Game.Entities.Character;
|
||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
@@ -25,12 +26,14 @@ using Torch.API;
|
|||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.Collections;
|
using Torch.Collections;
|
||||||
using Torch.Commands;
|
using Torch.Commands;
|
||||||
|
using Torch.Utils;
|
||||||
using Torch.ViewModels;
|
using Torch.ViewModels;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
using VRage.GameServices;
|
using VRage.GameServices;
|
||||||
using VRage.Library.Collections;
|
using VRage.Library.Collections;
|
||||||
using VRage.Network;
|
using VRage.Network;
|
||||||
|
using VRage.Steam;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
|
|
||||||
namespace Torch.Managers
|
namespace Torch.Managers
|
||||||
@@ -50,7 +53,16 @@ namespace Torch.Managers
|
|||||||
public IMyPlayer LocalPlayer => MySession.Static.LocalHumanPlayer;
|
public IMyPlayer LocalPlayer => MySession.Static.LocalHumanPlayer;
|
||||||
private static readonly Logger Log = LogManager.GetLogger(nameof(MultiplayerManager));
|
private static readonly Logger Log = LogManager.GetLogger(nameof(MultiplayerManager));
|
||||||
private static readonly Logger ChatLog = LogManager.GetLogger("Chat");
|
private static readonly Logger ChatLog = LogManager.GetLogger("Chat");
|
||||||
private Dictionary<MyPlayer.PlayerId, MyPlayer> _onlinePlayers;
|
|
||||||
|
[ReflectedGetter(Name = "m_players")]
|
||||||
|
private static Func<MyPlayerCollection, 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)
|
||||||
{
|
{
|
||||||
@@ -58,10 +70,10 @@ namespace Torch.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <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)
|
||||||
@@ -89,21 +101,19 @@ namespace Torch.Managers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IMyPlayer GetPlayerByName(string name)
|
public IMyPlayer GetPlayerByName(string name)
|
||||||
{
|
{
|
||||||
ValidateOnlinePlayersList();
|
return _onlinePlayers.Invoke(MySession.Static.Players).FirstOrDefault(x => x.Value.DisplayName == name).Value;
|
||||||
return _onlinePlayers.FirstOrDefault(x => x.Value.DisplayName == name).Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IMyPlayer GetPlayerBySteamId(ulong steamId)
|
public IMyPlayer GetPlayerBySteamId(ulong steamId)
|
||||||
{
|
{
|
||||||
ValidateOnlinePlayersList();
|
_onlinePlayers.Invoke(MySession.Static.Players).TryGetValue(new MyPlayer.PlayerId(steamId), out MyPlayer p);
|
||||||
_onlinePlayers.TryGetValue(new MyPlayer.PlayerId(steamId), out MyPlayer p);
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetSteamId(long identityId)
|
public ulong GetSteamId(long identityId)
|
||||||
{
|
{
|
||||||
foreach (var kv in _onlinePlayers)
|
foreach (var kv in _onlinePlayers.Invoke(MySession.Static.Players))
|
||||||
{
|
{
|
||||||
if (kv.Value.Identity.IdentityId == identityId)
|
if (kv.Value.Identity.IdentityId == identityId)
|
||||||
return kv.Key.SteamId;
|
return kv.Key.SteamId;
|
||||||
@@ -125,10 +135,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,30 +150,24 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateOnlinePlayersList()
|
|
||||||
{
|
|
||||||
if (_onlinePlayers == null)
|
|
||||||
_onlinePlayers = MySession.Static.Players.GetPrivateField<Dictionary<MyPlayer.PlayerId, MyPlayer>>("m_players");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSessionLoaded()
|
private void OnSessionLoaded()
|
||||||
{
|
{
|
||||||
Log.Info("Initializing Steam auth");
|
Log.Info("Initializing Steam auth");
|
||||||
MyMultiplayer.Static.ClientKicked += OnClientKicked;
|
MyMultiplayer.Static.ClientKicked += OnClientKicked;
|
||||||
MyMultiplayer.Static.ClientLeft += OnClientLeft;
|
MyMultiplayer.Static.ClientLeft += OnClientLeft;
|
||||||
|
|
||||||
ValidateOnlinePlayersList();
|
|
||||||
|
|
||||||
//TODO: Move these with the methods?
|
//TODO: Move these with the methods?
|
||||||
RemoveHandlers();
|
if (!RemoveHandlers())
|
||||||
SteamServerAPI.Instance.GameServer.ValidateAuthTicketResponse += ValidateAuthTicketResponse;
|
{
|
||||||
SteamServerAPI.Instance.GameServer.UserGroupStatus += UserGroupStatus;
|
Log.Error("Steam auth failed to initialize");
|
||||||
_members = (List<ulong>)typeof(MyDedicatedServerBase).GetField("m_members", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
|
return;
|
||||||
_waitingForGroup = (HashSet<ulong>)typeof(MyDedicatedServerBase).GetField("m_waitingForGroup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
|
}
|
||||||
|
MyGameService.GameServer.ValidateAuthTicketResponse += ValidateAuthTicketResponse;
|
||||||
|
MyGameService.GameServer.UserGroupStatusResponse += UserGroupStatusResponse;
|
||||||
Log.Info("Steam auth initialized");
|
Log.Info("Steam auth initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +189,12 @@ namespace Torch.Managers
|
|||||||
//TODO: Split the following into a new file?
|
//TODO: Split the following into a new file?
|
||||||
//These methods override some Keen code to allow us full control over client authentication.
|
//These methods override some Keen code to allow us full control over client authentication.
|
||||||
//This lets us have a server set to private (admins only) or friends (friends of all listed admins)
|
//This lets us have a server set to private (admins only) or friends (friends of all listed admins)
|
||||||
private List<ulong> _members;
|
[ReflectedGetter(Name = "m_members")]
|
||||||
private HashSet<ulong> _waitingForGroup;
|
private static Func<MyDedicatedServerBase, List<ulong>> _members;
|
||||||
|
[ReflectedGetter(Name = "m_waitingForGroup")]
|
||||||
|
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
|
||||||
|
[ReflectedGetter(Name = "m_kickedClients")]
|
||||||
|
private static Func<MyMultiplayerBase, Dictionary<ulong, int>> _kickedClients;
|
||||||
//private HashSet<ulong> _waitingForFriends;
|
//private HashSet<ulong> _waitingForFriends;
|
||||||
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
||||||
//private IMultiplayer _multiplayerImplementation;
|
//private IMultiplayer _multiplayerImplementation;
|
||||||
@@ -195,152 +202,137 @@ namespace Torch.Managers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes Keen's hooks into some Steam events so we have full control over client authentication
|
/// Removes Keen's hooks into some Steam events so we have full control over client authentication
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void RemoveHandlers()
|
private static bool RemoveHandlers()
|
||||||
{
|
{
|
||||||
var eventField = typeof(GameServer).GetField("<backing_store>ValidateAuthTicketResponse", BindingFlags.NonPublic | BindingFlags.Instance);
|
MethodInfo methodValidateAuthTicket = typeof(MyDedicatedServerBase).GetMethod("GameServer_ValidateAuthTicketResponse",
|
||||||
if (eventField?.GetValue(SteamServerAPI.Instance.GameServer) is MulticastDelegate eventDel)
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (methodValidateAuthTicket == null)
|
||||||
{
|
{
|
||||||
foreach (var handle in eventDel.GetInvocationList())
|
Log.Error("Unable to find the GameServer_ValidateAuthTicketResponse method to unhook");
|
||||||
{
|
return false;
|
||||||
if (handle.Method.Name == "GameServer_ValidateAuthTicketResponse")
|
|
||||||
{
|
|
||||||
SteamServerAPI.Instance.GameServer.ValidateAuthTicketResponse -= handle as ValidateAuthTicketResponse;
|
|
||||||
Log.Debug("Removed GameServer_ValidateAuthTicketResponse");
|
|
||||||
}
|
}
|
||||||
}
|
var eventValidateAuthTicket = Reflection.GetInstanceEvent(MyGameService.GameServer, nameof(MyGameService.GameServer.ValidateAuthTicketResponse))
|
||||||
}
|
.FirstOrDefault(x => x.Method == methodValidateAuthTicket) as Action<ulong, JoinResult, ulong>;
|
||||||
eventField = typeof(GameServer).GetField("<backing_store>UserGroupStatus", BindingFlags.NonPublic | BindingFlags.Instance);
|
if (eventValidateAuthTicket == null)
|
||||||
eventDel = eventField?.GetValue(SteamServerAPI.Instance.GameServer) as MulticastDelegate;
|
|
||||||
if (eventDel != null)
|
|
||||||
{
|
{
|
||||||
foreach (var handle in eventDel.GetInvocationList())
|
Log.Error(
|
||||||
|
"Unable to unhook the GameServer_ValidateAuthTicketResponse method from GameServer.ValidateAuthTicketResponse");
|
||||||
|
Log.Debug(" Want to unhook {0}", methodValidateAuthTicket);
|
||||||
|
Log.Debug(" Registered handlers: ");
|
||||||
|
foreach (Delegate method in Reflection.GetInstanceEvent(MyGameService.GameServer,
|
||||||
|
nameof(MyGameService.GameServer.ValidateAuthTicketResponse)))
|
||||||
|
Log.Debug(" - " + method.Method);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo methodUserGroupStatus = typeof(MyDedicatedServerBase).GetMethod("GameServer_UserGroupStatus",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (methodUserGroupStatus == null)
|
||||||
{
|
{
|
||||||
if (handle.Method.Name == "GameServer_UserGroupStatus")
|
Log.Error("Unable to find the GameServer_UserGroupStatus method to unhook");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var eventUserGroupStatus = Reflection.GetInstanceEvent(MyGameService.GameServer, nameof(MyGameService.GameServer.UserGroupStatusResponse))
|
||||||
|
.FirstOrDefault(x => x.Method == methodUserGroupStatus)
|
||||||
|
as Action<ulong, ulong, bool, bool>;
|
||||||
|
if (eventUserGroupStatus == null)
|
||||||
{
|
{
|
||||||
SteamServerAPI.Instance.GameServer.UserGroupStatus -= handle as UserGroupStatus;
|
Log.Error("Unable to unhook the GameServer_UserGroupStatus method from GameServer.UserGroupStatus");
|
||||||
Log.Debug("Removed GameServer_UserGroupStatus");
|
Log.Debug(" Want to unhook {0}", methodUserGroupStatus);
|
||||||
}
|
Log.Debug(" Registered handlers: ");
|
||||||
}
|
foreach (Delegate method in Reflection.GetInstanceEvent(MyGameService.GameServer, nameof(MyGameService.GameServer.UserGroupStatusResponse)))
|
||||||
|
Log.Debug(" - " + method.Method);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MyGameService.GameServer.ValidateAuthTicketResponse -=
|
||||||
|
eventValidateAuthTicket;
|
||||||
|
MyGameService.GameServer.UserGroupStatusResponse -=
|
||||||
|
eventUserGroupStatus;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Largely copied from SE
|
//Largely copied from SE
|
||||||
private void ValidateAuthTicketResponse(ulong steamID, AuthSessionResponseEnum response, ulong ownerSteamID)
|
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
|
||||||
{
|
{
|
||||||
Log.Info($"Server ValidateAuthTicketResponse ({response}), owner: {ownerSteamID}");
|
Log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
||||||
|
if (IsClientBanned.Invoke(MyMultiplayer.Static, steamOwner) || MySandboxGame.ConfigDedicated.Banned.Contains(steamOwner))
|
||||||
if (steamID != ownerSteamID)
|
|
||||||
{
|
{
|
||||||
Log.Info($"User {steamID} is using a game owned by {ownerSteamID}. Tracking...");
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.BannedByAdmins);
|
||||||
_gameOwnerIds[steamID] = ownerSteamID;
|
RaiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
||||||
|
|
||||||
if (MySandboxGame.ConfigDedicated.Banned.Contains(ownerSteamID))
|
|
||||||
{
|
|
||||||
Log.Info($"Game owner {ownerSteamID} is banned. Banning and rejecting client {steamID}...");
|
|
||||||
UserRejected(steamID, JoinResult.BannedByAdmins);
|
|
||||||
BanPlayer(steamID);
|
|
||||||
}
|
}
|
||||||
|
else if (IsClientKicked.Invoke(MyMultiplayer.Static, steamOwner))
|
||||||
|
{
|
||||||
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.KickedRecently);
|
||||||
|
RaiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
||||||
|
}
|
||||||
|
if (response != JoinResult.OK)
|
||||||
|
{
|
||||||
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MyMultiplayer.Static.MemberLimit > 0 && _members.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Count - 1 >= MyMultiplayer.Static.MemberLimit)
|
||||||
|
{
|
||||||
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.ServerFull);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(steamID.ToString()) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(ConvertSteamIDFrom64(steamID)))
|
||||||
|
{
|
||||||
|
this.UserAccepted(steamID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (GetServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
||||||
|
{
|
||||||
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.GroupIdInvalid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
||||||
|
{
|
||||||
|
_waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Add(steamID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.SteamServersOffline);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response == AuthSessionResponseEnum.OK)
|
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
|
||||||
{
|
{
|
||||||
if (MySession.Static.MaxPlayers > 0 && _members.Count - 1 >= MySession.Static.MaxPlayers)
|
if (groupId == MySandboxGame.ConfigDedicated.GroupID && _waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Remove(userId))
|
||||||
{
|
|
||||||
UserRejected(steamID, JoinResult.ServerFull);
|
|
||||||
}
|
|
||||||
else if (MySandboxGame.ConfigDedicated.Administrators.Contains(steamID.ToString()) /*|| MySandboxGame.ConfigDedicated.Administrators.Contains(MyDedicatedServerBase.ConvertSteamIDFrom64(steamID))*/)
|
|
||||||
{
|
|
||||||
UserAccepted(steamID);
|
|
||||||
}
|
|
||||||
else if (MySandboxGame.ConfigDedicated.GroupID == 0)
|
|
||||||
{
|
|
||||||
switch (MySession.Static.OnlineMode)
|
|
||||||
{
|
|
||||||
case MyOnlineModeEnum.PUBLIC:
|
|
||||||
UserAccepted(steamID);
|
|
||||||
break;
|
|
||||||
case MyOnlineModeEnum.PRIVATE:
|
|
||||||
UserRejected(steamID, JoinResult.NotInGroup);
|
|
||||||
break;
|
|
||||||
case MyOnlineModeEnum.FRIENDS:
|
|
||||||
//TODO: actually verify friendship
|
|
||||||
UserRejected(steamID, JoinResult.NotInGroup);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (SteamServerAPI.Instance.GetAccountType(MySandboxGame.ConfigDedicated.GroupID) != AccountType.Clan)
|
|
||||||
{
|
|
||||||
UserRejected(steamID, JoinResult.GroupIdInvalid);
|
|
||||||
}
|
|
||||||
else if (SteamServerAPI.Instance.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
|
||||||
{
|
|
||||||
// Returns false when there's no connection to Steam
|
|
||||||
_waitingForGroup.Add(steamID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UserRejected(steamID, JoinResult.SteamServersOffline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JoinResult joinResult = JoinResult.TicketInvalid;
|
|
||||||
switch (response)
|
|
||||||
{
|
|
||||||
case AuthSessionResponseEnum.AuthTicketCanceled:
|
|
||||||
joinResult = JoinResult.TicketCanceled;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.AuthTicketInvalidAlreadyUsed:
|
|
||||||
joinResult = JoinResult.TicketAlreadyUsed;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.LoggedInElseWhere:
|
|
||||||
joinResult = JoinResult.LoggedInElseWhere;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.NoLicenseOrExpired:
|
|
||||||
joinResult = JoinResult.NoLicenseOrExpired;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.UserNotConnectedToSteam:
|
|
||||||
joinResult = JoinResult.UserNotConnected;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.VACBanned:
|
|
||||||
joinResult = JoinResult.VACBanned;
|
|
||||||
break;
|
|
||||||
case AuthSessionResponseEnum.VACCheckTimedOut:
|
|
||||||
joinResult = JoinResult.VACCheckTimedOut;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
UserRejected(steamID, joinResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserGroupStatus(ulong userId, ulong groupId, bool member, bool officer)
|
|
||||||
{
|
|
||||||
if (groupId == MySandboxGame.ConfigDedicated.GroupID && _waitingForGroup.Remove(userId))
|
|
||||||
{
|
{
|
||||||
if (member || officer)
|
if (member || officer)
|
||||||
{
|
|
||||||
UserAccepted(userId);
|
UserAccepted(userId);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
UserRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, userId, JoinResult.NotInGroup);
|
||||||
UserRejected(userId, JoinResult.NotInGroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserAccepted(ulong steamId)
|
private void UserAccepted(ulong steamId)
|
||||||
{
|
{
|
||||||
typeof(MyDedicatedServerBase).GetMethod("UserAccepted", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(MyMultiplayer.Static, new object[] {steamId});
|
UserAcceptedImpl.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamId);
|
||||||
var vm = new PlayerViewModel(steamId) {State = ConnectionState.Connected};
|
|
||||||
|
var vm = new PlayerViewModel(steamId) { State = ConnectionState.Connected };
|
||||||
Log.Info($"Player {vm.Name} joined ({vm.SteamId})");
|
Log.Info($"Player {vm.Name} joined ({vm.SteamId})");
|
||||||
Players.Add(steamId, vm);
|
Players.Add(steamId, vm);
|
||||||
PlayerJoined?.Invoke(vm);
|
PlayerJoined?.Invoke(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserRejected(ulong steamId, JoinResult reason)
|
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase))]
|
||||||
{
|
private static Func<ulong, string> ConvertSteamIDFrom64;
|
||||||
typeof(MyDedicatedServerBase).GetMethod("UserRejected", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(MyMultiplayer.Static, new object[] {steamId, reason});
|
|
||||||
}
|
[ReflectedStaticMethod(Type = typeof(MyGameService))]
|
||||||
|
private static Func<ulong, MyGameServiceAccountType> GetServerAccountType;
|
||||||
|
|
||||||
|
[ReflectedMethod(Name = "UserAccepted")]
|
||||||
|
private static Action<MyDedicatedServerBase, ulong> UserAcceptedImpl;
|
||||||
|
|
||||||
|
[ReflectedMethod]
|
||||||
|
private static Action<MyDedicatedServerBase, ulong, JoinResult> UserRejected;
|
||||||
|
[ReflectedMethod]
|
||||||
|
private static Func<MyMultiplayerBase, ulong, bool> IsClientBanned;
|
||||||
|
[ReflectedMethod]
|
||||||
|
private static Func<MyMultiplayerBase, ulong, bool> IsClientKicked;
|
||||||
|
[ReflectedMethod]
|
||||||
|
private static Action<MyMultiplayerBase, ulong> RaiseClientKicked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ using Sandbox.Engine.Multiplayer;
|
|||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
using VRage;
|
using VRage;
|
||||||
using VRage.Library.Collections;
|
using VRage.Library.Collections;
|
||||||
using VRage.Network;
|
using VRage.Network;
|
||||||
@@ -20,12 +21,15 @@ namespace Torch.Managers
|
|||||||
private static Logger _log = LogManager.GetLogger(nameof(NetworkManager));
|
private static Logger _log = LogManager.GetLogger(nameof(NetworkManager));
|
||||||
|
|
||||||
private const string MyTransportLayerField = "TransportLayer";
|
private const string MyTransportLayerField = "TransportLayer";
|
||||||
private const string TypeTableField = "m_typeTable";
|
|
||||||
private const string TransportHandlersField = "m_handlers";
|
private const string TransportHandlersField = "m_handlers";
|
||||||
private MyTypeTable m_typeTable = new MyTypeTable();
|
|
||||||
private HashSet<INetworkHandler> _networkHandlers = new HashSet<INetworkHandler>();
|
private HashSet<INetworkHandler> _networkHandlers = new HashSet<INetworkHandler>();
|
||||||
private bool _init;
|
private bool _init;
|
||||||
|
|
||||||
|
[ReflectedGetter(Name = "m_typeTable")]
|
||||||
|
private static Func<MyReplicationLayerBase, MyTypeTable> _typeTableGetter;
|
||||||
|
[ReflectedGetter(Name = "m_methodInfoLookup")]
|
||||||
|
private static Func<MyEventTable, Dictionary<MethodInfo, CallSite>> _methodInfoLookupGetter;
|
||||||
|
|
||||||
public NetworkManager(ITorchBase torchInstance) : base(torchInstance)
|
public NetworkManager(ITorchBase torchInstance) : base(torchInstance)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -43,10 +47,6 @@ namespace Torch.Managers
|
|||||||
|
|
||||||
var transportLayerType = transportLayerField.FieldType;
|
var transportLayerType = transportLayerField.FieldType;
|
||||||
|
|
||||||
var replicationLayerType = typeof(MyReplicationLayerBase);
|
|
||||||
if (!Reflection.HasField(replicationLayerType, TypeTableField))
|
|
||||||
throw new TypeLoadException("Could not find TypeTable field");
|
|
||||||
|
|
||||||
if (!Reflection.HasField(transportLayerType, TransportHandlersField))
|
if (!Reflection.HasField(transportLayerType, TransportHandlersField))
|
||||||
throw new TypeLoadException("Could not find Handlers field");
|
throw new TypeLoadException("Could not find Handlers field");
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,6 @@ namespace Torch.Managers
|
|||||||
if (!ReflectionUnitTest())
|
if (!ReflectionUnitTest())
|
||||||
throw new InvalidOperationException("Reflection unit test failed.");
|
throw new InvalidOperationException("Reflection unit test failed.");
|
||||||
|
|
||||||
m_typeTable = typeof(MyReplicationLayerBase).GetField(TypeTableField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(MyMultiplayer.ReplicationLayer) as MyTypeTable;
|
|
||||||
//don't bother with nullchecks here, it was all handled in ReflectionUnitTest
|
//don't bother with nullchecks here, it was all handled in ReflectionUnitTest
|
||||||
var transportType = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance).FieldType;
|
var transportType = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance).FieldType;
|
||||||
var transportInstance = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(MyMultiplayer.Static.SyncLayer);
|
var transportInstance = typeof(MySyncLayer).GetField(MyTransportLayerField, BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(MyMultiplayer.Static.SyncLayer);
|
||||||
@@ -146,7 +145,7 @@ namespace Torch.Managers
|
|||||||
object obj;
|
object obj;
|
||||||
if (networkId.IsInvalid) // Static event
|
if (networkId.IsInvalid) // Static event
|
||||||
{
|
{
|
||||||
site = m_typeTable.StaticEventTable.Get(eventId);
|
site = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).StaticEventTable.Get(eventId);
|
||||||
obj = null;
|
obj = null;
|
||||||
}
|
}
|
||||||
else // Instance event
|
else // Instance event
|
||||||
@@ -156,7 +155,7 @@ namespace Torch.Managers
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var typeInfo = m_typeTable.Get(sendAs.GetType());
|
var typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(sendAs.GetType());
|
||||||
var eventCount = typeInfo.EventTable.Count;
|
var eventCount = typeInfo.EventTable.Count;
|
||||||
if (eventId < eventCount) // Directly
|
if (eventId < eventCount) // Directly
|
||||||
{
|
{
|
||||||
@@ -166,7 +165,7 @@ namespace Torch.Managers
|
|||||||
else // Through proxy
|
else // Through proxy
|
||||||
{
|
{
|
||||||
obj = ((IMyProxyTarget)sendAs).Target;
|
obj = ((IMyProxyTarget)sendAs).Target;
|
||||||
typeInfo = m_typeTable.Get(obj.GetType());
|
typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(obj.GetType());
|
||||||
site = typeInfo.EventTable.Get(eventId - (uint)eventCount); // Subtract max id of Proxy
|
site = typeInfo.EventTable.Get(eventId - (uint)eventCount); // Subtract max id of Proxy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +279,7 @@ namespace Torch.Managers
|
|||||||
if (obj != null && owner == null)
|
if (obj != null && owner == null)
|
||||||
throw new InvalidCastException("Provided event target is not of type IMyEventOwner!");
|
throw new InvalidCastException("Provided event target is not of type IMyEventOwner!");
|
||||||
|
|
||||||
if(!method.HasAttribute<EventAttribute>())
|
if (!method.HasAttribute<EventAttribute>())
|
||||||
throw new CustomAttributeFormatException("Provided event target does not have the Event attribute! Replication will not succeed!");
|
throw new CustomAttributeFormatException("Provided event target does not have the Event attribute! Replication will not succeed!");
|
||||||
|
|
||||||
//array to hold arguments to pass into DispatchEvent
|
//array to hold arguments to pass into DispatchEvent
|
||||||
@@ -356,17 +355,16 @@ namespace Torch.Managers
|
|||||||
|
|
||||||
private CallSite TryGetStaticCallSite(MethodInfo method)
|
private CallSite TryGetStaticCallSite(MethodInfo method)
|
||||||
{
|
{
|
||||||
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(m_typeTable.StaticEventTable);
|
MyTypeTable typeTable = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer);
|
||||||
if (!methodLookup.TryGetValue(method, out CallSite result))
|
if (!_methodInfoLookupGetter.Invoke(typeTable.StaticEventTable).TryGetValue(method, out CallSite result))
|
||||||
throw new MissingMemberException("Provided event target not found!");
|
throw new MissingMemberException("Provided event target not found!");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallSite TryGetCallSite(MethodInfo method, object arg)
|
private CallSite TryGetCallSite(MethodInfo method, object arg)
|
||||||
{
|
{
|
||||||
var typeInfo = m_typeTable.Get(arg.GetType());
|
MySynchronizedTypeInfo typeInfo = _typeTableGetter.Invoke(MyMultiplayer.ReplicationLayer).Get(arg.GetType());
|
||||||
var methodLookup = (Dictionary<MethodInfo, CallSite>)typeof(MyEventTable).GetField("m_methodInfoLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(typeInfo.EventTable);
|
if (!_methodInfoLookupGetter.Invoke(typeInfo.EventTable).TryGetValue(method, out CallSite result))
|
||||||
if (!methodLookup.TryGetValue(method, out CallSite result))
|
|
||||||
throw new MissingMemberException("Provided event target not found!");
|
throw new MissingMemberException("Provided event target not found!");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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();
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,12 @@ namespace Torch.Managers
|
|||||||
|
|
||||||
var zip = latest.Assets.FirstOrDefault(x => x.Name.Contains(".zip"));
|
var zip = latest.Assets.FirstOrDefault(x => x.Name.Contains(".zip"));
|
||||||
var versionName = Regex.Match(latest.TagName, "(\\d+\\.)+\\d+").ToString();
|
var versionName = Regex.Match(latest.TagName, "(\\d+\\.)+\\d+").ToString();
|
||||||
return new Tuple<Version, string>(new Version(string.IsNullOrWhiteSpace(versionName) ? versionName : "0.0"), zip?.BrowserDownloadUrl);
|
if (string.IsNullOrWhiteSpace(versionName))
|
||||||
|
{
|
||||||
|
_log.Warn("Unable to parse tag {0} for {1}/{2}", latest.TagName, owner, name);
|
||||||
|
versionName = "0.0";
|
||||||
|
}
|
||||||
|
return new Tuple<Version, string>(new Version(versionName), zip?.BrowserDownloadUrl);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -103,6 +108,9 @@ namespace Torch.Managers
|
|||||||
|
|
||||||
private async void CheckAndUpdateTorch()
|
private async void CheckAndUpdateTorch()
|
||||||
{
|
{
|
||||||
|
// Doesn't work properly or reliably, TODO update when Jenkins is fully configured
|
||||||
|
return;
|
||||||
|
|
||||||
if (!Torch.Config.GetTorchUpdates)
|
if (!Torch.Config.GetTorchUpdates)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -146,7 +154,7 @@ namespace Torch.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public override void Detach()
|
||||||
{
|
{
|
||||||
_updatePollTimer?.Dispose();
|
_updatePollTimer?.Dispose();
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
[assembly: AssemblyTitle("Torch")]
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("Piston")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("Piston")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
#if DEBUG
|
||||||
[assembly: Guid("7e01635c-3b67-472e-bcd6-c5539564f214")]
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
// Version information for an assembly consists of the following four values:
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
//
|
#endif
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@@ -1,131 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
namespace Torch
|
|
||||||
{
|
|
||||||
public static class Reflection
|
|
||||||
{
|
|
||||||
private static readonly Logger Log = LogManager.GetLogger("Reflection");
|
|
||||||
|
|
||||||
public static bool HasMethod(Type type, string methodName, Type[] argTypes = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(methodName))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (argTypes == null)
|
|
||||||
{
|
|
||||||
var methodInfo = type.GetMethod(methodName);
|
|
||||||
if (methodInfo == null)
|
|
||||||
methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (methodInfo == null && type.BaseType != null)
|
|
||||||
methodInfo = type.BaseType.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (methodInfo == null)
|
|
||||||
{
|
|
||||||
Log.Error( "Failed to find method '" + methodName + "' in type '" + type.FullName + "'" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MethodInfo method = type.GetMethod(methodName, argTypes);
|
|
||||||
if (method == null)
|
|
||||||
method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null);
|
|
||||||
if (method == null && type.BaseType != null)
|
|
||||||
method = type.BaseType.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
Log.Error( "Failed to find method '" + methodName + "' in type '" + type.FullName + "'" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (AmbiguousMatchException)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error( "Failed to find method '" + methodName + "' in type '" + type.FullName + "': " + ex.Message );
|
|
||||||
Log.Error( ex );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasField(Type type, string fieldName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(fieldName))
|
|
||||||
return false;
|
|
||||||
var field = type.GetField(fieldName);
|
|
||||||
if (field == null)
|
|
||||||
field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (field == null)
|
|
||||||
field = type.BaseType.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (field == null)
|
|
||||||
{
|
|
||||||
Log.Error("Failed to find field '{0}' in type '{1}'", fieldName, type.FullName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Failed to find field '{0}' in type '{1}'", fieldName, type.FullName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasProperty(Type type, string propertyName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(propertyName))
|
|
||||||
return false;
|
|
||||||
var prop = type.GetProperty(propertyName);
|
|
||||||
if (prop == null)
|
|
||||||
prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (prop == null)
|
|
||||||
prop = type.BaseType?.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
|
||||||
if (prop == null)
|
|
||||||
{
|
|
||||||
Log.Error("Failed to find property '{0}' in type '{1}'", propertyName, type.FullName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Failed to find property '{0}' in type '{1}'", propertyName, type.FullName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object InvokeStaticMethod(Type type, string methodName, params object[] args)
|
|
||||||
{
|
|
||||||
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
Log.Error($"Method {methodName} not found in static class {type.FullName}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return method.Invoke(null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T GetPrivateField<T>(this object obj, string fieldName)
|
|
||||||
{
|
|
||||||
var type = obj.GetType();
|
|
||||||
return (T)type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
49
Torch/Session/TorchSession.cs
Normal file
49
Torch/Session/TorchSession.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Managers;
|
||||||
|
|
||||||
|
namespace Torch.Session
|
||||||
|
{
|
||||||
|
public class TorchSession : ITorchSession
|
||||||
|
{
|
||||||
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Torch instance this session is bound to
|
||||||
|
/// </summary>
|
||||||
|
public ITorchBase Torch { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Space Engineers game session this session is bound to.
|
||||||
|
/// </summary>
|
||||||
|
public MySession KeenSession { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
|
public IDependencyManager Managers { get; }
|
||||||
|
|
||||||
|
public TorchSession(ITorchBase torch, MySession keenSession)
|
||||||
|
{
|
||||||
|
Torch = torch;
|
||||||
|
KeenSession = keenSession;
|
||||||
|
Managers = new DependencyManager(torch.Managers);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Attach()
|
||||||
|
{
|
||||||
|
Managers.Attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Detach()
|
||||||
|
{
|
||||||
|
Managers.Detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
Torch/Session/TorchSessionManager.cs
Normal file
93
Torch/Session/TorchSessionManager.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Managers;
|
||||||
|
using Torch.Session;
|
||||||
|
|
||||||
|
namespace Torch.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the creation and destruction of <see cref="TorchSession"/> instances for each <see cref="MySession"/> created by Space Engineers.
|
||||||
|
/// </summary>
|
||||||
|
public class TorchSessionManager : Manager, ITorchSessionManager
|
||||||
|
{
|
||||||
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
private TorchSession _currentSession;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ITorchSession CurrentSession => _currentSession;
|
||||||
|
|
||||||
|
private readonly HashSet<SessionManagerFactoryDel> _factories = new HashSet<SessionManagerFactoryDel>();
|
||||||
|
|
||||||
|
public TorchSessionManager(ITorchBase torchInstance) : base(torchInstance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool AddFactory(SessionManagerFactoryDel factory)
|
||||||
|
{
|
||||||
|
if (factory == null)
|
||||||
|
throw new ArgumentNullException(nameof(factory), "Factory must be non-null");
|
||||||
|
return _factories.Add(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool RemoveFactory(SessionManagerFactoryDel factory)
|
||||||
|
{
|
||||||
|
if (factory == null)
|
||||||
|
throw new ArgumentNullException(nameof(factory), "Factory must be non-null");
|
||||||
|
return _factories.Remove(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SessionLoaded()
|
||||||
|
{
|
||||||
|
if (_currentSession != null)
|
||||||
|
{
|
||||||
|
_log.Warn($"Override old torch session {_currentSession.KeenSession.Name}");
|
||||||
|
_currentSession.Detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.Info($"Starting new torch session for {MySession.Static.Name}");
|
||||||
|
_currentSession = new TorchSession(Torch, MySession.Static);
|
||||||
|
foreach (SessionManagerFactoryDel factory in _factories)
|
||||||
|
{
|
||||||
|
IManager manager = factory(CurrentSession);
|
||||||
|
if (manager != null)
|
||||||
|
CurrentSession.Managers.AddManager(manager);
|
||||||
|
}
|
||||||
|
(CurrentSession as TorchSession)?.Attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SessionUnloaded()
|
||||||
|
{
|
||||||
|
if (_currentSession == null)
|
||||||
|
return;
|
||||||
|
_log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}");
|
||||||
|
_currentSession.Detach();
|
||||||
|
_currentSession = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Attach()
|
||||||
|
{
|
||||||
|
MySession.AfterLoading += SessionLoaded;
|
||||||
|
MySession.OnUnloaded += SessionUnloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Detach()
|
||||||
|
{
|
||||||
|
_currentSession?.Detach();
|
||||||
|
_currentSession = null;
|
||||||
|
MySession.AfterLoading -= SessionLoaded;
|
||||||
|
MySession.OnUnloaded -= SessionUnloaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,9 +3,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
using SteamSDK;
|
using SteamSDK;
|
||||||
using VRage.Steam;
|
using VRage.Steam;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Torch.Utils;
|
||||||
|
using VRage.GameServices;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
@@ -17,48 +21,75 @@ namespace Torch
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SteamService : MySteamService
|
public class SteamService : MySteamService
|
||||||
{
|
{
|
||||||
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
#pragma warning disable 649
|
||||||
|
[ReflectedSetter(Name = nameof(SteamServerAPI))]
|
||||||
|
private static Action<MySteamService, SteamServerAPI> _steamServerAPISetter;
|
||||||
|
[ReflectedSetter(Name = "m_gameServer")]
|
||||||
|
private static Action<MySteamService, MySteamGameServer> _steamGameServerSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(AppId))]
|
||||||
|
private static Action<MySteamService, uint> _steamAppIdSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(API))]
|
||||||
|
private static Action<MySteamService, SteamAPI> _steamApiSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(IsActive))]
|
||||||
|
private static Action<MySteamService, bool> _steamIsActiveSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(UserId))]
|
||||||
|
private static Action<MySteamService, ulong> _steamUserIdSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(UserName))]
|
||||||
|
private static Action<MySteamService, string> _steamUserNameSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(OwnsGame))]
|
||||||
|
private static Action<MySteamService, bool> _steamOwnsGameSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(UserUniverse))]
|
||||||
|
private static Action<MySteamService, MyGameServiceUniverse> _steamUserUniverseSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(BranchName))]
|
||||||
|
private static Action<MySteamService, string> _steamBranchNameSetter;
|
||||||
|
[ReflectedSetter(Name = nameof(InventoryAPI))]
|
||||||
|
private static Action<MySteamService, MySteamInventory> _steamInventoryAPISetter;
|
||||||
|
[ReflectedMethod]
|
||||||
|
private static Action<MySteamService> RegisterCallbacks;
|
||||||
|
[ReflectedSetter(Name = nameof(Peer2Peer))]
|
||||||
|
private static Action<MySteamService, IMyPeer2Peer> _steamPeer2PeerSetter;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
public SteamService(bool isDedicated, uint appId)
|
public SteamService(bool isDedicated, uint appId)
|
||||||
: base(true, appId)
|
: base(true, appId)
|
||||||
{
|
{
|
||||||
// TODO: Add protection for this mess... somewhere
|
SteamServerAPI.Instance.Dispose();
|
||||||
SteamSDK.SteamServerAPI.Instance.Dispose();
|
_steamServerAPISetter.Invoke(this, null);
|
||||||
var steam = typeof(MySteamService);
|
_steamGameServerSetter.Invoke(this, null);
|
||||||
|
_steamAppIdSetter.Invoke(this, appId);
|
||||||
|
|
||||||
steam.GetProperty("SteamServerAPI").GetSetMethod(true).Invoke(this, new object[] { null });
|
|
||||||
steam.GetField("m_gameServer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(this, null);
|
|
||||||
|
|
||||||
steam.GetProperty("AppId").GetSetMethod(true).Invoke(this, new object[] { appId });
|
|
||||||
if (isDedicated)
|
if (isDedicated)
|
||||||
{
|
{
|
||||||
steam.GetProperty("SteamServerAPI").GetSetMethod(true).Invoke(this, new object[] { null });
|
_steamServerAPISetter.Invoke(this, null);
|
||||||
steam.GetField("m_gameServer").SetValue(this, new MySteamGameServer());
|
_steamGameServerSetter.Invoke(this, new MySteamGameServer());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var SteamAPI = SteamSDK.SteamAPI.Instance;
|
SteamAPI steamApi = SteamAPI.Instance;
|
||||||
steam.GetProperty("API").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.Instance });
|
_steamApiSetter.Invoke(this, steamApi);
|
||||||
steam.GetProperty("IsActive").GetSetMethod(true).Invoke(this, new object[] {
|
bool initResult = steamApi.Init();
|
||||||
SteamAPI.Instance.Init()
|
if (!initResult)
|
||||||
});
|
_log.Warn("Failed to initialize SteamService");
|
||||||
|
_steamIsActiveSetter.Invoke(this, initResult);
|
||||||
|
|
||||||
if (IsActive)
|
if (IsActive)
|
||||||
{
|
{
|
||||||
steam.GetProperty("UserId").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.GetSteamUserId() });
|
_steamUserIdSetter.Invoke(this, steamApi.GetSteamUserId());
|
||||||
steam.GetProperty("UserName").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.GetSteamName() });
|
_steamUserNameSetter.Invoke(this, steamApi.GetSteamName());
|
||||||
steam.GetProperty("OwnsGame").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.HasGame() });
|
_steamOwnsGameSetter.Invoke(this, steamApi.HasGame());
|
||||||
steam.GetProperty("UserUniverse").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.GetSteamUserUniverse() });
|
_steamUserUniverseSetter.Invoke(this, (MyGameServiceUniverse)steamApi.GetSteamUserUniverse());
|
||||||
steam.GetProperty("BranchName").GetSetMethod(true).Invoke(this, new object[] { SteamAPI.GetBranchName() });
|
_steamBranchNameSetter.Invoke(this, steamApi.GetBranchName());
|
||||||
SteamAPI.LoadStats();
|
steamApi.LoadStats();
|
||||||
|
|
||||||
steam.GetProperty("InventoryAPI").GetSetMethod(true).Invoke(this, new object[] { new MySteamInventory() });
|
_steamInventoryAPISetter.Invoke(this, new MySteamInventory());
|
||||||
|
RegisterCallbacks(this);
|
||||||
steam.GetMethod("RegisterCallbacks",
|
} else
|
||||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
_log.Warn("SteamService isn't initialized; Torch Client won't start");
|
||||||
.Invoke(this, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
steam.GetProperty("Peer2Peer").GetSetMethod(true).Invoke(this, new object[] { new MySteamPeer2Peer() });
|
_steamPeer2PeerSetter.Invoke(this, new MySteamPeer2Peer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
@@ -12,10 +10,12 @@
|
|||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</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 +23,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">
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<HintPath>..\GameBinaries\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\GameBinaries\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Octokit, Version=0.24.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Octokit, Version=0.24.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
@@ -143,14 +143,20 @@
|
|||||||
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
<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="Properties\AssemblyInfo.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" />
|
||||||
@@ -176,11 +182,15 @@
|
|||||||
<Compile Include="Managers\UpdateManager.cs" />
|
<Compile Include="Managers\UpdateManager.cs" />
|
||||||
<Compile Include="Persistent.cs" />
|
<Compile Include="Persistent.cs" />
|
||||||
<Compile Include="PluginManifest.cs" />
|
<Compile Include="PluginManifest.cs" />
|
||||||
<Compile Include="Reflection.cs" />
|
<Compile Include="Utils\Reflection.cs" />
|
||||||
<Compile Include="Managers\ScriptingManager.cs" />
|
<Compile Include="Managers\ScriptingManager.cs" />
|
||||||
|
<Compile Include="Utils\TorchAssemblyResolver.cs" />
|
||||||
|
<Compile Include="Utils\ReflectedManager.cs" />
|
||||||
|
<Compile Include="Session\TorchSessionManager.cs" />
|
||||||
<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="Session\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" />
|
||||||
@@ -189,7 +199,6 @@
|
|||||||
<Compile Include="ViewModels\PlayerViewModel.cs" />
|
<Compile Include="ViewModels\PlayerViewModel.cs" />
|
||||||
<Compile Include="ViewModels\ViewModel.cs" />
|
<Compile Include="ViewModels\ViewModel.cs" />
|
||||||
<Compile Include="Managers\PluginManager.cs" />
|
<Compile Include="Managers\PluginManager.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="ViewModels\PluginViewModel.cs" />
|
<Compile Include="ViewModels\PluginViewModel.cs" />
|
||||||
<Compile Include="Views\CollectionEditor.xaml.cs">
|
<Compile Include="Views\CollectionEditor.xaml.cs">
|
||||||
<DependentUpon>CollectionEditor.xaml</DependentUpon>
|
<DependentUpon>CollectionEditor.xaml</DependentUpon>
|
||||||
@@ -199,7 +208,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>
|
||||||
@@ -211,12 +220,9 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -19,8 +19,11 @@ using SpaceEngineers.Game;
|
|||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.ModAPI;
|
using Torch.API.ModAPI;
|
||||||
|
using Torch.API.Session;
|
||||||
using Torch.Commands;
|
using Torch.Commands;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Torch.Session;
|
||||||
using VRage.Collections;
|
using VRage.Collections;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game.ObjectBuilder;
|
using VRage.Game.ObjectBuilder;
|
||||||
@@ -36,6 +39,12 @@ namespace Torch
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class TorchBase : ViewModel, ITorchBase, IPlugin
|
public abstract class TorchBase : ViewModel, ITorchBase, IPlugin
|
||||||
{
|
{
|
||||||
|
static TorchBase()
|
||||||
|
{
|
||||||
|
// We can safely never detach this since we don't reload assemblies.
|
||||||
|
new ReflectedManager().Attach();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hack because *keen*.
|
/// Hack because *keen*.
|
||||||
/// Use only if necessary, prefer dependency injection.
|
/// Use only if necessary, prefer dependency injection.
|
||||||
@@ -44,21 +53,36 @@ namespace Torch
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ITorchConfig Config { get; protected set; }
|
public ITorchConfig Config { get; protected set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Version TorchVersion { get; protected set; }
|
public Version TorchVersion { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The version of Torch used, with extra data.
|
||||||
|
/// </summary>
|
||||||
|
public string TorchVersionVerbose { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Version GameVersion { get; private set; }
|
public Version GameVersion { get; private set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string[] RunArgs { get; set; }
|
public string[] RunArgs { get; set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public IPluginManager Plugins { get; protected set; }
|
public IPluginManager Plugins { get; protected set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public IMultiplayerManager Multiplayer { get; protected set; }
|
public IMultiplayerManager Multiplayer { get; protected set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public EntityManager Entities { get; protected set; }
|
public EntityManager Entities { get; protected set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public INetworkManager Network { get; protected set; }
|
public INetworkManager Network { get; protected set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public CommandManager Commands { get; protected set; }
|
public CommandManager Commands { get; protected set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ITorchSession CurrentSession => Managers?.GetManager<ITorchSessionManager>()?.CurrentSession;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action SessionLoading;
|
public event Action SessionLoading;
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -72,7 +96,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>
|
||||||
@@ -87,40 +114,40 @@ namespace Torch
|
|||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
TorchVersionVerbose = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? TorchVersion.ToString();
|
||||||
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 TorchSessionManager(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()
|
||||||
@@ -136,7 +163,7 @@ namespace Torch
|
|||||||
{
|
{
|
||||||
callback?.Invoke(SaveGameStatus.GameNotReady);
|
callback?.Invoke(SaveGameStatus.GameNotReady);
|
||||||
}
|
}
|
||||||
else if(MyAsyncSaving.InProgress)
|
else if (MyAsyncSaving.InProgress)
|
||||||
{
|
{
|
||||||
callback?.Invoke(SaveGameStatus.SaveInProgress);
|
callback?.Invoke(SaveGameStatus.SaveInProgress);
|
||||||
}
|
}
|
||||||
@@ -222,20 +249,24 @@ namespace Torch
|
|||||||
public virtual void Init()
|
public virtual void Init()
|
||||||
{
|
{
|
||||||
Debug.Assert(!_init, "Torch instance is already initialized.");
|
Debug.Assert(!_init, "Torch instance is already initialized.");
|
||||||
|
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
|
|
||||||
TorchVersion = Assembly.GetEntryAssembly().GetName().Version;
|
Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, "MyPerGameSettings.BasicGameInfo.GameVersion != null");
|
||||||
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
||||||
var verInfo = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}";
|
try { Console.Title = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}"; }
|
||||||
Console.Title = verInfo;
|
catch
|
||||||
|
{
|
||||||
|
///Running as service
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Log.Info("DEBUG");
|
Log.Info("DEBUG");
|
||||||
#else
|
#else
|
||||||
Log.Info("RELEASE");
|
Log.Info("RELEASE");
|
||||||
#endif
|
#endif
|
||||||
Log.Info(verInfo);
|
Log.Info($"Torch Version: {TorchVersionVerbose}");
|
||||||
|
Log.Info($"Game Version: {GameVersion}");
|
||||||
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
|
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
|
||||||
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
|
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
|
||||||
|
|
||||||
@@ -244,8 +275,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +343,7 @@ namespace Torch
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
Plugins.DisposePlugins();
|
Managers.Detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -325,7 +355,7 @@ namespace Torch
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Update()
|
public virtual void Update()
|
||||||
{
|
{
|
||||||
Plugins.UpdatePlugins();
|
GetManager<IPluginManager>().UpdatePlugins();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
338
Torch/Utils/ReflectedManager.cs
Normal file
338
Torch/Utils/ReflectedManager.cs
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Torch.API;
|
||||||
|
|
||||||
|
namespace Torch.Utils
|
||||||
|
{
|
||||||
|
public abstract class ReflectedMemberAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the member to access. If null, the tagged field's name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Declaring type of the member to access. If null, inferred from the instance argument type.
|
||||||
|
/// </summary>
|
||||||
|
public Type Type { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this field should contain a delegate capable of retrieving the value of a field.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// [ReflectedGetterAttribute(Name="_instanceField")]
|
||||||
|
/// private static Func<Example, int> _instanceGetter;
|
||||||
|
///
|
||||||
|
/// [ReflectedGetterAttribute(Name="_staticField", Type=typeof(Example))]
|
||||||
|
/// private static Func<int> _staticGetter;
|
||||||
|
///
|
||||||
|
/// private class Example {
|
||||||
|
/// private int _instanceField;
|
||||||
|
/// private static int _staticField;
|
||||||
|
/// }
|
||||||
|
/// ]]>
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class ReflectedGetterAttribute : ReflectedMemberAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this field should contain a delegate capable of setting the value of a field.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// [ReflectedSetterAttribute(Name="_instanceField")]
|
||||||
|
/// private static Action<Example, int> _instanceSetter;
|
||||||
|
///
|
||||||
|
/// [ReflectedSetterAttribute(Name="_staticField", Type=typeof(Example))]
|
||||||
|
/// private static Action<int> _staticSetter;
|
||||||
|
///
|
||||||
|
/// private class Example {
|
||||||
|
/// private int _instanceField;
|
||||||
|
/// private static int _staticField;
|
||||||
|
/// }
|
||||||
|
/// ]]>
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class ReflectedSetterAttribute : ReflectedMemberAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this field should contain a delegate capable of invoking an instance method.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// [ReflectedMethodAttribute]
|
||||||
|
/// private static Func<Example, int, float, string> ExampleInstance;
|
||||||
|
///
|
||||||
|
/// private class Example {
|
||||||
|
/// private int ExampleInstance(int a, float b) {
|
||||||
|
/// return a + ", " + b;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ]]>
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class ReflectedMethodAttribute : ReflectedMemberAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this field should contain a delegate capable of invoking a static method.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// [ReflectedMethodAttribute(Type = typeof(Example)]
|
||||||
|
/// private static Func<int, float, string> ExampleStatic;
|
||||||
|
///
|
||||||
|
/// private class Example {
|
||||||
|
/// private static int ExampleStatic(int a, float b) {
|
||||||
|
/// return a + ", " + b;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ]]>
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class ReflectedStaticMethodAttribute : ReflectedMethodAttribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Automatically calls <see cref="ReflectedManager.Process(Assembly)"/> for every assembly already loaded, and every assembly that is loaded in the future.
|
||||||
|
/// </summary>
|
||||||
|
public class ReflectedManager
|
||||||
|
{
|
||||||
|
private static readonly string[] _namespaceBlacklist = new[] {
|
||||||
|
"System", "VRage", "Sandbox", "SpaceEngineers"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the assembly load event and loads every already existing assembly.
|
||||||
|
/// </summary>
|
||||||
|
public void Attach()
|
||||||
|
{
|
||||||
|
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
Process(asm);
|
||||||
|
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deregisters the assembly load event
|
||||||
|
/// </summary>
|
||||||
|
public void Detach()
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.AssemblyLoad -= CurrentDomain_AssemblyLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
|
||||||
|
{
|
||||||
|
Process(args.LoadedAssembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly HashSet<Type> _processedTypes = new HashSet<Type>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures all reflected fields and methods contained in the given type are initialized
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">Type to process</param>
|
||||||
|
public static void Process(Type t)
|
||||||
|
{
|
||||||
|
if (_processedTypes.Add(t))
|
||||||
|
{
|
||||||
|
foreach (string ns in _namespaceBlacklist)
|
||||||
|
if (t.FullName.StartsWith(ns))
|
||||||
|
return;
|
||||||
|
foreach (FieldInfo field in t.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
Process(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures all types in the given assembly are initialized using <see cref="Process(Type)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asm">Assembly to process</param>
|
||||||
|
public static void Process(Assembly asm)
|
||||||
|
{
|
||||||
|
foreach (Type type in asm.GetTypes())
|
||||||
|
Process(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the given field, determines if it's reflected, and initializes it if it is.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="field">Field to process</param>
|
||||||
|
/// <returns>true if it was reflected, false if it wasn't reflectable</returns>
|
||||||
|
/// <exception cref="ArgumentException">If the field failed to process</exception>
|
||||||
|
public static bool Process(FieldInfo field)
|
||||||
|
{
|
||||||
|
var attr = field.GetCustomAttribute<ReflectedMethodAttribute>();
|
||||||
|
if (attr != null)
|
||||||
|
{
|
||||||
|
if (!field.IsStatic)
|
||||||
|
throw new ArgumentException("Field must be static to be reflected");
|
||||||
|
ProcessReflectedMethod(field, attr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var attr2 = field.GetCustomAttribute<ReflectedGetterAttribute>();
|
||||||
|
if (attr2 != null)
|
||||||
|
{
|
||||||
|
if (!field.IsStatic)
|
||||||
|
throw new ArgumentException("Field must be static to be reflected");
|
||||||
|
ProcessReflectedField(field, attr2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var attr3 = field.GetCustomAttribute<ReflectedSetterAttribute>();
|
||||||
|
if (attr3 != null)
|
||||||
|
{
|
||||||
|
if (!field.IsStatic)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Field must be static to be reflected");
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessReflectedField(field, attr3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessReflectedMethod(FieldInfo field, ReflectedMethodAttribute attr)
|
||||||
|
{
|
||||||
|
MethodInfo delegateMethod = field.FieldType.GetMethod("Invoke");
|
||||||
|
ParameterInfo[] parameters = delegateMethod.GetParameters();
|
||||||
|
Type trueType = attr.Type;
|
||||||
|
Type[] trueParameterTypes;
|
||||||
|
if (attr is ReflectedStaticMethodAttribute)
|
||||||
|
{
|
||||||
|
trueParameterTypes = parameters.Select(x => x.ParameterType).ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trueType = trueType ?? parameters[0].ParameterType;
|
||||||
|
trueParameterTypes = parameters.Skip(1).Select(x => x.ParameterType).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo methodInstance = trueType.GetMethod(attr.Name ?? field.Name,
|
||||||
|
(attr is ReflectedStaticMethodAttribute ? BindingFlags.Static : BindingFlags.Instance) |
|
||||||
|
BindingFlags.Public |
|
||||||
|
BindingFlags.NonPublic, null, CallingConventions.Any, trueParameterTypes, null);
|
||||||
|
if (methodInstance == null)
|
||||||
|
{
|
||||||
|
string methodType = attr is ReflectedStaticMethodAttribute ? "static" : "instance";
|
||||||
|
string methodParams = string.Join(", ",
|
||||||
|
trueParameterTypes.Select(x => x.Name));
|
||||||
|
throw new NoNullAllowedException(
|
||||||
|
$"Unable to find {methodType} method {attr.Name ?? field.Name} in type {trueType.FullName} with parameters {methodParams}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr is ReflectedStaticMethodAttribute)
|
||||||
|
field.SetValue(null, Delegate.CreateDelegate(field.FieldType, methodInstance));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParameterExpression[] paramExp = parameters.Select(x => Expression.Parameter(x.ParameterType)).ToArray();
|
||||||
|
field.SetValue(null,
|
||||||
|
Expression.Lambda(Expression.Call(paramExp[0], methodInstance, paramExp.Skip(1)), paramExp)
|
||||||
|
.Compile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T GetFieldPropRecursive<T>(Type baseType, string name, BindingFlags flags, Func<Type, string, BindingFlags, T> getter) where T : class
|
||||||
|
{
|
||||||
|
while (baseType != null)
|
||||||
|
{
|
||||||
|
T result = getter.Invoke(baseType, name, flags);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
baseType = baseType.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessReflectedField(FieldInfo field, ReflectedMemberAttribute attr)
|
||||||
|
{
|
||||||
|
MethodInfo delegateMethod = field.FieldType.GetMethod("Invoke");
|
||||||
|
ParameterInfo[] parameters = delegateMethod.GetParameters();
|
||||||
|
string trueName = attr.Name ?? field.Name;
|
||||||
|
Type trueType = attr.Type;
|
||||||
|
bool isStatic;
|
||||||
|
if (attr is ReflectedSetterAttribute)
|
||||||
|
{
|
||||||
|
if (delegateMethod.ReturnType != typeof(void) || (parameters.Length != 1 && parameters.Length != 2))
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(field),
|
||||||
|
"Delegate for setter must be an action with one or two arguments");
|
||||||
|
|
||||||
|
isStatic = parameters.Length == 1;
|
||||||
|
if (trueType == null && isStatic)
|
||||||
|
throw new ArgumentException("Static field setters need their type defined", nameof(field));
|
||||||
|
|
||||||
|
if (!isStatic)
|
||||||
|
trueType = parameters[0].ParameterType;
|
||||||
|
}
|
||||||
|
else if (attr is ReflectedGetterAttribute)
|
||||||
|
{
|
||||||
|
if (delegateMethod.ReturnType == typeof(void) || (parameters.Length != 0 && parameters.Length != 1))
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(field),
|
||||||
|
"Delegate for getter must be an function with one or no arguments");
|
||||||
|
|
||||||
|
isStatic = parameters.Length == 0;
|
||||||
|
if (trueType == null && isStatic)
|
||||||
|
throw new ArgumentException("Static field getters need their type defined", nameof(field));
|
||||||
|
|
||||||
|
if (!isStatic)
|
||||||
|
trueType = parameters[0].ParameterType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Field attribute type {attr.GetType().FullName} is invalid", nameof(field));
|
||||||
|
|
||||||
|
BindingFlags bindingFlags = (isStatic ? BindingFlags.Static : BindingFlags.Instance) |
|
||||||
|
BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Public;
|
||||||
|
FieldInfo sourceField = GetFieldPropRecursive(trueType, trueName, bindingFlags,
|
||||||
|
(a, b, c) => a.GetField(b, c));
|
||||||
|
PropertyInfo sourceProperty =
|
||||||
|
GetFieldPropRecursive(trueType, trueName, bindingFlags, (a, b, c) => a.GetProperty(b, c));
|
||||||
|
if (sourceField == null && sourceProperty == null)
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Unable to find field or property for {trueName} in {trueType.FullName} or its base types", nameof(field));
|
||||||
|
|
||||||
|
ParameterExpression[] paramExp = parameters.Select(x => Expression.Parameter(x.ParameterType)).ToArray();
|
||||||
|
|
||||||
|
MemberExpression fieldExp = sourceField != null
|
||||||
|
? Expression.Field(isStatic ? null : paramExp[0], sourceField)
|
||||||
|
: Expression.Property(isStatic ? null : paramExp[0], sourceProperty);
|
||||||
|
Expression impl;
|
||||||
|
if (attr is ReflectedSetterAttribute)
|
||||||
|
{
|
||||||
|
impl = Expression.Block(Expression.Assign(fieldExp, paramExp[isStatic ? 0 : 1]), Expression.Default(typeof(void)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
impl = fieldExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
field.SetValue(null, Expression.Lambda(impl, paramExp).Compile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
227
Torch/Utils/Reflection.cs
Normal file
227
Torch/Utils/Reflection.cs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using SteamSDK;
|
||||||
|
|
||||||
|
namespace Torch.Utils
|
||||||
|
{
|
||||||
|
public static class Reflection
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetLogger("Reflection");
|
||||||
|
|
||||||
|
public static bool HasMethod(Type type, string methodName, Type[] argTypes = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(methodName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (argTypes == null)
|
||||||
|
{
|
||||||
|
var methodInfo = type.GetMethod(methodName);
|
||||||
|
if (methodInfo == null)
|
||||||
|
methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (methodInfo == null && type.BaseType != null)
|
||||||
|
methodInfo = type.BaseType.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (methodInfo == null)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to find method '" + methodName + "' in type '" + type.FullName + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MethodInfo method = type.GetMethod(methodName, argTypes);
|
||||||
|
if (method == null)
|
||||||
|
method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null);
|
||||||
|
if (method == null && type.BaseType != null)
|
||||||
|
method = type.BaseType.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy, Type.DefaultBinder, argTypes, null);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to find method '" + methodName + "' in type '" + type.FullName + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (AmbiguousMatchException)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to find method '" + methodName + "' in type '" + type.FullName + "': " + ex.Message);
|
||||||
|
Log.Error(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasField(Type type, string fieldName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fieldName))
|
||||||
|
return false;
|
||||||
|
var field = type.GetField(fieldName);
|
||||||
|
if (field == null)
|
||||||
|
field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (field == null)
|
||||||
|
field = type.BaseType.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (field == null)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to find field '{0}' in type '{1}'", fieldName, type.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Failed to find field '{0}' in type '{1}'", fieldName, type.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasProperty(Type type, string propertyName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(propertyName))
|
||||||
|
return false;
|
||||||
|
var prop = type.GetProperty(propertyName);
|
||||||
|
if (prop == null)
|
||||||
|
prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (prop == null)
|
||||||
|
prop = type.BaseType?.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||||
|
if (prop == null)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to find property '{0}' in type '{1}'", propertyName, type.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Failed to find property '{0}' in type '{1}'", propertyName, type.FullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the static method of the given type, with the given arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type the method is contained in</param>
|
||||||
|
/// <param name="methodName">Method name</param>
|
||||||
|
/// <param name="args">Arguments to the method</param>
|
||||||
|
/// <returns>return value of the invoked method, or null if it failed</returns>
|
||||||
|
public static object InvokeStaticMethod(Type type, string methodName, params object[] args)
|
||||||
|
{
|
||||||
|
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Log.Error($"Method {methodName} not found in static class {type.FullName}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.Invoke(null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the private method with the given arguments on the instance. Includes base types of instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance"></param>
|
||||||
|
/// <param name="methodName"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
/// <returns>the return value of the method, or null if it failed</returns>
|
||||||
|
public static object InvokePrivateMethod(object instance, string methodName, params object[] args)
|
||||||
|
{
|
||||||
|
Type type = instance.GetType();
|
||||||
|
while (type != null)
|
||||||
|
{
|
||||||
|
MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (method != null)
|
||||||
|
return method.Invoke(instance, args);
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Error($"Method {methodName} not found in type {instance.GetType().FullName} or its parents");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of a private field in an instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the private field</typeparam>
|
||||||
|
/// <param name="obj">The instance</param>
|
||||||
|
/// <param name="fieldName">Field name</param>
|
||||||
|
/// <param name="recurse">Should the base types be examined</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T GetPrivateField<T>(this object obj, string fieldName, bool recurse = false)
|
||||||
|
{
|
||||||
|
var type = obj.GetType();
|
||||||
|
while (type != null)
|
||||||
|
{
|
||||||
|
FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
if (field != null)
|
||||||
|
return (T)field
|
||||||
|
.GetValue(obj);
|
||||||
|
|
||||||
|
if (!recurse)
|
||||||
|
break;
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
Log.Error($"Field {fieldName} not found in type {obj.GetType().FullName}" + (recurse ? " or its parents" : ""));
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of all delegates registered in the named static event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type (or child type) that contains the event</param>
|
||||||
|
/// <param name="eventName">Name of the event</param>
|
||||||
|
/// <returns>All delegates registered with the event</returns>
|
||||||
|
public static IEnumerable<Delegate> GetStaticEvent(Type type, string eventName)
|
||||||
|
{
|
||||||
|
return GetEventsInternal(null, eventName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of all delegates registered in the named event
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">Instance to retrieve the event list for</param>
|
||||||
|
/// <param name="eventName">Name of the event</param>
|
||||||
|
/// <returns>All delegates registered with the event</returns>
|
||||||
|
public static IEnumerable<Delegate> GetInstanceEvent(object instance, string eventName)
|
||||||
|
{
|
||||||
|
return GetEventsInternal(instance, eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly string[] _backingFieldForEvent = { "{0}", "<backing_store>{0}" };
|
||||||
|
private static IEnumerable<Delegate> GetEventsInternal(object instance, string eventName, Type baseType = null)
|
||||||
|
{
|
||||||
|
BindingFlags bindingFlags = BindingFlags.NonPublic |
|
||||||
|
(instance == null ? BindingFlags.Static : BindingFlags.Instance);
|
||||||
|
|
||||||
|
FieldInfo eventField = null;
|
||||||
|
baseType = baseType ?? instance?.GetType();
|
||||||
|
Type type = baseType;
|
||||||
|
while (type != null && eventField == null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _backingFieldForEvent.Length && eventField == null; i++)
|
||||||
|
eventField = type.GetField(string.Format(_backingFieldForEvent[i], eventName), bindingFlags);
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
if (eventField?.GetValue(instance) is MulticastDelegate eventDel)
|
||||||
|
{
|
||||||
|
foreach (Delegate handle in eventDel.GetInvocationList())
|
||||||
|
yield return handle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log.Error($"{eventName} doesn't have a backing store in {baseType} or its parents.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
Torch/Utils/TorchAssemblyResolver.cs
Normal file
98
Torch/Utils/TorchAssemblyResolver.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
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.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds and removes an additional library path
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an assembly resolver that looks at the given paths for assemblies
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="paths"></param>
|
||||||
|
public TorchAssemblyResolver(params string[] paths)
|
||||||
|
{
|
||||||
|
string location = Assembly.GetEntryAssembly()?.Location ?? GetType().Assembly.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters the assembly resolver
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainOnAssemblyResolve;
|
||||||
|
_assemblies.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.4.1" targetFramework="net461" />
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
<package id="Octokit" version="0.24.0" targetFramework="net461" />
|
<package id="Octokit" version="0.24.0" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
9
TransformOnBuild.targets
Normal file
9
TransformOnBuild.targets
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Target Name="TransformOnBuild">
|
||||||
|
<ItemGroup>
|
||||||
|
<TemplatePaths Include="@(None)" Condition="'%(None.Generator)' == 'TextTemplatingFileGenerator'" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Exec Command=""$(SolutionDir)\packages\Mono.TextTransform.1.0.0\tools\TextTransform.exe" "%(TemplatePaths.FullPath)"" Condition="'%(TemplatePaths.Identity)' != ''"/>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
4
Versioning/AssemblyVersion.cs
Normal file
4
Versioning/AssemblyVersion.cs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("0.0.0.0")]
|
||||||
|
[assembly: AssemblyInformationalVersion("v0.0.0-dev")]
|
20
Versioning/version.ps1
Normal file
20
Versioning/version.ps1
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
$gitVersion = git describe --tags
|
||||||
|
$gitSimpleVersion = git describe --tags --abbrev=0
|
||||||
|
if ($gitSimpleVersion.Equals($gitVersion)) {
|
||||||
|
$buildSalt = 0
|
||||||
|
} else {
|
||||||
|
$gitLatestCommit = git rev-parse --short HEAD
|
||||||
|
$buildSalt = [System.Numerics.BigInteger]::Abs([System.Numerics.BigInteger]::Parse($gitLatestCommit, [System.Globalization.NumberStyles]::HexNumber) % 16383) + 1
|
||||||
|
}
|
||||||
|
$dotNetVersion = echo $gitSimpleVersion | Select-String -Pattern "([0-9]+)\.([0-9]+)\.([0-9]+)" | % {$_.Matches} | %{$_.Groups[1].Value+"."+$_.Groups[2].Value+"."+$_.Groups[3].Value+".$buildSalt"}
|
||||||
|
|
||||||
|
$fileContent = @"
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("$dotNetVersion")]
|
||||||
|
[assembly: AssemblyInformationalVersion("$gitVersion")]
|
||||||
|
"@
|
||||||
|
|
||||||
|
echo $fileContent | Set-Content "$PSScriptRoot/AssemblyVersion.cs"
|
||||||
|
|
||||||
|
echo "$gitVersion / $dotNetVersion"
|
Reference in New Issue
Block a user