Compare commits

..

6 Commits

Author SHA1 Message Date
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
36 changed files with 453 additions and 321 deletions

5
Jenkins/get-version.ps1 Normal file
View File

@@ -0,0 +1,5 @@
$buildSalt = $Env:BUILD_NUMBER
$branchName = $Env:BRANCH_NAME
$gitSimpleVersion = git describe --tags --abbrev=0
$simpleVersionStandard = echo $gitSimpleVersion | Select-String -Pattern "([0-9]+)\.([0-9]+)\.([0-9]+)" | % {$_.Matches} | %{$_.Groups[1].Value+"."+$_.Groups[2].Value+"."+$_.Groups[3].Value}
Write-Host "$simpleVersionStandard.$buildSalt"

View File

@@ -0,0 +1,26 @@
pushd
$steamData = "C:/Steam/Data/"
$steamCMDPath = "C:/Steam/steamcmd/"
$steamCMDZip = "C:/Steam/steamcmd.zip"
if (!(Test-Path $steamData)) {
mkdir "$steamData"
}
if (!(Test-Path $steamCMDPath)) {
if (!(Test-Path $steamCMDZip)) {
Invoke-WebRequest -OutFile $steamCMDZip https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip
}
Expand-Archive $steamCMDZip -DestinationPath $steamCMDPath
}
& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740" "+quit"
$dataPath = $steamData.Replace("/", "\");
$contentPath = "$dataPath\Content";
if (Test-Path $contentPath) {
Remove-Item -LiteralPath $contentPath -Force -Recurse
}
cmd /S /C mklink /J .\GameBinaries $dataPath\DedicatedServer64
popd

55
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,55 @@
def packageAndArchive(buildMode, packageName) {
zipFile = "bin\\${packageName}.zip"
packageDir = "publish"
bat 'powershell -Command { Compress-Archive -Path ${packageDir}\\* -DestinationPath ${zipFile} }'
archiveArtifacts artifacts: zipFile, caseSensitive: false, onlyIfSuccessful: true
}
node('windows') {
stage('Checkout') {
checkout scm
bat 'git pull https://github.com/TorchAPI/Torch/ ${env.BRANCH_NAME} --tags'
}
stage('Acquire SE') {
bat 'powershell -File Jenkins/jenkins-grab-se.ps1'
}
stage('Build') {
dotnetVersion = bat(returnStdout: true, script: '@powershell -NonInteractive -NoLogo -NoProfile -File Jenkins/get-version.ps1').trim()
infoVersion = "${dotnetVersion}-${env.BRANCH_NAME}"
currentBuild.description = infoVersion
bat 'dotnet publish .\\Torch.Server\\Torch.Server.csproj -p:PackageVersion=${dotnetVersion} -p:InformationalVersion=${infoVersion} --self-contained -f net6-windows -r win-x64 -c Release -o .\\publish\\'
}
stage('Archive') {
//archiveArtifacts artifacts: "bin/x64/${buildMode}/Torch*", caseSensitive: false, fingerprint: true, onlyIfSuccessful: true
packageAndArchive(buildMode, "torch-server")
/*packageAndArchive(buildMode, "torch-client", "Torch.Server*")*/
}
/* Disabled because they fail builds more often than they detect actual problems
stage('Test') {
bat 'IF NOT EXIST reports MKDIR reports'
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/${buildMode}/Torch.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Server.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
step([
$class: 'XUnitBuilder',
thresholdMode: 1,
thresholds: [[$class: 'FailedThreshold', failureThreshold: '1']],
tools: [[
$class: 'XUnitDotNetTestType',
deleteOutputFiles: true,
failIfNotNew: true,
pattern: 'reports/*.xml',
skipNoTestFiles: false,
stopProcessingIfError: true
]]
])
}
*/
}

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

@@ -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,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net6-windows</TargetFramework>
<LangVersion>10</LangVersion>
<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>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
@@ -19,6 +19,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="NLog" Version="5.0.0-rc2" />
<PackageReference Include="SemanticVersioning" Version="2.0.0" /> <PackageReference Include="SemanticVersioning" Version="2.0.0" />
<PackageReference Include="System.Text.Json" Version="6.0.2" Condition="$(TargetFramework) == 'net48'" />
<PackageReference Include="System.Net.Http" Version="4.3.4" Condition="$(TargetFramework) == 'net48'" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" Condition="$(TargetFramework) == 'net48'" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
@@ -99,7 +102,4 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,8 @@
#if NETFRAMEWORK
// ReSharper disable once CheckNamespace
namespace System.Runtime.CompilerServices;
public class IsExternalInit
{
}
#endif

View File

@@ -68,7 +68,11 @@ namespace Torch.API.WebAPI
return false; return false;
} }
var s = await h.Content.ReadAsStreamAsync(); var s = await h.Content.ReadAsStreamAsync();
#if !NETFRAMEWORK
await using var fs = new FileStream(path, FileMode.Create); await using var fs = new FileStream(path, FileMode.Create);
#else
using var fs = new FileStream(path, FileMode.Create);
#endif
await s.CopyToAsync(fs); await s.CopyToAsync(fs);
return true; return true;
} }

