Compare commits

..

101 Commits

Author SHA1 Message Date
zznty
b1087822c9 gitignore moment 2023-02-08 21:50:23 +07:00
zznty
ef2d35879c bump harmony version 2023-02-08 21:10:17 +07:00
zznty
83d8eea9ef use harmony instead of torch patcher 2023-02-08 21:00:21 +07:00
zznty
8cdd992350 build torch reference assemblies package so it will reference both torch.server and se assemblies packages 2023-02-08 16:10:07 +07:00
zznty
b9cb71e11f introduction of nuget packages as plugins support 2023-02-08 15:56:50 +07:00
zznty
1a1a7e779a versions for all packages 2023-01-30 18:00:38 +07:00
zznty
6bcd2ea58e windows moment 2023-01-24 16:27:31 +07:00
zznty
b8a06f7bd7 publish nuget package on release 2023-01-24 16:25:10 +07:00
zznty
d44b1a592c rewrite torch mod because casitard broke original 2023-01-24 16:09:19 +07:00
zznty
d7d556d2f2 fix for restart on crash too 2023-01-12 23:37:43 +07:00
zznty
850b313269 fix updates again 2023-01-12 23:29:37 +07:00
zznty
fe985e1a9c fix restart again 2023-01-12 23:20:44 +07:00
zznty
aeea192860 exit on restart 2023-01-09 22:02:54 +07:00
zznty
b8be5b2dce fix auto-updates 2023-01-05 00:48:11 +07:00
zznty
ea08d60d58 update protobuf 2023-01-05 00:41:49 +07:00
zznty
6493a305f7 remove retarded assembly version checks 2023-01-04 23:59:33 +07:00
zznty
bc0a2b89b8 fix entity updates and steam errors on game exit 2022-12-14 19:57:14 +07:00
zznty
846c2aa42e remove cache from ci 2022-12-12 13:29:40 +03:00
zznty
ac1ec431fd use lock files 2022-12-12 17:27:21 +07:00
zznty
3acaf25376 fix plugins ui crashes 2022-12-12 17:25:03 +07:00
zznty
e8928b6b3b fix PropertyGrid readonly is not copyable (#516)
(cherry picked from commit 09ddad495988beed896077f879998bf62cd0c8a8)
2022-12-02 15:45:59 +07:00
zznty
8478ee3752 cleanup for webapi things 2022-12-02 15:44:38 +07:00
zznty
ead8e3a4fc update 2022-12-02 15:23:11 +07:00
zznty
f73b9df924 net7 things 2022-11-20 23:13:16 +07:00
zznty
d524651da9 im a true retard 2022-11-06 21:07:18 +06:00
zznty
2743e5b42d okay 2022-11-06 21:02:41 +06:00
zznty
02bd9df059 fix because retarded exmaples 2022-11-06 21:00:24 +06:00
zznty
a5cc132151 add version string normalization 2022-11-06 20:57:56 +06:00
zznty
92dea1986c linux ci v5 2022-11-06 20:49:18 +06:00
zznty
c03eb79f81 linux ci v4 2022-11-06 20:46:54 +06:00
zznty
0ee9c5f97c test linux ci v3 2022-11-06 20:44:17 +06:00
zznty
c283059106 test linux ci v2 2022-11-06 20:41:26 +06:00
zznty
422963517f test building linux 2022-11-06 17:36:29 +03:00
zznty
d2ac0e44be correct whitelist for compiler 2022-10-16 02:09:44 +07:00
zznty
c5acf61f7c add auto-updates from github 2022-10-15 15:33:57 +07:00
zznty
197d04a661 fix steamcmd default directory 2022-10-15 15:33:09 +07:00
zznty
99ab7d0eea Merge remote-tracking branch 'pve/master' 2022-10-15 02:19:05 +07:00
zznty
17f97af52f add gslt login option (#515)
(cherry picked from commit c81f139fe6b5de0c9f7a005dc2cbe576f4ca8f67)
2022-10-15 02:09:09 +07:00
zznty
ed7c897bd2 Update README.md 2022-10-14 21:59:18 +03:00
zznty
e4d3c3987f fix ci changelog building 2022-10-14 21:48:43 +03:00
zznty
067d8802b6 change instance path resolving back to bin folder
i dont think we need to place our instance at game dir by default
2022-10-15 00:55:44 +07:00
zznty
8f32f64ede change script compilation manager lifetime
seems game loads faster than torch internals
2022-10-15 00:50:10 +07:00
zznty
90ff3f93f0 disable gc patch because conflicting with the same from plugin 2022-10-15 00:44:49 +07:00
zznty
ad28b302f9 add harmony logging 2022-10-15 00:42:23 +07:00
zznty
a0d0976a6a c# 10 and assembly unloading for in-game scripts 2022-10-15 00:16:00 +07:00
zznty
a9c9a0de68 refactor restart and config handling 2022-10-12 16:36:25 +07:00
zznty
9a967345b9 skip obfuscated assemblies from compatibility fixes 2022-10-10 20:31:59 +07:00
zznty
98a4be655f fix plugins download 2022-10-10 20:31:10 +07:00
zznty
76de8f3d0b fix ci zipping 2022-10-09 20:34:22 +07:00
zznty
6f886d4f7d fix for releases 2022-10-09 20:28:58 +07:00
zznty
a753abb8fd i love github and windows 2022-10-09 20:25:00 +07:00
zznty
452970d2fd add nuget source to ci 2022-10-09 20:14:25 +07:00
zznty
e28c38e04f update ci 2022-10-09 20:07:08 +07:00
zznty
5d9fe01c69 Update README.md 2022-10-09 15:58:28 +03:00
zznty
04227912b0 github ci 2022-10-09 19:55:22 +07:00
zznty
44dd0805e2 get rid of old ci files 2022-10-09 17:54:36 +07:00
zznty
c5c8e527da projects cleanup 2022-10-09 17:52:30 +07:00
zznty
49f68a8fcc allow selection for log lines 2022-10-09 17:51:45 +07:00
zznty
f8a3647308 seems to be configuration rework 2022-09-09 16:48:14 +07:00
zznty
0a40b1fe0f move some parts of main class to other
fix poorly implemented features
2022-06-05 15:50:03 +07:00
zznty
a1e9434444 remove compat for netframework 2022-06-05 15:43:15 +07:00
zznty
d2d96df8be fix csproj 2022-05-08 12:39:09 +07:00
zznty
b40f288827 Merge remote-tracking branch 'zznty/master'
# Conflicts:
#	Torch.API/Torch.API.csproj
#	Torch/Patches/ScriptCompilerPatch.cs
2022-05-08 12:36:22 +07:00
zznty
6267cffebe update packages 2022-05-08 12:30:05 +07:00
zznty
5fe25fb781 ship harmony with torch to prevent crashes due to runtime version mismatch
add warning if plugin is using harmony
update monomod package
2022-05-08 12:26:31 +07:00
zznty
d05e979ede remove unnecessary async lambda 2022-05-08 12:25:06 +07:00
zznty
1afb126cb5 fix plugin downloading on separated runtime directory 2022-05-08 12:24:38 +07:00
zznty
d52348149a fix scripts compilation 2022-05-08 11:44:12 +07:00
zznty
20eac508d3 clean up project files 2022-05-08 11:22:39 +07:00
zznty
8ba02a84d3 correct logging indent 2022-05-08 11:22:14 +07:00
zznty
15be85b4f5 forced lang version to 10 (as ryo requested)
all libs are now packed into directory
jenkins file is back
some directives to get net48 supported too
2022-03-16 20:34:01 +07:00
zznty
cf5c00ce0e fixed steamcmd path does not follow TORCH_GAME_PATH env variable 2022-03-07 13:25:38 +07:00
zznty
9c185d5577 fixed mods not being properly cleared from bulk edit
fixed bulk edit crash if input format is invalid
2022-03-05 20:19:01 +07:00
LTP
8b6c401531 fixed plugin dependencies resolution 2022-03-03 22:30:55 +07:00
z__
92db8994ef fixed ScriptCompilerPatch 2022-02-28 17:16:23 +07:00
z__
aee36661fd fixed keen log indent 2022-02-28 17:15:33 +07:00
z__
feda84fac8 Merge remote-tracking branch 'zznty/master' 2022-02-18 17:27:52 +07:00
z__
2503cd6372 fixed NRE in edit roles button 2022-02-18 17:27:40 +07:00
zznty
f321034eeb Update README.md 2022-02-16 10:08:20 +07:00
z__
7573684520 remove unnecessary information that breaks common suffixes 2022-02-12 02:02:09 +07:00
z__
223eaa9fd0 rebase online players counter to events 2022-02-12 01:58:00 +07:00
z__
d138a46f25 forced stop & restart in separate thread 2022-02-12 01:42:46 +07:00
z__
ce2bbd4a61 fix not implemented property 2022-02-12 01:40:38 +07:00
z__
85dd4b46b8 mods loading fixes 2022-02-12 00:40:10 +07:00
z__
166a9d1dbe logs limit 2022-02-12 00:37:00 +07:00
z__
dfc15354ca replace log config if it's from previous versions 2022-02-11 23:04:03 +07:00
z__
57c977deb4 remove load order display 2022-02-11 21:26:47 +07:00
z__
f6cdc2fe79 removed missing dockerfile in solution files 2022-02-11 15:35:53 +07:00
z__
f42b9c6674 dont forget to call 2022-02-09 20:26:37 +07:00
zznty
227557f421 Merge branch 'fixes' 2022-02-09 20:25:46 +07:00
z__
0632f68aaf it was needed, but with some checks 2022-02-09 20:25:09 +07:00
z__
0a9f299527 we dont need it anymore 2022-02-09 15:34:13 +07:00
z__
67f25ab20b transform ctor to extension method 2022-02-08 23:36:39 +07:00
z__
ad19a7dc9e better exception for invalid operand type 2022-02-08 23:26:25 +07:00
z__
dd854a159a ok high iq solutions is bad for compatibility 2022-02-04 15:11:49 +07:00
z__
879a373e6a added possibility to call SetConfiguration again if needed 2022-02-04 14:42:37 +07:00
z__
ec1b017946 added nlog custom targets assemblies loading 2022-02-04 14:33:32 +07:00
z__
cf75210304 fixed game analytics logger crash 2022-02-04 13:44:04 +07:00
z__
3696f18714 move nlog config to instance and move default to resources 2022-02-04 13:25:56 +07:00
z__
1f7e4e869d final fixes for warfare 2 2022-02-04 12:09:17 +07:00
z__
ba5b611994 add STA thread back, not being added automatically on local build 2022-02-04 10:38:47 +07:00
134 changed files with 3877 additions and 3863 deletions

76
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Release
on:
push:
tags:
- '*'
jobs:
build:
name: Build
runs-on: ubuntu-latest
env:
TORCH_VERSION: ${{ github.ref_name }}
BUILD_CONFIGURATION: Release
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
steps:
- uses: actions/checkout@master
name: Checkout
- uses: actions/setup-dotnet@v3
name: Setup dotnet
with:
dotnet-version: '7.0.x'
- name: Add Gh Packages Nuget Source
run: dotnet nuget add source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github
- name: Restore dependencies
run: dotnet restore Torch.Server/Torch.Server.csproj --locked-mode
- name: Build
run: dotnet build Torch.Server/Torch.Server.csproj --no-restore -c ${{ env.BUILD_CONFIGURATION }} /p:AssemblyVersion=${{ env.TORCH_VERSION }} /p:Version=${{ env.TORCH_VERSION }}
- name: Publish
run: dotnet publish Torch.Server/Torch.Server.csproj --no-build -r win-x64 --sc -c ${{ env.BUILD_CONFIGURATION }} -o ./publish
- uses: vimtor/action-zip@v1
name: Zip Release
with:
files: publish/
dest: release.zip
- name: Build Changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commitMode: true
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.TORCH_VERSION }}
release_name: Release v${{ env.TORCH_VERSION }}
body: ${{ steps.github_release.outputs.changelog }}
draft: true
prerelease: false
- name: Upload release asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: release.zip
asset_name: torch-server.zip
asset_content_type: application/zip
- name: Publish release
uses: StuYarrow/publish-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
id: ${{ steps.create_release.outputs.id }}
- run: dotnet pack -c Release ./Torch.API/Torch.API.csproj -o pack --include-symbols -p:SymbolPackageFormat=snupkg -p:Version=${{ env.TORCH_VERSION }} --no-build
- run: dotnet pack -c Release ./Torch/Torch.csproj -o pack --include-symbols -p:SymbolPackageFormat=snupkg -p:Version=${{ env.TORCH_VERSION }} --no-build
- run: dotnet pack -c Release ./Torch.Server/Torch.Server.csproj -o pack --include-symbols -p:SymbolPackageFormat=snupkg -p:Version=${{ env.TORCH_VERSION }} --no-build
- run: mkdir blank && sed -i 's/torchVersion/${{ env.TORCH_VERSION }}/g' Torch.Server.ReferenceAssemblies.net7.nuspec && nuget pack Torch.Server.ReferenceAssemblies.net7.nuspec -BasePath ./blank -Version ${{ env.TORCH_VERSION }} -OutputDirectory pack -NonInteractive -NoPackageAnalysis
- run: dotnet nuget push ./pack/*.nupkg -s github

4
.gitignore vendored
View File

@@ -159,10 +159,6 @@ publish/
# NuGet Packages # NuGet Packages
*.nupkg *.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed # Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config #!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files # NuGet v3's project.json files produces more ignoreable files

View File

@@ -1,33 +0,0 @@
# Torch 1.1.229.265
* Features
- Added more lenient version parsing for plugins (v#.# should work)
- Added countdown option to restart command (!restart [seconds])
* Fixes
- General fixes to work with the latest SE version
- Fixed config changes not saving
- (hopefully) Fixed issue causing crashes on servers using the Windows Classic theme
# Torch 1.1.207.7
* Notes
- This release makes significant changes to TorchConfig.xml. It has been renamed to Torch.cfg and has different options.
* Features
- Plugins, Torch, and the DS can now all update automatically
- Changed command prefix to !
- Added manual save command (thanks to Maldark)
- Added restart command
- Improved instance creation: now creates an entire skeleton instance with blank config
- Added instance name to console title
* Fixes
- Optimized UI so it's snappier and freezes less often
- Fixed NetworkManager.RaiseEvent overload that had an off-by-one bug
- Fixed chat window so it automatically scrolls down
# Torch 1.0.182.329
* Improved logging, logs now to go the Logs folder and aren't deleted on start
* Fixed chat tab not enabling with -autostart
* Fixed player list
* Watchdog time-out is now configurable in TorchConfig.xml
* Fixed infinario log spam
* Fixed crash when sending empty message from chat tab
* Fixed permissions on Torch commands
* Changed plugin StoragePath to the current instance path (per-instance configs)

View File

@@ -1,6 +1,3 @@
[![Discord](https://discordapp.com/api/guilds/929141809769226271/widget.png)](https://discord.gg/trK6sYdcNE)
[![Build status](https://ci.appveyor.com/api/projects/status/us64kmwshl50f5a3/branch/master?svg=true)](https://ci.appveyor.com/project/zznty/torch/branch/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.
@@ -15,15 +12,25 @@ Torch is the successor to SE Server Extender and gives server admins the tools t
### Fork Difference ### Fork Difference
* .NET 6.0 runtime * .NET 6.0 runtime
* Additional options & features * Optimized in-game scripts (also newer compiler & language versions)
* Better configuration via cli arguments, environment variables or xml config
* Designed to run multiple instance from same install directory without having you to waste disk space
* Mostly compatible with original torch's plugins
### Discord
If you have any questions or issues please join our [discord](https://discord.gg/UyYFSe3TyQ)
### Installation ### Installation
* Unzip the Torch release into its own directory and run the executable. It will automatically download the SE DS and generate the other necessary files. * Unzip the Torch release into its own directory and run the executable. It will automatically download the SE DS and generate the other necessary files.
- If you already have a DS installed you can unzip the Torch files into the folder that contains the DedicatedServer64 folder. - If you already have a DS installed you can:
* Unzip the Torch files into the folder that contains the DedicatedServer64 folder.
* Pass path to game files using config parameter `gamePath`
# Building # Building
To build Torch you must first have a complete SE Dedicated installation somewhere. Before you open the solution, run the Setup batch file and enter the path of that installation's DedicatedServer64 folder. The script will make a symlink to that folder so the Torch solution can find the DLL references it needs.
As a regular dotnet project with cli by running `dotnet build Torch.Server/Torch.Server.csproj`, with VS 2022 or higher, with JB Rider or Fleet.
If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon. (https://www.patreon.com/TorchSE) If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon. (https://www.patreon.com/TorchSE)

View File

@@ -1,13 +0,0 @@
:: This script creates a symlink to the game binaries to account for different installation directories on different systems.
@echo off
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
cd %~dp0
mklink /J GameBinaries "%path%"
if errorlevel 1 goto Error
echo Done! You can now open the Torch solution without issue.
goto End
:Error
echo An error occured creating the symlink.
:End
pause

View File

@@ -0,0 +1,30 @@
using System.IO;
namespace Torch.API;
public interface IApplicationContext
{
/// <summary>
/// Directory contains torch binaries.
/// </summary>
public DirectoryInfo TorchDirectory { get; }
/// <summary>
/// Root directory for all game files.
/// </summary>
public DirectoryInfo GameFilesDirectory { get; }
/// <summary>
/// Directory contains game binaries.
/// </summary>
public DirectoryInfo GameBinariesDirectory { get; }
/// <summary>
/// Current instance directory.
/// </summary>
public DirectoryInfo InstanceDirectory { get; }
/// <summary>
/// Current instance name.
/// </summary>
public string InstanceName { get; }
/// <summary>
/// Application running in service mode.
/// </summary>
public bool IsService { get; }
}

View File

@@ -16,30 +16,6 @@ namespace Torch.API
/// </summary> /// </summary>
public interface ITorchBase public interface ITorchBase
{ {
/// <summary>
/// Fired when the session begins loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoading;
/// <summary>
/// Fired when the session finishes loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoaded;
/// <summary>
/// Fires when the session begins unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloading;
/// <summary>
/// Fired when the session finishes unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloaded;
/// <summary> /// <summary>
/// Gets the currently running session instance, or null if none exists. /// Gets the currently running session instance, or null if none exists.
/// </summary> /// </summary>
@@ -67,6 +43,16 @@ namespace Torch.API
/// The binary version of the current instance. /// The binary version of the current instance.
/// </summary> /// </summary>
Version TorchVersion { get; } Version TorchVersion { get; }
/// <summary>
/// Path of the dedicated instance folder.
/// </summary>
string InstancePath { get; }
/// <summary>
/// Name of the dedicated instance.
/// </summary>
string InstanceName { get; }
/// <summary> /// <summary>
/// Invoke an action on the game thread. /// Invoke an action on the game thread.
@@ -146,16 +132,6 @@ namespace Torch.API
/// </summary> /// </summary>
ServerState State { get; } ServerState State { get; }
/// <summary>
/// Path of the dedicated instance folder.
/// </summary>
string InstancePath { get; }
/// <summary>
/// Name of the dedicated instance.
/// </summary>
string InstanceName { get; }
/// <summary> /// <summary>
/// Raised when the server's Init() method has completed. /// Raised when the server's Init() method has completed.
/// </summary> /// </summary>

View File

@@ -22,7 +22,6 @@ namespace Torch
bool ShouldUpdatePlugins { get; } bool ShouldUpdatePlugins { get; }
bool ShouldUpdateTorch { get; } bool ShouldUpdateTorch { get; }
int TickTimeout { get; set; } int TickTimeout { get; set; }
string WaitForPID { get; set; }
string ChatName { get; set; } string ChatName { get; set; }
string ChatColor { get; set; } string ChatColor { get; set; }
string TestPlugin { get; set; } string TestPlugin { get; set; }
@@ -32,6 +31,24 @@ namespace Torch
int FontSize { get; set; } int FontSize { get; set; }
UGCServiceType UgcServiceType { get; set; } UGCServiceType UgcServiceType { get; set; }
bool EntityManagerEnabled { get; set; } bool EntityManagerEnabled { get; set; }
string LoginToken { get; set; }
UpdateSource UpdateSource { get; set; }
List<string> Packages { get; set; }
void Save(string path = null); void Save(string path = null);
} }
public class UpdateSource
{
public UpdateSourceType SourceType { get; set; }
public string Url { get; set; }
public string Repository { get; set; }
public string Branch { get; set; }
}
public enum UpdateSourceType
{
Github,
Jenkins
}
} }

View File

@@ -14,7 +14,6 @@ public interface IInstanceManager : IManager
public interface IWorld public interface IWorld
{ {
string FolderName { get; }
string WorldPath { get; } string WorldPath { get; }
MyObjectBuilder_SessionSettings KeenSessionSettings { get; } MyObjectBuilder_SessionSettings KeenSessionSettings { get; }
MyObjectBuilder_Checkpoint KeenCheckpoint { get; } MyObjectBuilder_Checkpoint KeenCheckpoint { get; }

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.Loader;
using Torch.API.WebAPI.Plugins;
namespace Torch.API.Managers;
public interface IPackageManager : IManager
{
IReadOnlySet<Package> Packages { get; }
bool TryGetPackageAssemblies(Package package, [MaybeNullWhen(false)] out Assembly[] assemblies);
}

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch API")]
[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

View File

@@ -1,105 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net7-windows</TargetFramework>
<AssemblyTitle>Torch API</AssemblyTitle> <AssemblyTitle>Torch API</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWpf>True</UseWpf> <UseWpf>True</UseWpf>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <EnableWindowsTargeting>true</EnableWindowsTargeting>
<PlatformTarget>x64</PlatformTarget> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="$(Configuration) == 'Release'"> <PropertyGroup Condition="$(Configuration) == 'Release'">
<NoWarn>1591</NoWarn> <NoWarn>1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup> <ItemGroup>
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="JorgeSerrano.Json.JsonSnakeCaseNamingPolicy" Version="0.9.0" />
<PackageReference Include="SemanticVersioning" Version="2.0.0" /> <PackageReference Include="NLog" Version="5.1.0" />
</ItemGroup> <PackageReference Include="NuGet.Commands" Version="6.4.0" />
<ItemGroup> <PackageReference Include="NuGet.DependencyResolver.Core" Version="6.4.0" />
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64"> <PackageReference Include="SemanticVersioning" Version="2.0.2" />
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath> <PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.201.13">
<Private>False</Private> <PrivateAssets>all</PrivateAssets>
<SpecificVersion>False</SpecificVersion> <IncludeAssets>compile</IncludeAssets>
</Reference> </PackageReference>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <PackageReference Include="System.Linq.Async" Version="6.0.1" />
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Game, Version=0.1.6305.30774, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.6305.30761, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="SpaceEngineers.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="SpaceEngineers.ObjectBuilders, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage">
<HintPath>..\GameBinaries\VRage.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Dedicated">
<HintPath>..\GameBinaries\VRage.Dedicated.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Library">
<HintPath>..\GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Render, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Render.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Render11, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Scripting, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using NLog;
using Torch.API.Utils;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI
{
public class JenkinsQuery
{
private const string BRANCH_QUERY = "http://136.243.80.164:2690/job/Torch/job/{0}/" + API_PATH;
private const string ARTIFACT_PATH = "artifact/bin/torch-server.zip";
private const string API_PATH = "api/json";
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static JenkinsQuery _instance;
public static JenkinsQuery Instance => _instance ??= new JenkinsQuery();
private HttpClient _client;
private JenkinsQuery()
{
_client = new HttpClient();
}
public async Task<Job> GetLatestVersion(string branch)
{
var h = await _client.GetAsync(string.Format(BRANCH_QUERY, branch));
if (!h.IsSuccessStatusCode)
{
Log.Error($"'{branch}' Branch query failed with code {h.StatusCode}");
if(h.StatusCode == HttpStatusCode.NotFound)
Log.Error("This likely means you're trying to update a branch that is not public on Jenkins. Sorry :(");
return null;
}
var branchResponse = await h.Content.ReadFromJsonAsync<BranchResponse>();
if (branchResponse is null)
{
Log.Error("Error reading branch response");
return null;
}
h = await _client.GetAsync($"{branchResponse.LastStableBuild.Url}{API_PATH}");
if (h.IsSuccessStatusCode)
return await h.Content.ReadFromJsonAsync<Job>();
Log.Error($"Job query failed with code {h.StatusCode}");
return null;
}
public async Task<bool> DownloadRelease(Job job, string path)
{
var h = await _client.GetAsync(job.Url + ARTIFACT_PATH);
if (!h.IsSuccessStatusCode)
{
Log.Error($"Job download failed with code {h.StatusCode}");
return false;
}
var s = await h.Content.ReadAsStreamAsync();
await using var fs = new FileStream(path, FileMode.Create);
await s.CopyToAsync(fs);
return true;
}
}
public record BranchResponse(string Name, string Url, Build LastBuild, Build LastStableBuild);
public record Build(int Number, string Url);
public record Job(int Number, bool Building, string Description, string Result, string Url,
[property: JsonConverter(typeof(SemanticVersionConverter))] Version Version);
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
namespace Torch.API.WebAPI.Plugin;
public interface IPluginQuery
{
Task<PluginsResponse> QueryAll();
Task<PluginItem> QueryOne(Guid guid);
Task<bool> DownloadPlugin(Guid guid, string path = null);
Task<bool> DownloadPlugin(PluginItem item, string path = null);
}

View File

@@ -0,0 +1,81 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
using NLog;
namespace Torch.API.WebAPI.Plugin;
public class LegacyPluginQuery : IPluginQuery
{
private const string BASE_URL = "https://torchapi.com/";
private readonly HttpClient _client;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private LegacyPluginQuery()
{
_client = new()
{
BaseAddress = new(BASE_URL)
};
}
public static LegacyPluginQuery Instance { get; } = new();
public async Task<PluginsResponse> QueryAll()
{
return await _client.GetFromJsonAsync<PluginsResponse>("/api/plugins/", CancellationToken.None);
}
public async Task<PluginItem> QueryOne(Guid guid)
{
using var res = await _client.GetAsync($"/api/plugins/search/{guid}");
if (!res.IsSuccessStatusCode)
return null;
return await res.Content.ReadFromJsonAsync<PluginItem>();
}
public async Task<bool> DownloadPlugin(Guid guid, string path = null)
{
var item = await QueryOne(guid);
if (item is null) return false;
return await DownloadPlugin(item, path);
}
public async Task<bool> DownloadPlugin(PluginItem item, string path = null)
{
try
{
path ??= Path.Combine(AppContext.BaseDirectory, "Plugins", $"{item.Name}.zip");
if (item.Versions.Length == 0)
{
Log.Error($"Selected plugin {item.Name} does not have any versions to download!");
return false;
}
var version = item.Versions.FirstOrDefault(v => v.Version == item.LatestVersion);
if (version is null)
{
Log.Error($"Could not find latest version for selected plugin {item.Name}");
return false;
}
var s = await _client.GetStreamAsync(version.Url);
if(File.Exists(path))
File.Delete(path);
await using var f = File.Create(path);
await s.CopyToAsync(f);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to download plugin!");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Text.Json.Serialization;
namespace Torch.API.WebAPI.Plugin;
public record PluginItem(Guid Id, string Name, string Author, string Description, string LatestVersion,
VersionItem[] Versions)
{
[JsonIgnore]
public bool Installed { get; set; }
}

View File

@@ -0,0 +1,3 @@
namespace Torch.API.WebAPI.Plugin;
public record PluginsResponse(PluginItem[] Plugins);

View File

@@ -0,0 +1,6 @@
using System.Text.Json.Serialization;
namespace Torch.API.WebAPI.Plugin;
public record VersionItem(string Version, string Note, [property: JsonPropertyName("is_beta")] bool IsBeta,
string Url);

View File

@@ -1,104 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using NLog;
namespace Torch.API.WebAPI
{
public class PluginQuery
{
private const string ALL_QUERY = "https://torchapi.com/api/plugins/";
private const string PLUGIN_QUERY = "https://torchapi.com/api/plugins/?guid={0}";
private readonly HttpClient _client;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static PluginQuery _instance;
public static PluginQuery Instance => _instance ??= new();
private PluginQuery()
{
_client = new();
}
public async Task<PluginsResponse> QueryAll()
{
return (PluginsResponse) await _client.GetFromJsonAsync(ALL_QUERY, typeof(PluginsResponse), CancellationToken.None);
}
public Task<PluginItem> QueryOne(Guid guid)
{
return QueryOne(guid.ToString());
}
public async Task<PluginItem> QueryOne(string guid)
{
using var res = await _client.GetAsync(string.Format(PLUGIN_QUERY, guid));
if (!res.IsSuccessStatusCode)
return null;
return await res.Content.ReadFromJsonAsync<PluginItem>();
}
public Task<bool> DownloadPlugin(Guid guid, string path = null)
{
return DownloadPlugin(guid.ToString(), path);
}
public async Task<bool> DownloadPlugin(string guid, string path = null)
{
var item = await QueryOne(guid);
if (item is null) return false;
return await DownloadPlugin(item, path);
}
public async Task<bool> DownloadPlugin(PluginItem item, string path = null)
{
try
{
path ??= Path.Combine(Directory.GetCurrentDirectory(), "Plugins", $"{item.Name}.zip");
if (item.Versions.Length == 0)
{
Log.Error($"Selected plugin {item.Name} does not have any versions to download!");
return false;
}
var version = item.Versions.FirstOrDefault(v => v.Version == item.LatestVersion);
if (version is null)
{
Log.Error($"Could not find latest version for selected plugin {item.Name}");
return false;
}
var s = await _client.GetStreamAsync(version.Url);
if(File.Exists(path))
File.Delete(path);
await using var f = File.Create(path);
await s.CopyToAsync(f);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to download plugin!");
}
return true;
}
}
public record PluginsResponse(PluginItem[] Plugins);
public record PluginItem(Guid Id, string Name, string Author, string Description, string LatestVersion,
VersionItem[] Versions)
{
[JsonIgnore]
public bool Installed { get; set; }
}
public record VersionItem(string Version, string Note, [property: JsonPropertyName("is_beta")] bool IsBeta,
string Url);
}

View File

@@ -0,0 +1,11 @@
#nullable enable
using System.IO;
using System.Threading.Tasks;
namespace Torch.API.WebAPI.Plugins;
public interface IPackageItem
{
Task<Stream> OpenFileAsync();
public string FileName { get; }
}

View File

@@ -0,0 +1,12 @@
#nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Torch.API.WebAPI.Plugins;
public interface IPackageReader
{
Task<(IEnumerable<IPackageItem> Root, IReadOnlyDictionary<PackageDependency, IEnumerable<IPackageItem>> Dependencies)> GetItemsAsync();
}

View File

@@ -0,0 +1,11 @@
#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Torch.API.WebAPI.Plugins;
public interface IPackageResolver
{
Task<IEnumerable<Package>> ResolvePackagesAsync(IReadOnlyDictionary<string, string> packages);
Task<IPackageReader> GetPackageAsync(Package package);
}

View File

@@ -0,0 +1,87 @@
#nullable enable
using System;
using System.Threading.Tasks;
using NuGet.Common;
namespace Torch.API.WebAPI.Plugins;
internal class NLogLogger : ILogger
{
private readonly NLog.ILogger _logger;
public NLogLogger(NLog.ILogger logger)
{
_logger = logger;
}
public void LogDebug(string data)
{
_logger.Debug(data);
}
public void LogVerbose(string data)
{
_logger.Trace(data);
}
public void LogInformation(string data)
{
_logger.Info(data);
}
public void LogMinimal(string data)
{
_logger.Debug(data);
}
public void LogWarning(string data)
{
_logger.Warn(data);
}
public void LogError(string data)
{
_logger.Error(data);
}
public void LogInformationSummary(string data)
{
_logger.Info(data);
}
public void Log(LogLevel level, string data)
{
_logger.Log(ToNLogLevel(level), data);
}
private static NLog.LogLevel ToNLogLevel(LogLevel level)
{
return level switch
{
LogLevel.Debug => NLog.LogLevel.Debug,
LogLevel.Verbose => NLog.LogLevel.Trace,
LogLevel.Information => NLog.LogLevel.Info,
LogLevel.Minimal => NLog.LogLevel.Debug,
LogLevel.Warning => NLog.LogLevel.Warn,
LogLevel.Error => NLog.LogLevel.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
};
}
public Task LogAsync(LogLevel level, string data)
{
Log(level, data);
return Task.CompletedTask;
}
public void Log(ILogMessage message)
{
_logger.Log(ToNLogLevel(message.Level), message.FormatWithCode);
}
public Task LogAsync(ILogMessage message)
{
Log(message);
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using NuGet.DependencyResolver;
using SemanticVersioning;
namespace Torch.API.WebAPI.Plugins;
public record Package(string Name, Version Version, IReadOnlySet<PackageDependency> Dependencies)
{
internal GraphItem<RemoteResolveResult> Graph { get; init; }
}

View File

@@ -0,0 +1,9 @@
using NuGet.DependencyResolver;
using SemanticVersioning;
namespace Torch.API.WebAPI.Plugins;
public record PackageDependency(string Name, Version Version, PackageDependencyKind Kind)
{
internal RemoteMatch Match { get; init; }
}

View File

@@ -0,0 +1,8 @@
namespace Torch.API.WebAPI.Plugins;
public enum PackageDependencyKind
{
None,
Transitive,
Direct
}

View File

@@ -0,0 +1,91 @@
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.DependencyResolver;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
namespace Torch.API.WebAPI.Plugins;
public class PackageReader : IPackageReader
{
private readonly Package _package;
private readonly SourceCacheContext _cacheContext;
private readonly ILogger _logger;
private readonly NuGetFramework _framework;
private readonly IFrameworkCompatibilityProvider _compatibilityProvider;
private readonly DirectoryInfo _packagesDirectory;
public PackageReader(Package package, SourceCacheContext cacheContext, ILogger logger, NuGetFramework framework,
IFrameworkCompatibilityProvider compatibilityProvider, DirectoryInfo packagesDirectory)
{
_package = package;
_cacheContext = cacheContext;
_logger = logger;
_framework = framework;
_compatibilityProvider = compatibilityProvider;
_packagesDirectory = packagesDirectory;
}
public async Task<(IEnumerable<IPackageItem> Root, IReadOnlyDictionary<PackageDependency, IEnumerable<IPackageItem>>
Dependencies)>
GetItemsAsync()
{
async Task<IEnumerable<IPackageItem>> GetPackageItemsAsync(string id, NuGetVersion version,
IRemoteDependencyProvider provider)
{
var downloader =
await provider.GetPackageDownloaderAsync(new(id, version), _cacheContext, _logger,
CancellationToken.None);
await downloader.CopyNupkgFileToAsync(Path.Combine(_packagesDirectory.FullName, $"{id}.{version}.nupkg"),
CancellationToken.None);
var frameworks = await downloader.ContentReader.GetReferenceItemsAsync(CancellationToken.None);
var items = frameworks.Where(b => _compatibilityProvider.IsCompatible(_framework, b.TargetFramework))
.MaxBy(b => b.TargetFramework.Version)?.Items;
return items?.Select(b => new PackageItem(b, downloader)) ?? ImmutableArray<PackageItem>.Empty;
}
var rootIdentity = _package.Graph.Key;
return (await GetPackageItemsAsync(rootIdentity.Name, rootIdentity.Version, _package.Graph.Data.Match.Provider),
await _package.Dependencies.ToAsyncEnumerable().SelectManyAwait(async b =>
(await GetPackageItemsAsync(
b.Match.Library.Name,
b.Match.Library.Version,
b.Match.Provider))
.ToAsyncEnumerable()
.Select(c => (b, c)))
.GroupBy(b => b.b, b => b.c)
.ToDictionaryAwaitAsync<IAsyncGrouping<PackageDependency, IPackageItem>, PackageDependency,
IEnumerable<IPackageItem>>(b => ValueTask.FromResult(b.Key),
async b => await b.ToArrayAsync()));
}
}
file class PackageItem : IPackageItem
{
private readonly string _path;
private readonly IPackageDownloader _downloader;
public string FileName => Path.GetFileName(_path);
public PackageItem(string path, IPackageDownloader downloader)
{
_path = path;
_downloader = downloader;
}
public Task<Stream> OpenFileAsync()
{
return _downloader.CoreReader.GetStreamAsync(_path, CancellationToken.None);
}
}

View File

@@ -0,0 +1,104 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using NuGet.Commands;
using NuGet.Configuration;
using NuGet.DependencyResolver;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI.Plugins;
public class PackageResolver : IPackageResolver
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
private readonly NuGetFramework _framework = NuGetFramework.Parse("net7.0-windows7.0");
private readonly NLogLogger _logger = new(Log);
private readonly SourceCacheContext _sourceCacheContext = new();
private readonly RemoteWalkContext _remoteWalkContext;
private readonly DirectoryInfo _packagesDirectory;
private readonly IFrameworkCompatibilityProvider _compatibilityProvider = DefaultCompatibilityProvider.Instance;
public PackageResolver(IEnumerable<PackageSource> sources, DirectoryInfo packagesDirectory)
{
_packagesDirectory = packagesDirectory;
IReadOnlySet<PackageSource> packageSources = sources.Where(b => b.Type is PackageSourceType.NuGet).ToImmutableHashSet();
var mapping = new PackageSourceMapping(packageSources.ToDictionary(b => b.Name, b => b.Patterns));
_remoteWalkContext = new RemoteWalkContext(_sourceCacheContext, mapping, _logger);
foreach (var (name, url, _, _) in packageSources)
{
var packageSource = new NuGet.Configuration.PackageSource(url, name);
var sourceRepository = new SourceRepository(packageSource, new INuGetResourceProvider[]
{
new DownloadResourceV3Provider(),
new DependencyInfoResourceV3Provider(),
new ServiceIndexResourceV3Provider(),
new RemoteV3FindPackageByIdResourceProvider(),
new V3FeedListResourceProvider(),
new HttpSourceResourceProvider(),
new RegistrationResourceV3Provider(),
new HttpHandlerResourceV3Provider()
}.Select(b => new Lazy<INuGetResourceProvider>(b)), FeedType.HttpV3);
_remoteWalkContext.RemoteLibraryProviders.Add(
new SourceRepositoryDependencyProvider(sourceRepository, _logger, _sourceCacheContext, true, false));
}
}
public async Task<IEnumerable<Package>> ResolvePackagesAsync(
IReadOnlyDictionary<string, string> packages)
{
Log.Info("Restoring {0} packages", packages.Count);
var graphs = await Task.WhenAll(packages.Select(b =>
{
var (key, versionRange) = b;
var libraryRange = new LibraryRange(key, VersionRange.Parse(versionRange), LibraryDependencyTarget.All);
return ResolverUtility.FindLibraryEntryAsync(libraryRange, _framework, "win-x64",
_remoteWalkContext, CancellationToken.None);
}));
return await graphs.ToAsyncEnumerable().SelectAwait(async graph =>
{
return new Package(graph.Key.Name, Version.Parse(graph.Key.Version.ToFullString()),
await graph.Data.Dependencies
.ToAsyncEnumerable()
.SelectAwait(async b =>
{
var match = await ResolverUtility.FindLibraryByVersionAsync(
b.LibraryRange, _framework, _remoteWalkContext.RemoteLibraryProviders,
_sourceCacheContext, _logger, CancellationToken.None);
return new PackageDependency(
b.Name, Version.Parse(match.Library.Version.ToFullString()),
(PackageDependencyKind)b.ReferenceType)
{
Match = match
};
})
.ToHashSetAsync())
{
Graph = graph
};
}).ToArrayAsync();
}
public Task<IPackageReader> GetPackageAsync(Package package)
{
var reader = new PackageReader(package, _sourceCacheContext, _logger, _framework, _compatibilityProvider, _packagesDirectory);
return Task.FromResult<IPackageReader>(reader);
}
}

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Torch.API.WebAPI.Plugins;
#nullable enable
public record PackageSource
#nullable restore
(string Name, string Url, IReadOnlyList<string> Patterns, PackageSourceType Type);

View File

@@ -0,0 +1,8 @@
#nullable enable
namespace Torch.API.WebAPI.Plugins;
public enum PackageSourceType
{
NuGet,
LegacyTorch
}

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using JorgeSerrano.Json;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI.Update;
public class GithubQuery : IUpdateQuery
{
private readonly HttpClient _client;
public GithubQuery(string url)
{
if (url == null) throw new ArgumentNullException(nameof(url));
_client = new()
{
BaseAddress = new(url),
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
DefaultRequestHeaders =
{
{"User-Agent", "TorchAPI"}
}
};
}
public void Dispose()
{
_client?.Dispose();
}
public async Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null)
{
var response = await _client.GetFromJsonAsync<Release>($"/repos/{repository}/releases/latest", new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy()
});
if (response is null)
throw new($"Unable to get latest release for {repository}");
return new(Version.Parse(response.TagName), response.Assets.First(b => b.Name == "torch-server.zip").BrowserDownloadUrl);
}
private record Asset(
string Url,
int Id,
string NodeId,
string Name,
string Label,
Uploader Uploader,
string ContentType,
string State,
int Size,
int DownloadCount,
DateTime CreatedAt,
DateTime UpdatedAt,
string BrowserDownloadUrl
);
private record Author(
string Login,
int Id,
string NodeId,
string AvatarUrl,
string GravatarId,
string Url,
string HtmlUrl,
string FollowersUrl,
string FollowingUrl,
string GistsUrl,
string StarredUrl,
string SubscriptionsUrl,
string OrganizationsUrl,
string ReposUrl,
string EventsUrl,
string ReceivedEventsUrl,
string Type,
bool SiteAdmin
);
private record Release(
string Url,
string AssetsUrl,
string UploadUrl,
string HtmlUrl,
int Id,
Author Author,
string NodeId,
string TagName,
string TargetCommitish,
string Name,
bool Draft,
bool Prerelease,
DateTime CreatedAt,
DateTime PublishedAt,
IReadOnlyList<Asset> Assets,
string TarballUrl,
string ZipballUrl,
string Body
);
private record Uploader(
string Login,
int Id,
string NodeId,
string AvatarUrl,
string GravatarId,
string Url,
string HtmlUrl,
string FollowersUrl,
string FollowingUrl,
string GistsUrl,
string StarredUrl,
string SubscriptionsUrl,
string OrganizationsUrl,
string ReposUrl,
string EventsUrl,
string ReceivedEventsUrl,
string Type,
bool SiteAdmin
);
}

View File

@@ -0,0 +1,9 @@
using System;
using System.Threading.Tasks;
namespace Torch.API.WebAPI.Update;
public interface IUpdateQuery : IDisposable
{
Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null);
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Torch.API.Utils;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI.Update
{
public class JenkinsQuery : IUpdateQuery
{
private const string ApiPath = "api/json";
private readonly HttpClient _client;
public JenkinsQuery(string url)
{
if (url == null) throw new ArgumentNullException(nameof(url));
_client = new()
{
BaseAddress = new(url)
};
}
public async Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null)
{
branch ??= "master";
var response = await _client.GetFromJsonAsync<BranchResponse>($"/job/{repository}/job/{branch}/{ApiPath}");
if (response is null)
throw new($"Unable to get latest release for {repository}");
var job = await _client.GetFromJsonAsync<Job>(
$"/job/{repository}/job/{branch}/{response.LastBuild.Number}/{ApiPath}");
if (job is null)
throw new($"Unable to get latest release for job {repository}/{response.LastBuild.Number}");
return new(job.Version, job.Url + job.Artifacts.First(b => b.FileName == "torch-server.zip").RelativePath);
}
public void Dispose()
{
_client?.Dispose();
}
}
public record BranchResponse(string Name, string Url, Build LastBuild, Build LastStableBuild);
public record Build(int Number, string Url);
public record Job(int Number, bool Building, string Description, string Result, string Url,
[property: JsonConverter(typeof(SemanticVersionConverter))] Version Version,
IReadOnlyList<Artifact> Artifacts);
public record Artifact(
string DisplayPath,
string FileName,
string RelativePath
);
}

View File

@@ -0,0 +1,5 @@
using SemanticVersioning;
namespace Torch.API.WebAPI.Update;
public record UpdateRelease(Version Version, string ArtifactUrl);

View File

@@ -0,0 +1,178 @@
{
"version": 1,
"dependencies": {
"net7.0-windows7.0": {
"JorgeSerrano.Json.JsonSnakeCaseNamingPolicy": {
"type": "Direct",
"requested": "[0.9.0, )",
"resolved": "0.9.0",
"contentHash": "xCqODS+wzpUXNtg4bMMvXG5PLbP0iTwRzRn2R+zWHKm83E6tbV2bCagawXp1EnZeNpd5OXpMxehulZWns8efzQ=="
},
"NLog": {
"type": "Direct",
"requested": "[5.1.0, )",
"resolved": "5.1.0",
"contentHash": "oW7ekrkRG9okpDMUcEglunWj8Qf2RY8qkgl+/chJoavzg3dbT13y32t19R54FKkmq80fKzw4ZekZkCrRGanKgQ=="
},
"NuGet.Commands": {
"type": "Direct",
"requested": "[6.4.0, )",
"resolved": "6.4.0",
"contentHash": "j3ma45boGZADsHpJcu3Y2yq+n2luicC6ezd61TXTTzbOzA452oAPaSsFGUB1stIsuP/DVoqkTzjHXjaCHuJKPQ==",
"dependencies": {
"NuGet.Credentials": "6.4.0",
"NuGet.ProjectModel": "6.4.0"
}
},
"NuGet.DependencyResolver.Core": {
"type": "Direct",
"requested": "[6.4.0, )",
"resolved": "6.4.0",
"contentHash": "AKomZEKuhQlshujuiHbKvwl2cZNGq3SHsXFbpjCfCjMFMLwwA8saJGQQZp1lzsqQWcQWa6hLOcPtm7T3rd0SVg==",
"dependencies": {
"NuGet.Configuration": "6.4.0",
"NuGet.LibraryModel": "6.4.0",
"NuGet.Protocol": "6.4.0"
}
},
"SemanticVersioning": {
"type": "Direct",
"requested": "[2.0.2, )",
"resolved": "2.0.2",
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.201.13, )",
"resolved": "1.201.13",
"contentHash": "FRXuNLENsz5jGQvL0QkogAGsxEJyUQlXhvWHBWfZiHCnCrAWMD60nEis6yt0xCQpazszfMtHonbxz72XMxnFoQ==",
"dependencies": {
"protobuf-net": "1.0.0"
}
},
"System.Linq.Async": {
"type": "Direct",
"requested": "[6.0.1, )",
"resolved": "6.0.1",
"contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "6.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.1",
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
},
"NuGet.Common": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "srECugLk+LB1bXelDCDhHoi6do/EYTXzuntKhjHraS4roVB3NfWohEdCSiAPdpSV9M40Q6jo6MV2Srml9e+jHQ==",
"dependencies": {
"NuGet.Frameworks": "6.4.0"
}
},
"NuGet.Configuration": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "vPjauG9AoacEjiZWGIs+d11FCRVmseqAw78FIApfLvZrYMEbbwc9vc0LdC3PpoW5FxYkktyZSiiXVKXGLu+gXw==",
"dependencies": {
"NuGet.Common": "6.4.0",
"System.Security.Cryptography.ProtectedData": "4.4.0"
}
},
"NuGet.Credentials": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "tebsxclknVz3D4FrvE2MzVcsOyf6PffjGNQ77X9Yvbj9x5YpVWfumVPetqETcdsNEgiN0bBzfMre33lhrY7Itw==",
"dependencies": {
"NuGet.Protocol": "6.4.0"
}
},
"NuGet.Frameworks": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "qcufbjJIDtyY/Hah7JJfcRVpRYM3scgPqYBnukjO9kfADCFGr2azvVBozuwzljA6w/cR3w8bXLq6vW5xGrsmHw=="
},
"NuGet.LibraryModel": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "K6ROQpWr34Aje81G0HfipiznLTB8vD4BO8sF6FEwx1KjJVdFkSmGZPmAhc6L1vZPs8TKY5BqoH72zG13zVzW2w==",
"dependencies": {
"NuGet.Common": "6.4.0",
"NuGet.Versioning": "6.4.0"
}
},
"NuGet.Packaging": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "aR10aYqcUMGC2mwMGH5rls/MGaz3EVH8DKTTHQ/EC91hXNtrCTTAQonaRR+v1EItcoxtQeZ/WQOorv4z270Tgg==",
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"NuGet.Configuration": "6.4.0",
"NuGet.Versioning": "6.4.0",
"System.Security.Cryptography.Cng": "5.0.0",
"System.Security.Cryptography.Pkcs": "5.0.0"
}
},
"NuGet.ProjectModel": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "eW9Q7vPk8cpXDd5b+vtIPkl8dSDCPkPJPrjXPTfZGxhstldnhJrj1XPaonsDZLQ24YY7LrYCzC0BiHh3iO5zUA==",
"dependencies": {
"NuGet.DependencyResolver.Core": "6.4.0"
}
},
"NuGet.Protocol": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "KIPjsWP0P3EMsDsXaa6YBCTvYKur/zI0luS1kO5G37ci8mHs2kJFsuG5qaMhGvgyHASu54sxlic1n1oza2Pcbw==",
"dependencies": {
"NuGet.Packaging": "6.4.0"
}
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "YE8p3TpX4jIw+Gb24maE8YRDoqWA4imLmCbdOj5IvslLrZJXQ8akeFOGOplxICNVevON1g1SFYT2+cq4yy0nQQ=="
},
"protobuf-net": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.Security.Cryptography.Cng": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
"dependencies": {
"System.Formats.Asn1": "5.0.0"
}
},
"System.Security.Cryptography.Pkcs": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "9TPLGjBCGKmNvG8pjwPeuYy0SMVmGZRwlTZvyPHDbYv/DRkoeumJdfumaaDNQzVGMEmbWtg07zUpSW9q70IlDQ==",
"dependencies": {
"System.Formats.Asn1": "5.0.0",
"System.Security.Cryptography.Cng": "5.0.0"
}
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog=="
}
}
}
}

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch Client 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

View File

@@ -1,51 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Client Tests</AssemblyTitle>
<Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DebugType>pdbonly</DebugType>
<DocumentationFile>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\Torch.Client.Tests.xml</DocumentationFile>
</PropertyGroup>
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
<ItemGroup>
<PackageReference Include="Mono.TextTransform" Version="1.0.0" />
<PackageReference Include="NLog" Version="4.4.12" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.1" />
<PackageReference Include="xunit.assert" Version="2.2.0" />
<PackageReference Include="xunit.core" Version="2.2.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.2.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.2.0" />
<PackageReference Include="xunit.runner.console" Version="2.2.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch.Client\Torch.Client.csproj" />
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="obj\x64\Debug\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
<Compile Remove="obj\x64\Release\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
</ItemGroup>
</Project>

View File

@@ -1,94 +0,0 @@
using System;
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;
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
public static IEnumerable<object[]> Events => Manager().Events;
#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));
}
[Theory]
[MemberData(nameof(MemberInfo))]
public void TestBindingMemberInfo(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(Events))]
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
((Func<ReflectedEventReplacer>)field.Field.GetValue(null)).Invoke();
}
#endregion
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
namespace Torch.Client.Manager
{
public class MultiplayerManagerClient : MultiplayerManagerBase, IMultiplayerManagerClient
{
/// <inheritdoc />
public MultiplayerManagerClient(ITorchBase torch) : base(torch) { }
/// <inheritdoc />
public override void Attach()
{
base.Attach();
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
}
/// <inheritdoc />
public override void Detach()
{
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
base.Detach();
}
}
}

View File

@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.World;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using VRage.Game.ModAPI;
namespace Torch.Client.Manager
{
public class MultiplayerManagerLobby : MultiplayerManagerBase, IMultiplayerManagerServer
{
/// <inheritdoc />
public IReadOnlyList<ulong> BannedPlayers => new List<ulong>();
/// <inheritdoc />
public MultiplayerManagerLobby(ITorchBase torch) : base(torch) { }
/// <inheritdoc />
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
/// <inheritdoc />
public void BanPlayer(ulong steamId, bool banned = true) => Torch.Invoke(() => MyMultiplayer.Static.BanClient(steamId, banned));
/// <inheritdoc />
public void PromoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p < MyPromoteLevel.Admin) //cannot promote to owner by design
MySession.Static.SetUserPromoteLevel(steamId, p + 1);
});
}
/// <inheritdoc />
public void DemoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p > MyPromoteLevel.None && p < MyPromoteLevel.Owner) //owner cannot be demoted by design
MySession.Static.SetUserPromoteLevel(steamId, p - 1);
});
}
/// <inheritdoc />
public MyPromoteLevel GetUserPromoteLevel(ulong steamId)
{
return MySession.Static.GetUserPromoteLevel(steamId);
}
/// <inheritdoc />
public bool IsBanned(ulong steamId) => false;
/// <inheritdoc />
public event Action<ulong> PlayerKicked
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc />
public event Action<ulong, bool> PlayerBanned
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc />
public event Action<ulong, MyPromoteLevel> PlayerPromoted
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc/>
public override void Attach()
{
base.Attach();
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
}
/// <inheritdoc/>
public override void Detach()
{
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
base.Detach();
}
}
}

View File

@@ -1,194 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using NLog;
using Torch.Utils;
using MessageBox = System.Windows.MessageBox;
namespace Torch.Client
{
public static class Program
{
public const string SpaceEngineersBinaries = "Bin64";
private static string _spaceEngInstallAlias = null;
public static string SpaceEngineersInstallAlias
{
get
{
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
if (_spaceEngInstallAlias == null)
{
// ReSharper disable once AssignNullToNotNullAttribute
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location),
"SpaceEngineersAlias");
}
return _spaceEngInstallAlias;
}
}
private const string _steamSpaceEngineersDirectory = @"steamapps\common\SpaceEngineers\";
private const string _spaceEngineersVerifyFile = SpaceEngineersBinaries + @"\SpaceEngineers.exe";
public const string ConfigName = "Torch.cfg";
private static Logger _log = LogManager.GetLogger("Torch");
#if DEBUG
[DllImport("kernel32.dll")]
private static extern void AllocConsole();
[DllImport("kernel32.dll")]
private static extern void FreeConsole();
#endif
public static void Main(string[] args)
{
#if DEBUG
try
{
AllocConsole();
#endif
if (!TorchLauncher.IsTorchWrapped())
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// Early config: Resolve SE install directory.
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
SetupSpaceEngInstallAlias();
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args,
Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries));
return;
}
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);
spaceEngineersDirectory = null;
}
}
if (spaceEngineersDirectory == null)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog
{
Description = "Please select the SpaceEngineers installation folder"
};
do
{
if (dialog.ShowDialog() != DialogResult.OK)
{
var ex = new FileNotFoundException(
"Unable to find the Space Engineers install directory, aborting");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
spaceEngineersDirectory = dialog.SelectedPath;
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
break;
if (MessageBox.Show(
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
break;
} while (true); // Repeat until they confirm.
}
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
{
var ex = new IOException(
$"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
if (!File.Exists(junctionVerify))
{
var ex = new FileNotFoundException(
$"Junction link is not working. File {junctionVerify} does not exist");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
}
private static bool JunctionLink(string linkName, string targetDir)
{
var junctionLinkProc = new ProcessStartInfo("cmd.exe", $"/c mklink /J \"{linkName}\" \"{targetDir}\"")
{
WorkingDirectory = Directory.GetCurrentDirectory(),
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
};
Process cmd = Process.Start(junctionLinkProc);
// ReSharper disable once PossibleNullReferenceException
while (!cmd.HasExited)
{
string line = cmd.StandardOutput.ReadLine();
if (!string.IsNullOrWhiteSpace(line))
_log.Info(line);
Thread.Sleep(100);
}
if (cmd.ExitCode != 0)
_log.Error("Unable to create junction link {0} => {1}", linkName, targetDir);
return cmd.ExitCode == 0;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception) e.ExceptionObject;
_log.Error(ex);
LogManager.Flush();
MessageBox.Show(ex.StackTrace, ex.Message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void RunClient()
{
var client = new TorchClient();
try
{
client.Init();
}
catch (Exception e)
{
_log.Fatal("Torch encountered an error trying to initialize the game.");
_log.Fatal(e);
return;
}
client.Start();
}
}
}

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch Client")]
[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

View File

@@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Torch.Client.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Torch.Client.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Torch.Client.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -1,122 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net461</TargetFramework>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<AssemblyTitle>Torch Client</AssemblyTitle>
<Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutputPath>
<Prefer32Bit>true</Prefer32Bit>
<UseWPF>true</UseWPF>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DebugType>pdbonly</DebugType>
<DocumentationFile>$(SolutionDir)\bin\$(Platform)\$(Configuration)\Torch.Client.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>torchicon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
<ItemGroup>
<PackageReference Include="Mono.TextTransform" Version="1.0.0" />
<PackageReference Include="NLog" Version="4.4.12" />
</ItemGroup>
<ItemGroup>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Game">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.6108.20417, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="SpaceEngineers.Game">
<HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml" />
<Reference Include="VRage">
<HintPath>..\GameBinaries\VRage.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game">
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game.XmlSerializers">
<HintPath>..\GameBinaries\VRage.Game.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Library">
<HintPath>..\GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Render">
<HintPath>..\GameBinaries\VRage.Render.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Render11">
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Steam">
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="torchicon.ico" />
</ItemGroup>
<ItemGroup>
<Compile Remove="obj\x64\Debug\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
<Compile Remove="obj\x64\Release\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
</ItemGroup>
</Project>

View File

@@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Platform;
using Sandbox.Game;
using SpaceEngineers.Game;
using VRage.Steam;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Session;
using Torch.Client.Manager;
using Torch.Client.UI;
using Torch.Session;
using VRage;
using VRage.FileSystem;
using VRage.GameServices;
using VRageRender;
using VRageRender.ExternalApp;
namespace Torch.Client
{
public class TorchClient : TorchBase, ITorchClient
{
protected override uint SteamAppId => 244850;
protected override string SteamAppName => "SpaceEngineers";
public TorchClient()
{
Config = new TorchClientConfig();
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
? new MultiplayerManagerLobby(this)
: null);
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
? new MultiplayerManagerClient(this)
: null);
}
public override void Init()
{
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
Log.Info("Initializing Torch Client");
Config.InstancePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
SteamAppName);
base.Init();
OverrideMenus();
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
}
private void OverrideMenus()
{
var credits = new MyCreditsDepartment("Torch Developed By")
{
Persons = new List<MyCreditsPerson>
{
new MyCreditsPerson("THE TORCH TEAM"),
new MyCreditsPerson("http://github.com/TorchSE"),
}
};
MyPerGameSettings.Credits.Departments.Insert(0, credits);
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
}
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; });
}
public override void Restart()
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,34 +0,0 @@
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 string ChatName { get; set; }
public string ChatColor { get; set; }
public bool Save(string path = null)
{
return true;
}
}
}

View File

@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using Sandbox.Gui;
using VRage.Utils;
using VRageMath;
namespace Torch.Client
{
public class TorchConsoleScreen : MyGuiScreenBase
{
private MyGuiControlTextbox _textBox;
public override string GetFriendlyName()
{
return "Torch Console";
}
public TorchConsoleScreen() : base(isTopMostScreen: true)
{
BackgroundColor = new Vector4(0, 0, 0, 0.5f);
Size = new Vector2(0.5f);
RecreateControls(true);
}
public sealed override void RecreateControls(bool constructor)
{
Elements.Clear();
Elements.Add(new MyGuiControlLabel
{
Text = "Torch Console",
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP,
Position = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP)
});
Controls.Clear();
_textBox = new MyGuiControlTextbox
{
BorderEnabled = false,
Enabled = true,
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP,
Position = new Vector2(-0.5f)
};
Controls.Add(_textBox);
var pistonBtn = new MyGuiControlImageButton
{
Name = "TorchButton",
Text = "Torch",
HighlightType = MyGuiControlHighlightType.WHEN_CURSOR_OVER,
Visible = true,
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP
};
Controls.Add(pistonBtn);
}
}
}

View File

@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using VRage.Utils;
using VRageMath;
namespace Torch.Client
{
public class TorchSettingsScreen : MyGuiScreenBase
{
public override string GetFriendlyName() => "Torch Settings";
public TorchSettingsScreen() : base(new Vector2(0.5f), null, Vector2.One, true)
{
RecreateControls(true);
}
public sealed override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
AddCaption(MyStringId.GetOrCompute("Torch Settings"), null, new Vector2(0, 0), 1.2f);
var pluginList = new MyGuiControlListbox
{
VisibleRowsCount = 10,
};
foreach (var plugin in TorchBase.Instance.Plugins)
{
pluginList.Items.Add(new MyGuiControlListbox.Item(new StringBuilder(plugin.Name)));
}
Controls.Add(pluginList);
}
}
}

View File

@@ -1,56 +0,0 @@
#pragma warning disable 618
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using Sandbox.Gui;
using SpaceEngineers.Game.GUI;
using Torch.Utils;
using VRage.Game;
using VRage.Utils;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchMainMenuScreen : MyGuiScreenMainMenu
{
#pragma warning disable 169
[ReflectedGetter(Name = "m_elementGroup")]
private static Func<MyGuiScreenMainMenu, MyGuiControlElementGroup> _elementsGroup;
#pragma warning restore 169
public TorchMainMenuScreen() : this(false)
{
}
public TorchMainMenuScreen(bool pauseGame)
: base(pauseGame)
{
}
/// <inheritdoc />
public override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
Vector2 minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
Vector2 value = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_BOTTOM, 54, 54) + new Vector2(minSizeGui.X / 2f, 0f) + new Vector2(15f, 0f) / MyGuiConstants.GUI_OPTIMAL_SIZE;
MyGuiControlButton myGuiControlButton = MakeButton(value - 9 * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA,
MyStringId.GetOrCompute("Torch"), TorchButtonClicked, MyCommonTexts.ToolTipExitToWindows);
Controls.Add(myGuiControlButton);
_elementsGroup.Invoke(this).Add(myGuiControlButton);
}
private void TorchButtonClicked(MyGuiControlButton obj)
{
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchNavScreen>());
}
}
}

View File

@@ -1,49 +0,0 @@
using Sandbox;
using Sandbox.Game.Gui;
using Sandbox.Graphics.GUI;
using VRage;
using VRage.Game;
using VRage.Utils;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchNavScreen : MyGuiScreenBase
{
private MyGuiControlElementGroup _elementGroup;
public TorchNavScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR, new Vector2(0.35875f, 0.558333337f))
{
EnabledBackgroundFade = true;
RecreateControls(true);
}
public override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
_elementGroup = new MyGuiControlElementGroup();
_elementGroup.HighlightChanged += ElementGroupHighlightChanged;
AddCaption(MyCommonTexts.ScreenCaptionOptions, null, null);
var value = new Vector2(0f, -m_size.Value.Y / 2f + 0.146f);
var num = 0;
var myGuiControlButton = new MyGuiControlButton(value + num++ * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, null, MyTexts.Get(MyCommonTexts.ScreenOptionsButtonGame), 0.8f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, MyGuiControlHighlightType.WHEN_ACTIVE, delegate(MyGuiControlButton sender)
{
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchSettingsScreen>());
}, GuiSounds.MouseClick, 1f, null);
Controls.Add(myGuiControlButton);
_elementGroup.Add(myGuiControlButton);
CloseButtonEnabled = true;
}
private void ElementGroupHighlightChanged(MyGuiControlElementGroup obj)
{
foreach (MyGuiControlBase current in _elementGroup)
if (current.HasFocus && obj.SelectedElement != current)
FocusedControl = obj.SelectedElement;
}
public override string GetFriendlyName() => "Torch";
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
}
}

View File

@@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Graphics.GUI;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchSettingsScreen : MyGuiScreenBase
{
public TorchSettingsScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR,
new Vector2(0.35875f, 0.558333337f))
{
EnabledBackgroundFade = true;
RecreateControls(true);
}
/// <inheritdoc />
public override string GetFriendlyName() => "Torch Settings";
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Torch.Mod.Messages
{
/// <summary>
/// shim to store incoming message data
/// </summary>
internal class IncomingMessage : MessageBase
{
public IncomingMessage()
{
}
public override void ProcessClient()
{
throw new Exception();
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -32,11 +32,6 @@ namespace Torch.Mod.Messages
public override void ProcessClient() public override void ProcessClient()
{ {
if (TorchModCore.Debug)
{
MyAPIGateway.Utilities.ShowMessage("Torch", $"Joining server {Address} with delay {Delay}");
}
if (Delay <= 0) if (Delay <= 0)
{ {
MyAPIGateway.Multiplayer.JoinServer(Address); MyAPIGateway.Multiplayer.JoinServer(Address);

View File

@@ -1,9 +1,4 @@
using System; using ProtoBuf;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
namespace Torch.Mod.Messages namespace Torch.Mod.Messages
{ {
@@ -17,36 +12,9 @@ namespace Torch.Mod.Messages
[ProtoContract] [ProtoContract]
public abstract class MessageBase public abstract class MessageBase
{ {
[ProtoMember(101)]
public ulong SenderId; public ulong SenderId;
public abstract void ProcessClient(); public abstract void ProcessClient();
public abstract void ProcessServer(); public abstract void ProcessServer();
//members below not serialized, they're just metadata about the intended target(s) of this message
internal MessageTarget TargetType;
internal ulong Target;
internal ulong[] Ignore;
internal byte[] CompressedData;
}
public enum MessageTarget
{
/// <summary>
/// Send to Target
/// </summary>
Single,
/// <summary>
/// Send to Server
/// </summary>
Server,
/// <summary>
/// Send to all Clients (only valid from server)
/// </summary>
AllClients,
/// <summary>
/// Send to all except those steam ID listed in Ignore
/// </summary>
AllExcept,
} }
} }

View File

@@ -1,161 +1,50 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using Torch.Mod.Messages; using Torch.Mod.Messages;
using VRage; using VRage.Game.Components;
using VRage.Collections;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
using VRage.Network; #if TORCH
using VRage.Utils; using Torch.Utils;
using Task = ParallelTasks.Task; using VRage.Library.Collections;
using System.Reflection;
#endif
namespace Torch.Mod namespace Torch.Mod
{ {
public static class ModCommunication [MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
public class ModCommunication : MySessionComponentBase
{ {
public const ushort NET_ID = 4352; public const ulong MOD_ID = 2915950488;
private static bool _closing = false; private const ushort CHANNEL = 7654;
private static BlockingCollection<MessageBase> _processing;
private static MyConcurrentPool<IncomingMessage> _messagePool;
private static List<IMyPlayer> _playerCache;
public static void Register() public override void BeforeStart()
{ {
MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication."); base.BeforeStart();
_processing = new BlockingCollection<MessageBase>(new ConcurrentQueue<MessageBase>()); MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(CHANNEL, MessageHandler);
_playerCache = new List<IMyPlayer>();
_messagePool = new MyConcurrentPool<IncomingMessage>(8);
MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler);
//background thread to handle de/compression and processing
_closing = false;
MyAPIGateway.Parallel.StartBackground(DoProcessing);
MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully.");
} }
public static void Unregister() private void MessageHandler(ushort channel, byte[] data, ulong sender, bool fromServer)
{ {
MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication."); if (!fromServer)
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler); return;
_processing?.CompleteAdding();
_closing = true; var message = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(data);
//_task.Wait(); message.SenderId = sender;
if (MyAPIGateway.Multiplayer.IsServer) message.ProcessServer();
else message.ProcessClient();
} }
private static void MessageHandler(byte[] bytes)
{
var m = _messagePool.Get();
m.CompressedData = bytes;
#if TORCH #if TORCH
m.SenderId = MyEventContext.Current.Sender.Value; [ReflectedMethodInfo(typeof(MyAPIUtilities), "VRage.Game.ModAPI.IMyUtilities.SerializeToBinary")]
#endif private static MethodInfo _serializeMethod = null!;
_processing.Add(m); private static readonly CacheList<IMyPlayer> Players = new();
}
public static void DoProcessing() private static byte[] Serialize(MessageBase message)
{ {
while (!_closing) return (byte[])_serializeMethod.MakeGenericMethod(message.GetType())
{ .Invoke(MyAPIGateway.Utilities, new object[] { message });
try
{
MessageBase m;
try
{
m = _processing.Take();
}
catch
{
continue;
}
MyLog.Default.WriteLineAndConsole($"Processing message: {m.GetType().Name}");
if (m is IncomingMessage) //process incoming messages
{
MessageBase i;
try
{
var o = MyCompression.Decompress(m.CompressedData);
m.CompressedData = null;
_messagePool.Return((IncomingMessage)m);
i = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o);
}
catch (Exception ex)
{
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}");
continue;
}
if (TorchModCore.Debug)
MyAPIGateway.Utilities.ShowMessage("Torch", $"Received message of type {i.GetType().Name}");
if (MyAPIGateway.Multiplayer.IsServer)
i.ProcessServer();
else
i.ProcessClient();
}
else //process outgoing messages
{
if (TorchModCore.Debug)
MyAPIGateway.Utilities.ShowMessage("Torch", $"Sending message of type {m.GetType().Name}");
var b = MyAPIGateway.Utilities.SerializeToBinary(m);
m.CompressedData = MyCompression.Compress(b);
switch (m.TargetType)
{
case MessageTarget.Single:
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target);
break;
case MessageTarget.Server:
MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData);
break;
case MessageTarget.AllClients:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId)
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
case MessageTarget.AllExcept:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId))
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
default:
throw new Exception();
}
_playerCache.Clear();
}
}
catch (Exception ex)
{
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Exception occurred in communication thread! {ex}");
}
}
MyLog.Default.WriteLineAndConsole("TORCH MOD: INFO: Communication thread shut down successfully! THIS IS NOT AN ERROR");
//exit signal received. Clean everything and GTFO
_processing?.Dispose();
_processing = null;
_messagePool?.Clean();
_messagePool = null;
_playerCache = null;
} }
public static void SendMessageTo(MessageBase message, ulong target) public static void SendMessageTo(MessageBase message, ulong target)
@@ -163,12 +52,7 @@ namespace Torch.Mod
if (!MyAPIGateway.Multiplayer.IsServer) if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages"); throw new Exception("Only server can send targeted messages");
if (_closing) MyAPIGateway.Multiplayer.SendMessageTo(CHANNEL, Serialize(message), target);
return;
message.Target = target;
message.TargetType = MessageTarget.Single;
_processing.Add(message);
} }
public static void SendMessageToClients(MessageBase message) public static void SendMessageToClients(MessageBase message)
@@ -176,11 +60,7 @@ namespace Torch.Mod
if (!MyAPIGateway.Multiplayer.IsServer) if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages"); throw new Exception("Only server can send targeted messages");
if (_closing) MyAPIGateway.Multiplayer.SendMessageToOthers(CHANNEL, Serialize(message));
return;
message.TargetType = MessageTarget.AllClients;
_processing.Add(message);
} }
public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers) public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers)
@@ -188,21 +68,20 @@ namespace Torch.Mod
if (!MyAPIGateway.Multiplayer.IsServer) if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages"); throw new Exception("Only server can send targeted messages");
if (_closing) using var players = Players;
return; MyAPIGateway.Multiplayer.Players.GetPlayers(players, player => !ignoredUsers.Contains(player.SteamUserId));
message.TargetType = MessageTarget.AllExcept; var data = Serialize(message);
message.Ignore = ignoredUsers; foreach (var player in players)
_processing.Add(message); {
MyAPIGateway.Multiplayer.SendMessageTo(CHANNEL, data, player.SteamUserId);
}
} }
public static void SendMessageToServer(MessageBase message) public static void SendMessageToServer(MessageBase message)
{ {
if (_closing) throw new NotSupportedException();
return;
message.TargetType = MessageTarget.Server;
_processing.Add(message);
} }
#endif
} }
} }

View File

@@ -9,13 +9,11 @@
<Import_RootNamespace>Torch.Mod</Import_RootNamespace> <Import_RootNamespace>Torch.Mod</Import_RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Messages\IncomingMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\JoinServerMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\JoinServerMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\VoxelResetMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\VoxelResetMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ModCommunication.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ModCommunication.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TorchModCore.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.ModAPI;
using VRage.Game.Components;
namespace Torch.Mod
{
[MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
public class TorchModCore : MySessionComponentBase
{
public const ulong MOD_ID = 2722000298;
private static bool _init;
public static bool Debug;
public override void UpdateAfterSimulation()
{
if (_init)
return;
_init = true;
ModCommunication.Register();
MyAPIGateway.Utilities.MessageEntered += Utilities_MessageEntered;
}
private void Utilities_MessageEntered(string messageText, ref bool sendToOthers)
{
if (messageText == "@!debug")
{
Debug = !Debug;
MyAPIGateway.Utilities.ShowMessage("Torch", $"Debug: {Debug}");
sendToOthers = false;
}
}
protected override void UnloadData()
{
try
{
MyAPIGateway.Utilities.MessageEntered -= Utilities_MessageEntered;
ModCommunication.Unregister();
}
catch
{
//session unloading, don't care
}
}
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Torch.Server.ReferenceAssemblies.net7</id>
<version>1.0.1</version>
<title>Torch Server Reference Assemblies</title>
<authors>zznty</authors>
<owners>zznty</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Torch Server Reference Assemblies (.NET 7 edition)</description>
<repository type="git" url="https://github.com/PveTeam/Torch" />
<dependencies>
<group targetFramework="net7.0-windows7.0">
<dependency id="SpaceEngineersDedicated.ReferenceAssemblies" version="1.201.13" />
<dependency id="Torch.Server" version="torchVersion" />
</group>
</dependencies>
<frameworkReferences>
<group targetFramework="net7.0-windows7.0">
<frameworkReference name="Microsoft.WindowsDesktop.App.WPF" />
</group>
</frameworkReferences>
</metadata>
</package>

View File

@@ -1,17 +0,0 @@
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

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net7-windows</TargetFramework>
<NoWarn>1591,0649</NoWarn> <NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Server Tests</AssemblyTitle> <AssemblyTitle>Torch Server Tests</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
@@ -17,9 +17,9 @@
</PropertyGroup> </PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> --> <!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="NLog" Version="5.1.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
@@ -27,9 +27,6 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" /> <ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch.Server\Torch.Server.csproj" /> <ProjectReference Include="..\Torch.Server\Torch.Server.csproj" />

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

View File

@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using Microsoft.Extensions.Configuration;
using NLog; using NLog;
using NLog.Targets; using NLog.Targets;
using Sandbox.Engine.Utils; using Sandbox.Engine.Utils;
@@ -29,25 +30,20 @@ namespace Torch.Server
private const string STEAMCMD_DIR = "steamcmd"; private const string STEAMCMD_DIR = "steamcmd";
private const string STEAMCMD_ZIP = "temp.zip"; private const string STEAMCMD_ZIP = "temp.zip";
private static readonly string STEAMCMD_EXE = "steamcmd.exe"; private static readonly string STEAMCMD_EXE = "steamcmd.exe";
private static readonly string RUNSCRIPT_FILE = "runscript.txt"; private const string STEAMCMD_ARGS = "+force_install_dir \"{0}\" +login anonymous +app_update 298740 +quit";
private const string RUNSCRIPT = @"force_install_dir ../
login anonymous
app_update 298740
quit";
private TorchServer _server; private TorchServer _server;
internal Persistent<TorchConfig> ConfigPersistent { get; } internal Persistent<TorchConfig> ConfigPersistent { get; }
public TorchConfig Config => ConfigPersistent?.Data; public TorchConfig Config => ConfigPersistent?.Data;
public TorchServer Server => _server; public TorchServer Server => _server;
public Initializer(string basePath, Persistent<TorchConfig> torchConfig) public Initializer(Persistent<TorchConfig> torchConfig)
{ {
Instance = this; Instance = this;
ConfigPersistent = torchConfig; ConfigPersistent = torchConfig;
} }
public bool Initialize(string[] args) public bool Initialize(IConfiguration configuration)
{ {
if (_init) if (_init)
return false; return false;
@@ -59,19 +55,18 @@ quit";
LogManager.ReconfigExistingLoggers(); LogManager.ReconfigExistingLoggers();
Log.Debug("Debug logging enabled."); Log.Debug("Debug logging enabled.");
#endif #endif
if (!configuration.GetValue("noupdate", false))
RunSteamCmd(configuration);
// This is what happens when Keen is bad and puts extensions into the System namespace. var processPid = configuration.GetValue<int>("waitForPid");
if (!Enumerable.Contains(args, "-noupdate")) if (processPid != 0)
RunSteamCmd();
if (!string.IsNullOrEmpty(Config.WaitForPID))
{ {
try try
{ {
var pid = int.Parse(Config.WaitForPID); var waitProc = Process.GetProcessById(processPid);
var waitProc = Process.GetProcessById(pid);
Log.Info("Continuing in 5 seconds."); Log.Info("Continuing in 5 seconds.");
Log.Warn($"Waiting for process {pid} to close"); Log.Warn($"Waiting for process {processPid} to close");
while (!waitProc.HasExited) while (!waitProc.HasExited)
{ {
Console.Write("."); Console.Write(".");
@@ -88,11 +83,11 @@ quit";
return true; return true;
} }
public void Run(bool isService, string instanceName, string instancePath) public void Run()
{ {
_server = new TorchServer(Config, instancePath, instanceName); _server = new TorchServer(Config, ApplicationContext.Current.InstanceDirectory.FullName, ApplicationContext.Current.InstanceName);
if (isService || Config.NoGui) if (ApplicationContext.Current.IsService || Config.NoGui)
{ {
_server.Init(); _server.Init();
_server.Start(); _server.Start();
@@ -129,21 +124,18 @@ quit";
} }
} }
public static void RunSteamCmd() public static void RunSteamCmd(IConfiguration configuration)
{ {
var log = LogManager.GetLogger("SteamCMD"); var log = LogManager.GetLogger("SteamCMD");
var path = Environment.GetEnvironmentVariable("TORCH_STEAMCMD") ?? Path.GetFullPath(STEAMCMD_DIR); var path = configuration.GetValue<string>("steamCmdPath") ?? ApplicationContext.Current.TorchDirectory
.CreateSubdirectory(STEAMCMD_DIR).FullName;
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
} }
var runScriptPath = Path.Combine(path, RUNSCRIPT_FILE);
if (!File.Exists(runScriptPath))
File.WriteAllText(runScriptPath, RUNSCRIPT);
var steamCmdExePath = Path.Combine(path, STEAMCMD_EXE); var steamCmdExePath = Path.Combine(path, STEAMCMD_EXE);
if (!File.Exists(steamCmdExePath)) if (!File.Exists(steamCmdExePath))
{ {
@@ -152,7 +144,8 @@ quit";
log.Info("Downloading SteamCMD."); log.Info("Downloading SteamCMD.");
using (var client = new HttpClient()) using (var client = new HttpClient())
using (var file = File.Create(STEAMCMD_ZIP)) using (var file = File.Create(STEAMCMD_ZIP))
client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file); using (var stream = client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result)
stream.CopyTo(file);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, path); ZipFile.ExtractToDirectory(STEAMCMD_ZIP, path);
File.Delete(STEAMCMD_ZIP); File.Delete(STEAMCMD_ZIP);
@@ -166,8 +159,9 @@ quit";
} }
log.Info("Checking for DS updates."); log.Info("Checking for DS updates.");
var steamCmdProc = new ProcessStartInfo(steamCmdExePath, "+runscript runscript.txt") var steamCmdProc = new ProcessStartInfo(steamCmdExePath)
{ {
Arguments = string.Format(STEAMCMD_ARGS, configuration.GetValue("gamePath", "../")),
WorkingDirectory = path, WorkingDirectory = path,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,

View File

@@ -17,7 +17,7 @@ namespace Torch.Server
{ {
public IList<LogEntry> LogEntries { get; set; } public IList<LogEntry> LogEntries { get; set; }
public SynchronizationContext TargetContext { get; set; } public SynchronizationContext TargetContext { get; set; }
private readonly int _maxLines = 1000; private const int MAX_LINES = 1000;
/// <inheritdoc /> /// <inheritdoc />
protected override void Write(LogEventInfo logEvent) protected override void Write(LogEventInfo logEvent)
@@ -29,6 +29,11 @@ namespace Torch.Server
{ {
var logEvent = (LogEventInfo) state; var logEvent = (LogEventInfo) state;
LogEntries?.Add(new(logEvent.TimeStamp, Layout.Render(logEvent), LogLevelColors[logEvent.Level])); LogEntries?.Add(new(logEvent.TimeStamp, Layout.Render(logEvent), LogLevelColors[logEvent.Level]));
if (LogEntries is not {Count: > MAX_LINES}) return;
for (var i = 0; LogEntries.Count > MAX_LINES; i++)
{
LogEntries.RemoveAt(i);
}
} }
private static readonly Dictionary<LogLevel, SolidColorBrush> LogLevelColors = new() private static readonly Dictionary<LogLevel, SolidColorBrush> LogLevelColors = new()

View File

@@ -90,8 +90,7 @@ namespace Torch.Server.Managers
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("Failed to load world at path: " + f); Log.Error(ex, "Failed to load world at path: " + f);
continue;
} }
} }
@@ -137,7 +136,7 @@ namespace Torch.Server.Managers
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("Failed to load world at path: " + worldPath); Log.Error(ex, "Failed to load world at path: " + worldPath);
DedicatedConfig.LoadWorld = null; DedicatedConfig.LoadWorld = null;
return; return;
} }
@@ -147,7 +146,7 @@ namespace Torch.Server.Managers
{ {
DedicatedConfig.Mods.Clear(); DedicatedConfig.Mods.Clear();
//remove the Torch mod to avoid running multiple copies of it //remove the Torch mod to avoid running multiple copies of it
DedicatedConfig.SelectedWorld.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID); DedicatedConfig.SelectedWorld.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == ModCommunication.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods) foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods)
DedicatedConfig.Mods.Add(new ModItemInfo(m)); DedicatedConfig.Mods.Add(new ModItemInfo(m));
Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync()); Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync());
@@ -162,7 +161,7 @@ namespace Torch.Server.Managers
{ {
DedicatedConfig.Mods.Clear(); DedicatedConfig.Mods.Clear();
//remove the Torch mod to avoid running multiple copies of it //remove the Torch mod to avoid running multiple copies of it
DedicatedConfig.SelectedWorld.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID); DedicatedConfig.SelectedWorld.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == ModCommunication.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods) foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods)
DedicatedConfig.Mods.Add(new ModItemInfo(m)); DedicatedConfig.Mods.Add(new ModItemInfo(m));
Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync()); Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync());
@@ -234,9 +233,11 @@ namespace Torch.Server.Managers
try try
{ {
var world = DedicatedConfig.Worlds.FirstOrDefault(x => x.WorldPath == DedicatedConfig.LoadWorld) ?? new WorldViewModel(DedicatedConfig.LoadWorld); var world = DedicatedConfig.SelectedWorld;
world.Checkpoint.SessionName = DedicatedConfig.WorldName; world.Checkpoint.SessionName = string.IsNullOrEmpty(world.Checkpoint.SessionName)
? Path.GetDirectoryName(DedicatedConfig.LoadWorld)
: world.Checkpoint.SessionName;
world.WorldConfiguration.Settings = DedicatedConfig.SessionSettings; world.WorldConfiguration.Settings = DedicatedConfig.SessionSettings;
world.WorldConfiguration.Mods.Clear(); world.WorldConfiguration.Mods.Clear();
@@ -268,7 +269,7 @@ namespace Torch.Server.Managers
private void ValidateInstance(string path) private void ValidateInstance(string path)
{ {
Directory.CreateDirectory(Path.Combine(path, "Saves")); Directory.CreateDirectory(Path.Combine(path, "Saves"));
Directory.CreateDirectory(Path.Combine(path, "Mods")); // Directory.CreateDirectory(Path.Combine(path, "Mods"));
var configPath = Path.Combine(path, CONFIG_NAME); var configPath = Path.Combine(path, CONFIG_NAME);
if (File.Exists(configPath)) if (File.Exists(configPath))
return; return;
@@ -282,7 +283,6 @@ namespace Torch.Server.Managers
{ {
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string FolderName { get; set; }
public string WorldPath { get; } public string WorldPath { get; }
public MyObjectBuilder_SessionSettings KeenSessionSettings => WorldConfiguration.Settings; public MyObjectBuilder_SessionSettings KeenSessionSettings => WorldConfiguration.Settings;
public MyObjectBuilder_Checkpoint KeenCheckpoint => Checkpoint; public MyObjectBuilder_Checkpoint KeenCheckpoint => Checkpoint;
@@ -311,7 +311,6 @@ namespace Torch.Server.Managers
WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024; WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024;
_checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc"); _checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc");
_worldConfigPath = Path.Combine(WorldPath, "Sandbox_config.sbc"); _worldConfigPath = Path.Combine(WorldPath, "Sandbox_config.sbc");
FolderName = Path.GetFileName(worldPath);
if (loadFiles) if (loadFiles)
LoadSandbox(); LoadSandbox();
} }

View File

@@ -12,7 +12,6 @@ using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Sandbox.Game.Gui; using Sandbox.Game.Gui;
using Sandbox.Game.World; using Sandbox.Game.World;
using Steamworks;
using Torch.API; using Torch.API;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.Managers; using Torch.Managers;

View File

@@ -3,6 +3,7 @@ using NLog;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Server;
using Torch.Server.Managers; using Torch.Server.Managers;
using Torch.Utils; using Torch.Utils;
using VRage.Game; using VRage.Game;
@@ -25,6 +26,9 @@ public static class CheckpointLoadPatch
private static bool Prefix(ref MyObjectBuilder_Checkpoint __result) private static bool Prefix(ref MyObjectBuilder_Checkpoint __result)
{ {
#pragma warning disable CS0618 #pragma warning disable CS0618
if (!((TorchServer)TorchBase.Instance).HasRun)
return true;
var world = TorchBase.Instance.Managers.GetManager<InstanceManager>().DedicatedConfig.SelectedWorld; var world = TorchBase.Instance.Managers.GetManager<InstanceManager>().DedicatedConfig.SelectedWorld;
#pragma warning restore CS0618 #pragma warning restore CS0618
if (world is null) if (world is null)
@@ -33,6 +37,9 @@ public static class CheckpointLoadPatch
return false; return false;
} }
world.KeenCheckpoint.Settings = world.WorldConfiguration.Settings;
world.KeenCheckpoint.Mods = world.WorldConfiguration.Mods;
__result = world.Checkpoint; __result = world.Checkpoint;
return false; return false;
} }

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using NLog;
using Steamworks;
using Torch.Managers.PatchManager;
using Torch.Utils;
namespace Torch.Patches;
[PatchShim]
public static class SteamLoginPatch
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
[ReflectedMethodInfo(null, "LogOnAnonymous", TypeName = "VRage.Steam.MySteamGameServer, VRage.Steam")]
private static MethodInfo LoginMethod = null!;
public static void Patch(PatchContext context)
{
context.GetPattern(LoginMethod).AddPrefix();
}
private static bool Prefix()
{
#pragma warning disable CS0618
var token = TorchBase.Instance.Config.LoginToken;
#pragma warning restore CS0618
if (string.IsNullOrEmpty(token))
return true;
Log.Info("Logging in to Steam with GSLT");
SteamGameServer.LogOn(token);
return false;
}
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
@@ -8,6 +9,7 @@ using NLog;
using Sandbox; using Sandbox;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.MSIL;
using Torch.Utils;
namespace Torch.Patches namespace Torch.Patches
{ {
@@ -17,12 +19,14 @@ namespace Torch.Patches
[PatchShim] [PatchShim]
public static class WorldLoadExceptionPatch public static class WorldLoadExceptionPatch
{ {
private static readonly ILogger _log = LogManager.GetCurrentClassLogger(); private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
[ReflectedMethodInfo(typeof(MySandboxGame), "InitQuickLaunch")]
private static MethodInfo _quickLaunchMethod = null!;
public static void Patch(PatchContext ctx) public static void Patch(PatchContext ctx)
{ {
ctx.GetPattern(typeof(MySandboxGame).GetMethod("InitQuickLaunch", BindingFlags.Instance | BindingFlags.NonPublic)) ctx.GetPattern(_quickLaunchMethod).AddTranspiler(nameof(Transpile));
.Transpilers.Add(typeof(WorldLoadExceptionPatch).GetMethod(nameof(Transpile), BindingFlags.Static | BindingFlags.NonPublic));
} }
private static IEnumerable<MsilInstruction> Transpile(IEnumerable<MsilInstruction> method) private static IEnumerable<MsilInstruction> Transpile(IEnumerable<MsilInstruction> method)
@@ -30,19 +34,19 @@ namespace Torch.Patches
var msil = method.ToList(); var msil = method.ToList();
for (var i = 0; i < msil.Count; i++) for (var i = 0; i < msil.Count; i++)
{ {
if (msil[i].TryCatchOperations.All(x => x.Type != MsilTryCatchOperationType.BeginClauseBlock)) var instruction = msil[i];
continue; if (instruction.IsLocalStore() && instruction.Operand is MsilOperandInline.MsilOperandLocal {Value.Index: 19} operand)
for (; i < msil.Count; i++)
{ {
if (msil[i].OpCode != OpCodes.Leave) msil.InsertRange(i + 1, new []
continue; {
operand.Instruction.CopyWith(OpCodes.Ldloc_S),
msil[i] = new MsilInstruction(OpCodes.Rethrow); new MsilInstruction(OpCodes.Call).InlineValue(new Action<Exception>(LogFatal).Method)
break; });
} }
} }
return msil; return msil;
} }
private static void LogFatal(Exception e) => Log.Fatal(e.ToStringDemystified());
} }
} }

View File

@@ -1,41 +1,96 @@
using System; using System;
using System.IO; using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Xml;
using NLog;
using NLog.Config;
using NLog.Targets; using NLog.Targets;
using Torch.API;
using Torch.Utils; using Torch.Utils;
namespace Torch.Server namespace Torch.Server
{ {
internal static class Program internal static class Program
{ {
[STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
var isService = Environment.GetEnvironmentVariable("TORCH_SERVICE") var configurationBuilder = new ConfigurationBuilder()
?.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) ?? false; .AddEnvironmentVariables("TORCH")
Target.Register<LogViewerTarget>(nameof(LogViewerTarget)); .AddCommandLine(args);
//Ensures that all the files are downloaded in the Torch directory. var configuration = configurationBuilder.Build();
var workingDir = AppContext.BaseDirectory;
var binDir = Path.Combine(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir, "DedicatedServer64");
Directory.SetCurrentDirectory(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir);
if (!isService && Directory.Exists(binDir)) var context = CreateApplicationContext(configuration);
foreach (var file in Directory.GetFiles(binDir, "System.*.dll"))
{
File.Delete(file);
}
TorchLauncher.Launch(workingDir, binDir); SetupLogging(context, configuration);
var config = SetupConfiguration(context, configurationBuilder, out configuration);
// Breaks on Windows Server 2019 var handler = new UnhandledExceptionHandler(config.Data);
#if TORCH_SERVICE AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException;
if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive)
{ var initializer = new Initializer(config);
using (var service = new TorchService(args)) if (!initializer.Initialize(configuration))
ServiceBase.Run(service); Environment.Exit(1);
return;
} #if DEBUG
TorchLauncher.Launch(context.TorchDirectory.FullName, context.GameBinariesDirectory.FullName);
#else
TorchLauncher.Launch(context.TorchDirectory.FullName, Path.Combine(context.TorchDirectory.FullName, "torch64"),
context.GameBinariesDirectory.FullName);
#endif #endif
var instanceName = Environment.GetEnvironmentVariable("TORCH_INSTANCE") ?? "Instance"; initializer.Run();
}
private static void SetupLogging(IApplicationContext context, IConfiguration configuration)
{
var oldNlog = Path.Combine(context.TorchDirectory.FullName, "NLog.config");
var newNlog = configuration.GetValue("loggingConfigPath", Path.Combine(context.InstanceDirectory.FullName, "NLog.config"));
if (File.Exists(oldNlog) && !File.ReadAllText(oldNlog).Contains("FlowDocument"))
File.Move(oldNlog, newNlog);
else if (!File.Exists(newNlog))
using (var f = File.Create(newNlog!))
typeof(Program).Assembly.GetManifestResourceStream("Torch.Server.NLog.config")!.CopyTo(f);
Target.Register<LogViewerTarget>(nameof(LogViewerTarget));
TorchLogManager.RegisterTargets(configuration.GetValue("loggingExtensionsPath",
Path.Combine(
context.InstanceDirectory.FullName,
"LoggingExtensions")));
TorchLogManager.SetConfiguration(new XmlLoggingConfiguration(newNlog));
}
private static Persistent<TorchConfig> SetupConfiguration(IApplicationContext context,
IConfigurationBuilder builder,
out IConfigurationRoot configuration)
{
var oldTorchCfg = Path.Combine(context.TorchDirectory.FullName, "Torch.cfg");
var torchCfg = Path.Combine(context.InstanceDirectory.FullName, "Torch.cfg");
if (File.Exists(oldTorchCfg))
File.Move(oldTorchCfg, torchCfg);
builder.AddXmlFile(torchCfg, true, true);
configuration = builder.Build();
var config = new Persistent<TorchConfig>(torchCfg, configuration.Get<TorchConfig>() ?? new());
config.Data.InstanceName = context.InstanceName;
config.Data.InstancePath = context.InstanceDirectory.FullName;
return config;
}
private static IApplicationContext CreateApplicationContext(IConfiguration configuration)
{
var isService = configuration.GetValue("service", false);
var workingDir = AppContext.BaseDirectory;
var gamePath = configuration.GetValue("gamePath", workingDir);
var binDir = Path.Combine(gamePath, "DedicatedServer64");
var instanceName = configuration.GetValue("instanceName", "Instance");
string instancePath; string instancePath;
if (Path.IsPathRooted(instanceName)) if (Path.IsPathRooted(instanceName))
@@ -45,61 +100,13 @@ namespace Torch.Server
} }
else else
{ {
instancePath = Path.GetFullPath(instanceName); instancePath = Directory.CreateDirectory(instanceName!).FullName;
}
var oldTorchCfg = Path.Combine(workingDir, "Torch.cfg");
var torchCfg = Path.Combine(instancePath, "Torch.cfg");
if (File.Exists(oldTorchCfg))
File.Move(oldTorchCfg, torchCfg, true);
var config = Persistent<TorchConfig>.Load(torchCfg);
config.Data.InstanceName = instanceName;
config.Data.InstancePath = instancePath;
if (!config.Data.Parse(args))
{
Console.WriteLine("Invalid arguments");
Environment.Exit(1);
}
var handler = new UnhandledExceptionHandler(config.Data, isService);
AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException;
var initializer = new Initializer(workingDir, config);
if (!initializer.Initialize(args))
Environment.Exit(1);
CopyNative(binDir);
initializer.Run(isService, instanceName, instancePath);
}
private static void CopyNative(string binPath)
{
var apiSource = Path.Combine(binPath, "steam_api64.dll");
var apiTarget = Path.Combine(AppContext.BaseDirectory, "steam_api64.dll");
if (!File.Exists(apiTarget))
{
File.Copy(apiSource, apiTarget);
}
else if (File.GetLastWriteTime(apiTarget) < File.GetLastWriteTime(binPath))
{
File.Delete(apiTarget);
File.Copy(apiSource, apiTarget);
} }
var havokSource = Path.Combine(binPath, "Havok.dll"); Directory.SetCurrentDirectory(gamePath);
var havokTarget = Path.Combine(AppContext.BaseDirectory, "Havok.dll");
return new ApplicationContext(new(workingDir), new(gamePath), new(binDir),
if (!File.Exists(havokTarget)) new(instancePath), instanceName, isService);
{
File.Copy(havokSource, havokTarget);
}
else if (File.GetLastWriteTime(havokTarget) < File.GetLastWriteTime(havokSource))
{
File.Delete(havokTarget);
File.Copy(havokSource, havokTarget);
}
} }
} }
} }

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch Server")]
[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

View File

@@ -3,7 +3,7 @@
"profiles": { "profiles": {
"Torch.Server": { "Torch.Server": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "-noupdate", "commandLineArgs": "--noupdate true --gamepath \"C:\\Program Files (x86)\\Steam\\steamapps\\common\\SpaceEngineersDedicatedServer\"",
"use64Bit": true, "use64Bit": true,
"hotReloadEnabled": false "hotReloadEnabled": false
} }

View File

@@ -1,165 +1,79 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net7-windows</TargetFramework>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust> <UseApplicationTrust>false</UseApplicationTrust>
<AssemblyTitle>Torch Server</AssemblyTitle> <AssemblyTitle>Torch Server</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
<IsPackable>false</IsPackable>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<TieredPGO>true</TieredPGO>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<BeautyLibsDir>torch64</BeautyLibsDir>
<NoBeautyFlag>True</NoBeautyFlag>
<ForceBeauty>True</ForceBeauty>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>Torch.Server.Program</StartupObject> <StartupObject>Torch.Server.Program</StartupObject>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>torchicon.ico</ApplicationIcon> <ApplicationIcon>torchicon.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoCompleteTextBox" Version="1.3.0" /> <PackageReference Include="AutoCompleteTextBox" Version="1.6.0" />
<PackageReference Include="Ben.Demystifier" Version="0.4.1" /> <PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="ControlzEx" Version="5.0.1" /> <PackageReference Include="ControlzEx" Version="5.0.2" />
<PackageReference Include="MahApps.Metro" Version="2.4.9" /> <PackageReference Include="MahApps.Metro" Version="2.4.9" />
<PackageReference Include="MdXaml" Version="1.12.0" /> <PackageReference Include="MdXaml" Version="1.16.2" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.226801" /> <PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.343001" />
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="7.0.0" />
<PackageReference Include="NLog" Version="5.1.0" />
<PackageReference Include="PropertyChanged.Fody" Version="4.0.3" PrivateAssets="all" />
<PackageReference Include="Steamworks.NET" Version="20.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Management" Version="6.0.0" /> <PackageReference Include="System.Management" Version="7.0.0" />
<PackageReference Include="nulastudio.NetCoreBeauty" Version="1.2.9.3" />
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.201.13">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Microsoft.CodeAnalysis, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Microsoft.CodeAnalysis.CSharp, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Game, Version=0.1.6305.30774, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.6305.30761, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="SpaceEngineers.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Steamworks.NET">
<HintPath>..\GameBinaries\Steamworks.NET.dll</HintPath>
</Reference>
<Reference Include="VRage, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Dedicated, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Dedicated.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Library, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Platform.Windows, Culture=neutral, PublicKeyToken=null">
<HintPath>..\GameBinaries\VRage.Platform.Windows.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Render, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Render.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Render11, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Scripting, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="VRage.Steam">
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Update="TorchService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Update="TorchServiceInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Remove="ServerManager.cs" /> <Compile Remove="ServerManager.cs" />
<Compile Remove="ViewModels\SessionSettingsViewModel1.cs" /> <Compile Remove="ViewModels\SessionSettingsViewModel1.cs" />
<Compile Remove="Views\WorldSelectControl.xaml.cs" /> <Compile Remove="Views\WorldSelectControl.xaml.cs" />
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" /> <ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" /> <ProjectReference Include="..\Torch\Torch.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="torchicon.ico" /> <Resource Include="torchicon.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Remove="Views\WorldSelectControl.xaml" /> <Page Remove="Views\WorldSelectControl.xaml" />
<None Include="..\NLog.config" Visible="false" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Always" /> <EmbeddedResource Include="..\NLog.config" Visible="false" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,171 +1,127 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml.Serialization; using System.Xml.Serialization;
using NLog;
using Torch.API; using Torch.API;
using Torch.Views; using Torch.Views;
namespace Torch.Server namespace Torch.Server;
public class TorchConfig : ViewModel, ITorchConfig
{ {
// TODO: redesign this gerbage public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate;
public class TorchConfig : CommandLine, ITorchConfig, INotifyPropertyChanged public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate;
/// <inheritdoc />
[XmlIgnore]
public bool NoUpdate { get; set; }
/// <inheritdoc />
[XmlIgnore]
public bool ForceUpdate { get; set; }
/// <summary>
/// Permanent flag to ALWAYS automatically start the server
/// </summary>
[Display(Name = "Auto Start", Description = "Permanent flag to ALWAYS automatically start the server.", GroupName = "Server")]
public bool Autostart { get; set; }
/// <summary>
/// Temporary flag to automatically start the server only on the next run
/// </summary>
[XmlIgnore]
public bool TempAutostart { get; set; }
/// <inheritdoc />
[Display(Name = "Restart On Crash", Description = "Automatically restart the server if it crashes.", GroupName = "Server")]
public bool RestartOnCrash { get; set; }
public string InstancePath { get; set; }
/// <inheritdoc />
[Display(Name = "No GUI", Description = "Do not show the Torch UI.", GroupName = "Window")]
public bool NoGui { get; set; }
/// <inheritdoc />
[Display(Name = "Update Torch", Description = "Check every start for new versions of torch.",
GroupName = "Server")]
public bool GetTorchUpdates { get; set; } = true;
public string InstanceName { get; set; }
/// <inheritdoc />
[Display(Name = "Update Plugins", Description = "Check every start for new versions of plugins.",
GroupName = "Server")]
public bool GetPluginUpdates { get; set; } = true;
/// <inheritdoc />
[Display(Name = "Watchdog Timeout", Description = "Watchdog timeout (in seconds).", GroupName = "Server")]
public int TickTimeout { get; set; } = 60;
/// <inheritdoc />
public List<Guid> Plugins { get; set; } = new();
[Display(Name = "Local Plugins", Description = "Loads all pluhins from disk, ignores the plugins defined in config.", GroupName = "In-Game")]
public bool LocalPlugins { get; set; }
[Display(Name = "Auto Disconnect", Description = "When server restarts, all clients are rejected to main menu to prevent auto rejoin.", GroupName = "In-Game")]
public bool DisconnectOnRestart { get; set; }
[Display(Name = "Chat Name", Description = "Default name for chat from gui, broadcasts etc..",
GroupName = "In-Game")]
public string ChatName { get; set; } = "Server";
[Display(Name = "Chat Color",
Description = "Default color for chat from gui, broadcasts etc.. (Red, Blue, White, Green)",
GroupName = "In-Game")]
public string ChatColor { get; set; } = "Red";
[Display(Name = "Enable Whitelist", Description = "Enable Whitelist to prevent random players join while maintance, tests or other.", GroupName = "In-Game")]
public bool EnableWhitelist { get; set; }
[Display(Name = "Whitelist", Description = "Collection of whitelisted steam ids.", GroupName = "In-Game")]
public List<ulong> Whitelist { get; set; } = new();
[Display(Name = "Width", Description = "Default window width.", GroupName = "Window")]
public int WindowWidth { get; set; } = 980;
[Display(Name = "Height", Description = "Default window height", GroupName = "Window")]
public int WindowHeight { get; set; } = 588;
[Display(Name = "Font Size", Description = "Font size for logging text box. (default is 16)",
GroupName = "Window")]
public int FontSize { get; set; } = 16;
[Display(Name = "UGC Service Type", Description = "Service for downloading mods", GroupName = "Server")]
public UGCServiceType UgcServiceType { get; set; } = UGCServiceType.Steam;
public string LastUsedTheme { get; set; } = "Torch Theme";
[Display(Name = "Independent Console", Description = "Keeps a separate console window open after the main UI loads.", GroupName = "Window")]
public bool IndependentConsole { get; set; }
[XmlIgnore]
public string TestPlugin { get; set; }
[Display(Name = "Enable Asserts", Description = "Enable Keen's assert logging.", GroupName = "Server")]
public bool EnableAsserts { get; set; }
[Display(Name = "Enable Entity Manager", Description = "Enable Entity Manager tab. (can affect performance)",
GroupName = "Server")]
public bool EntityManagerEnabled { get; set; } = true;
[Display(Name = "Login Token", Description = "Steam GSLT (can be used if you have dynamic ip)", GroupName = "Server")]
public string LoginToken { get; set; }
public UpdateSource UpdateSource { get; set; } = new()
{ {
private static Logger _log = LogManager.GetLogger("Config"); Repository = "PveTeam/Torch",
Url = "https://api.github.com",
SourceType = UpdateSourceType.Github
};
public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate; [Display(Name = "Packages", Description = "Packages to install and use.", GroupName = "Server")]
public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate; public List<string> Packages { get; set; } = new();
private bool _autostart;
private bool _restartOnCrash;
private bool _noGui;
private bool _getPluginUpdates = true;
private bool _getTorchUpdates = true;
private int _tickTimeout = 60;
private bool _localPlugins;
private bool _disconnectOnRestart;
private string _chatName = "Server";
private string _chatColor = "Red";
private bool _enableWhitelist = false;
private List<ulong> _whitelist = new List<ulong>();
private int _windowWidth = 980;
private int _windowHeight = 588;
private bool _independentConsole = false;
private bool _enableAsserts = false;
private int _fontSize = 16;
private UGCServiceType _ugcServiceType = UGCServiceType.Steam;
private bool _entityManagerEnabled = true;
/// <inheritdoc /> // for backward compatibility
[XmlIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")] public void Save(string path = null) => Initializer.Instance?.ConfigPersistent?.Save(path);
public bool NoUpdate { get; set; } }
/// <inheritdoc />
[XmlIgnore, Arg("forceupdate", "Manually check for and install updates.")]
public bool ForceUpdate { get; set; }
/// <summary>
/// Permanent flag to ALWAYS automatically start the server
/// </summary>
[Display(Name = "Auto Start", Description = "Permanent flag to ALWAYS automatically start the server.", GroupName = "Server")]
public bool Autostart { get => _autostart; set => Set(value, ref _autostart); }
/// <summary>
/// Temporary flag to automatically start the server only on the next run
/// </summary>
[Arg("autostart", "Start the server immediately.")]
[XmlIgnore]
public bool TempAutostart { get; set; }
/// <inheritdoc />
[Arg("restartoncrash", "Automatically restart the server if it crashes.")]
[Display(Name = "Restart On Crash", Description = "Automatically restart the server if it crashes.", GroupName = "Server")]
public bool RestartOnCrash { get => _restartOnCrash; set => Set(value, ref _restartOnCrash); }
public string InstancePath { get; set; }
/// <inheritdoc />
[Arg("nogui", "Do not show the Torch UI.")]
[Display(Name = "No GUI", Description = "Do not show the Torch UI.", GroupName = "Window")]
public bool NoGui { get => _noGui; set => Set(value, ref _noGui); }
/// <inheritdoc />
[XmlIgnore, Arg("waitforpid", "Makes Torch wait for another process to exit.")]
public string WaitForPID { get; set; }
/// <inheritdoc />
[Display(Name = "Update Torch", Description = "Check every start for new versions of torch.", GroupName = "Server")]
public bool GetTorchUpdates { get => _getTorchUpdates; set => Set(value, ref _getTorchUpdates); }
public string InstanceName { get; set; }
/// <inheritdoc />
[Display(Name = "Update Plugins", Description = "Check every start for new versions of plugins.", GroupName = "Server")]
public bool GetPluginUpdates { get => _getPluginUpdates; set => Set(value, ref _getPluginUpdates); }
/// <inheritdoc />
[Display(Name = "Watchdog Timeout", Description = "Watchdog timeout (in seconds).", GroupName = "Server")]
public int TickTimeout { get => _tickTimeout; set => Set(value, ref _tickTimeout); }
/// <inheritdoc />
[Arg("plugins", "Starts Torch with the given plugin GUIDs (space delimited).")]
public List<Guid> Plugins { get; set; } = new List<Guid>();
[Arg("localplugins", "Loads all pluhins from disk, ignores the plugins defined in config.")]
[Display(Name = "Local Plugins", Description = "Loads all pluhins from disk, ignores the plugins defined in config.", GroupName = "In-Game")]
public bool LocalPlugins { get => _localPlugins; set => Set(value, ref _localPlugins); }
[Arg("disconnect", "When server restarts, all clients are rejected to main menu to prevent auto rejoin.")]
[Display(Name = "Auto Disconnect", Description = "When server restarts, all clients are rejected to main menu to prevent auto rejoin.", GroupName = "In-Game")]
public bool DisconnectOnRestart { get => _disconnectOnRestart; set => Set(value, ref _disconnectOnRestart); }
[Display(Name = "Chat Name", Description = "Default name for chat from gui, broadcasts etc..", GroupName = "In-Game")]
public string ChatName { get => _chatName; set => Set(value, ref _chatName); }
[Display(Name = "Chat Color", Description = "Default color for chat from gui, broadcasts etc.. (Red, Blue, White, Green)", GroupName = "In-Game")]
public string ChatColor { get => _chatColor; set => Set(value, ref _chatColor); }
[Display(Name = "Enable Whitelist", Description = "Enable Whitelist to prevent random players join while maintance, tests or other.", GroupName = "In-Game")]
public bool EnableWhitelist { get => _enableWhitelist; set => Set(value, ref _enableWhitelist); }
[Display(Name = "Whitelist", Description = "Collection of whitelisted steam ids.", GroupName = "In-Game")]
public List<ulong> Whitelist { get => _whitelist; set => Set(value, ref _whitelist); }
[Display(Name = "Width", Description = "Default window width.", GroupName = "Window")]
public int WindowWidth { get => _windowWidth; set => Set(value, ref _windowWidth); }
[Display(Name = "Height", Description = "Default window height", GroupName = "Window")]
public int WindowHeight { get => _windowHeight; set => Set(value, ref _windowHeight); }
[Display(Name = "Font Size", Description = "Font size for logging text box. (default is 16)", GroupName = "Window")]
public int FontSize { get => _fontSize; set => Set(value, ref _fontSize); }
[Display(Name = "UGC Service Type", Description = "Service for downloading mods", GroupName = "Server")]
public UGCServiceType UgcServiceType
{
get => _ugcServiceType;
set => Set(value, ref _ugcServiceType);
}
public string LastUsedTheme { get; set; } = "Torch Theme";
//Prevent reserved players being written to disk, but allow it to be read
//remove this when ReservedPlayers is removed
private bool ShouldSerializeReservedPlayers() => false;
[Arg("console", "Keeps a separate console window open after the main UI loads.")]
[Display(Name = "Independent Console", Description = "Keeps a separate console window open after the main UI loads.", GroupName = "Window")]
public bool IndependentConsole { get => _independentConsole; set => Set(value, ref _independentConsole); }
[XmlIgnore]
[Arg("testplugin", "Path to a plugin to debug. For development use only.")]
public string TestPlugin { get; set; }
[Arg("asserts", "Enable Keen's assert logging.")]
[Display(Name = "Enable Asserts", Description = "Enable Keen's assert logging.", GroupName = "Server")]
public bool EnableAsserts { get => _enableAsserts; set => Set(value, ref _enableAsserts); }
[Display(Name = "Enable Entity Manager", Description = "Enable Entity Manager tab. (can affect performance)",
GroupName = "Server")]
public bool EntityManagerEnabled
{
get => _entityManagerEnabled;
set => Set(value, ref _entityManagerEnabled);
}
public event PropertyChangedEventHandler PropertyChanged;
public TorchConfig() { }
protected void Set<T>(T value, ref T field, [CallerMemberName] string callerName = default)
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(callerName));
}
// for backward compatibility
public void Save(string path = null) => Initializer.Instance?.ConfigPersistent?.Save(path);
}
}

View File

@@ -3,11 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime;
using NLog; using NLog;
using PropertyChanged; using PropertyChanged;
@@ -21,8 +20,6 @@ using Torch.API.Session;
using Torch.Commands; using Torch.Commands;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Mod; using Torch.Mod;
using Torch.Mod.Messages;
using Torch.Patches;
using Torch.Server.Commands; using Torch.Server.Commands;
using Torch.Server.Managers; using Torch.Server.Managers;
using Torch.Utils; using Torch.Utils;
@@ -37,15 +34,9 @@ namespace Torch.Server
{ {
public class TorchServer : TorchBase, ITorchServer public class TorchServer : TorchBase, ITorchServer
{ {
private bool _hasRun;
private bool _canRun;
private TimeSpan _elapsedPlayTime;
private bool _isRunning;
private float _simRatio; private float _simRatio;
private ServerState _state;
private Stopwatch _uptime; private Stopwatch _uptime;
private Timer _watchdog; private Timer _watchdog;
private int _players;
private MultiplayerManagerDedicated _multiplayerManagerDedicated; private MultiplayerManagerDedicated _multiplayerManagerDedicated;
internal bool FatalException { get; set; } internal bool FatalException { get; set; }
@@ -67,11 +58,13 @@ namespace Torch.Server
var sessionManager = Managers.GetManager<ITorchSessionManager>(); var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory(x => new MultiplayerManagerDedicated(this)); sessionManager.AddFactory(x => new MultiplayerManagerDedicated(this));
sessionManager.SessionStateChanged += OnSessionStateChanged;
// Needs to be done at some point after MyVRageWindows.Init // Needs to be done at some point after MyVRageWindows.Init
// where the debug listeners are registered // where the debug listeners are registered
if (!((TorchConfig)Config).EnableAsserts) if (!((TorchConfig)Config).EnableAsserts)
MyDebug.Listeners.Clear(); MyDebug.Listeners.Clear();
_simUpdateTimer.Elapsed += SimUpdateElapsed; _simUpdateTimer.Elapsed += SimUpdateElapsed;
_simUpdateTimer.Start(); _simUpdateTimer.Start();
} }
@@ -85,7 +78,7 @@ namespace Torch.Server
} }
} }
public bool HasRun { get => _hasRun; set => SetValue(ref _hasRun, value); } public bool HasRun { get; set; }
/// <inheritdoc /> /// <inheritdoc />
@@ -104,22 +97,19 @@ namespace Torch.Server
} }
/// <inheritdoc /> /// <inheritdoc />
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set => SetValue(ref _elapsedPlayTime, value); } public TimeSpan ElapsedPlayTime { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public Thread GameThread { get; private set; } public Thread GameThread => MySandboxGame.Static?.UpdateThread;
/// <inheritdoc /> /// <inheritdoc />
public bool IsRunning { get => _isRunning; set => SetValue(ref _isRunning, value); } public bool IsRunning { get; set; }
public bool CanRun { get => _canRun; set => SetValue(ref _canRun, value); } public bool CanRun { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public InstanceManager DedicatedInstance { get; } public InstanceManager DedicatedInstance { get; }
/// <inheritdoc />
public string InstanceName { get; }
/// <inheritdoc /> /// <inheritdoc />
protected override uint SteamAppId => 244850; protected override uint SteamAppId => 244850;
@@ -127,22 +117,17 @@ namespace Torch.Server
protected override string SteamAppName => "SpaceEngineersDedicated"; protected override string SteamAppName => "SpaceEngineersDedicated";
/// <inheritdoc /> /// <inheritdoc />
public ServerState State { get => _state; private set => SetValue(ref _state, value); } public ServerState State { get; private set; }
public event Action<ITorchServer> Initialized; public event Action<ITorchServer> Initialized;
/// <inheritdoc /> public int OnlinePlayers { get; private set; }
public string InstancePath { get; }
public int OnlinePlayers { get => _players; private set => SetValue(ref _players, value); }
/// <inheritdoc /> /// <inheritdoc />
public override void Init() public override void Init()
{ {
Log.Info("Initializing server"); Log.Info("Initializing server");
MySandboxGame.IsDedicated = true;
base.Init(); base.Init();
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
GetManager<InstanceManager>().LoadInstance(InstancePath); GetManager<InstanceManager>().LoadInstance(InstancePath);
CanRun = true; CanRun = true;
Initialized?.Invoke(this); Initialized?.Invoke(this);
@@ -178,6 +163,17 @@ namespace Torch.Server
{ {
if (State == ServerState.Stopped) if (State == ServerState.Stopped)
Log.Error("Server is already stopped"); Log.Error("Server is already stopped");
if (Thread.CurrentThread == GameThread)
new Thread(StopInternal)
{
Name = "Stopping Thread"
}.Start();
else
StopInternal();
}
private void StopInternal()
{
Log.Info("Stopping server."); Log.Info("Stopping server.");
base.Stop(); base.Stop();
Log.Info("Server stopped."); Log.Info("Server stopped.");
@@ -185,6 +181,7 @@ namespace Torch.Server
State = ServerState.Stopped; State = ServerState.Stopped;
IsRunning = false; IsRunning = false;
CanRun = true; CanRun = true;
SimulationRatio = 0;
} }
/// <summary> /// <summary>
@@ -202,35 +199,73 @@ namespace Torch.Server
Log.Info("Ejected all players from server for restart."); Log.Info("Ejected all players from server for restart.");
} }
Stop(); new Thread(() =>
// TODO clone this {
var config = (TorchConfig)Config; StopInternal();
LogManager.Flush(); LogManager.Flush();
#if DEBUG
Environment.Exit(0);
#endif
string exe = Assembly.GetExecutingAssembly().Location.Replace("dll", "exe"); var exe = Path.Combine(AppContext.BaseDirectory, "Torch.Server.exe");
config.WaitForPID = Environment.ProcessId.ToString();
config.TempAutostart = true;
Process.Start(exe, config.ToString());
Environment.Exit(0); var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length; i++)
{
if (args[i].Contains(' '))
args[i] = $"\"{args[i]}\"";
if (!args[i].Contains("--tempAutostart", StringComparison.InvariantCultureIgnoreCase) &&
!args[i].Contains("--waitForPid", StringComparison.InvariantCultureIgnoreCase))
continue;
args[i] = string.Empty;
args[++i] = string.Empty;
}
Process.Start(exe, $"--waitForPid {Environment.ProcessId} --tempAutostart true {string.Join(" ", args)}");
Environment.Exit(0);
})
{
Name = "Restart thread"
}.Start();
} }
[SuppressPropertyChangedWarnings] [SuppressPropertyChangedWarnings]
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState) private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
{ {
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded) switch (newState)
{ {
_watchdog?.Dispose(); case TorchSessionState.Unloading:
_watchdog = null; _watchdog?.Dispose();
ModCommunication.Unregister(); _watchdog = null;
break;
case TorchSessionState.Loaded:
_multiplayerManagerDedicated = CurrentSession.Managers.GetManager<MultiplayerManagerDedicated>();
_multiplayerManagerDedicated.PlayerJoined += MultiplayerManagerDedicatedOnPlayerJoined;
_multiplayerManagerDedicated.PlayerLeft += MultiplayerManagerDedicatedOnPlayerLeft;
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands));
break;
case TorchSessionState.Loading:
case TorchSessionState.Unloaded:
break;
default:
throw new ArgumentOutOfRangeException(nameof(newState), newState, null);
} }
if (newState == TorchSessionState.Loaded) }
{
_multiplayerManagerDedicated = CurrentSession.Managers.GetManager<MultiplayerManagerDedicated>(); private void MultiplayerManagerDedicatedOnPlayerLeft(IPlayer player)
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands)); {
ModCommunication.Register(); OnlinePlayers--;
} }
private void MultiplayerManagerDedicatedOnPlayerJoined(IPlayer player)
{
OnlinePlayers++;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -251,7 +286,6 @@ namespace Torch.Server
SimulationRatio = Math.Min(Sync.ServerSimulationRatio, 1); SimulationRatio = Math.Min(Sync.ServerSimulationRatio, 1);
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds)); var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
ElapsedPlayTime = elapsed; ElapsedPlayTime = elapsed;
OnlinePlayers = _multiplayerManagerDedicated?.Players.Count ?? 0;
if (_watchdog == null && Config.TickTimeout > 0) if (_watchdog == null && Config.TickTimeout > 0)
{ {
@@ -342,46 +376,34 @@ namespace Torch.Server
// return stack.ToString(); // return stack.ToString();
// Modified from https://www.examplefiles.net/cs/579311 // Modified from https://www.examplefiles.net/cs/579311
using (var target = DataTarget.CreateSnapshotAndAttach(Environment.ProcessId)) using var target = DataTarget.CreateSnapshotAndAttach(Environment.ProcessId);
var runtime = target.ClrVersions[0].CreateRuntime();
var clrThread = runtime.Threads.First(b => b.ManagedThreadId == thread.ManagedThreadId);
var sb = new StringBuilder();
foreach (var frame in clrThread.EnumerateStackTrace())
{ {
var runtime = target.ClrVersions[0].CreateRuntime(); sb.Append('\t');
switch (frame.Kind)
var clrThread = runtime.Threads.First(b => b.ManagedThreadId == thread.ManagedThreadId);
var sb = new StringBuilder();
sb.AppendFormat(
"ManagedThreadId: {0}, Name: {1}, OSThreadId: {2}, Thread: IsAlive: {3}, IsBackground: {4}, IsThreadPool: {5}",
thread.ManagedThreadId,
thread.Name,
clrThread.OSThreadId,
thread.IsAlive,
thread.IsBackground,
thread.IsThreadPoolThread)
.AppendLine();
sb.AppendLine("Stack trace:");
foreach (var frame in clrThread.EnumerateStackTrace())
{ {
sb.Append('\t'); case ClrStackFrameKind.Unknown:
switch (frame.Kind) sb.AppendLine("[Unknown]");
{ break;
case ClrStackFrameKind.Unknown: case ClrStackFrameKind.ManagedMethod:
sb.AppendLine("[Unknown]"); sb.AppendLine(frame.Method?.Signature ?? "[Unable to get method signature]");
break; break;
case ClrStackFrameKind.ManagedMethod: case ClrStackFrameKind.Runtime:
sb.AppendLine(frame.Method?.Signature ?? "[Unable to get method signature]"); sb.AppendLine("[CLR Runtime]");
break; break;
case ClrStackFrameKind.Runtime: default:
sb.AppendLine("[CLR Runtime]"); throw new ArgumentOutOfRangeException(nameof(frame.Kind), frame.Kind, "Incorrect value in EnumerateStackTrace");
break;
default:
throw new ArgumentOutOfRangeException(nameof(frame.Kind), frame.Kind, "Incorrect value in EnumerateStackTrace");
}
} }
return sb.ToString();
} }
return sb.ToString();
} }
#endregion #endregion

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Threading; using System.Threading;
using NLog; using NLog;
using VRage; using VRage;
@@ -9,13 +10,11 @@ namespace Torch.Server;
internal class UnhandledExceptionHandler internal class UnhandledExceptionHandler
{ {
private readonly TorchConfig _config; private readonly TorchConfig _config;
private readonly bool _isService;
private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
public UnhandledExceptionHandler(TorchConfig config, bool isService) public UnhandledExceptionHandler(TorchConfig config)
{ {
_config = config; _config = config;
_isService = isService;
} }
internal void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) internal void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
@@ -26,16 +25,16 @@ internal class UnhandledExceptionHandler
Log.Fatal(ex.ToStringDemystified()); Log.Fatal(ex.ToStringDemystified());
LogManager.Flush(); LogManager.Flush();
if (_isService) if (ApplicationContext.Current.IsService)
Environment.Exit(1); Environment.Exit(1);
if (_config.RestartOnCrash) if (_config.RestartOnCrash)
{ {
Console.WriteLine("Restarting in 5 seconds."); Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000); Thread.Sleep(5000);
var exe = typeof(Program).Assembly.Location; var exe = Path.Combine(AppContext.BaseDirectory, "Torch.Server.exe");
_config.WaitForPID = Environment.ProcessId.ToString();
Process.Start(exe, _config.ToString()); Process.Start(exe, $"-waitForPid {Environment.ProcessId} {_config}");
} }
else else
{ {

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Steamworks;
namespace Torch.Server.ViewModels
{
public class SteamUserViewModel : ViewModel
{
public string Name { get; }
public ulong SteamId { get; }
public SteamUserViewModel(ulong id)
{
SteamId = id;
Name = SteamFriends.GetFriendPersonaName(new CSteamID(id));
}
public SteamUserViewModel() : this(0) { }
}
}

View File

@@ -32,7 +32,7 @@ public class CommandSuggestionsProvider : ISuggestionProvider
{ {
if (_commandManager is null || !_commandManager.IsCommand(filter)) if (_commandManager is null || !_commandManager.IsCommand(filter))
yield break; yield break;
var args = filter[1..].Split(' ').ToList(); var args = filter.Substring(1).Split(' ').ToList();
var skip = _commandManager.Commands.GetNode(args, out var node); var skip = _commandManager.Commands.GetNode(args, out var node);
if (skip == -1) if (skip == -1)
yield break; yield break;
@@ -42,7 +42,7 @@ public class CommandSuggestionsProvider : ISuggestionProvider
{ {
if (lastArg != node.Name && !subcommandsKey.Contains(lastArg)) if (lastArg != node.Name && !subcommandsKey.Contains(lastArg))
continue; continue;
yield return $"!{string.Join(' ', node.GetPath())} {subcommandsKey}"; yield return $"!{string.Join(" ", node.GetPath())} {subcommandsKey}";
} }
} }
} }

View File

@@ -144,22 +144,17 @@ namespace Torch.Server.Views
{ {
//var w = new RoleEditor(_instanceManager.DedicatedConfig.SelectedWorld); //var w = new RoleEditor(_instanceManager.DedicatedConfig.SelectedWorld);
//w.Show(); //w.Show();
var d = new RoleEditor();
var w = _instanceManager.DedicatedConfig.SelectedWorld; var w = _instanceManager.DedicatedConfig.SelectedWorld;
if(w.Checkpoint.PromotedUsers == null) { if (w is null)
w.Checkpoint.PromotedUsers = new VRage.Serialization.SerializableDictionary<ulong, MyPromoteLevel>();
}
if (w == null)
{ {
MessageBox.Show("A world is not selected."); MessageBox.Show("A world is not selected.");
return; return;
} }
if (w.Checkpoint.PromotedUsers == null) w.Checkpoint.PromotedUsers ??= new();
w.Checkpoint.PromotedUsers = new SerializableDictionary<ulong, MyPromoteLevel>();
d.Edit(w.Checkpoint.PromotedUsers.Dictionary); new RoleEditor().Edit(w.Checkpoint.PromotedUsers.Dictionary);
_instanceManager.DedicatedConfig.Administrators = w.Checkpoint.PromotedUsers.Dictionary.Where(k => k.Value >= MyPromoteLevel.Admin).Select(k => k.Key.ToString()).ToList(); _instanceManager.DedicatedConfig.Administrators = w.Checkpoint.PromotedUsers.Dictionary.Where(k => k.Value >= MyPromoteLevel.Admin).Select(k => k.Key.ToString()).ToList();
} }
} }

View File

@@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Threading.Tasks;
using Torch.Server.ViewModels;
using NLog;
using Torch.Collections;
namespace Torch.Server.Views.Converters
{
/// <summary>
/// A converter to get the index of a ModItemInfo object within a collection of ModItemInfo objects
/// </summary>
public class ModToListIdConverter : IMultiValueConverter
{
/// <summary>
/// Converts a ModItemInfo object into its index within a Collection of ModItemInfo objects
/// </summary>
/// <param name="values">
/// Expected to contain a ModItemInfo object at index 0
/// and a Collection of ModItemInfo objects at index 1
/// </param>
/// <param name="targetType">This parameter will be ignored</param>
/// <param name="parameter">This parameter will be ignored</param>
/// <param name="culture"> This parameter will be ignored</param>
/// <returns>the index of the mod within the provided mod list.</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//if (targetType != typeof(int))
// throw new NotSupportedException("ModToIdConverter can only convert mods into int values or vise versa!");
if (values[0] is ModItemInfo mod && values[1] is MtObservableList<ModItemInfo> modList)
{
return modList.IndexOf(mod);
}
else
{
return null;
}
}
/// <summary>
/// It is not supported to reverse this converter
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns>Raises a NotSupportedException</returns>
public object[] ConvertBack(object values, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("ModToIdConverter can not convert back!");
}
}
}

View File

@@ -15,7 +15,8 @@
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate> <ControlTemplate>
<ScrollViewer CanContentScroll="True" ScrollChanged="ScrollViewer_OnScrollChanged" Loaded="ScrollViewer_OnLoaded" Unloaded="ScrollViewer_OnUnloaded"> <ScrollViewer CanContentScroll="True" ScrollChanged="ScrollViewer_OnScrollChanged"
Loaded="ScrollViewer_OnLoaded" Unloaded="ScrollViewer_OnUnloaded">
<ItemsPresenter /> <ItemsPresenter />
</ScrollViewer> </ScrollViewer>
</ControlTemplate> </ControlTemplate>
@@ -30,10 +31,6 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="{x:Type TextBlock}" x:Key="TextBlockBaseStyle">
<Setter Property="FontFamily" Value="Consolas" />
</Style>
<DataTemplate DataType="{x:Type viewModels:LogEntry}"> <DataTemplate DataType="{x:Type viewModels:LogEntry}">
<Grid IsSharedSizeScope="True"> <Grid IsSharedSizeScope="True">
@@ -43,10 +40,16 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Text="{Binding Timestamp, StringFormat=HH:mm:ss.fff}" Grid.Column="0" <TextBlock Text="{Binding Timestamp, StringFormat=HH:mm:ss.fff}" Grid.Column="0"
FontWeight="Bold" Style="{StaticResource TextBlockBaseStyle}" Foreground="{Binding Color}" Margin="5,0,5,0" /> FontWeight="Bold" FontFamily="Consolas" Foreground="{Binding Color}" Margin="5,0,5,0" />
<TextBlock Text="{Binding Message}" Grid.Column="1" <TextBox Grid.Column="1"
TextWrapping="Wrap" Style="{StaticResource TextBlockBaseStyle}" Foreground="{Binding Color}" /> Text="{Binding Message, Mode=OneWay}"
TextWrapping="Wrap"
FontFamily="Consolas"
Background="Transparent"
BorderThickness="0"
IsReadOnly="True"
Foreground="{Binding Color}" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</UserControl.Resources> </UserControl.Resources>

View File

@@ -5,8 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels" xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800">
MouseMove="UserControl_MouseMove">
<!--<UserControl.DataContext> <!--<UserControl.DataContext>
<viewModels:ConfigDedicatedViewModel /> <viewModels:ConfigDedicatedViewModel />
</UserControl.DataContext>--> </UserControl.DataContext>-->
@@ -18,7 +17,7 @@
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<Style TargetType="Grid" x:Key="RootGridStyle"> <Style TargetType="Grid" x:Key="RootGridStyle">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding Mode=OneWay, UpdateSourceTrigger=PropertyChanged, BindingGroupName=RootEnabledBinding}" Value="{x:Null}"> <DataTrigger Binding="{Binding Mode=OneWay, BindingGroupName=RootEnabledBinding}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/> <Setter Property="IsEnabled" Value="False"/>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@@ -36,38 +35,25 @@
<RowDefinition Height="80px"/> <RowDefinition Height="80px"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DataGrid Name="ModList" Grid.Column="0" Grid.ColumnSpan="1" ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}" <DataGrid Name="ModList" Grid.Column="0" ItemsSource="{Binding Mods}"
Sorting="ModList_Sorting"
SelectionMode="Single" SelectionMode="Single"
SelectionUnit="FullRow" SelectionUnit="FullRow"
AllowDrop="True" AllowDrop="True"
CanUserReorderColumns="False" CanUserReorderColumns="False"
CanUserSortColumns="True" CanUserSortColumns="True"
PreviewMouseLeftButtonDown="ModList_MouseLeftButtonDown"
MouseLeftButtonUp="ModList_MouseLeftButtonUp"
SelectedCellsChanged="ModList_Selected" SelectedCellsChanged="ModList_Selected"
AutoGenerateColumns="False"> AutoGenerateColumns="False">
<!--:DesignSource="{d:DesignInstance Type={x:Type MyObjectBuilder_Checkpoint:ModItem, CreateList=True}}">--> <!--:DesignSource="{d:DesignInstance Type={x:Type MyObjectBuilder_Checkpoint:ModItem, CreateList=True}}">-->
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="Load Order"
Width="Auto"
IsReadOnly="True">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource ModToListIdConverter}" StringFormat="{}{0}">
<Binding />
<Binding ElementName="ModList" Path="DataContext"></Binding>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Workshop Id" <DataGridTextColumn Header="Workshop Id"
IsReadOnly="True" IsReadOnly="True"
Binding="{Binding PublishedFileId, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}"> Binding="{Binding PublishedFileId}">
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Name" <DataGridTextColumn Header="Name"
Width="*" Width="*"
IsReadOnly="True" IsReadOnly="True"
Binding="{Binding FriendlyName, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}"> Binding="{Binding FriendlyName}">
</DataGridTextColumn> </DataGridTextColumn>
</DataGrid.Columns> </DataGrid.Columns>
<DataGrid.ItemContainerStyle> <DataGrid.ItemContainerStyle>
@@ -88,12 +74,12 @@
</Style> </Style>
</DataGrid.ItemContainerStyle> </DataGrid.ItemContainerStyle>
</DataGrid> </DataGrid>
<ScrollViewer Grid.Column="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Background="#1b2838"> <ScrollViewer Grid.Row="0" Grid.Column="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Background="#1b2838">
<TextBlock Name="ModDescription" TextWrapping="Wrap" Foreground="White" Padding="2px" <TextBlock Name="ModDescription" TextWrapping="Wrap" Foreground="White" Padding="2px"
Text="{Binding ElementName=ModList, Path=SelectedItem.Description}"> Text="{Binding ElementName=ModList, Path=SelectedItem.Description}">
</TextBlock> </TextBlock>
</ScrollViewer> </ScrollViewer>
<Grid Grid.Row="2" Margin="0 0 0 6px"> <Grid Grid.Row="1" Grid.Column="0" Margin="0 0 0 6px">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -108,10 +94,10 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<CheckBox Name="ShowDependencyModsCheckBox" VerticalAlignment="Center" <CheckBox Name="ShowDependencyModsCheckBox" VerticalAlignment="Center"
HorizontalAlignment="Left" Margin="6px 0" Grid.Column="0" Grid.Row="0"/> HorizontalAlignment="Left" Margin="6px 0" Grid.Column="0" Grid.Row="0"/>
<Label Content="Show Dependency Mods" Padding="0" Margin="6px 0" Grid.Column="1" VerticalAlignment="Center"/> <Label Content="Show Dependency Mods" Padding="0" Margin="6px 0" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"/>
<Label Content="ID/URL:" Padding="0" Margin="6px 0" HorizontalAlignment="Left" <Label Content="ID/URL:" Padding="0" Margin="6px 0" HorizontalAlignment="Left"
VerticalAlignment="Center" Grid.Column="0" Grid.Row="1"/> VerticalAlignment="Center" Grid.Column="0" Grid.Row="1"/>
<TextBox Name="AddModIDTextBox" Grid.Column="1" VerticalContentAlignment="Center" <TextBox Name="AddModIdTextBox" Grid.Column="1" VerticalContentAlignment="Center"
HorizontalAlignment="Stretch" MinWidth="100px" Margin="6px 4px" Grid.Row="1"/> HorizontalAlignment="Stretch" MinWidth="100px" Margin="6px 4px" Grid.Row="1"/>
<ComboBox Grid.Column="2" Grid.Row="1" x:Name="UgcServiceTypeBox" SelectionChanged="UgcServiceTypeBox_OnSelectionChanged" SelectedValuePath="Value" DisplayMemberPath="Key"/> <ComboBox Grid.Column="2" Grid.Row="1" x:Name="UgcServiceTypeBox" SelectionChanged="UgcServiceTypeBox_OnSelectionChanged" SelectedValuePath="Value" DisplayMemberPath="Key"/>
<Button Content="Add" Grid.Column="3" Margin="6px 0" Width="60px" Height="40px" Click="AddBtn_OnClick" Grid.Row="1"/> <Button Content="Add" Grid.Column="3" Margin="6px 0" Width="60px" Height="40px" Click="AddBtn_OnClick" Grid.Row="1"/>
@@ -120,6 +106,6 @@
<Button Content="Bulk Edit" Grid.Column="5" Margin="6px 0" Width="60px" Height="40px" Click="BulkButton_OnClick" Grid.Row="1"/> <Button Content="Bulk Edit" Grid.Column="5" Margin="6px 0" Width="60px" Height="40px" Click="BulkButton_OnClick" Grid.Row="1"/>
</Grid> </Grid>
<Button Content="Save Config" Grid.Row="2" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="6px" Grid.Column="3" Width="80px" Height="40px" Click="SaveBtn_OnClick"/> <Button Content="Save Config" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="6px" Grid.Column="3" Width="80px" Height="40px" Click="SaveBtn_OnClick"/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -1,32 +1,15 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.CompilerServices;
using System.Windows.Threading;
using VRage.Game;
using NLog; using NLog;
using Sandbox.Engine.Networking;
using Torch.API; using Torch.API;
using Torch.Server.Managers; using Torch.Server.Managers;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.Server.ViewModels; using Torch.Server.ViewModels;
using Torch.Server.Annotations;
using Torch.Collections;
using Torch.Utils; using Torch.Utils;
using Torch.Views; using Torch.Views;
@@ -35,14 +18,12 @@ namespace Torch.Server.Views
/// <summary> /// <summary>
/// Interaction logic for ModListControl.xaml /// Interaction logic for ModListControl.xaml
/// </summary> /// </summary>
public partial class ModListControl : UserControl, INotifyPropertyChanged public partial class ModListControl : UserControl
{ {
private static Logger Log = LogManager.GetLogger(nameof(ModListControl)); private static Logger Log = LogManager.GetLogger(nameof(ModListControl));
private InstanceManager _instanceManager; private InstanceManager _instanceManager;
ModItemInfo _draggedMod;
bool _hasOrderChanged = false;
bool _isSortedByLoadOrder = true;
private readonly ITorchConfig _config; private readonly ITorchConfig _config;
private ConfigDedicatedViewModel _viewModel;
//private List<BindingExpression> _bindingExpressions = new List<BindingExpression>(); //private List<BindingExpression> _bindingExpressions = new List<BindingExpression>();
/// <summary> /// <summary>
@@ -51,9 +32,11 @@ namespace Torch.Server.Views
public ModListControl() public ModListControl()
{ {
InitializeComponent(); InitializeComponent();
#pragma warning disable CS0618
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>(); _instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
_config = TorchBase.Instance.Config; _config = TorchBase.Instance.Config;
#pragma warning restore CS0618
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
//var mods = _instanceManager.DedicatedConfig?.Mods; //var mods = _instanceManager.DedicatedConfig?.Mods;
//if( mods != null) //if( mods != null)
// DataContext = new ObservableCollection<MyObjectBuilder_Checkpoint.ModItem>(); // DataContext = new ObservableCollection<MyObjectBuilder_Checkpoint.ModItem>();
@@ -67,39 +50,22 @@ namespace Torch.Server.Views
//Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyStyles)); //Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyStyles));
} }
private void ModListControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
throw new NotImplementedException();
}
private void ResetSorting()
{
CollectionViewSource.GetDefaultView(ModList.ItemsSource).SortDescriptions.Clear();
}
private void _instanceManager_InstanceLoaded(ConfigDedicatedViewModel obj) private void _instanceManager_InstanceLoaded(ConfigDedicatedViewModel obj)
{ {
Dispatcher.Invoke(() => { Dispatcher.InvokeAsync(() =>
DataContext = obj?.Mods ?? new MtObservableList<ModItemInfo>(); {
_viewModel = obj;
DataContext = obj;
UpdateLayout(); UpdateLayout();
((MtObservableList<ModItemInfo>)DataContext).CollectionChanged += OnModlistUpdate; Task.Run(async () =>
if (obj is { }) {
Task.Run(async () => await obj.UpdateAllModInfosAsync();
{ Log.Info("Instance loaded.");
await obj.UpdateAllModInfosAsync(); });
Log.Info("Instance loaded.");
});
}); });
} }
private void OnModlistUpdate(object sender, NotifyCollectionChangedEventArgs e)
{
ModList.Items.Refresh();
//if (e.Action == NotifyCollectionChangedAction.Remove)
// _instanceManager.SaveConfig();
}
private void SaveBtn_OnClick(object sender, RoutedEventArgs e) private void SaveBtn_OnClick(object sender, RoutedEventArgs e)
{ {
_instanceManager.SaveConfig(); _instanceManager.SaveConfig();
@@ -108,31 +74,28 @@ namespace Torch.Server.Views
private void AddBtn_OnClick(object sender, RoutedEventArgs e) private void AddBtn_OnClick(object sender, RoutedEventArgs e)
{ {
if (TryExtractId(AddModIDTextBox.Text, out ulong id)) if (TryExtractId(AddModIdTextBox.Text, out ulong id))
{ {
var mod = new ModItemInfo(ModItemUtils.Create(id, UgcServiceTypeBox.SelectedValue?.ToString())); var mod = new ModItemInfo(ModItemUtils.Create(id, UgcServiceTypeBox.SelectedValue?.ToString()));
_instanceManager.DedicatedConfig.Mods.Add(mod); _instanceManager.DedicatedConfig.Mods.Add(mod);
Task.Run(mod.UpdateModInfoAsync) Task.Run(mod.UpdateModInfoAsync)
.ContinueWith((t) => .ContinueWith(_ =>
{ {
Dispatcher.Invoke(() => _instanceManager.DedicatedConfig.Save();
{
_instanceManager.DedicatedConfig.Save();
});
}); });
AddModIDTextBox.Text = ""; AddModIdTextBox.Text = "";
} }
else else
{ {
AddModIDTextBox.BorderBrush = Brushes.Red; AddModIdTextBox.BorderBrush = Brushes.Red;
Log.Warn("Invalid mod id!"); Log.Warn("Invalid mod id!");
MessageBox.Show("Invalid mod id!"); MessageBox.Show("Invalid mod id!");
} }
} }
private void RemoveBtn_OnClick(object sender, RoutedEventArgs e) private void RemoveBtn_OnClick(object sender, RoutedEventArgs e)
{ {
var modList = ((MtObservableList<ModItemInfo>)DataContext); var modList = _viewModel.Mods;
if (ModList.SelectedItem is ModItemInfo mod && modList.Contains(mod)) if (ModList.SelectedItem is ModItemInfo mod && modList.Contains(mod))
modList.Remove(mod); modList.Remove(mod);
} }
@@ -150,113 +113,9 @@ namespace Torch.Server.Views
return success; return success;
} }
private void ModList_Sorting(object sender, DataGridSortingEventArgs e)
{
Log.Info($"Sorting by '{e.Column.Header}'");
if (e.Column == ModList.Columns[0])
{
var dataView = CollectionViewSource.GetDefaultView(ModList.ItemsSource);
dataView.SortDescriptions.Clear();
dataView.Refresh();
_isSortedByLoadOrder = true;
}
else
_isSortedByLoadOrder = false;
}
private void ModList_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//return;
_draggedMod = (ModItemInfo) TryFindRowAtPoint((UIElement) sender, e.GetPosition(ModList))?.DataContext;
//DraggedMod = (ModItemInfo) ModList.SelectedItem;
}
private static DataGridRow TryFindRowAtPoint(UIElement reference, Point point)
{
var element = reference.InputHitTest(point) as DependencyObject;
if (element == null)
return null;
if (element is DataGridRow row)
return row;
else
return TryFindParent<DataGridRow>(element);
}
private static T TryFindParent<T>(DependencyObject child) where T : DependencyObject
{
DependencyObject parent;
if (child == null)
return null;
if (child is ContentElement contentElement)
{
parent = ContentOperations.GetParent(contentElement);
if (parent == null && child is FrameworkContentElement fce)
parent = fce.Parent;
}
else
{
parent = VisualTreeHelper.GetParent(child);
}
if (parent is T result)
return result;
else
return TryFindParent<T>(parent);
}
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (_draggedMod == null)
return;
if (!_isSortedByLoadOrder)
return;
var targetMod = (ModItemInfo)TryFindRowAtPoint((UIElement)sender, e.GetPosition(ModList))?.DataContext;
if( targetMod != null && !ReferenceEquals(_draggedMod, targetMod))
{
_hasOrderChanged = true;
var modList = (MtObservableList<ModItemInfo>)DataContext;
modList.Move(modList.IndexOf(targetMod), _draggedMod);
//modList.RemoveAt(modList.IndexOf(_draggedMod));
//modList.Insert(modList.IndexOf(targetMod), _draggedMod);
ModList.Items.Refresh();
ModList.SelectedItem = _draggedMod;
}
}
private void ModList_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!_isSortedByLoadOrder)
{
var targetMod = (ModItemInfo)TryFindRowAtPoint((UIElement)sender, e.GetPosition(ModList))?.DataContext;
if (targetMod != null && !ReferenceEquals(_draggedMod, targetMod))
{
var msg = "Drag and drop is only available when sorted by load order!";
Log.Warn(msg);
MessageBox.Show(msg);
}
}
//if (DraggedMod != null && HasOrderChanged)
//Log.Info("Dragging over, saving...");
//_instanceManager.SaveConfig();
_draggedMod = null;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ModList_Selected(object sender, SelectedCellsChangedEventArgs e) private void ModList_Selected(object sender, SelectedCellsChangedEventArgs e)
{ {
if (_draggedMod != null) if( e.AddedCells.Count > 0)
ModList.SelectedItem = _draggedMod;
else if( e.AddedCells.Count > 0)
ModList.SelectedItem = e.AddedCells[0].Item; ModList.SelectedItem = e.AddedCells[0].Item;
} }
@@ -265,23 +124,23 @@ namespace Torch.Server.Views
var editor = new CollectionEditor(); var editor = new CollectionEditor();
//let's see just how poorly we can do this //let's see just how poorly we can do this
var modList = ((MtObservableList<ModItemInfo>)DataContext).ToList(); var modList = _viewModel.Mods.ToList();
var idList = modList.Select(m => m.ToString()).ToList(); var idList = modList.Select(m => m.ToString()).ToList();
var tasks = new List<Task>(); var tasks = new List<Task>();
//blocking //blocking
editor.Edit<string>(idList, "Mods"); editor.Edit<string>(idList, "Mods");
modList.RemoveAll(m => modList.Clear();
{
var mod = m.ToString();
return idList.Any(mod.Equals);
});
modList.AddRange(idList.Select(id => modList.AddRange(idList.Select(id =>
{ {
var info = new ModItemInfo(ModItemUtils.Create(id)); if (!ModItemUtils.TryParse(id, out var item))
return null;
var info = new ModItemInfo(item);
tasks.Add(Task.Run(info.UpdateModInfoAsync)); tasks.Add(Task.Run(info.UpdateModInfoAsync));
return info; return info;
})); }).Where(b => b is not null));
_instanceManager.DedicatedConfig.Mods.Clear(); _instanceManager.DedicatedConfig.Mods.Clear();
foreach (var mod in modList) foreach (var mod in modList)
_instanceManager.DedicatedConfig.Mods.Add(mod); _instanceManager.DedicatedConfig.Mods.Add(mod);

View File

@@ -22,6 +22,8 @@ using Torch.Collections;
using Torch.Server.Annotations; using Torch.Server.Annotations;
using Torch.Managers; using Torch.Managers;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.API.Plugins;
using Torch.API.WebAPI.Plugin;
namespace Torch.Server.Views namespace Torch.Server.Views
{ {
@@ -55,39 +57,38 @@ namespace Torch.Server.Views
InitializeComponent(); InitializeComponent();
var installedPlugins = pluginManager.Plugins; var installedPlugins = pluginManager.Plugins;
BindingOperations.EnableCollectionSynchronization(Plugins, _syncLock); LoadAsync(installedPlugins);
Task.Run(async () =>
{
try
{
var res = await PluginQuery.Instance.QueryAll();
foreach (var item in res.Plugins.OrderBy(i => i.Name)) {
lock (_syncLock)
{
var pluginItem = item with
{
Description = item.Description.Replace("&lt;", "<").Replace("&gt;", ">"),
Installed = installedPlugins.Keys.Contains(item.Id)
};
Plugins.Add(pluginItem);
PluginsSource.Add(pluginItem);
}
}
Dispatcher.Invoke(() => PluginsList.SelectedIndex = 0);
CurrentDescription = "Please select a plugin...";
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "An Error Occurred", MessageBoxButton.OK, MessageBoxImage.Error);
Close();
throw;
}
});
MarkdownFlow.CommandBindings.Add(new CommandBinding(NavigationCommands.GoToPage, (sender, e) => OpenUri((string)e.Parameter))); MarkdownFlow.CommandBindings.Add(new CommandBinding(NavigationCommands.GoToPage, (sender, e) => OpenUri((string)e.Parameter)));
} }
private async void LoadAsync(IReadOnlyDictionary<Guid, ITorchPlugin> installedPlugins)
{
try
{
var res = await LegacyPluginQuery.Instance.QueryAll();
foreach (var item in res.Plugins.OrderBy(i => i.Name))
{
var pluginItem = item with
{
Description = item.Description?.Replace("&lt;", "<").Replace("&gt;", ">") ?? string.Empty,
Installed = installedPlugins.Keys.Contains(item.Id)
};
Plugins.Add(pluginItem);
PluginsSource.Add(pluginItem);
}
PluginsList.SelectedIndex = 0;
CurrentDescription = "Please select a plugin...";
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "An Error Occurred", MessageBoxButton.OK, MessageBoxImage.Error);
Close();
throw;
}
}
public static bool IsValidUri(string uri) public static bool IsValidUri(string uri)
{ {
if (!Uri.IsWellFormedUriString(uri, UriKind.Absolute)) if (!Uri.IsWellFormedUriString(uri, UriKind.Absolute))

View File

@@ -14,6 +14,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Shapes; using System.Windows.Shapes;
using System.ComponentModel; using System.ComponentModel;
using Torch.API.WebAPI.Plugin;
namespace Torch.Server.Views namespace Torch.Server.Views
{ {
@@ -50,7 +51,7 @@ namespace Torch.Server.Views
var PercentChangeOnDownload = 100 / PluginsToDownload.Count; var PercentChangeOnDownload = 100 / PluginsToDownload.Count;
foreach (PluginItem PluginItem in PluginsToDownload) { foreach (PluginItem PluginItem in PluginsToDownload) {
if (!Task.Run(async () => await PluginQuery.Instance.DownloadPlugin(PluginItem.Id)).Result) { if (!LegacyPluginQuery.Instance.DownloadPlugin(PluginItem.Id).Result) {
failedDownloads++; failedDownloads++;
DownloadProgress += PercentChangeOnDownload; DownloadProgress += PercentChangeOnDownload;
(sender as BackgroundWorker).ReportProgress(DownloadProgress); (sender as BackgroundWorker).ReportProgress(DownloadProgress);

View File

@@ -71,7 +71,7 @@ namespace Torch.Server.Views
private void OpenFolder_OnClick(object sender, RoutedEventArgs e) private void OpenFolder_OnClick(object sender, RoutedEventArgs e)
{ {
if (_plugins?.PluginDir != null) if (_plugins?.PluginDir != null)
Process.Start(_plugins.PluginDir); Process.Start("explorer", _plugins.PluginDir);
} }
private void BrowsPlugins_OnClick(object sender, RoutedEventArgs e) private void BrowsPlugins_OnClick(object sender, RoutedEventArgs e)

View File

@@ -18,7 +18,6 @@
</Style> </Style>
<converters:ListConverter x:Key="ListConverterString" Type="system:String"/> <converters:ListConverter x:Key="ListConverterString" Type="system:String"/>
<converters:ListConverter x:Key="ListConverterUInt64" Type="system:UInt64"/> <converters:ListConverter x:Key="ListConverterUInt64" Type="system:UInt64"/>
<converters:ModToListIdConverter x:Key="ModToListIdConverter"/>
<converters:ListConverterWorkshopId x:Key="ListConverterWorkshopId"/> <converters:ListConverterWorkshopId x:Key="ListConverterWorkshopId"/>
<converters:BooleanAndConverter x:Key="BooleanAndConverter"/> <converters:BooleanAndConverter x:Key="BooleanAndConverter"/>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -0,0 +1,659 @@
{
"version": 1,
"dependencies": {
"net7.0-windows7.0": {
"AutoCompleteTextBox": {
"type": "Direct",
"requested": "[1.6.0, )",
"resolved": "1.6.0",
"contentHash": "xTQB0o3yZzBqKqWTZYlu22wTpIeXAsBU3+wswQ44D6rTMMTBXqr3AjxqrvjFcnoZXtLZgjPNRIUwnfsFSsH46g=="
},
"Ben.Demystifier": {
"type": "Direct",
"requested": "[0.4.1, )",
"resolved": "0.4.1",
"contentHash": "axFeEMfmEORy3ipAzOXG/lE+KcNptRbei3F0C4kQCdeiQtW+qJW90K5iIovITGrdLt8AjhNCwk5qLSX9/rFpoA==",
"dependencies": {
"System.Reflection.Metadata": "5.0.0"
}
},
"ControlzEx": {
"type": "Direct",
"requested": "[5.0.2, )",
"resolved": "5.0.2",
"contentHash": "f724LoDJ36LxaLR62G4ek9ZAJI8BiiYRJJ04furC/qjXSeIwU0qmHFIe19xB1/FwxyZjevdFguEr9ZUjf3dZgw==",
"dependencies": {
"Microsoft.Xaml.Behaviors.Wpf": "1.1.31",
"System.Text.Json": "5.0.1"
}
},
"MahApps.Metro": {
"type": "Direct",
"requested": "[2.4.9, )",
"resolved": "2.4.9",
"contentHash": "eMTkg6TBnCwHzszw7CP+pxsBeB4ZMsJFiTJJoifUVBysRyEenzzR+TKQJuMvvKK6KzvLxwHmJsFhi9o5p4vxhQ==",
"dependencies": {
"ControlzEx": "[4.4.0, 6.0.0)"
}
},
"MdXaml": {
"type": "Direct",
"requested": "[1.16.2, )",
"resolved": "1.16.2",
"contentHash": "UxYWJaXugQ+v4txS7K8sw26bxS6xvjSW+f79U/oMBtm+cVvZSmBGRZkB77rzaoXAyrbixX0Vk0U+1f22TT2uWg==",
"dependencies": {
"AvalonEdit": "6.0.0",
"MdXaml.Plugins": "1.16.2"
}
},
"Microsoft.Diagnostics.Runtime": {
"type": "Direct",
"requested": "[2.2.343001, )",
"resolved": "2.2.343001",
"contentHash": "nVwXLEJiXbnnNkQPcWy64DMWNBvMg9XKWpfBBNMVs1gbEJ9HUVTcAA862mOjgyKryAtvGVKkeQ/9cW6pK8YAiA==",
"dependencies": {
"Microsoft.Diagnostics.NETCore.Client": "0.2.251802",
"System.Collections.Immutable": "5.0.0",
"System.Runtime.CompilerServices.Unsafe": "5.0.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "tgU4u7bZsoS9MKVRiotVMAwHtbREHr5/5zSEV+JPhg46+ox47Au84E3D2IacAaB0bk5ePNaNieTlPrfjbbRJkg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "a8Iq8SCw5m8W5pZJcPCgBpBO4E89+NaObPng+ApIhrGSv9X4JPrcFAaGM4sDgR0X83uhLgsNJq8VnGP/wqhr8A==",
"dependencies": {
"Microsoft.Extensions.Configuration": "7.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "RIkfqCkvrAogirjsqSrG1E1FxgrLsOZU2nhRbl07lrajnxzSU2isj2lwQah0CtCbLWo/pOIukQzM1GfneBUnxA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "7.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0"
}
},
"Microsoft.Extensions.Configuration.Xml": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "KXmN/llitPZydHbW3cac7MQHGPUXPi4783tYxsBqSAFJgcaYTdLmsboFL7Ss4TsnelF0+v4ZMpEkKTtCtpysiA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "7.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0",
"Microsoft.Extensions.Configuration.FileExtensions": "7.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "7.0.0",
"System.Security.Cryptography.Xml": "7.0.0"
}
},
"NLog": {
"type": "Direct",
"requested": "[5.1.0, )",
"resolved": "5.1.0",
"contentHash": "oW7ekrkRG9okpDMUcEglunWj8Qf2RY8qkgl+/chJoavzg3dbT13y32t19R54FKkmq80fKzw4ZekZkCrRGanKgQ=="
},
"nulastudio.NetCoreBeauty": {
"type": "Direct",
"requested": "[1.2.9.3, )",
"resolved": "1.2.9.3",
"contentHash": "ToUOoX/sNCg6oeMcSrHYBBRPjKp1gDS7I5WNnY+59t7iNFPcAojpc2lYmgCm3hxwXW/xCxyQIV4k5agegqc3fg=="
},
"PropertyChanged.Fody": {
"type": "Direct",
"requested": "[4.0.3, )",
"resolved": "4.0.3",
"contentHash": "vRxEDydahhcetswKaoUug7mQywgpZpHoeHZWa7Opb+H+eWPUQo+f+v37Jy+rrG1RVOCkyziTgCkNetXzNlUXJw==",
"dependencies": {
"Fody": "6.6.3"
}
},
"SpaceEngineersDedicated.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.201.13, )",
"resolved": "1.201.13",
"contentHash": "FRXuNLENsz5jGQvL0QkogAGsxEJyUQlXhvWHBWfZiHCnCrAWMD60nEis6yt0xCQpazszfMtHonbxz72XMxnFoQ==",
"dependencies": {
"protobuf-net": "1.0.0"
}
},
"Steamworks.NET": {
"type": "Direct",
"requested": "[20.1.0, )",
"resolved": "20.1.0",
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
},
"System.ComponentModel.Annotations": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
"System.Management": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "A4jed4QUviDOm7fJNKAJObEAEkEUXmkGL/w0iyCYTzrl1rezTj8LGFHfsVst4Vb9JwFcTpboiDrvdST48avBpw==",
"dependencies": {
"System.CodeDom": "7.0.0"
}
},
"AvalonEdit": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "QMbyJrlhOuWzLRPqvW724ly9XbSEkp8Xg2mQY7tvsh1se1pDEJnmDjS6c6OuqDe2Q37uCnXwKdV8tJUx2iLUnw=="
},
"Fody": {
"type": "Transitive",
"resolved": "6.6.3",
"contentHash": "X1WKKgSNYVtLzdO8dK3YWmfmljA4L0soePcWNouq9X6WbVF4bmivuJdlhTZx/2L3ml6tMvhFe8skIbVQ24PAYA=="
},
"HarmonyX": {
"type": "Transitive",
"resolved": "2.10.2-prerelease.3",
"contentHash": "XnR2oKlFTCb7F+90OkJR0cqOYOsSPgsQrnX2v9qR0nmM6SPSET/v2SR9vjY/DQhBYUQia16NWv0Mwd8Ml4ZqKA==",
"dependencies": {
"MonoModReorg.RuntimeDetour": "23.1.2-prerelease.1"
}
},
"JorgeSerrano.Json.JsonSnakeCaseNamingPolicy": {
"type": "Transitive",
"resolved": "0.9.0",
"contentHash": "xCqODS+wzpUXNtg4bMMvXG5PLbP0iTwRzRn2R+zWHKm83E6tbV2bCagawXp1EnZeNpd5OXpMxehulZWns8efzQ=="
},
"MdXaml.Plugins": {
"type": "Transitive",
"resolved": "1.16.2",
"contentHash": "EOMENHbLkpRk7+UItuutDuETiJpSCZgBATBRk7WSuxO16NUfzrS39ja4RhIQ0Ax8u0RH1wCAS0VINYPZqxfJeg=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "3.3.3",
"contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
},
"Microsoft.CodeAnalysis.Common": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "JfHupS/B7Jb5MZoYkFFABn3mux0wQgxi2D8F/rJYZeRBK2ZOyk7TjQ2Kq9rh6W/DCh0KNbbSbn5qoFar+ueHqw==",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.3",
"System.Collections.Immutable": "6.0.0",
"System.Memory": "4.5.5",
"System.Reflection.Metadata": "5.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "6.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.CodeAnalysis.CSharp": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "eD2w0xHRoaqK07hjlOKGR9eLNy3nimiGNeCClNax1NDgS/+DBtBqCjXelOa+TNy99kIB3nHhUqDmr46nDXy/RQ==",
"dependencies": {
"Microsoft.CodeAnalysis.Common": "[4.4.0]"
}
},
"Microsoft.Diagnostics.NETCore.Client": {
"type": "Transitive",
"resolved": "0.2.251802",
"contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "1.1.0",
"Microsoft.Extensions.Logging": "2.1.1"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "tldQUBWt/xeH2K7/hMPPo5g8zuLc3Ro9I5d4o/XrxvxOCA2EZBtW7bCHHTc49fcBtvB8tLAb/Qsmfrq+2SJ4vA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "f34u2eaqIjNO9YLHBz8rozVZ+TcFiFs0F3r7nUJd7FRkVSxk8u4OpoK226mi49MwexHOR2ibP9MFvRUaLilcQQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "xk2lRJ1RDuqe57BmgvRPyCt6zyePKUmvT6iuXqiHR+/OIIgWVR8Ff5k2p6DwmqY8a17hx/OnrekEhziEIeQP6Q==",
"dependencies": {
"Microsoft.Extensions.Configuration": "7.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "7.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "7.0.0",
"Microsoft.Extensions.FileProviders.Physical": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA=="
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "NyawiW9ZT/liQb34k9YqBSNPLuuPkrjMgQZ24Y/xXX1RoiBkLUdPMaQTmxhZ5TYu8ZKZ9qayzil75JX95vGQUg==",
"dependencies": {
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "K8D2MTR+EtzkbZ8z80LrG7Ur64R7ZZdRLt1J5cgpc/pUWl0C6IkAUapPuK28oionHueCPELUqq0oYEvZfalNdg==",
"dependencies": {
"Microsoft.Extensions.FileProviders.Abstractions": "7.0.0",
"Microsoft.Extensions.FileSystemGlobbing": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "2jONjKHiF+E92ynz2ZFcr9OvxIw+rTGMPEH+UZGeHTEComVav93jQUWGkso8yWwVBcEJGcNcZAaqY01FFJcj7w=="
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "2.1.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
"Microsoft.Extensions.Logging.Abstractions": "2.1.1",
"Microsoft.Extensions.Options": "2.1.1"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "XRzK7ZF+O6FzdfWrlFTi1Rgj2080ZDsd46vzOjadHUB0Cz5kOvDG8vI7caa5YFrsHQpcfn0DxtjS4E46N4FZsA=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
"Microsoft.Extensions.Primitives": "2.1.1"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q=="
},
"Microsoft.Xaml.Behaviors.Wpf": {
"type": "Transitive",
"resolved": "1.1.31",
"contentHash": "LZpuf82ACZWldmfMuv3CTUMDh3o0xo0uHUaybR5HgqVLDBJJ9RZLykplQ/bTJd0/VDt3EhD4iDgUgbdIUAM+Kg=="
},
"Mono.Cecil": {
"type": "Transitive",
"resolved": "0.11.4",
"contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ=="
},
"MonoModReorg.Backports": {
"type": "Transitive",
"resolved": "23.1.2-prerelease.1",
"contentHash": "m1wlCgVjZTFJs3mUxmC1aE/O0RIvsNbSFBI/g93Bqzz1tHa+LhXFyrHzL60PeZMQBIPVy3CeDX4um/UrqLOn/g==",
"dependencies": {
"MonoModReorg.ILHelpers": "23.1.2-prerelease.1"
}
},
"MonoModReorg.Core": {
"type": "Transitive",
"resolved": "23.1.2-prerelease.1",
"contentHash": "t1Y89M0rbwUx2VjDMCJOWgtSdsi1F5KNu0O6JAMOtwo2EWJ0HfYj9nS8UWWPwrgRpsquGjqbmYA8jhb59F2a/A==",
"dependencies": {
"Mono.Cecil": "0.11.4",
"MonoModReorg.Backports": "23.1.2-prerelease.1",
"MonoModReorg.ILHelpers": "23.1.2-prerelease.1",
"MonoModReorg.Utils": "23.1.2-prerelease.1"
}
},
"MonoModReorg.ILHelpers": {
"type": "Transitive",
"resolved": "23.1.2-prerelease.1",
"contentHash": "GVh1cmrTCAK0zHr3t8aHnKsyKIlDFiDERn++lCZomHcYc8dgcOAhpkZ7KmaKgZCTJuBIrc44RjpKFr/4ScQnGA=="
},
"MonoModReorg.RuntimeDetour": {
"type": "Transitive",
"resolved": "23.1.2-prerelease.1",
"contentHash": "UZyJ7OIbLCIBg+dzLejWq2paL1s11koUrq1noSLGCP9uNmFjwDPK+lRmGs0X4qg+Alfq6VsOpI45pGqmaAvP+Q==",
"dependencies": {
"Mono.Cecil": "0.11.4",
"MonoModReorg.Backports": "23.1.2-prerelease.1",
"MonoModReorg.Core": "23.1.2-prerelease.1",
"MonoModReorg.ILHelpers": "23.1.2-prerelease.1",
"MonoModReorg.Utils": "23.1.2-prerelease.1"
}
},
"MonoModReorg.Utils": {
"type": "Transitive",
"resolved": "23.1.2-prerelease.1",
"contentHash": "6N4LNG+x4RVPLOc8QWL7dc5sqWdl0gxR+4ASRd1CvvappsK84ISgD9qgeYHgQQtTgE+h6Cuqr3Om4Ly0roLfoA==",
"dependencies": {
"Mono.Cecil": "0.11.4",
"MonoModReorg.Backports": "23.1.2-prerelease.1",
"MonoModReorg.ILHelpers": "23.1.2-prerelease.1"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.1",
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
},
"NuGet.Commands": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "j3ma45boGZADsHpJcu3Y2yq+n2luicC6ezd61TXTTzbOzA452oAPaSsFGUB1stIsuP/DVoqkTzjHXjaCHuJKPQ==",
"dependencies": {
"NuGet.Credentials": "6.4.0",
"NuGet.ProjectModel": "6.4.0"
}
},
"NuGet.Common": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "srECugLk+LB1bXelDCDhHoi6do/EYTXzuntKhjHraS4roVB3NfWohEdCSiAPdpSV9M40Q6jo6MV2Srml9e+jHQ==",
"dependencies": {
"NuGet.Frameworks": "6.4.0"
}
},
"NuGet.Configuration": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "vPjauG9AoacEjiZWGIs+d11FCRVmseqAw78FIApfLvZrYMEbbwc9vc0LdC3PpoW5FxYkktyZSiiXVKXGLu+gXw==",
"dependencies": {
"NuGet.Common": "6.4.0",
"System.Security.Cryptography.ProtectedData": "4.4.0"
}
},
"NuGet.Credentials": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "tebsxclknVz3D4FrvE2MzVcsOyf6PffjGNQ77X9Yvbj9x5YpVWfumVPetqETcdsNEgiN0bBzfMre33lhrY7Itw==",
"dependencies": {
"NuGet.Protocol": "6.4.0"
}
},
"NuGet.DependencyResolver.Core": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "AKomZEKuhQlshujuiHbKvwl2cZNGq3SHsXFbpjCfCjMFMLwwA8saJGQQZp1lzsqQWcQWa6hLOcPtm7T3rd0SVg==",
"dependencies": {
"NuGet.Configuration": "6.4.0",
"NuGet.LibraryModel": "6.4.0",
"NuGet.Protocol": "6.4.0"
}
},
"NuGet.Frameworks": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "qcufbjJIDtyY/Hah7JJfcRVpRYM3scgPqYBnukjO9kfADCFGr2azvVBozuwzljA6w/cR3w8bXLq6vW5xGrsmHw=="
},
"NuGet.LibraryModel": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "K6ROQpWr34Aje81G0HfipiznLTB8vD4BO8sF6FEwx1KjJVdFkSmGZPmAhc6L1vZPs8TKY5BqoH72zG13zVzW2w==",
"dependencies": {
"NuGet.Common": "6.4.0",
"NuGet.Versioning": "6.4.0"
}
},
"NuGet.Packaging": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "aR10aYqcUMGC2mwMGH5rls/MGaz3EVH8DKTTHQ/EC91hXNtrCTTAQonaRR+v1EItcoxtQeZ/WQOorv4z270Tgg==",
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"NuGet.Configuration": "6.4.0",
"NuGet.Versioning": "6.4.0",
"System.Security.Cryptography.Cng": "5.0.0",
"System.Security.Cryptography.Pkcs": "5.0.0"
}
},
"NuGet.ProjectModel": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "eW9Q7vPk8cpXDd5b+vtIPkl8dSDCPkPJPrjXPTfZGxhstldnhJrj1XPaonsDZLQ24YY7LrYCzC0BiHh3iO5zUA==",
"dependencies": {
"NuGet.DependencyResolver.Core": "6.4.0"
}
},
"NuGet.Protocol": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "KIPjsWP0P3EMsDsXaa6YBCTvYKur/zI0luS1kO5G37ci8mHs2kJFsuG5qaMhGvgyHASu54sxlic1n1oza2Pcbw==",
"dependencies": {
"NuGet.Packaging": "6.4.0"
}
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "6.4.0",
"contentHash": "YE8p3TpX4jIw+Gb24maE8YRDoqWA4imLmCbdOj5IvslLrZJXQ8akeFOGOplxICNVevON1g1SFYT2+cq4yy0nQQ=="
},
"protobuf-net": {
"type": "Transitive",
"resolved": "3.1.26",
"contentHash": "iy+VROYtWZEqDvkUjS6FfzKZNpWRoVGc5h0cupGQT/37Ox0LxPblRKLnw6V0RT1MPclLo8nZp7E2M0f7PNn/Tw==",
"dependencies": {
"protobuf-net.Core": "3.1.26"
}
},
"protobuf-net.Core": {
"type": "Transitive",
"resolved": "3.1.26",
"contentHash": "fuKoDWgAf5ju+ixbJoasVFhnUaT0oWUgrWh1AZ5D0Y5keKHpA93vQB5g6aQNtB4zgeQ4hGEeh0MY9bIbAUPgEw=="
},
"SemanticVersioning": {
"type": "Transitive",
"resolved": "2.0.2",
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
},
"SixLabors.Core": {
"type": "Transitive",
"resolved": "1.0.0-beta0007",
"contentHash": "s9aPl6yxwcvoKRD0u0zjkCISZCCifbUi9/XVFjdvlx5Pt7vRYmGV0anq1EEftUjIEHbEu5aNBipbUSBIV2CE7w==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"System.CodeDom": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A=="
},
"System.Collections.Immutable": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "+nfpV0afLmvJW8+pLlHxRjz3oZJw4fkyU9MMEaMhCsHi/SN9bGF9q79ROubDiwTiCHezmK0uCWkPP7tGFP/4yg=="
},
"System.Linq.Async": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "6.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
},
"System.Reflection.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Security.Cryptography.Cng": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
"dependencies": {
"System.Formats.Asn1": "5.0.0"
}
},
"System.Security.Cryptography.Pkcs": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "mjUbEXkR6DYRef6dnEYKdfec9otcAkibExL+1f9hmbGlWIUyaCnS3Y3oGZEet38waXmuY1ORE8vgv4sgD5nMYg==",
"dependencies": {
"System.Formats.Asn1": "7.0.0"
}
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog=="
},
"System.Security.Cryptography.Xml": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "LHc5PUypYGAMNf/2DzCxOXREKamwCtdUtxb/WpCnHngORVYZbUdSMnm1PcKvEvzKUTKSC0CL6aVAywzbEEeNQg==",
"dependencies": {
"System.Security.Cryptography.Pkcs": "7.0.0"
}
},
"System.Text.Encoding.CodePages": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "5.0.1",
"contentHash": "/UM3UK1dXKl8Ybysg/21gM4S8DJgkR+yLU8JwqCVbuNqQNImelntgYFAN5QxR8sJJ1kMx//hOUdf0lltosi8cQ=="
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
},
"Torch.SixLabors.ImageSharp": {
"type": "Transitive",
"resolved": "1.0.0-beta6",
"contentHash": "WJ7ocT79HgmuKi0+ltpvXTiMI80UcI3DeS8XSfYwJtTB1tcQws6zLPGuUwra6qe6qRrFfpABeDP3xvHV1rJgfg==",
"dependencies": {
"SixLabors.Core": "1.0.0-beta0007",
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"torch": {
"type": "Project",
"dependencies": {
"ControlzEx": "[5.0.2, )",
"HarmonyX": "[2.10.2-prerelease.3, )",
"MahApps.Metro": "[2.4.9, )",
"Microsoft.CodeAnalysis.CSharp": "[4.4.0, )",
"Microsoft.CodeAnalysis.Common": "[4.4.0, )",
"MonoModReorg.RuntimeDetour": "[23.1.2-prerelease.1, )",
"NLog": "[5.1.0, )",
"System.ComponentModel.Annotations": "[5.0.0, )",
"Torch.API": "[1.0.0, )",
"Torch.SixLabors.ImageSharp": "[1.0.0-beta6, )",
"protobuf-net": "[3.1.26, )"
}
},
"torch.api": {
"type": "Project",
"dependencies": {
"JorgeSerrano.Json.JsonSnakeCaseNamingPolicy": "[0.9.0, )",
"NLog": "[5.1.0, )",
"NuGet.Commands": "[6.4.0, )",
"NuGet.DependencyResolver.Core": "[6.4.0, )",
"SemanticVersioning": "[2.0.2, )",
"System.Linq.Async": "[6.0.1, )"
}
}
},
"net7.0-windows7.0/win-x64": {
"Steamworks.NET": {
"type": "Direct",
"requested": "[20.1.0, )",
"resolved": "20.1.0",
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
},
"System.Management": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "A4jed4QUviDOm7fJNKAJObEAEkEUXmkGL/w0iyCYTzrl1rezTj8LGFHfsVst4Vb9JwFcTpboiDrvdST48avBpw==",
"dependencies": {
"System.CodeDom": "7.0.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
"dependencies": {
"System.Formats.Asn1": "5.0.0"
}
},
"System.Security.Cryptography.Pkcs": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "mjUbEXkR6DYRef6dnEYKdfec9otcAkibExL+1f9hmbGlWIUyaCnS3Y3oGZEet38waXmuY1ORE8vgv4sgD5nMYg==",
"dependencies": {
"System.Formats.Asn1": "7.0.0"
}
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog=="
},
"System.Text.Encoding.CodePages": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
}
}
}
}

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch 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

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net7-windows</TargetFramework>
<NoWarn>1591,0649</NoWarn> <NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Tests</AssemblyTitle> <AssemblyTitle>Torch Tests</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
@@ -8,7 +8,6 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\</OutputPath>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
@@ -18,22 +17,15 @@
</PropertyGroup> </PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> --> <!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="NLog" Version="5.1.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" /> <ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" /> <ProjectReference Include="..\Torch\Torch.csproj" />

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

View File

@@ -12,20 +12,19 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AD02A71-1D4C-48F9-A8C1-789A5512424F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AD02A71-1D4C-48F9-A8C1-789A5512424F}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
NLog.config = NLog.config NLog.config = NLog.config
Dockerfile = Dockerfile
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Tests", "Torch.Tests\Torch.Tests.csproj", "{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Tests", "Torch.Tests\Torch.Tests.csproj", "{C3C8B671-6AD1-44AA-A8DA-E0C0DC0FEDF5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Server.Tests", "Torch.Server.Tests\Torch.Server.Tests.csproj", "{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Server.Tests", "Torch.Server.Tests\Torch.Server.Tests.csproj", "{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}"
EndProject 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
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Torch.Mod", "Torch.Mod\Torch.Mod.shproj", "{3CE4D2E9-B461-4F19-8233-F87E0DFDDD74}" Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Torch.Mod", "Torch.Mod\Torch.Mod.shproj", "{3CE4D2E9-B461-4F19-8233-F87E0DFDDD74}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI/CD workflows", "CI/CD workflows", "{DFC9AB6D-BD21-4748-8CE8-57097DC673AF}"
ProjectSection(SolutionItems) = preProject
.github\workflows\release.yaml = .github\workflows\release.yaml
EndProjectSection
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
Torch.Mod\Torch.Mod.projitems*{3ce4d2e9-b461-4f19-8233-f87e0dfddd74}*SharedItemsImports = 13 Torch.Mod\Torch.Mod.projitems*{3ce4d2e9-b461-4f19-8233-f87e0dfddd74}*SharedItemsImports = 13
@@ -57,7 +56,7 @@ Global
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{762F6A0D-55EF-4173-8CDE-309D183F40C4} = {7AD02A71-1D4C-48F9-A8C1-789A5512424F} {DFC9AB6D-BD21-4748-8CE8-57097DC673AF} = {7AD02A71-1D4C-48F9-A8C1-789A5512424F}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB51D91F-958D-4B63-A897-3C40642ACD3E} SolutionGuid = {BB51D91F-958D-4B63-A897-3C40642ACD3E}

View File

@@ -0,0 +1,32 @@
using System.IO;
using Torch.API;
namespace Torch;
public class ApplicationContext : IApplicationContext
{
public static IApplicationContext Current { get; private set; }
public ApplicationContext(DirectoryInfo torchDirectory, DirectoryInfo gameFilesDirectory, DirectoryInfo gameBinariesDirectory,
DirectoryInfo instanceDirectory, string instanceName, bool isService)
{
TorchDirectory = torchDirectory;
GameFilesDirectory = gameFilesDirectory;
GameBinariesDirectory = gameBinariesDirectory;
InstanceDirectory = instanceDirectory;
InstanceName = instanceName;
IsService = isService;
Current = this;
}
/// <inheritdoc />
public DirectoryInfo TorchDirectory { get; }
/// <inheritdoc />
public DirectoryInfo GameFilesDirectory { get; }
/// <inheritdoc />
public DirectoryInfo GameBinariesDirectory { get; }
/// <inheritdoc />
public DirectoryInfo InstanceDirectory { get; }
/// <inheritdoc />
public string InstanceName { get; }
/// <inheritdoc />
public bool IsService { get; }
}

View File

@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using NLog;
namespace Torch
{
/// <summary>
/// Base class that adds tools for setting type properties through the command line.
/// </summary>
public abstract class CommandLine
{
private readonly string _argPrefix;
private readonly Dictionary<ArgAttribute, PropertyInfo> _args = new Dictionary<ArgAttribute, PropertyInfo>();
private readonly Logger _log = LogManager.GetCurrentClassLogger();
protected CommandLine(string argPrefix = "-")
{
_argPrefix = argPrefix;
foreach (var prop in GetType().GetProperties())
{
var attr = prop.GetCustomAttribute<ArgAttribute>();
if (attr == null)
continue;
_args.Add(attr, prop);
}
}
public string GetHelp()
{
var sb = new StringBuilder();
foreach (var property in _args)
{
var attr = property.Key;
sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(24)}{attr.Description}");
}
return sb.ToString();
}
public override string ToString()
{
var args = new List<string>();
foreach (var prop in _args)
{
var attr = prop.Key;
if (prop.Value.PropertyType == typeof(bool) && (bool)prop.Value.GetValue(this))
{
args.Add($"{_argPrefix}{attr.Name}");
}
else if (prop.Value.PropertyType == typeof(string))
{
var str = (string)prop.Value.GetValue(this);
if (string.IsNullOrEmpty(str))
continue;
args.Add($"{_argPrefix}{attr.Name} \"{str}\"");
}
}
return string.Join(" ", args);
}
public bool Parse(string[] args)
{
if (args.Length == 0)
return true;
if (args[0] == $"{_argPrefix}help")
{
Console.WriteLine(GetHelp());
return false;
}
for (var i = 0; i < args.Length; i++)
{
if (!args[i].StartsWith(_argPrefix))
continue;
foreach (var property in _args)
{
var argName = property.Key.Name;
if (argName == null)
continue;
try
{
if (string.Compare(argName, 0, args[i], 1, argName.Length, StringComparison.InvariantCultureIgnoreCase) == 0)
{
if (property.Value.PropertyType == typeof(bool))
property.Value.SetValue(this, true);
if (property.Value.PropertyType == typeof(string))
property.Value.SetValue(this, args[++i]);
if (property.Value.PropertyType == typeof(List<Guid>))
{
i++;
var l = new List<Guid>(16);
while (i < args.Length && !args[i].StartsWith(_argPrefix))
{
if (Guid.TryParse(args[i], out Guid g))
{
l.Add(g);
_log.Info($"added plugin {g}");
}
else
_log.Warn($"Failed to parse GUID {args[i]}");
i++;
}
property.Value.SetValue(this, l);
}
}
}
catch
{
Console.WriteLine($"Error parsing arg {argName}");
}
}
}
return true;
}
public class ArgAttribute : Attribute
{
public string Name { get; }
public string Description { get; }
public ArgAttribute(string name, string description)
{
Name = name;
Description = description;
}
}
}
}

View File

@@ -11,7 +11,6 @@ using System.Timers;
using NLog; using NLog;
using Sandbox.Game.Multiplayer; using Sandbox.Game.Multiplayer;
using Sandbox.ModAPI; using Sandbox.ModAPI;
using Steamworks;
using Torch; using Torch;
using Torch.API; using Torch.API;
using Torch.API.Managers; using Torch.API.Managers;

Some files were not shown because too many files have changed in this diff Show More