View File

@@ -78,7 +78,11 @@ namespace Torch.API.WebAPI
if(File.Exists(path)) if(File.Exists(path))
File.Delete(path); File.Delete(path);
#if NETFRAMEWORK
using var f = File.Create(path);
#else
await using var f = File.Create(path); await using var f = File.Create(path);
#endif
await s.CopyToAsync(f); await s.CopyToAsync(f);
} }
catch (Exception ex) catch (Exception ex)

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,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net6-windows</TargetFramework>
<LangVersion>10</LangVersion>
<NoWarn>1591,0649</NoWarn> <NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Server Tests</AssemblyTitle> <AssemblyTitle>Torch Server Tests</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
@@ -27,9 +28,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

@@ -29,19 +29,14 @@ 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;
@@ -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();
@@ -140,10 +135,6 @@ quit";
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))
{ {
@@ -166,8 +157,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, Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? "../"),
WorkingDirectory = path, WorkingDirectory = path,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,

View File

@@ -40,7 +40,13 @@ namespace Torch.Server.Managers
protected abstract EntityControlViewModel Create(EntityViewModel evm); protected abstract EntityControlViewModel Create(EntityViewModel evm);
#if NETFRAMEWORK
[ReflectedGetter(Name = "Keys")]
private static readonly Func<ConditionalWeakTable<EntityViewModel, EntityControlViewModel>, ICollection<EntityViewModel>> WeakTableKeys = null!;
internal IEnumerable<EntityViewModel> Keys => WeakTableKeys(_models);
#else
internal IEnumerable<EntityViewModel> Keys => _models.Select(b => b.Key); internal IEnumerable<EntityViewModel> Keys => _models.Select(b => b.Key);
#endif
internal EntityControlViewModel GetOrCreate(EntityViewModel evm) internal EntityControlViewModel GetOrCreate(EntityViewModel evm)
{ {

View File

@@ -3,6 +3,7 @@ using System.IO;
using NLog; using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
using Torch.API;
using Torch.Utils; using Torch.Utils;
namespace Torch.Server namespace Torch.Server
@@ -12,112 +13,71 @@ namespace Torch.Server
[STAThread] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
var isService = Environment.GetEnvironmentVariable("TORCH_SERVICE") var context = CreateApplicationContext();
?.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) ?? false;
//Ensures that all the files are downloaded in the Torch directory.
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))
foreach (var file in Directory.GetFiles(binDir, "System.*.dll"))
{
File.Delete(file);
}
// Breaks on Windows Server 2019 SetupLogging();
#if TORCH_SERVICE
if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive)
{
using (var service = new TorchService(args))
ServiceBase.Run(service);
return;
}
#endif
var instanceName = Environment.GetEnvironmentVariable("TORCH_INSTANCE") ?? "Instance"; var oldTorchCfg = Path.Combine(context.TorchDirectory.FullName, "Torch.cfg");
string instancePath; var torchCfg = Path.Combine(context.InstanceDirectory.FullName, "Torch.cfg");
if (Path.IsPathRooted(instanceName))
{
instancePath = instanceName;
instanceName = Path.GetDirectoryName(instanceName);
}
else
{
instancePath = Directory.CreateDirectory(instanceName).FullName;
}
var oldNlog = Path.Combine(workingDir, "NLog.config");
var newNlog = Path.Combine(instancePath, "NLog.config");
if (File.Exists(oldNlog) && !File.ReadAllText(oldNlog).Contains("FlowDocument", StringComparison.Ordinal))
File.Move(oldNlog, newNlog, true);
else if (!File.Exists(newNlog))
using (var f = File.Create(newNlog))
typeof(Program).Assembly.GetManifestResourceStream("Torch.Server.NLog.config")!.CopyTo(f);
var oldTorchCfg = Path.Combine(workingDir, "Torch.cfg");
var torchCfg = Path.Combine(instancePath, "Torch.cfg");
if (File.Exists(oldTorchCfg)) if (File.Exists(oldTorchCfg))
File.Move(oldTorchCfg, torchCfg, true); File.Move(oldTorchCfg, torchCfg);
var config = Persistent<TorchConfig>.Load(torchCfg); var config = Persistent<TorchConfig>.Load(torchCfg);
config.Data.InstanceName = instanceName; config.Data.InstanceName = context.InstanceName;
config.Data.InstancePath = instancePath; config.Data.InstancePath = context.InstanceDirectory.FullName;
if (!config.Data.Parse(args)) if (!config.Data.Parse(args))
{ {
Console.WriteLine("Invalid arguments"); Console.WriteLine("Invalid arguments");
Environment.Exit(1); Environment.Exit(1);
} }
var handler = new UnhandledExceptionHandler(config.Data, isService); var handler = new UnhandledExceptionHandler(config.Data);
AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException; AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException;
Target.Register<LogViewerTarget>(nameof(LogViewerTarget));
TorchLogManager.RegisterTargets(Environment.GetEnvironmentVariable("TORCH_LOG_EXTENSIONS_PATH") ??
Path.Combine(instancePath, "LoggingExtensions"));
TorchLogManager.SetConfiguration(new XmlLoggingConfiguration(newNlog));
var initializer = new Initializer(workingDir, config); var initializer = new Initializer(config);
if (!initializer.Initialize(args)) if (!initializer.Initialize(args))
Environment.Exit(1); Environment.Exit(1);
TorchLauncher.Launch(workingDir, binDir); #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
CopyNative(binDir); CopyNative();
initializer.Run(isService, instanceName, instancePath); initializer.Run();
} }
private static void CopyNative(string binPath) private static void CopyNative()
{ {
var log = LogManager.GetLogger("TorchLauncher"); var log = LogManager.GetLogger("TorchLauncher");
var workingDir = new DirectoryInfo(Directory.GetCurrentDirectory()); if (ApplicationContext.Current.GameFilesDirectory.Attributes.HasFlag(FileAttributes.ReadOnly))
if (workingDir.Attributes.HasFlag(FileAttributes.ReadOnly))
{ {
log.Warn("Game directory is readonly. You should copy steam_api64.dll, Havok.dll from bin manually"); log.Warn("Torch directory is readonly. You should copy steam_api64.dll, Havok.dll from bin manually");
return; return;
} }
try try
{ {
var apiSource = Path.Combine(binPath, "steam_api64.dll"); var apiSource = Path.Combine(ApplicationContext.Current.GameBinariesDirectory.FullName, "steam_api64.dll");
var apiTarget = Path.Combine(workingDir.FullName, "steam_api64.dll"); var apiTarget = Path.Combine(ApplicationContext.Current.GameFilesDirectory.FullName, "steam_api64.dll");
if (!File.Exists(apiTarget)) if (!File.Exists(apiTarget))
{ {
File.Copy(apiSource, apiTarget); File.Copy(apiSource, apiTarget);
} }
else if (File.GetLastWriteTime(apiTarget) < File.GetLastWriteTime(binPath)) else if (File.GetLastWriteTime(apiTarget) < ApplicationContext.Current.GameBinariesDirectory.LastWriteTime)
{ {
File.Delete(apiTarget); File.Delete(apiTarget);
File.Copy(apiSource, apiTarget); File.Copy(apiSource, apiTarget);
} }
var havokSource = Path.Combine(binPath, "Havok.dll"); var havokSource = Path.Combine(ApplicationContext.Current.GameBinariesDirectory.FullName, "Havok.dll");
var havokTarget = Path.Combine(workingDir.FullName, "Havok.dll"); var havokTarget = Path.Combine(ApplicationContext.Current.GameFilesDirectory.FullName, "Havok.dll");
if (!File.Exists(havokTarget)) if (!File.Exists(havokTarget))
{ {
@@ -138,5 +98,49 @@ namespace Torch.Server
log.Error(e); log.Error(e);
} }
} }
private static void SetupLogging()
{
var oldNlog = Path.Combine(ApplicationContext.Current.TorchDirectory.FullName, "NLog.config");
var newNlog = Path.Combine(ApplicationContext.Current.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(Environment.GetEnvironmentVariable("TORCH_LOG_EXTENSIONS_PATH") ??
Path.Combine(ApplicationContext.Current.InstanceDirectory.FullName, "LoggingExtensions"));
TorchLogManager.SetConfiguration(new XmlLoggingConfiguration(newNlog));
}
private static IApplicationContext CreateApplicationContext()
{
var isService = Environment.GetEnvironmentVariable("TORCH_SERVICE")
?.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) ?? false;
var workingDir = AppContext.BaseDirectory;
var gamePath = Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir;
var binDir = Path.Combine(gamePath, "DedicatedServer64");
Directory.SetCurrentDirectory(gamePath);
var instanceName = Environment.GetEnvironmentVariable("TORCH_INSTANCE") ?? "Instance";
string instancePath;
if (Path.IsPathRooted(instanceName))
{
instancePath = instanceName;
instanceName = Path.GetDirectoryName(instanceName);
}
else
{
instancePath = Directory.CreateDirectory(instanceName).FullName;
}
return new ApplicationContext(new(workingDir), new(gamePath), new(binDir),
new(instancePath), instanceName, isService);
}
} }
} }

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

@@ -2,11 +2,10 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net6-windows</TargetFramework>
<LangVersion>10</LangVersion>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <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>
@@ -14,13 +13,16 @@
<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>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<BeautyLibsDir>torch64</BeautyLibsDir>
<NoBeautyFlag>True</NoBeautyFlag>
<ForceBeauty>True</ForceBeauty>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>Torch.Server.Program</StartupObject> <StartupObject>Torch.Server.Program</StartupObject>
@@ -40,6 +42,7 @@
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="all" /> <PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="all" />
<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="6.0.0" />
<PackageReference Include="nulastudio.NetCoreBeauty" Version="1.2.9.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64">
@@ -140,16 +143,9 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<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" />

View File

@@ -221,7 +221,11 @@ namespace Torch.Server
LogManager.Flush(); LogManager.Flush();
string exe = Assembly.GetExecutingAssembly().Location.Replace("dll", "exe"); string exe = Assembly.GetExecutingAssembly().Location.Replace("dll", "exe");
#if NETFRAMEWORK
config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
#else
config.WaitForPID = Environment.ProcessId.ToString(); config.WaitForPID = Environment.ProcessId.ToString();
#endif
config.TempAutostart = true; config.TempAutostart = true;
Process.Start(exe, config.ToString()); Process.Start(exe, config.ToString());
@@ -370,35 +374,37 @@ 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)) #if NETFRAMEWORK
{ using var target = DataTarget.CreateSnapshotAndAttach(Process.GetCurrentProcess().Id);
var runtime = target.ClrVersions[0].CreateRuntime(); #else
using var target = DataTarget.CreateSnapshotAndAttach(Environment.ProcessId);
#endif
var runtime = target.ClrVersions[0].CreateRuntime();
var clrThread = runtime.Threads.First(b => b.ManagedThreadId == thread.ManagedThreadId); var clrThread = runtime.Threads.First(b => b.ManagedThreadId == thread.ManagedThreadId);
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var frame in clrThread.EnumerateStackTrace()) foreach (var frame in clrThread.EnumerateStackTrace())
{
sb.Append('\t');
switch (frame.Kind)
{ {
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

@@ -9,13 +9,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,7 +24,7 @@ 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)
@@ -34,7 +32,11 @@ internal class UnhandledExceptionHandler
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 = typeof(Program).Assembly.Location;
#if NETFRAMEWORK
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
#else
_config.WaitForPID = Environment.ProcessId.ToString(); _config.WaitForPID = Environment.ProcessId.ToString();
#endif
Process.Start(exe, _config.ToString()); Process.Start(exe, _config.ToString());
} }
else else

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

@@ -130,17 +130,17 @@ namespace Torch.Server.Views
//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

@@ -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,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net6-windows</TargetFramework>
<LangVersion>10</LangVersion>
<NoWarn>1591,0649</NoWarn> <NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Tests</AssemblyTitle> <AssemblyTitle>Torch Tests</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
@@ -8,7 +9,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>
@@ -27,13 +27,6 @@
<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,17 +12,13 @@ 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
Jenkinsfile = Jenkinsfile
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
Global Global
@@ -56,7 +52,6 @@ Global
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{762F6A0D-55EF-4173-8CDE-309D183F40C4} = {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

@@ -103,27 +103,27 @@ namespace Torch.Patches
private static bool PrefixWriteLine(MyLog __instance, string msg) private static bool PrefixWriteLine(MyLog __instance, string msg)
{ {
if (__instance.LogEnabled && _log.IsDebugEnabled) if (__instance.LogEnabled && _log.IsDebugEnabled)
_log.Debug($"{" ".PadRight(3 * GetIndentByCurrentThread())}{msg}"); _log.Debug($"{string.Empty.PadRight(3 * GetIndentByCurrentThread(), ' ')}{msg}");
return false; return false;
} }
private static bool PrefixWriteLineConsole(MyLog __instance, string msg) private static bool PrefixWriteLineConsole(MyLog __instance, string msg)
{ {
if (__instance.LogEnabled && _log.IsInfoEnabled) if (__instance.LogEnabled && _log.IsInfoEnabled)
_log.Info($"{" ".PadRight(3 * GetIndentByCurrentThread())}{msg}"); _log.Info($"{string.Empty.PadRight(3 * GetIndentByCurrentThread(), ' ')}{msg}");
return false; return false;
} }
private static bool PrefixAppendToClosedLog(MyLog __instance, string text) private static bool PrefixAppendToClosedLog(MyLog __instance, string text)
{ {
if (__instance.LogEnabled && _log.IsDebugEnabled) if (__instance.LogEnabled && _log.IsDebugEnabled)
_log.Debug($"{" ".PadRight(3 * GetIndentByCurrentThread())}{text}"); _log.Debug($"{string.Empty.PadRight(3 * GetIndentByCurrentThread(), ' ')}{text}");
return false; return false;
} }
private static bool PrefixWriteLineOptions(MyLog __instance, string message, LoggingOptions option) private static bool PrefixWriteLineOptions(MyLog __instance, string message, LoggingOptions option)
{ {
if (__instance.LogEnabled && __instance.LogFlag(option) && _log.IsDebugEnabled) if (__instance.LogEnabled && __instance.LogFlag(option) && _log.IsDebugEnabled)
_log.Info($"{" ".PadRight(3 * GetIndentByCurrentThread())}{message}"); _log.Info($"{string.Empty.PadRight(3 * GetIndentByCurrentThread(), ' ')}{message}");
return false; return false;
} }
@@ -145,7 +145,7 @@ namespace Torch.Patches
return false; return false;
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem // ReSharper disable once TemplateIsNotCompileTimeConstantProblem
_log.Log(new(LogLevelFor(severity), _log.Name, $"{" ".PadRight(3 * GetIndentByCurrentThread())}{string.Format(format, args)}")); _log.Log(new(LogLevelFor(severity), _log.Name, $"{string.Empty.PadRight(3 * GetIndentByCurrentThread(), ' ')}{string.Format(format, args)}"));
return false; return false;
} }

View File

@@ -1,9 +1,10 @@
using System; #if !NETFRAMEWORK
using System.Collections; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -13,6 +14,7 @@ using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using ProtoBuf; using ProtoBuf;
using ProtoBuf.Meta;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.MSIL;
using Torch.Utils; using Torch.Utils;
@@ -42,10 +44,14 @@ namespace Torch.Patches
context.GetPattern(Register2Method).AddTranspiler(nameof(RegisterTranspiler)); context.GetPattern(Register2Method).AddTranspiler(nameof(RegisterTranspiler));
} }
private static void WhitelistCtorPrefix(MyScriptCompiler scriptCompiler) private static void WhitelistCtorPrefix(MyScriptCompiler scriptCompiler, MyScriptWhitelist __instance)
{ {
var basePath = new FileInfo(typeof(object).Assembly.Location).DirectoryName!;
scriptCompiler.AddReferencedAssemblies( scriptCompiler.AddReferencedAssemblies(
typeof(ValueType).Assembly.Location, Path.Combine(basePath, "netstandard.dll"),
Path.Combine(basePath, "mscorlib.dll"),
Path.Combine(basePath, "System.Runtime.dll"),
typeof(LinkedList<>).Assembly.Location, typeof(LinkedList<>).Assembly.Location,
typeof(Regex).Assembly.Location, typeof(Regex).Assembly.Location,
typeof(Enumerable).Assembly.Location, typeof(Enumerable).Assembly.Location,
@@ -53,8 +59,9 @@ namespace Torch.Patches
typeof(ImmutableArray).Assembly.Location, typeof(ImmutableArray).Assembly.Location,
typeof(PropertyChangedEventArgs).Assembly.Location, typeof(PropertyChangedEventArgs).Assembly.Location,
typeof(TypeConverter).Assembly.Location, typeof(TypeConverter).Assembly.Location,
typeof(System.Diagnostics.TraceSource).Assembly.Location, typeof(TraceSource).Assembly.Location,
typeof(ProtoBuf.Meta.RuntimeTypeModel).Assembly.Location, typeof(RuntimeTypeModel).Assembly.Location,
typeof(ProtoMemberAttribute).Assembly.Location,
Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll"), Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll"),
Path.Combine(MyFileSystem.ExePath, "Sandbox.Common.dll"), Path.Combine(MyFileSystem.ExePath, "Sandbox.Common.dll"),
Path.Combine(MyFileSystem.ExePath, "Sandbox.Graphics.dll"), Path.Combine(MyFileSystem.ExePath, "Sandbox.Graphics.dll"),
@@ -73,6 +80,9 @@ namespace Torch.Patches
MyModWatchdog.Init(updateThread); MyModWatchdog.Init(updateThread);
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(referencedTypes); MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(referencedTypes);
MyScriptCompiler.Static.AddConditionalCompilationSymbols(symbols); MyScriptCompiler.Static.AddConditionalCompilationSymbols(symbols);
using var batch = MyScriptCompiler.Static.Whitelist.OpenBatch();
// Dict and queue in different assemblies, microsoft being microsoft
batch.AllowNamespaceOfTypes(MyWhitelistTarget.ModApi, typeof(ConcurrentQueue<>));
return false; return false;
} }
@@ -101,4 +111,5 @@ namespace Torch.Patches
return ins; return ins;
} }
} }
} }
#endif

View File

@@ -1,34 +1,52 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Mono.Cecil; using Mono.Cecil;
using NLog;
namespace Torch.Plugins; namespace Torch.Plugins;
internal static class AssemblyRewriter internal static class AssemblyRewriter
{ {
private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); private static readonly ZipResolver _zipResolver;
private static readonly DefaultAssemblyResolver _defaultResolver;
private static readonly IAssemblyResolver Resolver;
static AssemblyRewriter() static AssemblyRewriter()
{ {
var resolver = new DefaultAssemblyResolver(); _defaultResolver = new();
Resolver = resolver; _zipResolver = new(_defaultResolver);
resolver.AddSearchDirectory(Directory.GetCurrentDirectory()); _defaultResolver.AddSearchDirectory(Directory.GetCurrentDirectory());
resolver.AddSearchDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DedicatedServer64")); _defaultResolver.AddSearchDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DedicatedServer64"));
} }
public static Assembly ProcessWeavers(this Stream stream) public static Assembly ProcessWeavers(this Stream stream, ZipArchive archive)
{ {
_zipResolver.Archive = archive;
using var assStream = new MemoryStream(); using var assStream = new MemoryStream();
stream.CopyTo(assStream); stream.CopyTo(assStream);
assStream.Position = 0; assStream.Position = 0;
using var module = ModuleDefinition.ReadModule(assStream, new() var ass = ProcessInternal(assStream, _zipResolver);
_zipResolver.Archive = null;
return ass;
}
public static Assembly ProcessWeavers(this Stream stream, string path)
{
_defaultResolver.AddSearchDirectory(path);
using var assStream = new MemoryStream();
stream.CopyTo(assStream);
assStream.Position = 0;
var ass = ProcessInternal(assStream, _defaultResolver);
_defaultResolver.RemoveSearchDirectory(path);
return ass;
}
private static Assembly ProcessInternal(Stream inputStream, IAssemblyResolver resolver)
{
using var module = ModuleDefinition.ReadModule(inputStream, new()
{ {
AssemblyResolver = Resolver AssemblyResolver = _zipResolver
}); });
foreach (var fieldDefinition in FindAllToRewrite(module)) foreach (var fieldDefinition in FindAllToRewrite(module))
{ {
@@ -47,4 +65,40 @@ internal static class AssemblyRewriter
private static bool HasValidAttributes(FieldDefinition definition) => private static bool HasValidAttributes(FieldDefinition definition) =>
definition.CustomAttributes.Any(b => b.AttributeType.Name.Contains("Reflected") || b.AttributeType.Name == "DependencyAttribute"); definition.CustomAttributes.Any(b => b.AttributeType.Name.Contains("Reflected") || b.AttributeType.Name == "DependencyAttribute");
private class ZipResolver : IAssemblyResolver
{
private readonly IAssemblyResolver _fallbackResolver;
public ZipArchive Archive { get; set; }
public ZipResolver(IAssemblyResolver fallbackResolver)
{
_fallbackResolver = fallbackResolver;
}
public void Dispose()
{
_fallbackResolver.Dispose();
}
public AssemblyDefinition Resolve(AssemblyNameReference name)
{
return Resolve(name, new());
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
var fileName = $"{name.Name}.dll";
if (Archive.Entries.FirstOrDefault(entry => entry.Name == fileName) is not { } archiveEntry)
return _fallbackResolver.Resolve(name, parameters);
using var stream = archiveEntry.Open();
using var memStream = new MemoryStream();
stream.CopyTo(memStream);
memStream.Position = 0;
return AssemblyDefinition.ReadAssembly(memStream, parameters);
}
}
} }

View File

@@ -360,7 +360,7 @@ namespace Torch.Managers
using var stream = entry.Open(); using var stream = entry.Open();
assemblies.Add(stream.ProcessWeavers()); assemblies.Add(stream.ProcessWeavers(zipFile));
} }
} }
else else
@@ -378,7 +378,7 @@ namespace Torch.Managers
// continue; // continue;
using var stream = File.OpenRead(file); using var stream = File.OpenRead(file);
assemblies.Add(stream.ProcessWeavers()); assemblies.Add(stream.ProcessWeavers(item.Path));
} }

View File

@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch")]
[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,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6-windows</TargetFramework> <TargetFramework>net6-windows</TargetFramework>
<LangVersion>10</LangVersion>
<AssemblyTitle>Torch</AssemblyTitle> <AssemblyTitle>Torch</AssemblyTitle>
<Product>Torch</Product> <Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright> <Copyright>Copyright © Torch API 2017</Copyright>
@@ -8,7 +9,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWpf>True</UseWpf> <UseWpf>True</UseWpf>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
@@ -122,31 +122,6 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="Views\CollectionEditor.xaml.cs">
<DependentUpon>CollectionEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\DictionaryEditor.xaml.cs">
<DependentUpon>DictionaryEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\EmbeddedCollectionEditor.xaml.cs">
<DependentUpon>EmbeddedCollectionEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\FlagsEditor.xaml.cs">
<DependentUpon>FlagsEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\ObjectCollectionEditor.xaml.cs">
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\ObjectEditor.xaml.cs">
<DependentUpon>ObjectEditor.xaml</DependentUpon>
</Compile>
<Compile Update="Views\PropertyGrid.xaml.cs">
<DependentUpon>PropertyGrid.xaml</DependentUpon>
</Compile>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
<Compile Remove="Commands\Permissions\PermissionManager.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" /> <ProjectReference Include="..\Torch.API\Torch.API.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Torch.API; using Torch.API;
@@ -18,6 +19,43 @@ namespace Torch.Utils
var arr = str.Split('-'); var arr = str.Split('-');
return new MyObjectBuilder_Checkpoint.ModItem(ulong.Parse(arr[0]), arr[1]); return new MyObjectBuilder_Checkpoint.ModItem(ulong.Parse(arr[0]), arr[1]);
} }
public static bool TryParse(string str, out MyObjectBuilder_Checkpoint.ModItem item)
{
item = default;
var arr = str.Split('-');
if (arr.Length is 0 or > 2)
return false;
if (!ulong.TryParse(arr[0], out var id))
return false;
if (arr.Length == 1 || !TryParseServiceName(arr[1], out var serviceName))
serviceName = GetDefaultServiceName();
item = new(id, serviceName);
return true;
}
public static bool TryParseServiceName(string str, out string serviceName)
{
if (str.Equals("steam", StringComparison.OrdinalIgnoreCase))
{
serviceName = "Steam";
return true;
}
if (str.Equals("mod.io", StringComparison.OrdinalIgnoreCase) ||
str.Equals("eos", StringComparison.OrdinalIgnoreCase))
{
serviceName = "mod.io";
return true;
}
serviceName = null;
return false;
}
//because KEEEN! //because KEEEN!
public static string GetDefaultServiceName() public static string GetDefaultServiceName()

View File

@@ -1,14 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using Torch.API;
namespace Torch.Utils namespace Torch.Utils
{ {
@@ -23,7 +17,13 @@ namespace Torch.Utils
try try
{ {
var name = AssemblyName.GetAssemblyName(file); var name = AssemblyName.GetAssemblyName(file);
Assemblies.TryAdd(name.Name ?? name.FullName.Split(',')[0], file); var key = name.Name ?? name.FullName.Split(',')[0];
#if NETFRAMEWORK
if (!Assemblies.ContainsKey(key))
Assemblies.Add(key, file);
#else
Assemblies.TryAdd(key, file);
#endif
} }
catch (BadImageFormatException) catch (BadImageFormatException)
{ {
@@ -37,7 +37,7 @@ namespace Torch.Utils
private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{ {
var name = args.Name; var name = args.Name;
return Assemblies.TryGetValue(name[..name.IndexOf(',')], out var path) ? Assembly.LoadFrom(path) : null; return Assemblies.TryGetValue(name.IndexOf(',') > 0 ? name.Substring(0, name.IndexOf(',')) : name, out var path) ? Assembly.LoadFrom(path) : null;
} }
} }
} }

View File

@@ -2,7 +2,9 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
#if !NETFRAMEWORK
using System.Runtime.Loader; using System.Runtime.Loader;
#endif
using NLog; using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
@@ -11,7 +13,9 @@ namespace Torch.Utils;
public static class TorchLogManager public static class TorchLogManager
{ {
#if !NETFRAMEWORK
private static AssemblyLoadContext LoadContext = new("TorchLog"); private static AssemblyLoadContext LoadContext = new("TorchLog");
#endif
public static LoggingConfiguration Configuration { get; private set; } public static LoggingConfiguration Configuration { get; private set; }
@@ -26,7 +30,12 @@ public static class TorchLogManager
{ {
if (!Directory.Exists(dir)) return; if (!Directory.Exists(dir)) return;
foreach (var type in Directory.EnumerateFiles(dir, "*.dll").Select(LoadContext.LoadFromAssemblyPath) foreach (var type in Directory.EnumerateFiles(dir, "*.dll")
#if NETFRAMEWORK
.Select(Assembly.LoadFile)
#else
.Select(LoadContext.LoadFromAssemblyPath)
#endif
.SelectMany(b => b.ExportedTypes) .SelectMany(b => b.ExportedTypes)
.Where(b => b.GetCustomAttribute<TargetAttribute>() is { })) .Where(b => b.GetCustomAttribute<TargetAttribute>() is { }))
{ {

View File

@@ -1,4 +0,0 @@
using System.Reflection;
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyInformationalVersion("v0.0.0-dev")]