feature: first
All checks were successful
Build / Compute Version (push) Successful in 4s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 2m47s
Build / Build Nuget package (CringePlugins) (push) Successful in 5m31s
Build / Build Nuget package (NuGet) (push) Successful in 6m2s
Build / Build Nuget package (SharedCringe) (push) Successful in 7m25s
Build / Build Launcher (push) Successful in 9m11s
All checks were successful
Build / Compute Version (push) Successful in 4s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 2m47s
Build / Build Nuget package (CringePlugins) (push) Successful in 5m31s
Build / Build Nuget package (NuGet) (push) Successful in 6m2s
Build / Build Nuget package (SharedCringe) (push) Successful in 7m25s
Build / Build Launcher (push) Successful in 9m11s
This commit is contained in:
commit
aa979e9519
110
.github/workflows/build.yml
vendored
Normal file
110
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
compute-version:
|
||||
name: Compute Version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- id: version
|
||||
uses: paulhatch/semantic-version@v5.3.0
|
||||
with:
|
||||
tag_prefix: ''
|
||||
major_pattern: 'breaking:'
|
||||
minor_pattern: 'feature:'
|
||||
bump_each_commit: true
|
||||
|
||||
build-nuget:
|
||||
name: Build Nuget package
|
||||
runs-on: ubuntu-latest
|
||||
needs: [compute-version]
|
||||
strategy:
|
||||
matrix:
|
||||
project: [CringeBootstrap.Abstractions, NuGet, SharedCringe, CringePlugins]
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
|
||||
- run: dotnet restore ${{ matrix.project }}/${{ matrix.project }}.csproj --locked-mode
|
||||
name: Restore Project
|
||||
|
||||
- run: dotnet pack -c Release -o ./pub ${{ matrix.project }}/${{ matrix.project }}.csproj --no-restore -p:Version="${{ needs.compute-version.outputs.version }}"
|
||||
name: Pack Project
|
||||
|
||||
- name: Install Sleet
|
||||
run: dotnet tool install -g sleet
|
||||
|
||||
- name: Push Nuget Package
|
||||
env:
|
||||
SLEET_FEED_TYPE: s3
|
||||
SLEET_FEED_PATH: https://dl.zznty.ru/
|
||||
SLEET_FEED_BUCKETNAME: ${{ secrets.S3_BUCKET }}
|
||||
SLEET_FEED_SERVICEURL: ${{ secrets.S3_ENDPOINT }}
|
||||
SLEET_FEED_ACCESSKEYID: ${{ secrets.S3_KEY_ID }}
|
||||
SLEET_FEED_SECRETACCESSKEY: ${{ secrets.S3_SECRET }}
|
||||
run: ~/.dotnet/tools/sleet push ./pub
|
||||
|
||||
build:
|
||||
name: Build Launcher
|
||||
runs-on: ubuntu-latest
|
||||
needs: [compute-version]
|
||||
env:
|
||||
VPK_CHANNEL: win
|
||||
VPK_OUTPUT_DIR: Releases
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
|
||||
- run: dotnet restore CringeBootstrap/CringeBootstrap.csproj --locked-mode -r win-x64
|
||||
name: Restore Project
|
||||
|
||||
- run: dotnet publish -c Release -o ./pub CringeBootstrap/CringeBootstrap.csproj --no-restore -p:Version="${{ needs.compute-version.outputs.version }}" -r win-x64 --no-self-contained
|
||||
id: publish
|
||||
name: Build Project
|
||||
|
||||
- run: dotnet tool install -g vpk
|
||||
|
||||
- run: ~/.dotnet/tools/vpk download s3 --bucket $VPK_BUCKET --endpoint $VPK_ENDPOINT
|
||||
name: Download previous release
|
||||
env:
|
||||
VPK_KEY_ID: ${{ secrets.S3_KEY_ID }}
|
||||
VPK_SECRET: ${{ secrets.S3_SECRET }}
|
||||
VPK_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||
VPK_BUCKET: ${{ secrets.S3_BUCKET }}
|
||||
VPK_PREFIX: CringeLauncher
|
||||
|
||||
- run: ~/.dotnet/tools/vpk [win] pack -u $VPK_PACK_ID -v $VPK_PACK_VERSION -p $VPK_PACK_DIR
|
||||
name: Pack Project
|
||||
env:
|
||||
VPK_RUNTIME: win-x64
|
||||
VPK_PACK_ID: CringeLauncher
|
||||
VPK_PACK_VERSION: ${{ needs.compute-version.outputs.version }}
|
||||
VPK_PACK_AUTHORS: zznty
|
||||
VPK_PACK_TITLE: SE Cringe Launcher
|
||||
VPK_PACK_DIR: ./pub
|
||||
VPK_MAIN_EXE: CringeBootstrap.exe
|
||||
VPK_NO_PORTABLE: true
|
||||
VPK_FRAMEWORK: net8.0-x64-desktop,vcredist120-x64,vcredist143-x64
|
||||
VPK_SHORTCUTS: ''
|
||||
|
||||
- run: ~/.dotnet/tools/vpk upload s3 -o $VPK_OUTPUT_DIR --bucket $VPK_BUCKET --endpoint $VPK_ENDPOINT
|
||||
name: Upload new release
|
||||
env:
|
||||
VPK_KEY_ID: ${{ secrets.S3_KEY_ID }}
|
||||
VPK_SECRET: ${{ secrets.S3_SECRET }}
|
||||
VPK_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||
VPK_BUCKET: ${{ secrets.S3_BUCKET }}
|
||||
VPK_PREFIX: CringeLauncher
|
||||
VPK_KEEP_MAX_RELEASES: 3
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
/.idea/.idea.CringeLauncher/.idea
|
||||
/.vs
|
||||
/CringeLauncher/Bin64
|
||||
/CringeLauncher/pub
|
||||
*.user
|
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
9
CringeBootstrap.Abstractions/ICoreLoadContext.cs
Normal file
9
CringeBootstrap.Abstractions/ICoreLoadContext.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace CringeBootstrap.Abstractions;
|
||||
|
||||
public interface ICoreLoadContext
|
||||
{
|
||||
Assembly? ResolveFromAssemblyName(AssemblyName assemblyName);
|
||||
nint ResolveUnmanagedDll(string unmanagedDllName);
|
||||
}
|
7
CringeBootstrap.Abstractions/ICorePlugin.cs
Normal file
7
CringeBootstrap.Abstractions/ICorePlugin.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace CringeBootstrap.Abstractions;
|
||||
|
||||
public interface ICorePlugin : IDisposable
|
||||
{
|
||||
void Initialize(string[] args);
|
||||
void Run();
|
||||
}
|
7
CringeBootstrap.Abstractions/packages.lock.json
Normal file
7
CringeBootstrap.Abstractions/packages.lock.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
27
CringeBootstrap/CringeBootstrap.csproj
Normal file
27
CringeBootstrap/CringeBootstrap.csproj
Normal file
@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gameloop.Vdf" Version="0.6.2" />
|
||||
<PackageReference Include="NLog.Schema" Version="5.3.4" />
|
||||
<PackageReference Include="Velopack" Version="0.0.630-g9c52e40" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CringeBootstrap.Abstractions\CringeBootstrap.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\CringeLauncher\CringeLauncher.csproj" ExcludeAssets="compile" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
67
CringeBootstrap/GameDirectoryAssemblyLoadContext.cs
Normal file
67
CringeBootstrap/GameDirectoryAssemblyLoadContext.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using CringeBootstrap.Abstractions;
|
||||
|
||||
namespace CringeBootstrap;
|
||||
|
||||
public class GameDirectoryAssemblyLoadContext : AssemblyLoadContext, ICoreLoadContext
|
||||
{
|
||||
private static readonly ImmutableHashSet<string> ReferenceAssemblies = ["netstandard"];
|
||||
private readonly Dictionary<string, string> _assemblyNames = [];
|
||||
|
||||
public GameDirectoryAssemblyLoadContext(string dir) : base("CringeBootstrap")
|
||||
{
|
||||
var files = Directory.GetFiles(dir, "*.dll");
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (File.Exists(Path.Join(AppContext.BaseDirectory, Path.GetFileName(file))))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var name = AssemblyName.GetAssemblyName(file);
|
||||
|
||||
AddOverride(name, file);
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
// if we are trying to load native image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddOverride(AssemblyName name, string file)
|
||||
{
|
||||
var key = name.Name ?? name.FullName[..','];
|
||||
|
||||
if (key.StartsWith("System.") || ReferenceAssemblies.Contains(key))
|
||||
return;
|
||||
|
||||
_assemblyNames.TryAdd(key, file);
|
||||
}
|
||||
|
||||
public void AddDependencyOverride(string name)
|
||||
{
|
||||
AddOverride(new(name), Path.Join(AppContext.BaseDirectory, name + ".dll"));
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName name)
|
||||
{
|
||||
var key = name.Name ?? name.FullName[..','];
|
||||
|
||||
try
|
||||
{
|
||||
return _assemblyNames.TryGetValue(key, out var value) ? LoadFromAssemblyPath(value) : null;
|
||||
}
|
||||
catch (BadImageFormatException e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Assembly? ResolveFromAssemblyName(AssemblyName assemblyName) => Load(assemblyName);
|
||||
public nint ResolveUnmanagedDll(string unmanagedDllName) => LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
34
CringeBootstrap/NLog.config
Normal file
34
CringeBootstrap/NLog.config
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
|
||||
autoReload="true"
|
||||
internalLogLevel="Info">
|
||||
|
||||
<!-- the targets to write to -->
|
||||
<targets>
|
||||
<default-wrapper xsi:type="AsyncWrapper" overflowAction="Block" />
|
||||
|
||||
<!-- write logs to file -->
|
||||
<target xsi:type="File" name="allfile" archiveAboveSize="10485760" fileName="${specialfolder:folder=ApplicationData}/CringeLauncher/logs/log-${shortdate}.log"
|
||||
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${message}" />
|
||||
|
||||
<target xsi:type="File" archiveAboveSize="10485760" name="error" fileName="${specialfolder:folder=ApplicationData}/CringeLauncher/logs/log-error-${shortdate}.log"
|
||||
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${message} ${cringe-exception}" />
|
||||
|
||||
<target name="debugger" xsi:type="Debugger" layout="${message} ${cringe-exception}"/>
|
||||
|
||||
<target xsi:type="ColoredConsole" name="console" layout="[${longdate}] ${uppercase:${level}}: ${message} ${cringe-exception}" />
|
||||
</targets>
|
||||
|
||||
<!-- rules to map from logger name to target -->
|
||||
<rules>
|
||||
<logger name="*" level="Info,Error,Fatal" writeTo="allfile,debugger,console" />
|
||||
<logger name="*" level="Debug" writeTo="debugger" />
|
||||
<logger name="*" level="Error" writeTo="error,debugger" />
|
||||
</rules>
|
||||
</nlog>
|
||||
|
||||
</configuration>
|
55
CringeBootstrap/Program.cs
Normal file
55
CringeBootstrap/Program.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Web;
|
||||
using CringeBootstrap;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using Velopack;
|
||||
|
||||
#if DEBUG
|
||||
while (!Debugger.IsAttached)
|
||||
Thread.Sleep(100);
|
||||
#endif
|
||||
|
||||
VelopackApp.Build().Run();
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
var path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CringeLauncher",
|
||||
"current", "CringeBootstrap.exe");
|
||||
|
||||
Console.Write("Set your Launch Options under ");
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.Write("Space Engineers -> Properties -> Launch Options");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine(" in steam and launch the game as usual");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.WriteLine($"\"{path}\" %command%");
|
||||
Console.ResetColor();
|
||||
Console.Read();
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
AssemblyLoadContext.Default.Resolving += (loadContext, name) =>
|
||||
{
|
||||
Debug.WriteLine($"resolving {name} in {loadContext}");
|
||||
return null;
|
||||
};
|
||||
#endif
|
||||
|
||||
var dir = Path.GetDirectoryName(args[0])!;
|
||||
var context = new GameDirectoryAssemblyLoadContext(dir);
|
||||
|
||||
// a list of assemblies which are not in the game binaries but reference them
|
||||
context.AddDependencyOverride("CringeLauncher");
|
||||
context.AddDependencyOverride("CringePlugins");
|
||||
|
||||
var launcher = context.LoadFromAssemblyName(new AssemblyName("CringeLauncher"));
|
||||
|
||||
using var corePlugin = (ICorePlugin) launcher.CreateInstance("CringeLauncher.Launcher")!;
|
||||
|
||||
corePlugin.Initialize(args);
|
||||
corePlugin.Run();
|
2204
CringeBootstrap/packages.lock.json
Normal file
2204
CringeBootstrap/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
55
CringeLauncher.sln
Normal file
55
CringeLauncher.sln
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeBootstrap", "CringeBootstrap\CringeBootstrap.csproj", "{219C897E-452D-49B5-80C4-F3008718C16A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{C1670878-2301-4AE5-ABD3-5C4D0882CB02} = {C1670878-2301-4AE5-ABD3-5C4D0882CB02}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeLauncher", "CringeLauncher\CringeLauncher.csproj", "{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringeBootstrap.Abstractions", "CringeBootstrap.Abstractions\CringeBootstrap.Abstractions.csproj", "{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CringePlugins", "CringePlugins\CringePlugins.csproj", "{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{C1670878-2301-4AE5-ABD3-5C4D0882CB02}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet", "NuGet\NuGet.csproj", "{CC5362CA-881A-4867-9642-033269C1F7D7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedCringe", "SharedCringe\SharedCringe.csproj", "{BDA680D8-D4E3-48A9-8ED4-C4991098B71D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{219C897E-452D-49B5-80C4-F3008718C16A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{219C897E-452D-49B5-80C4-F3008718C16A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{219C897E-452D-49B5-80C4-F3008718C16A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{219C897E-452D-49B5-80C4-F3008718C16A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2A1B48E9-ED82-4EEB-A18A-E4148DFE3A19}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12AA2BBC-E795-4065-AF4A-9A44AFF69D92}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16F4D4BF-D363-4C71-8364-ACD0D63B0AE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C1670878-2301-4AE5-ABD3-5C4D0882CB02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C1670878-2301-4AE5-ABD3-5C4D0882CB02}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C1670878-2301-4AE5-ABD3-5C4D0882CB02}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C1670878-2301-4AE5-ABD3-5C4D0882CB02}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC5362CA-881A-4867-9642-033269C1F7D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC5362CA-881A-4867-9642-033269C1F7D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC5362CA-881A-4867-9642-033269C1F7D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC5362CA-881A-4867-9642-033269C1F7D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BDA680D8-D4E3-48A9-8ED4-C4991098B71D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BDA680D8-D4E3-48A9-8ED4-C4991098B71D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BDA680D8-D4E3-48A9-8ED4-C4991098B71D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BDA680D8-D4E3-48A9-8ED4-C4991098B71D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
55
CringeLauncher/CringeLauncher.csproj
Normal file
55
CringeLauncher/CringeLauncher.csproj
Normal file
@ -0,0 +1,55 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Publicize Include="VRage:VRage.Plugins.MyPlugins.LoadPlugins" />
|
||||
<Publicize Include="SpaceEngineers.Game:SpaceEngineers.Game.SpaceEngineersGame.OnRenderInitialized" />
|
||||
<Publicize Include="Sandbox.Game:Sandbox.Engine.Platform.Game.set_DrawThread" />
|
||||
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.form" />
|
||||
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.RenderThread_SizeChanged" />
|
||||
<Publicize Include="VRage.Render11;VRage.Platform.Windows;VRage.Scripting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dnlib" Version="4.4.0" />
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="8.0.0" />
|
||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||
<PackageReference Include="System.Private.ServiceModel" Version="4.10.3" />
|
||||
<PackageReference Include="Torch.SixLabors.ImageSharp" Version="1.0.0-beta6" />
|
||||
<PackageReference Include="Velopack" Version="0.0.630-g9c52e40" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog" Version="5.3.4" />
|
||||
<PackageReference Include="Lib.Harmony.Thin" Version="2.3.3" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
|
||||
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Steamworks.NET" Version="20.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CringeBootstrap.Abstractions\CringeBootstrap.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\CringePlugins\CringePlugins.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="NativeMethods.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
93
CringeLauncher/ImGuiHandler.cs
Normal file
93
CringeLauncher/ImGuiHandler.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using CringePlugins.Abstractions;
|
||||
using CringePlugins.Render;
|
||||
using ImGuiNET;
|
||||
using SharpDX.Direct3D11;
|
||||
using static ImGuiNET.ImGui;
|
||||
|
||||
namespace CringeLauncher;
|
||||
|
||||
internal class ImGuiHandler : IDisposable
|
||||
{
|
||||
private readonly DeviceContext _deviceContext;
|
||||
private static nint _wndproc;
|
||||
|
||||
public static ImGuiHandler? Instance;
|
||||
|
||||
public static RenderTargetView? Rtv;
|
||||
private readonly IRootRenderComponent _renderHandler;
|
||||
|
||||
public ImGuiHandler(nint windowHandle, Device1 device, DeviceContext deviceContext)
|
||||
{
|
||||
_deviceContext = deviceContext;
|
||||
|
||||
_renderHandler = new RenderHandler();
|
||||
|
||||
CreateContext();
|
||||
|
||||
var io = GetIO();
|
||||
|
||||
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
||||
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable;
|
||||
|
||||
ImGui_ImplWin32_Init(windowHandle);
|
||||
ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer);
|
||||
}
|
||||
|
||||
public void HookWindow(HWND windowHandle)
|
||||
{
|
||||
_wndproc = PInvoke.GetWindowLongPtr(windowHandle, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC);
|
||||
|
||||
unsafe
|
||||
{
|
||||
delegate* unmanaged[Stdcall]<HWND, int, nint, nint, int> wndProcHook = &WndProcHook;
|
||||
|
||||
PInvoke.SetWindowLongPtr(windowHandle, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, (nint)wndProcHook);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoRender()
|
||||
{
|
||||
if (Rtv is null)
|
||||
throw new InvalidOperationException("Rtv is null");
|
||||
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
NewFrame();
|
||||
|
||||
_renderHandler.OnFrame();
|
||||
|
||||
Render();
|
||||
|
||||
_deviceContext.ClearState();
|
||||
_deviceContext.OutputMerger.SetRenderTargets(Rtv);
|
||||
|
||||
ImGui_ImplDX11_RenderDrawData(GetDrawData());
|
||||
|
||||
UpdatePlatformWindows();
|
||||
RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
|
||||
private static int WndProcHook(HWND hWnd, int msg, nint wParam, nint lParam)
|
||||
{
|
||||
var hookResult = ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
|
||||
return hookResult == 0 ? CallWindowProc(_wndproc, hWnd, msg, wParam, lParam) : hookResult;
|
||||
}
|
||||
|
||||
[DllImport("USER32.dll", ExactSpelling = true, EntryPoint = "CallWindowProcW")]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
private static extern int CallWindowProc(nint lpPrevWndFunc, HWND hWnd, int msg, nint wParam, nint lParam);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_deviceContext.Dispose();
|
||||
_renderHandler.Dispose();
|
||||
}
|
||||
}
|
240
CringeLauncher/Launcher.cs
Normal file
240
CringeLauncher/Launcher.cs
Normal file
@ -0,0 +1,240 @@
|
||||
using System.Reflection;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using CringeLauncher.Utils;
|
||||
using CringePlugins.Loader;
|
||||
using CringePlugins.Splash;
|
||||
using HarmonyLib;
|
||||
using NLog;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Platform.VideoMode;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game;
|
||||
using SpaceEngineers.Game;
|
||||
using SpaceEngineers.Game.Achievements;
|
||||
using SpaceEngineers.Game.GUI;
|
||||
using Velopack;
|
||||
using VRage;
|
||||
using VRage.Audio;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.Localization;
|
||||
using VRage.GameServices;
|
||||
using VRage.Mod.Io;
|
||||
using VRage.Platform.Windows;
|
||||
using VRage.Steam;
|
||||
using VRage.UserInterface;
|
||||
using VRageRender;
|
||||
using VRageRender.ExternalApp;
|
||||
|
||||
namespace CringeLauncher;
|
||||
|
||||
public class Launcher : ICorePlugin
|
||||
{
|
||||
private const uint AppId = 244850U;
|
||||
private SpaceEngineersGame? _game;
|
||||
private readonly Harmony _harmony = new("CringeBootstrap");
|
||||
private PluginsLifetime? _lifetime;
|
||||
|
||||
private MyGameRenderComponent? _renderComponent;
|
||||
|
||||
public void Initialize(string[] args)
|
||||
{
|
||||
LogManager.Setup()
|
||||
.SetupExtensions(s =>
|
||||
{
|
||||
s.RegisterLayoutRenderer("cringe-exception", e =>
|
||||
{
|
||||
if (e.Exception is null) return string.Empty;
|
||||
e.Exception.FormatStackTrace();
|
||||
return e.Exception.ToString();
|
||||
});
|
||||
});
|
||||
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
|
||||
//environment variable for viktor's plugins
|
||||
Environment.SetEnvironmentVariable("SE_PLUGIN_DISABLE_METHOD_VERIFICATION", "True");
|
||||
|
||||
_harmony.PatchAll(typeof(Launcher).Assembly);
|
||||
|
||||
#if !DEBUG
|
||||
CheckUpdates().GetAwaiter().GetResult();
|
||||
#endif
|
||||
|
||||
var splash = new Splash();
|
||||
|
||||
splash.DefineStage(_lifetime = new PluginsLifetime());
|
||||
|
||||
InitTexts();
|
||||
SpaceEngineersGame.SetupBasicGameInfo();
|
||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion.GetValueOrDefault();
|
||||
MyShaderCompiler.Init(MyShaderCompiler.TargetPlatform.PC, false);
|
||||
MyVRageWindows.Init(MyPerGameSettings.BasicGameInfo.ApplicationName, MySandboxGame.Log,
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
MyPerGameSettings.BasicGameInfo.ApplicationName),
|
||||
false, false);
|
||||
|
||||
MyPlatformGameSettings.SAVE_TO_CLOUD_OPTION_AVAILABLE = true;
|
||||
MyXAudio2.DEVICE_DETAILS_SUPPORTED = false;
|
||||
|
||||
if (MyVRage.Platform.System.SimulationQuality == SimulationQuality.Normal)
|
||||
{
|
||||
MyPlatformGameSettings.SIMPLIFIED_SIMULATION_OVERRIDE = false;
|
||||
}
|
||||
|
||||
MyFileSystem.ExePath = Path.GetDirectoryName(args.ElementAtOrDefault(0) ?? Assembly.GetExecutingAssembly().Location)!;
|
||||
MyFileSystem.RootPath = new DirectoryInfo(MyFileSystem.ExePath).Parent!.FullName;
|
||||
MyInitializer.InvokeBeforeRun(AppId, MyPerGameSettings.BasicGameInfo.ApplicationName,
|
||||
MyVRage.Platform.System.GetRootPath(), MyVRage.Platform.System.GetAppDataPath(),true, 3, () =>
|
||||
{
|
||||
MyFakes.VOICE_CHAT_MIC_SENSITIVITY = MySandboxGame.Config.MicSensitivity;
|
||||
MyPlatformGameSettings.VOICE_CHAT_AUTOMATIC_ACTIVATION = MySandboxGame.Config.AutomaticVoiceChatActivation;
|
||||
});
|
||||
MyVRage.Platform.Init();
|
||||
SpaceEngineersGame.SetupPerGameSettings();
|
||||
ConfigureSettings();
|
||||
InitThreadPool();
|
||||
MyVRage.Platform.System.OnThreadpoolInitialized();
|
||||
InitRender();
|
||||
|
||||
_renderComponent = new();
|
||||
_renderComponent.Start(new(), InitEarlyWindow, MyVideoSettingsManager.Initialize(), MyPerGameSettings.MaxFrameRate);
|
||||
_renderComponent.RenderThread.BeforeDraw += MyFpsManager.Update;
|
||||
_renderComponent.RenderThread.SizeChanged += RenderThreadOnSizeChanged;
|
||||
|
||||
splash.ExecuteLoadingStages();
|
||||
|
||||
InitUgc();
|
||||
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
|
||||
_game = new(args)
|
||||
{
|
||||
GameRenderComponent = _renderComponent,
|
||||
DrawThread = _renderComponent.RenderThread.SystemThread,
|
||||
form = MyVRage.Platform.Windows.Window
|
||||
};
|
||||
|
||||
_renderComponent.RenderThread.SizeChanged += _game.RenderThread_SizeChanged;
|
||||
_renderComponent.RenderThread.UpdateSize();
|
||||
}
|
||||
|
||||
private void RenderThreadOnSizeChanged(int width, int height, MyViewport viewport)
|
||||
{
|
||||
if (_renderComponent is not null)
|
||||
_renderComponent.RenderThread.SizeChanged -= RenderThreadOnSizeChanged;
|
||||
|
||||
MyVRage.Platform.Windows.Window.ShowAndFocus();
|
||||
}
|
||||
|
||||
public void Run() => _game?.Run();
|
||||
|
||||
private static IVRageWindow InitEarlyWindow()
|
||||
{
|
||||
MyVRage.Platform.Windows.CreateWindow("Cringe Launcher", MyPerGameSettings.GameIcon, null);
|
||||
|
||||
MyVRage.Platform.Windows.Window.OnExit += MySandboxGame.ExitThreadSafe;
|
||||
|
||||
return MyVRage.Platform.Windows.Window;
|
||||
}
|
||||
|
||||
private async Task CheckUpdates()
|
||||
{
|
||||
var mgr = new UpdateManager("https://dl.zznty.ru/CringeLauncher/");
|
||||
|
||||
// check for new version
|
||||
var newVersion = await mgr.CheckForUpdatesAsync();
|
||||
if (newVersion == null)
|
||||
return; // no update available
|
||||
|
||||
// download new version
|
||||
await mgr.DownloadUpdatesAsync(newVersion);
|
||||
|
||||
// install new version and restart app
|
||||
mgr.ApplyUpdatesAndRestart(newVersion);
|
||||
}
|
||||
|
||||
#region Keen shit
|
||||
|
||||
private static void InitThreadPool()
|
||||
{
|
||||
ParallelTasks.Parallel.Scheduler = new ThreadPoolScheduler();
|
||||
}
|
||||
|
||||
private static void ConfigureSettings()
|
||||
{
|
||||
MyPlatformGameSettings.ENABLE_LOGOS = false;
|
||||
}
|
||||
|
||||
private static void InitTexts()
|
||||
{
|
||||
var textsPath = Path.Combine(MyFileSystem.RootPath, @"Content\Data\Localization\CoreTexts");
|
||||
var hashSet = new HashSet<MyLanguagesEnum>();
|
||||
MyTexts.LoadSupportedLanguages(textsPath, hashSet);
|
||||
|
||||
if (!MyTexts.Languages.TryGetValue(MyLanguage.Instance.GetOsLanguageCurrentOfficial(), out var description) &&
|
||||
!MyTexts.Languages.TryGetValue(MyLanguagesEnum.English, out description))
|
||||
return;
|
||||
|
||||
MyTexts.LoadTexts(textsPath, description.CultureName, description.SubcultureName);
|
||||
}
|
||||
|
||||
public static void InitUgc()
|
||||
{
|
||||
var steamGameService = MySteamGameService.Create(false, AppId);
|
||||
MyServiceManager.Instance.AddService(steamGameService);
|
||||
|
||||
var aggregator = new MyServerDiscoveryAggregator();
|
||||
MySteamGameService.InitNetworking(false, steamGameService, MyPerGameSettings.GameName, aggregator);
|
||||
// MyEOSService.InitNetworking(false, false, MyPerGameSettings.GameName, steamGameService, "xyza7891964JhtVD93nm3nZp8t1MbnhC",
|
||||
// "AKGM16qoFtct0IIIA8RCqEIYG4d4gXPPDNpzGuvlhLA", "24b1cd652a18461fa9b3d533ac8d6b5b",
|
||||
// "1958fe26c66d4151a327ec162e4d49c8", "07c169b3b641401496d352cad1c905d6",
|
||||
// "https://retail.epicgames.com/", MyEOSService.CreatePlatform(),
|
||||
// MyPlatformGameSettings.VERBOSE_NETWORK_LOGGING, ArraySegment<string>.Empty, aggregator,
|
||||
// MyMultiplayer.Channels);
|
||||
// EOS networking is disabled due to memory leak, waiting for update with EOSSDK >= 1.15.4
|
||||
|
||||
MyServiceManager.Instance.AddService<IMyServerDiscovery>(aggregator);
|
||||
|
||||
MyServiceManager.Instance.AddService(MySteamGameService.CreateMicrophone());
|
||||
|
||||
MyGameService.WorkshopService.AddAggregate(MySteamUgcService.Create(AppId, steamGameService));
|
||||
|
||||
var modUgc = MyModIoService.Create(MyServiceManager.Instance.GetService<IMyGameService>(), "spaceengineers",
|
||||
"264",
|
||||
"1fb4489996a5e8ffc6ec1135f9985b5b", "331", "f2b64abe55452252b030c48adc0c1f0e",
|
||||
MyPlatformGameSettings.UGC_TEST_ENVIRONMENT, false, MyPlatformGameSettings.MODIO_PLATFORM,
|
||||
MyPlatformGameSettings.MODIO_PORTAL);
|
||||
modUgc.IsConsentGiven = MySandboxGame.Config.ModIoConsent;
|
||||
MyGameService.WorkshopService.AddAggregate(modUgc);
|
||||
|
||||
MySpaceEngineersAchievements.Initialize();
|
||||
}
|
||||
|
||||
private static void InitRender()
|
||||
{
|
||||
var renderQualityHint = MyVRage.Platform.Render.GetRenderQualityHint();
|
||||
var preset = MyGuiScreenOptionsGraphics.GetPreset(renderQualityHint);
|
||||
|
||||
MyRenderProxy.Settings.User = MyVideoSettingsManager
|
||||
.GetGraphicsSettingsFromConfig(ref preset, renderQualityHint > MyRenderPresetEnum.CUSTOM)
|
||||
.PerformanceSettings.RenderSettings;
|
||||
MyRenderProxy.Settings.EnableAnsel = MyPlatformGameSettings.ENABLE_ANSEL;
|
||||
MyRenderProxy.Settings.EnableAnselWithSprites = MyPlatformGameSettings.ENABLE_ANSEL_WITH_SPRITES;
|
||||
|
||||
var graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
|
||||
MySandboxGame.Config.GraphicsRenderer = graphicsRenderer;
|
||||
|
||||
_ = new MyEngine();
|
||||
MyRenderProxy.Initialize(new MyDX11Render(MyRenderProxy.Settings));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_game?.Dispose();
|
||||
MyGameService.ShutDown();
|
||||
MyInitializer.InvokeAfterRun();
|
||||
MyVRage.Done();
|
||||
}
|
||||
}
|
7
CringeLauncher/Loader/ModAssemblyLoadContext.cs
Normal file
7
CringeLauncher/Loader/ModAssemblyLoadContext.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using CringeBootstrap.Abstractions;
|
||||
using SharedCringe.Loader;
|
||||
|
||||
namespace CringeLauncher.Loader;
|
||||
|
||||
public class ModAssemblyLoadContext(ICoreLoadContext parentContext)
|
||||
: DerivedAssemblyLoadContext(parentContext, "World Mods Context");
|
2
CringeLauncher/NativeMethods.txt
Normal file
2
CringeLauncher/NativeMethods.txt
Normal file
@ -0,0 +1,2 @@
|
||||
GetWindowLongPtr
|
||||
SetWindowLongPtr
|
19
CringeLauncher/Patches/DarkTardMissingNamespacePatch.cs
Normal file
19
CringeLauncher/Patches/DarkTardMissingNamespacePatch.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using HarmonyLib;
|
||||
using Sandbox.Game.World;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(MyScriptManager), nameof(MyScriptManager.Init))]
|
||||
public static class DarkTardMissingNamespacePatch
|
||||
{
|
||||
private static void Prefix(Dictionary<string, string> ___m_compatibilityChanges)
|
||||
{
|
||||
___m_compatibilityChanges["using System.Runtime.Remoting.Metadata.W3cXsd2001;"] = "";
|
||||
___m_compatibilityChanges["using System.IO.Ports;"] = "";
|
||||
___m_compatibilityChanges["using System.Runtime.Remoting;"] = "";
|
||||
___m_compatibilityChanges["using System.Runtime.Remoting.Messaging;"] = "";
|
||||
___m_compatibilityChanges["using System.Numerics;"] = "";
|
||||
___m_compatibilityChanges["using System.Runtime.Remoting.Lifetime;"] = "";
|
||||
___m_compatibilityChanges["using System.Net.Configuration;"] = "";
|
||||
}
|
||||
}
|
30
CringeLauncher/Patches/EosInitPatch.cs
Normal file
30
CringeLauncher/Patches/EosInitPatch.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
// eos is disabled
|
||||
// [HarmonyPatch]
|
||||
public class EosInitPatch
|
||||
{
|
||||
private static MethodInfo TargetMethod() =>
|
||||
AccessTools.Method(Type.GetType("VRage.EOS.MyEOSNetworking, VRage.EOS", true), "Init");
|
||||
|
||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
var ins = instructions.ToList();
|
||||
|
||||
var stIndex = ins.FindIndex(b => b.opcode == OpCodes.Stloc_1);
|
||||
|
||||
ins.InsertRange(stIndex, new []
|
||||
{
|
||||
new CodeInstruction(OpCodes.Dup),
|
||||
new(OpCodes.Ldc_I4_2), // PlatformFlags.DisableOverlay
|
||||
new(OpCodes.Conv_I8),
|
||||
CodeInstruction.Call("Epic.OnlineServices.Platform.Options:set_Flags"),
|
||||
});
|
||||
|
||||
return ins;
|
||||
}
|
||||
}
|
122
CringeLauncher/Patches/IntrospectionPatches.cs
Normal file
122
CringeLauncher/Patches/IntrospectionPatches.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Xml.Serialization;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using CringePlugins.Utils;
|
||||
using HarmonyLib;
|
||||
using Sandbox.Game.World;
|
||||
using VRage;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.Common;
|
||||
using VRage.Game.Components;
|
||||
using VRage.Game.Definitions;
|
||||
using VRage.Game.Entity.UseObject;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.ObjectBuilders.Private;
|
||||
using VRage.Plugins;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class IntrospectionPatches
|
||||
{
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(Assembly), nameof(Assembly.GetTypes))]
|
||||
private static bool GetTypesPrefix(Assembly __instance, ref Type[] __result)
|
||||
{
|
||||
if (AssemblyLoadContext.GetLoadContext(__instance) is ICoreLoadContext || __instance.FullName?.StartsWith("System.") == true)
|
||||
return true;
|
||||
|
||||
Debug.WriteLine($"Blocking GetTypes for {__instance.FullName}");
|
||||
|
||||
__result = [];
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(AccessTools), nameof(AccessTools.GetTypesFromAssembly))]
|
||||
private static bool GetTypesHarmonyPrefix(Assembly assembly, ref Type[] __result)
|
||||
{
|
||||
if (AssemblyLoadContext.GetLoadContext(assembly) is ICoreLoadContext)
|
||||
return true;
|
||||
|
||||
__result = IntrospectionContext.Global.CollectAttributedTypes<HarmonyAttribute>(assembly.GetMainModule())
|
||||
.ToArray();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MySession), "PrepareBaseSession", typeof(MyObjectBuilder_Checkpoint), typeof(MyObjectBuilder_Sector))]
|
||||
private static void PrepareSessionPrefix()
|
||||
{
|
||||
// i hate keen for that in MyUseObjectFactory..cctor
|
||||
// MyUseObjectFactory.RegisterAssemblyTypes(Assembly.LoadFrom(Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll")));
|
||||
|
||||
MyUseObjectFactory.RegisterAssemblyTypes(MyPlugins.SandboxGameAssembly);
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MyPlugins), nameof(MyPlugins.LoadPlugins))]
|
||||
private static bool LoadPluginsPrefix(List<Assembly> assemblies, List<IPlugin> ___m_plugins, List<IHandleInputPlugin> ___m_handleInputPlugins)
|
||||
{
|
||||
foreach (var type in assemblies.SelectMany(b => IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(b.GetMainModule())))
|
||||
{
|
||||
var instance = Activator.CreateInstance(type);
|
||||
|
||||
if (instance is IPlugin plugin)
|
||||
___m_plugins.Add(plugin);
|
||||
|
||||
if (instance is IHandleInputPlugin handleInputPlugin)
|
||||
___m_handleInputPlugins.Add(handleInputPlugin);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MyXmlSerializerManager), "TryLoadSerializerFrom")]
|
||||
private static bool LoadSerializerPrefix(string assemblyName, string typeName, ref XmlSerializer? __result)
|
||||
{
|
||||
if (AssemblyLoadContext.GetLoadContext(typeof(IntrospectionPatches).Assembly) is not ICoreLoadContext context)
|
||||
return false;
|
||||
|
||||
var assembly = context.ResolveFromAssemblyName(new(assemblyName));
|
||||
|
||||
if (assembly?.GetType($"Microsoft.Xml.Serialization.GeneratedAssembly.{typeName}Serializer") is not { } type)
|
||||
return false;
|
||||
|
||||
__result = Activator.CreateInstance(type) as XmlSerializer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MyXmlSerializerManager), nameof(MyXmlSerializerManager.RegisterFromAssembly))]
|
||||
private static bool XmlManagerRegisterPrefix(Assembly assembly) => AssemblyLoadContext.GetLoadContext(assembly) is ICoreLoadContext;
|
||||
|
||||
[HarmonyPatch]
|
||||
private static class GameAssembliesPatch
|
||||
{
|
||||
private static IEnumerable<MethodInfo> TargetMethods()
|
||||
{
|
||||
return AccessTools.GetDeclaredMethods(typeof(MyPlugins))
|
||||
.Where(b => b.Name.StartsWith("Register") &&
|
||||
b.GetParameters() is [var param] && param.ParameterType == typeof(string));
|
||||
}
|
||||
|
||||
private static bool Prefix([HarmonyArgument(0)] string file)
|
||||
{
|
||||
var name = AssemblyName.GetAssemblyName(Path.Join(MyFileSystem.ExePath, file));
|
||||
|
||||
ref var staticAssembly = ref AccessTools.StaticFieldRefAccess<Assembly>(typeof(MyPlugins), name.Name switch
|
||||
{
|
||||
"Sandbox.Common" => "m_sandboxAssembly",
|
||||
"Sandbox.Game" => "m_sandboxGameAssembly",
|
||||
"SpaceEngineers.Game" => "m_gamePluginAssembly",
|
||||
"SpaceEngineers.ObjectBuilders" => "m_gameObjBuildersPlugin",
|
||||
_ => throw new ArgumentOutOfRangeException(null, $"Unknown assembly name: {name}")
|
||||
});
|
||||
|
||||
staticAssembly = AssemblyLoadContext.GetLoadContext(typeof(GameAssembliesPatch).Assembly)!
|
||||
.LoadFromAssemblyName(name);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
70
CringeLauncher/Patches/ModAssemblyLoadContextPatches.cs
Normal file
70
CringeLauncher/Patches/ModAssemblyLoadContextPatches.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Loader;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using CringeLauncher.Loader;
|
||||
using HarmonyLib;
|
||||
using Sandbox.Game.World;
|
||||
using VRage.Scripting;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class ModAssemblyLoadContextPatches
|
||||
{
|
||||
private static ModAssemblyLoadContext? _currentSessionContext;
|
||||
|
||||
[HarmonyPatch(typeof(MyScriptCompiler), nameof(MyScriptCompiler.Compile), MethodType.Async)]
|
||||
[HarmonyTranspiler]
|
||||
private static IEnumerable<CodeInstruction> CompilerTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
|
||||
{
|
||||
var matcher = new CodeMatcher(instructions);
|
||||
|
||||
var load1Method = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.Load), [typeof(byte[]), typeof(byte[])]);
|
||||
var load2Method = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.Load), [typeof(byte[])]);
|
||||
|
||||
matcher.SearchForward(i => i.Calls(load1Method))
|
||||
.InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target"))
|
||||
.SetInstruction(CodeInstruction.CallClosure((byte[] assembly, byte[] symbols, MyApiTarget target) =>
|
||||
{
|
||||
if (target is not MyApiTarget.Mod) return Assembly.Load(assembly, symbols);
|
||||
ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context");
|
||||
return _currentSessionContext.LoadFromStream(new MemoryStream(assembly), new MemoryStream(symbols));
|
||||
}));
|
||||
|
||||
matcher.SearchForward(i => i.Calls(load2Method))
|
||||
.InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target"))
|
||||
.SetInstruction(CodeInstruction.CallClosure((byte[] assembly, MyApiTarget target) =>
|
||||
{
|
||||
if (target is not MyApiTarget.Mod) return Assembly.Load(assembly);
|
||||
ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context");
|
||||
return _currentSessionContext.LoadFromStream(new MemoryStream(assembly));
|
||||
}));
|
||||
|
||||
return matcher.Instructions();
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MySession), nameof(MySession.Load))]
|
||||
[HarmonyPatch(typeof(MySession), "LoadMultiplayer")]
|
||||
[HarmonyPrefix]
|
||||
private static void LoadPrefix()
|
||||
{
|
||||
if (_currentSessionContext is not null)
|
||||
throw new InvalidOperationException("Previous session context was not disposed");
|
||||
|
||||
if (AssemblyLoadContext.GetLoadContext(typeof(MySession).Assembly) is not ICoreLoadContext coreContext)
|
||||
throw new NotSupportedException("Mod loading is not supported in this context");
|
||||
|
||||
_currentSessionContext = new ModAssemblyLoadContext(coreContext);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MySession), nameof(MySession.Unload))]
|
||||
[HarmonyPostfix]
|
||||
private static void UnloadPostfix()
|
||||
{
|
||||
if (_currentSessionContext is null) return;
|
||||
|
||||
_currentSessionContext.Unload();
|
||||
_currentSessionContext = null;
|
||||
}
|
||||
}
|
309
CringeLauncher/Patches/ModScriptCompilerPatch.cs
Normal file
309
CringeLauncher/Patches/ModScriptCompilerPatch.cs
Normal file
@ -0,0 +1,309 @@
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
#if false
|
||||
[HarmonyPatch]
|
||||
public static class ModScriptCompilerPatch
|
||||
{
|
||||
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
|
||||
private static AssemblyLoadContext _modContext = new(null, true);
|
||||
private static readonly HashSet<string> LoadedModAssemblyNames = new();
|
||||
|
||||
private static readonly ConditionalWeakTable<MyProgrammableBlock, AssemblyLoadContext> LoadContexts = new();
|
||||
|
||||
private static readonly FieldInfo InstanceField = AccessTools.Field(typeof(MyProgrammableBlock), "m_instance");
|
||||
private static readonly FieldInfo AssemblyField = AccessTools.Field(typeof(MyProgrammableBlock), "m_assembly");
|
||||
private static readonly FieldInfo CompilerErrorsField = AccessTools.Field(typeof(MyProgrammableBlock), "m_compilerErrors");
|
||||
|
||||
static ModScriptCompilerPatch()
|
||||
{
|
||||
MySession.OnUnloaded += OnUnloaded;
|
||||
|
||||
ModWhitelistAnalyzer =
|
||||
AccessTools.FieldRefAccess<MyScriptCompiler, DiagnosticAnalyzer>(
|
||||
MyScriptCompiler.Static, "m_modApiWhitelistDiagnosticAnalyzer");
|
||||
ScriptWhitelistAnalyzer =
|
||||
AccessTools.FieldRefAccess<MyScriptCompiler, DiagnosticAnalyzer>(
|
||||
MyScriptCompiler.Static, "m_ingameWhitelistDiagnosticAnalyzer");
|
||||
|
||||
MetadataReferences =
|
||||
AccessTools.FieldRefAccess<MyScriptCompiler, List<MetadataReference>>(
|
||||
MyScriptCompiler.Static, "m_metadataReferences");
|
||||
|
||||
InjectMod = AccessTools.MethodDelegate<Func<CSharpCompilation, SyntaxTree, int, SyntaxTree>>(
|
||||
AccessTools.Method(typeof(MyScriptCompiler), "InjectMod"), MyScriptCompiler.Static);
|
||||
|
||||
InjectInstructionCounter = AccessTools.MethodDelegate<Func<CSharpCompilation, SyntaxTree, SyntaxTree>>(
|
||||
AccessTools.Method(typeof(MyScriptCompiler), "InjectInstructionCounter"), MyScriptCompiler.Static);
|
||||
|
||||
EmitDiagnostics = AccessTools.MethodDelegate<Func<CompilationWithAnalyzers, EmitResult, List<Message>, bool, Task<bool>>>(
|
||||
AccessTools.Method(typeof(MyScriptCompiler), "EmitDiagnostics"), MyScriptCompiler.Static);
|
||||
|
||||
MakeAssemblyName =
|
||||
AccessTools.MethodDelegate<Func<string, string>>(AccessTools.Method(typeof(MyScriptCompiler),
|
||||
"MakeAssemblyName"));
|
||||
|
||||
CreateInstanceMethod = AccessTools.Method(typeof(MyProgrammableBlock), "CreateInstance");
|
||||
SetDetailedInfoMethod = AccessTools.Method(typeof(MyProgrammableBlock), "SetDetailedInfo");
|
||||
}
|
||||
|
||||
private static void OnUnloaded()
|
||||
{
|
||||
LoadedModAssemblyNames.Clear();
|
||||
if (!_modContext.Assemblies.Any())
|
||||
return;
|
||||
|
||||
_modContext.Unload();
|
||||
_modContext = new(null, true);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MyProgrammableBlock), "Compile")]
|
||||
[HarmonyPrefix]
|
||||
private static bool CompilePrefix(MyProgrammableBlock __instance, string program, string storage, bool instantiate,
|
||||
ref MyProgrammableBlock.ScriptTerminationReason ___m_terminationReason,
|
||||
MyIngameScriptComponent ___ScriptComponent)
|
||||
{
|
||||
if (!MySession.Static.EnableIngameScripts || __instance.CubeGrid is {IsPreview: true} or {CreatePhysics: false})
|
||||
return false;
|
||||
|
||||
___m_terminationReason = MyProgrammableBlock.ScriptTerminationReason.None;
|
||||
CompileAsync(__instance, program, storage, instantiate, ___ScriptComponent);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MyGuiScreenEditor), "CheckCodeButtonClicked")]
|
||||
[HarmonyPrefix]
|
||||
private static bool GuiCompilePrefix(List<string> ___m_compilerErrors, MyGuiScreenEditor __instance)
|
||||
{
|
||||
___m_compilerErrors.Clear();
|
||||
|
||||
var progress = new MyGuiScreenProgress(MyTexts.Get(MySpaceTexts.ProgrammableBlock_Editor_CheckingCode));
|
||||
MyScreenManager.AddScreen(progress);
|
||||
|
||||
if (__instance.Description.Text.Length > 0)
|
||||
CompileAsync(__instance, ___m_compilerErrors, __instance.Description.Text.ToString(), progress).Wait();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MyScriptCompiler), nameof(MyScriptCompiler.Compile))]
|
||||
[HarmonyPrefix]
|
||||
private static bool Prefix(ref Task<Assembly?> __result, MyApiTarget target, string assemblyName, IEnumerable<Script> scripts,
|
||||
List<Message> messages, string friendlyName, bool enableDebugInformation = false)
|
||||
{
|
||||
__result = CompileAsync(_modContext, target, assemblyName, scripts, messages, friendlyName,
|
||||
enableDebugInformation);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task CompileAsync(MyGuiScreenEditor editor, List<string> errors, string program, MyGuiScreenProgress progress)
|
||||
{
|
||||
var context = new AssemblyLoadContext(null, true);
|
||||
var messages = new List<Message>();
|
||||
var script = MyVRage.Platform.Scripting.GetIngameScript(program, "Program", nameof(MyGridProgram));
|
||||
await CompileAsync(context, MyApiTarget.Ingame, "check", new[] { script }, messages,
|
||||
"PB Code Editor");
|
||||
|
||||
errors.AddRange(messages.OrderBy(b => b.IsError ? 0 : 1).Select(b => b.Text));
|
||||
context.Unload();
|
||||
|
||||
progress.CloseScreen();
|
||||
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
var sb = new StringBuilder(errors.Sum(b => b.Length + Environment.NewLine.Length));
|
||||
foreach (var error in errors)
|
||||
{
|
||||
sb.AppendLine(error);
|
||||
}
|
||||
|
||||
MyScreenManager.AddScreen(new MyGuiScreenEditorError(sb.ToString()));
|
||||
return;
|
||||
}
|
||||
|
||||
var messageBox = MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Info, MyMessageBoxButtonsType.OK,
|
||||
MyTexts.Get(MySpaceTexts.ProgrammableBlock_Editor_CompilationOk),
|
||||
MyTexts.Get(MySpaceTexts.ProgrammableBlock_CodeEditor_Title));
|
||||
MyGuiSandbox.AddScreen(messageBox);
|
||||
}
|
||||
|
||||
private static async void CompileAsync(MyProgrammableBlock block,
|
||||
string program,
|
||||
string storage,
|
||||
bool instantiate, MyIngameScriptComponent scriptComponent)
|
||||
{
|
||||
scriptComponent.NextUpdate = UpdateType.None;
|
||||
scriptComponent.NeedsUpdate = MyEntityUpdateEnum.NONE;
|
||||
|
||||
SetDetailedInfoMethod.Invoke(block, new object?[] { "Compiling..." });
|
||||
|
||||
try
|
||||
{
|
||||
if (LoadContexts.TryGetValue(block, out var context))
|
||||
{
|
||||
AccessTools.FieldRefAccess<MyProgrammableBlock, IMyGridProgram?>(block, InstanceField) = null;
|
||||
AccessTools.FieldRefAccess<MyProgrammableBlock, Assembly?>(block, AssemblyField) = null;
|
||||
context.Unload();
|
||||
}
|
||||
|
||||
LoadContexts.AddOrUpdate(block, context = new(null, true));
|
||||
|
||||
var messages = new List<Message>();
|
||||
var assembly = await CompileAsync(context, MyApiTarget.Ingame,
|
||||
$"pb_{block.EntityId}_{Random.Shared.NextInt64()}",
|
||||
new[]
|
||||
{
|
||||
MyVRage.Platform.Scripting.GetIngameScript(
|
||||
program, "Program", nameof(MyGridProgram))
|
||||
}, messages, $"PB: {block.DisplayName} ({block.EntityId})");
|
||||
|
||||
AccessTools.FieldRefAccess<MyProgrammableBlock, Assembly?>(block, AssemblyField) = assembly;
|
||||
|
||||
var errors = AccessTools.FieldRefAccess<MyProgrammableBlock, List<string>>(block, CompilerErrorsField);
|
||||
|
||||
errors.Clear();
|
||||
errors.AddRange(messages.Select(b => b.Text));
|
||||
|
||||
if (instantiate)
|
||||
MySandboxGame.Static.Invoke(
|
||||
() => CreateInstanceMethod.Invoke(block, new object?[] { assembly, errors, storage }),
|
||||
nameof(CompileAsync));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
SetDetailedInfoMethod.Invoke(block, new object?[] { e.ToString() });
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Assembly?> CompileAsync(AssemblyLoadContext context, MyApiTarget target,
|
||||
string assemblyName, IEnumerable<Script> scripts,
|
||||
List<Message> messages, string? friendlyName,
|
||||
bool enableDebugInformation = false)
|
||||
{
|
||||
friendlyName ??= "<No Name>";
|
||||
var assemblyFileName = MakeAssemblyName(assemblyName);
|
||||
Func<CSharpCompilation, SyntaxTree, SyntaxTree>? syntaxTreeInjector;
|
||||
DiagnosticAnalyzer? whitelistAnalyzer;
|
||||
switch (target)
|
||||
{
|
||||
case MyApiTarget.None:
|
||||
whitelistAnalyzer = null;
|
||||
syntaxTreeInjector = null;
|
||||
break;
|
||||
case MyApiTarget.Mod:
|
||||
{
|
||||
var modId = MyModWatchdog.AllocateModId(friendlyName);
|
||||
whitelistAnalyzer = ModWhitelistAnalyzer;
|
||||
syntaxTreeInjector = (c, st) => InjectMod(c, st, modId);
|
||||
|
||||
//skip if name exists already
|
||||
if (!LoadedModAssemblyNames.Add(assemblyFileName))
|
||||
{
|
||||
Console.WriteLine($"{assemblyFileName} is already loaded, skipping");
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MyApiTarget.Ingame:
|
||||
syntaxTreeInjector = InjectInstructionCounter;
|
||||
whitelistAnalyzer = ScriptWhitelistAnalyzer;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(target), target, "Invalid compilation target");
|
||||
}
|
||||
var compilation = CreateCompilation(assemblyFileName, scripts);
|
||||
var compilationWithoutInjection = compilation;
|
||||
var injectionFailed = false;
|
||||
|
||||
if (syntaxTreeInjector != null)
|
||||
{
|
||||
SyntaxTree[]? newSyntaxTrees = null;
|
||||
try
|
||||
{
|
||||
var syntaxTrees = compilation.SyntaxTrees;
|
||||
if (syntaxTrees.Length == 1)
|
||||
{
|
||||
newSyntaxTrees = new[] { syntaxTreeInjector(compilation, syntaxTrees[0]) };
|
||||
}
|
||||
else
|
||||
{
|
||||
var compilation1 = compilation;
|
||||
newSyntaxTrees = await Task
|
||||
.WhenAll(syntaxTrees.Select(
|
||||
x => Task.Run(() => syntaxTreeInjector(compilation1, x)))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warn(e);
|
||||
injectionFailed = true;
|
||||
}
|
||||
|
||||
if (newSyntaxTrees is not null)
|
||||
compilation = compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newSyntaxTrees);
|
||||
|
||||
}
|
||||
CompilationWithAnalyzers? analyticCompilation = null;
|
||||
if (whitelistAnalyzer != null)
|
||||
{
|
||||
analyticCompilation = compilation.WithAnalyzers(ImmutableArray.Create(whitelistAnalyzer));
|
||||
compilation = (CSharpCompilation)analyticCompilation.Compilation;
|
||||
}
|
||||
|
||||
using var assemblyStream = new MemoryStream();
|
||||
|
||||
var emitResult = compilation.Emit(assemblyStream);
|
||||
var success = emitResult.Success;
|
||||
var myBlacklistSyntaxVisitor = new MyBlacklistSyntaxVisitor();
|
||||
foreach (var syntaxTree in compilation.SyntaxTrees)
|
||||
{
|
||||
myBlacklistSyntaxVisitor.SetSemanticModel(compilation.GetSemanticModel(syntaxTree, false));
|
||||
myBlacklistSyntaxVisitor.Visit(await syntaxTree.GetRootAsync());
|
||||
}
|
||||
if (myBlacklistSyntaxVisitor.HasAnyResult())
|
||||
{
|
||||
myBlacklistSyntaxVisitor.GetResultMessages(messages);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = await EmitDiagnostics(analyticCompilation, emitResult, messages, success).ConfigureAwait(false);
|
||||
assemblyStream.Seek(0, SeekOrigin.Begin);
|
||||
if (injectionFailed) return null;
|
||||
if (success)
|
||||
return context.LoadFromStream(assemblyStream);
|
||||
|
||||
await EmitDiagnostics(analyticCompilation, compilationWithoutInjection.Emit(assemblyStream), messages,
|
||||
false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static readonly CSharpCompilationOptions CompilationOptions =
|
||||
new(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, platform: Platform.X64);
|
||||
private static readonly CSharpParseOptions ParseOptions = new(LanguageVersion.CSharp11, DocumentationMode.None);
|
||||
private static readonly DiagnosticAnalyzer ModWhitelistAnalyzer;
|
||||
private static readonly DiagnosticAnalyzer ScriptWhitelistAnalyzer;
|
||||
private static readonly List<MetadataReference> MetadataReferences;
|
||||
private static readonly Func<CSharpCompilation, SyntaxTree, int, SyntaxTree> InjectMod;
|
||||
private static readonly Func<CSharpCompilation, SyntaxTree, SyntaxTree> InjectInstructionCounter;
|
||||
private static readonly Func<CompilationWithAnalyzers, EmitResult, List<Message>, bool, Task<bool>> EmitDiagnostics;
|
||||
private static readonly Func<string, string> MakeAssemblyName;
|
||||
private static readonly MethodInfo CreateInstanceMethod;
|
||||
private static readonly MethodInfo SetDetailedInfoMethod;
|
||||
|
||||
|
||||
private static CSharpCompilation CreateCompilation(string assemblyFile, IEnumerable<Script>? scripts)
|
||||
{
|
||||
if (scripts == null)
|
||||
return CSharpCompilation.Create(assemblyFile, null, MetadataReferences,
|
||||
CompilationOptions);
|
||||
|
||||
|
||||
var parseOptions = ParseOptions.WithPreprocessorSymbols(MyScriptCompiler.Static.ConditionalCompilationSymbols);
|
||||
var enumerable = scripts.Select(s => CSharpSyntaxTree.ParseText(s.Code, parseOptions, s.Name, Encoding.UTF8));
|
||||
|
||||
return CSharpCompilation.Create(assemblyFile, enumerable, MetadataReferences, CompilationOptions);
|
||||
}
|
||||
}
|
||||
#endif
|
22
CringeLauncher/Patches/RenderHookPatch.cs
Normal file
22
CringeLauncher/Patches/RenderHookPatch.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Windows.Win32.Foundation;
|
||||
using HarmonyLib;
|
||||
using SharpDX.DXGI;
|
||||
using VRage.Platform.Windows.Forms;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class RenderHookPatch
|
||||
{
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(SwapChain), nameof(SwapChain.Present))]
|
||||
private static void PresentPrefix()
|
||||
{
|
||||
ImGuiHandler.Instance?.DoRender();
|
||||
}
|
||||
|
||||
[HarmonyPostfix, HarmonyPatch(typeof(MyGameForm), "OnLoad")]
|
||||
private static void LoadPostfix(MyGameForm __instance)
|
||||
{
|
||||
ImGuiHandler.Instance?.HookWindow((HWND)__instance.Handle);
|
||||
}
|
||||
}
|
10
CringeLauncher/Patches/RenderInitPatch.cs
Normal file
10
CringeLauncher/Patches/RenderInitPatch.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using HarmonyLib;
|
||||
using SpaceEngineers.Game;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(SpaceEngineersGame), "InitializeRender")]
|
||||
public static class RenderInitPatch
|
||||
{
|
||||
private static bool Prefix() => false;
|
||||
}
|
30
CringeLauncher/Patches/ScriptCompilerInitializationPatch.cs
Normal file
30
CringeLauncher/Patches/ScriptCompilerInitializationPatch.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using VRage.Scripting;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class ScriptCompilerInitializationPatch
|
||||
{
|
||||
private static MethodInfo TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(Type.GetType("VRage.Scripting.MyVRageScriptingInternal, VRage.Scripting", true),
|
||||
"Initialize");
|
||||
}
|
||||
|
||||
private static bool Prefix(Thread updateThread, Type[] referencedTypes, string[] symbols)
|
||||
{
|
||||
MyModWatchdog.Init(updateThread);
|
||||
MyScriptCompiler.Static.AddImplicitIngameNamespacesFromTypes(referencedTypes);
|
||||
MyScriptCompiler.Static.AddConditionalCompilationSymbols(symbols);
|
||||
|
||||
using var batch = MyScriptCompiler.Static.Whitelist.OpenBatch();
|
||||
batch.AllowTypes(MyWhitelistTarget.ModApi, typeof(ConcurrentQueue<>));
|
||||
batch.AllowNamespaceOfTypes(MyWhitelistTarget.Both, typeof(ImmutableArray));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
85
CringeLauncher/Patches/SwapChainPatch.cs
Normal file
85
CringeLauncher/Patches/SwapChainPatch.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using HarmonyLib;
|
||||
using SharpDX.Direct3D11;
|
||||
using SharpDX.DXGI;
|
||||
using VRage.Platform.Windows.Render;
|
||||
using VRage.Render11.Resources;
|
||||
using VRageRender;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class SwapChainPatch
|
||||
{
|
||||
internal static nint WindowHandle;
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MyPlatformRender), nameof(MyPlatformRender.CreateSwapChain))]
|
||||
private static bool SwapChainPrefix(nint windowHandle)
|
||||
{
|
||||
WindowHandle = windowHandle;
|
||||
MyPlatformRender.DisposeSwapChain();
|
||||
MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain");
|
||||
|
||||
if (MyPlatformRender.m_swapchain != null)
|
||||
return false;
|
||||
|
||||
var chainDescription = new SwapChainDescription
|
||||
{
|
||||
BufferCount = 2,
|
||||
Flags = SwapChainFlags.AllowModeSwitch,
|
||||
IsWindowed = true,
|
||||
ModeDescription = MyPlatformRender.GetCurrentModeDescriptor(MyPlatformRender.m_settings) with
|
||||
{
|
||||
Format = Format.R8G8B8A8_UNorm
|
||||
},
|
||||
SampleDescription = {
|
||||
Count = 1,
|
||||
Quality = 0
|
||||
},
|
||||
OutputHandle = windowHandle,
|
||||
Usage = Usage.ShaderInput | Usage.RenderTargetOutput,
|
||||
SwapEffect = SwapEffect.Discard
|
||||
};
|
||||
|
||||
var factory = MyPlatformRender.GetFactory();
|
||||
try
|
||||
{
|
||||
MyPlatformRender.m_swapchain = new SwapChain(factory, MyPlatformRender.DeviceInstance, chainDescription);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MyPlatformRender.Log.WriteLine("SwapChain factory = " + factory);
|
||||
MyPlatformRender.Log.WriteLine("SwapChain Device = " + MyPlatformRender.DeviceInstance);
|
||||
MyPlatformRender.PrintSwapChainDescriptionToLog(chainDescription);
|
||||
throw;
|
||||
}
|
||||
factory.MakeWindowAssociation(windowHandle, WindowAssociationFlags.IgnoreAll);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix, HarmonyPatch(typeof(MyRender11), nameof(MyRender11.CreateDeviceInternal))]
|
||||
private static void CreateDevicePostfix()
|
||||
{
|
||||
ImGuiHandler.Instance ??= new ImGuiHandler(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
|
||||
}
|
||||
|
||||
[HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))]
|
||||
private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB)
|
||||
{
|
||||
__instance.m_resource = swapChainBB;
|
||||
__instance.m_rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new()
|
||||
{
|
||||
Format = Format.R8G8B8A8_UNorm_SRgb,
|
||||
Dimension = RenderTargetViewDimension.Texture2D,
|
||||
});
|
||||
__instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB);
|
||||
|
||||
ImGuiHandler.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new()
|
||||
{
|
||||
Format = Format.R8G8B8A8_UNorm,
|
||||
Dimension = RenderTargetViewDimension.Texture2D,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
14
CringeLauncher/Patches/TypeTableRegisterPatch.cs
Normal file
14
CringeLauncher/Patches/TypeTableRegisterPatch.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using HarmonyLib;
|
||||
using VRage.Network;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(MyTypeTable), "IsSerializableClass")]
|
||||
public static class TypeTableRegisterPatch
|
||||
{
|
||||
private static void Postfix(Type type, ref bool __result)
|
||||
{
|
||||
if (type == typeof(Delegate) || type == typeof(MulticastDelegate))
|
||||
__result = true;
|
||||
}
|
||||
}
|
20
CringeLauncher/Patches/WhitelistAllowPatch.cs
Normal file
20
CringeLauncher/Patches/WhitelistAllowPatch.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using VRage.Scripting;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class WhitelistAllowPatch
|
||||
{
|
||||
private static MethodInfo TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(AccessTools.Inner(typeof(MyScriptWhitelist), "MyWhitelistBatch"), "AllowMembers");
|
||||
}
|
||||
|
||||
private static void Prefix(ref MemberInfo[] members)
|
||||
{
|
||||
if (members.Any(b => b is null))
|
||||
members = members.Where(b => b is { }).ToArray();
|
||||
}
|
||||
}
|
46
CringeLauncher/Patches/WhitelistPatch.cs
Normal file
46
CringeLauncher/Patches/WhitelistPatch.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel;
|
||||
using System.Text.RegularExpressions;
|
||||
using HarmonyLib;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Scripting;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(MyScriptWhitelist), MethodType.Constructor, typeof(MyScriptCompiler))]
|
||||
public static class WhitelistPatch
|
||||
{
|
||||
private static void Prefix(MyScriptCompiler scriptCompiler)
|
||||
{
|
||||
var baseDir = new FileInfo(typeof(Type).Assembly.Location).DirectoryName!;
|
||||
|
||||
scriptCompiler.AddReferencedAssemblies(
|
||||
typeof(Type).Assembly.Location,
|
||||
typeof(LinkedList<>).Assembly.Location,
|
||||
typeof(Regex).Assembly.Location,
|
||||
typeof(Enumerable).Assembly.Location,
|
||||
typeof(ConcurrentBag<>).Assembly.Location,
|
||||
typeof(ImmutableArray).Assembly.Location,
|
||||
typeof(PropertyChangedEventArgs).Assembly.Location,
|
||||
typeof(TypeConverter).Assembly.Location,
|
||||
typeof(System.Diagnostics.TraceSource).Assembly.Location,
|
||||
typeof(System.Security.Policy.Evidence).Assembly.Location,
|
||||
Path.Combine(baseDir, "System.Xml.ReaderWriter.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "ProtoBuf.Net.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "ProtoBuf.Net.Core.dll"),
|
||||
Path.Combine(baseDir, "netstandard.dll"),
|
||||
Path.Combine(baseDir, "System.Runtime.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "Sandbox.Common.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "Sandbox.Graphics.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.Library.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.Math.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.Game.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.Render.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "VRage.Input.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "SpaceEngineers.ObjectBuilders.dll"),
|
||||
Path.Combine(MyFileSystem.ExePath, "SpaceEngineers.Game.dll"));
|
||||
}
|
||||
}
|
37
CringeLauncher/Patches/WhitelistRegistrationPatch.cs
Normal file
37
CringeLauncher/Patches/WhitelistRegistrationPatch.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using VRage.Scripting;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class WhitelistRegistrationPatch
|
||||
{
|
||||
private static IEnumerable<MethodInfo> TargetMethods()
|
||||
{
|
||||
yield return AccessTools.Method(typeof(MyScriptWhitelist), "Register",
|
||||
new[] { typeof(MyWhitelistTarget), typeof(INamespaceSymbol), typeof(Type) });
|
||||
yield return AccessTools.Method(typeof(MyScriptWhitelist), "Register",
|
||||
new[] { typeof(MyWhitelistTarget), typeof(ITypeSymbol), typeof(Type) });
|
||||
}
|
||||
|
||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
var ins = instructions.ToList();
|
||||
var throwIns = ins.FindAll(b => b.opcode == OpCodes.Throw).Select(b => ins.IndexOf(b));
|
||||
foreach (var index in throwIns)
|
||||
{
|
||||
var i = index;
|
||||
do
|
||||
{
|
||||
ins[i] = new(OpCodes.Nop);
|
||||
} while (ins[--i].opcode.FlowControl != FlowControl.Cond_Branch);
|
||||
|
||||
ins[index] = new(OpCodes.Ret);
|
||||
}
|
||||
|
||||
return ins;
|
||||
}
|
||||
}
|
36
CringeLauncher/Patches/XmlRootWriterPatch.cs
Normal file
36
CringeLauncher/Patches/XmlRootWriterPatch.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection.Emit;
|
||||
using System.Xml;
|
||||
using HarmonyLib;
|
||||
using VRage;
|
||||
|
||||
namespace CringeLauncher.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(CustomRootWriter), "Init")]
|
||||
public class XmlRootWriterPatch
|
||||
{
|
||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
var ins = instructions.ToList();
|
||||
|
||||
var index = ins.FindIndex(b =>
|
||||
b.opcode == OpCodes.Ldstr && b.operand is "xsi:type");
|
||||
ins[index].operand = "xsi";
|
||||
|
||||
ins.InsertRange(index + 1, new[]
|
||||
{
|
||||
new CodeInstruction(OpCodes.Ldstr, "type"),
|
||||
new CodeInstruction(OpCodes.Ldstr, "http://www.w3.org/2001/XMLSchema-instance")
|
||||
});
|
||||
|
||||
var instruction = ins[ins.FindIndex(b => b.opcode == OpCodes.Callvirt)];
|
||||
instruction.operand = AccessTools.Method(typeof(XmlWriter), "WriteAttributeString", new[]
|
||||
{
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string)
|
||||
});
|
||||
|
||||
return ins;
|
||||
}
|
||||
}
|
108
CringeLauncher/Utils/ExceptionFormatter.cs
Normal file
108
CringeLauncher/Utils/ExceptionFormatter.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace CringeLauncher.Utils;
|
||||
|
||||
public static class ExceptionFormatter
|
||||
{
|
||||
private static readonly AccessTools.FieldRef<Exception, string> StackTraceField = AccessTools.FieldRefAccess<Exception, string>("_remoteStackTraceString");
|
||||
|
||||
public static void FormatStackTrace(this Exception exception)
|
||||
{
|
||||
var stackTrace = new StackTrace(exception, true);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var i = 0;
|
||||
while (stackTrace.GetFrame(i++) is { } frame)
|
||||
{
|
||||
var method = frame.GetMethod();
|
||||
if (method is null)
|
||||
continue;
|
||||
|
||||
sb.Append("at ");
|
||||
if (method.DeclaringType is { } declaringType &&
|
||||
AssemblyLoadContext.GetLoadContext(declaringType.Assembly) is { } assemblyLoadContext)
|
||||
sb.Append(assemblyLoadContext).Append("//");
|
||||
|
||||
if (method.IsStatic)
|
||||
sb.Append("static ");
|
||||
|
||||
if (method is MethodInfo methodInfo)
|
||||
sb.Append(methodInfo.ReturnType, false);
|
||||
else
|
||||
sb.Append("new");
|
||||
|
||||
sb.Append(' ');
|
||||
|
||||
if (method.DeclaringType is null)
|
||||
sb.Append("<null>");
|
||||
else
|
||||
sb.Append(method.DeclaringType, true);
|
||||
|
||||
if (method is MethodInfo)
|
||||
{
|
||||
sb.Append('.');
|
||||
sb.Append(method.Name);
|
||||
}
|
||||
|
||||
if (method.ContainsGenericParameters)
|
||||
sb.Append(method.GetGenericArguments(), false);
|
||||
|
||||
sb.Append('(');
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
for (var j = 0; j < parameters.Length; j++)
|
||||
{
|
||||
sb.Append(parameters[j].ParameterType, false);
|
||||
if (!string.IsNullOrEmpty(parameters[j].Name))
|
||||
sb.Append(' ').Append(parameters[j].Name);
|
||||
if (j < parameters.Length - 1)
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Append(')');
|
||||
|
||||
if (frame.GetFileName() is { } fileName)
|
||||
{
|
||||
sb.Append(" in ").Append(fileName).Append('(').Append(frame.GetFileLineNumber()).Append(':').Append(frame.GetFileColumnNumber()).Append(')');
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
ref var stackTraceString = ref StackTraceField(exception);
|
||||
|
||||
stackTraceString = sb.ToString();
|
||||
}
|
||||
|
||||
private static StringBuilder Append(this StringBuilder sb, Type type, bool fullName = false)
|
||||
{
|
||||
if (fullName && !string.IsNullOrEmpty(type.Namespace))
|
||||
sb.Append(type.Namespace).Append('.');
|
||||
sb.Append(type.Name);
|
||||
if (type.ContainsGenericParameters)
|
||||
sb.Append(type.GetGenericArguments(), fullName);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static StringBuilder Append(this StringBuilder sb, Type[] genericArguments, bool fullName)
|
||||
{
|
||||
sb.Append('<');
|
||||
for (var i = 0; i < genericArguments.Length; i++)
|
||||
{
|
||||
sb.Append(genericArguments[i], fullName);
|
||||
if (i < genericArguments.Length - 1)
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Append('>');
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
19
CringeLauncher/Utils/MethodTools.cs
Normal file
19
CringeLauncher/Utils/MethodTools.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace CringeLauncher.Utils;
|
||||
|
||||
public static class MethodTools
|
||||
{
|
||||
public static MethodInfo AsyncMethodBody(MethodInfo method)
|
||||
{
|
||||
var (_, operand) = PatchProcessor.ReadMethodBody(method).First();
|
||||
|
||||
if (operand is not LocalVariableInfo localVar)
|
||||
throw new InvalidOperationException($"Method {method.FullDescription()} does not contain a valid async state machine");
|
||||
|
||||
return AccessTools.Method(localVar.LocalType, "MoveNext") ??
|
||||
throw new InvalidOperationException(
|
||||
$"Async State machine of method {method.FullDescription()} does not contain a valid MoveNext method");
|
||||
}
|
||||
}
|
85
CringeLauncher/Utils/ThreadPoolScheduler.cs
Normal file
85
CringeLauncher/Utils/ThreadPoolScheduler.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using Havok;
|
||||
using ParallelTasks;
|
||||
using CringeTask = ParallelTasks.Task;
|
||||
|
||||
namespace CringeLauncher.Utils;
|
||||
|
||||
public class ThreadPoolScheduler : IWorkScheduler
|
||||
{
|
||||
public void Schedule(CringeTask item)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(new ThreadPoolWorkItemTask(item), false);
|
||||
}
|
||||
|
||||
public bool WaitForTasksToFinish(TimeSpan waitTimeout) => true;
|
||||
|
||||
public void ScheduleOnEachWorker(Action action)
|
||||
{
|
||||
}
|
||||
|
||||
public int ReadAndClearExecutionTime() => 0;
|
||||
|
||||
public void SuspendThreads(TimeSpan waitTimeout)
|
||||
{
|
||||
}
|
||||
|
||||
public void ResumeThreads()
|
||||
{
|
||||
}
|
||||
|
||||
public int ThreadCount { get; } = ThreadPool.ThreadCount;
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class KeenThreadingPatch
|
||||
{
|
||||
private static MethodBase TargetMethod()
|
||||
{
|
||||
var type = Type.GetType(
|
||||
"VRage.Render11.Resources.Textures.MyTextureStreamingManager+MyStreamedTextureBase, VRage.Render11");
|
||||
|
||||
return AccessTools.FirstConstructor(type, _ => true);
|
||||
}
|
||||
|
||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
var property = AccessTools.Property(typeof(Environment), nameof(Environment.ProcessorCount));
|
||||
|
||||
return instructions.Manipulator(i => i.Calls(property.GetMethod),
|
||||
i =>
|
||||
{
|
||||
i.opcode = OpCodes.Ldc_I4;
|
||||
i.operand = 1024;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class ThreadPoolWorkItemTask(CringeTask task) : IThreadPoolWorkItem
|
||||
{
|
||||
public void Execute()
|
||||
{
|
||||
if (HkBaseSystem.IsInitialized && !HkBaseSystem.IsThreadInitialized)
|
||||
HkBaseSystem.InitThread(Thread.CurrentThread.Name);
|
||||
|
||||
task.Execute();
|
||||
}
|
||||
|
||||
private static MethodBase TargetMethod()
|
||||
{
|
||||
var type = Type.GetType("System.Threading.PortableThreadPool+WorkerThread, System.Private.CoreLib", true);
|
||||
return AccessTools.DeclaredMethod(type, "WorkerThreadStart");
|
||||
}
|
||||
|
||||
private static void Postfix()
|
||||
{
|
||||
if (!HkBaseSystem.IsInitialized || !HkBaseSystem.IsThreadInitialized) return;
|
||||
|
||||
HkBaseSystem.QuitThread();
|
||||
Debug.WriteLine($"Hk Shutdown for {Thread.CurrentThread.Name}");
|
||||
}
|
||||
}
|
472
CringeLauncher/packages.lock.json
Normal file
472
CringeLauncher/packages.lock.json
Normal file
@ -0,0 +1,472 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0-windows10.0.19041": {
|
||||
"dnlib": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.4.0, )",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ=="
|
||||
},
|
||||
"Krafs.Publicizer": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.2.1, )",
|
||||
"resolved": "2.2.1",
|
||||
"contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q=="
|
||||
},
|
||||
"Lib.Harmony.Thin": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.3.3, )",
|
||||
"resolved": "2.3.3",
|
||||
"contentHash": "jsaFv7XnWJnyfyvFbkgIkZtV6tWMteNUcDK3idq+3LwPqpTFNxsOv2eKmj4qqP8QR8UynG1Y9AUaC/+dVruMHg==",
|
||||
"dependencies": {
|
||||
"MonoMod.Core": "1.1.0",
|
||||
"System.Text.Json": "8.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.CodeAnalysis.CSharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.11.0, )",
|
||||
"resolved": "4.11.0",
|
||||
"contentHash": "6XYi2EusI8JT4y2l/F3VVVS+ISoIX9nqHsZRaG6W5aFeJ5BEuBosHfT/ABb73FN0RZ1Z3cj2j7cL28SToJPXOw==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
|
||||
"Microsoft.CodeAnalysis.Common": "[4.11.0]",
|
||||
"System.Collections.Immutable": "8.0.0",
|
||||
"System.Reflection.Metadata": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Windows.CsWin32": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.3.106, )",
|
||||
"resolved": "0.3.106",
|
||||
"contentHash": "Mx5fK7uN6fwLR4wUghs6//HonAnwPBNmC2oonyJVhCUlHS/r6SUS3NkBc3+gaQiv+0/9bqdj1oSCKQFkNI+21Q==",
|
||||
"dependencies": {
|
||||
"Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha",
|
||||
"Microsoft.Windows.SDK.Win32Metadata": "60.0.34-preview",
|
||||
"Microsoft.Windows.WDK.Win32Metadata": "0.11.4-experimental"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.3.4, )",
|
||||
"resolved": "5.3.4",
|
||||
"contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A=="
|
||||
},
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.*, )",
|
||||
"resolved": "1.205.26",
|
||||
"contentHash": "kQSFCLgi0nFUhLvXlp9D2w4cTnXtEIctWtNlK+Vw4FAofws60rwR6Kil9YBNlaDyoo19dcyNt3bs4G9VculcIA==",
|
||||
"dependencies": {
|
||||
"SharpDX": "4.2.0-keen-cringe",
|
||||
"protobuf-net": "1.0.0"
|
||||
}
|
||||
},
|
||||
"Steamworks.NET": {
|
||||
"type": "Direct",
|
||||
"requested": "[20.1.0, )",
|
||||
"resolved": "20.1.0",
|
||||
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
|
||||
},
|
||||
"System.Diagnostics.PerformanceCounter": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Management": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==",
|
||||
"dependencies": {
|
||||
"System.CodeDom": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Private.ServiceModel": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.10.3, )",
|
||||
"resolved": "4.10.3",
|
||||
"contentHash": "BcUV7OERlLqGxDXZuIyIMMmk1PbqBblLRbAoigmzIUx/M8A+8epvyPyXRpbgoucKH7QmfYdQIev04Phx2Co08A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "5.0.10",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Reflection.DispatchProxy": "4.7.1",
|
||||
"System.Security.Cryptography.Xml": "6.0.1",
|
||||
"System.Security.Principal.Windows": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Torch.SixLabors.ImageSharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0-beta6, )",
|
||||
"resolved": "1.0.0-beta6",
|
||||
"contentHash": "WJ7ocT79HgmuKi0+ltpvXTiMI80UcI3DeS8XSfYwJtTB1tcQws6zLPGuUwra6qe6qRrFfpABeDP3xvHV1rJgfg==",
|
||||
"dependencies": {
|
||||
"SixLabors.Core": "1.0.0-beta0007",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Velopack": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.0.630-g9c52e40, )",
|
||||
"resolved": "0.0.630-g9c52e40",
|
||||
"contentHash": "4xNKtV+vyZPdlaQMNeen7p3WqGoq7CkVVkneywod1kTM3zKphQQwxo6KESGxsolwImXM8cZk4E1Zu64SYbaJhQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
||||
"NuGet.Versioning": "6.10.1"
|
||||
}
|
||||
},
|
||||
"Basic.Reference.Assemblies.Net80": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.7.9",
|
||||
"contentHash": "1wbS9ZJLFVrKD2jqv27gekIrpjpLffR9sitLQh5drWoG9KbyR/CgrAhw5I0c8Eq3zFMOToCmrpZi3VpRoInCgg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Common": "4.11.0"
|
||||
}
|
||||
},
|
||||
"Basic.Reference.Assemblies.Net80Windows": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.7.9",
|
||||
"contentHash": "98GFm8MC+pv37rTHaxBm5KFucqdJj0jK0XRHSGt2sXK9HNqtGImIFCFahxjUzskQjiUkPAzVhTou2OYZOuhhEg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Common": "4.11.0"
|
||||
}
|
||||
},
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ=="
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.3.4",
|
||||
"contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g=="
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.11.0",
|
||||
"contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
|
||||
"System.Collections.Immutable": "8.0.0",
|
||||
"System.Reflection.Metadata": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.10",
|
||||
"contentHash": "pp9tbGqIhdEXL6Q1yJl+zevAJSq4BsxqhS1GXzBvEsEz9DDNu9GLNzgUy2xyFc4YjB4m4Ff2YEWTnvQvVYdkvQ=="
|
||||
},
|
||||
"Microsoft.Windows.SDK.Win32Docs": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.1.42-alpha",
|
||||
"contentHash": "Z/9po23gUA9aoukirh2ItMU2ZS9++Js9Gdds9fu5yuMojDrmArvY2y+tq9985tR3cxFxpZO1O35Wjfo0khj5HA=="
|
||||
},
|
||||
"Microsoft.Windows.SDK.Win32Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "60.0.34-preview",
|
||||
"contentHash": "TA3DUNi4CTeo+ItTXBnGZFt2159XOGSl0UOlG5vjDj4WHqZjhwYyyUnzOtrbCERiSaP2Hzg7otJNWwOSZgutyA=="
|
||||
},
|
||||
"Microsoft.Windows.WDK.Win32Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.4-experimental",
|
||||
"contentHash": "bf5MCmUyZf0gBlYQjx9UpRAZWBkRndyt9XicR+UNLvAUAFTZQbu6YaX/sNKZlR98Grn0gydfh/yT4I3vc0AIQA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Windows.SDK.Win32Metadata": "60.0.34-preview"
|
||||
}
|
||||
},
|
||||
"Mono.Cecil": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.5",
|
||||
"contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g=="
|
||||
},
|
||||
"MonoMod.Backports": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "GUAjCrCZEddqHKHFA7Lh61PgTzoKY7gfBShFe0hQe0p8iynHhBK3TWGyRi+QIw/PGfaRPwx6c33CPGFURBVM6g==",
|
||||
"dependencies": {
|
||||
"MonoMod.ILHelpers": "1.0.1"
|
||||
}
|
||||
},
|
||||
"MonoMod.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "Ks8RntZGVcktr2QF/AovTEbuOkrgXz6omjrvT5LRveOIQJuy+IFuEQPBVWu+cSKVIoZD5XkpRFvlVrItgPIrXw==",
|
||||
"dependencies": {
|
||||
"Mono.Cecil": "0.11.5",
|
||||
"MonoMod.Backports": "1.1.0",
|
||||
"MonoMod.ILHelpers": "1.0.1",
|
||||
"MonoMod.Utils": "25.0.4"
|
||||
}
|
||||
},
|
||||
"MonoMod.ILHelpers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "6djj/Hz+/eTomo1H/sJEJNxBz2ZdhXjvH0MOmyU2xRtbjaIfBQuyVV0zNUbJhMY/8qoWrz7WXfskfFhdaY0afA=="
|
||||
},
|
||||
"MonoMod.Utils": {
|
||||
"type": "Transitive",
|
||||
"resolved": "25.0.4",
|
||||
"contentHash": "cB94MaZtFD9u4clYEFTwM4jGXnJnzXsxYF3yBpMZKHhXOas66tMF2frbdYte023i0MH4C5iRJbDjxHmA4x5VgA==",
|
||||
"dependencies": {
|
||||
"Mono.Cecil": "0.11.5",
|
||||
"MonoMod.Backports": "1.1.0",
|
||||
"MonoMod.ILHelpers": "1.0.1"
|
||||
}
|
||||
},
|
||||
"NuGet.Frameworks": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg=="
|
||||
},
|
||||
"NuGet.Versioning": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg=="
|
||||
},
|
||||
"protobuf-net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
|
||||
},
|
||||
"SharpDX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.2.0-keen-cringe",
|
||||
"contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw=="
|
||||
},
|
||||
"SixLabors.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0-beta0007",
|
||||
"contentHash": "s9aPl6yxwcvoKRD0u0zjkCISZCCifbUi9/XVFjdvlx5Pt7vRYmGV0anq1EEftUjIEHbEu5aNBipbUSBIV2CE7w==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
|
||||
},
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.EventLog": "8.0.0",
|
||||
"System.Security.Cryptography.ProtectedData": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.EventLog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A=="
|
||||
},
|
||||
"System.Formats.Asn1": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA=="
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
|
||||
},
|
||||
"System.Reflection.DispatchProxy": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.1",
|
||||
"contentHash": "C1sMLwIG6ILQ2bmOT4gh62V6oJlyF4BlHcVMrOoor49p0Ji2tA8QAoqyMcIhAdH6OHKJ8m7BU+r4LK2CUEOKqw=="
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
|
||||
},
|
||||
"System.Security.Cryptography.Pkcs": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "ynmbW2GjIGg9K1wXmVIRs4IlyDolf0JXNpzFQ8JCVgwM+myUC2JeUggl2PwQig2PNVMegKmN1aAx7WPQ8tI3vA==",
|
||||
"dependencies": {
|
||||
"System.Formats.Asn1": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
|
||||
},
|
||||
"System.Security.Cryptography.Xml": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "5e5bI28T0x73AwTsbuFP4qSRzthmU2C0Gqgg3AZ3KTxmSyA+Uhk31puA3srdaeWaacVnHhLdJywCzqOiEpbO/w==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "6.0.0",
|
||||
"System.Security.Cryptography.Pkcs": "6.0.1"
|
||||
}
|
||||
},
|
||||
"System.Security.Principal.Windows": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.1",
|
||||
"contentHash": "7AWk2za1hSEJBppe/Lg+uDcam2TrDqwIKa9XcPssSwyjC2xa39EKEGul3CO5RWNF+hMuZG4zlBDrvhBdDTg4lg==",
|
||||
"dependencies": {
|
||||
"System.Text.Encodings.Web": "8.0.0"
|
||||
}
|
||||
},
|
||||
"cringebootstrap.abstractions": {
|
||||
"type": "Project"
|
||||
},
|
||||
"cringeplugins": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Basic.Reference.Assemblies.Net80": "[1.7.9, )",
|
||||
"Basic.Reference.Assemblies.Net80Windows": "[1.7.9, )",
|
||||
"ImGui.NET.DirectX": "[1.91.0.1, )",
|
||||
"NLog": "[5.3.4, )",
|
||||
"NuGet": "[1.0.0, )",
|
||||
"SharedCringe": "[1.0.0, )",
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
|
||||
"dnlib": "[4.4.0, )"
|
||||
}
|
||||
},
|
||||
"nuget": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"NuGet.Frameworks": "[6.11.1, )",
|
||||
"NuGet.Versioning": "[6.11.1, )"
|
||||
}
|
||||
},
|
||||
"sharedcringe": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"CringeBootstrap.Abstractions": "[1.0.0, )",
|
||||
"NLog": "[5.3.4, )",
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0-windows10.0.19041/win-x64": {
|
||||
"Steamworks.NET": {
|
||||
"type": "Direct",
|
||||
"requested": "[20.1.0, )",
|
||||
"resolved": "20.1.0",
|
||||
"contentHash": "+GntwnyJ5tCNvUIaQxv2+ehDvZJzGUqlSB5xRBk1hTj1qqBJ6s4vK/OfGD/jae7aTmXiGSm8wpJORosNtQevJQ=="
|
||||
},
|
||||
"System.Diagnostics.PerformanceCounter": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "lX6DXxtJqVGWw7N/QmVoiCyVQ+Q/Xp+jVXPr3gLK1jJExSn1qmAjJQeb8gnOYeeBTG3E3PmG1nu92eYj/TEjpg==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Management": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==",
|
||||
"dependencies": {
|
||||
"System.CodeDom": "8.0.0"
|
||||
}
|
||||
},
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.EventLog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
|
||||
},
|
||||
"System.Security.Cryptography.Pkcs": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.1",
|
||||
"contentHash": "ynmbW2GjIGg9K1wXmVIRs4IlyDolf0JXNpzFQ8JCVgwM+myUC2JeUggl2PwQig2PNVMegKmN1aAx7WPQ8tI3vA==",
|
||||
"dependencies": {
|
||||
"System.Formats.Asn1": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Principal.Windows": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
CringePlugins/Abstractions/IRenderComponent.cs
Normal file
11
CringePlugins/Abstractions/IRenderComponent.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace CringePlugins.Abstractions;
|
||||
|
||||
public interface IRenderComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets called while the dear ImGui frame is being composed
|
||||
/// </summary>
|
||||
void OnFrame();
|
||||
}
|
||||
|
||||
internal interface IRootRenderComponent : IRenderComponent, IDisposable;
|
15
CringePlugins/Config/PackagesConfig.cs
Normal file
15
CringePlugins/Config/PackagesConfig.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Collections.Immutable;
|
||||
using CringePlugins.Resolver;
|
||||
using NuGet;
|
||||
using NuGet.Models;
|
||||
|
||||
namespace CringePlugins.Config;
|
||||
|
||||
public record PackagesConfig(ImmutableArray<PackageSource> Sources, ImmutableArray<PackageReference> Packages)
|
||||
{
|
||||
public static PackagesConfig Default { get; } = new([
|
||||
new("SpaceEngineersDedicated.ReferenceAssemblies", "https://nuget.storage.yandexcloud.net/index.json"),
|
||||
new("ImGui.NET.DirectX", "https://nuget.storage.yandexcloud.net/index.json"),
|
||||
new(string.Empty, "https://api.nuget.org/v3/index.json")
|
||||
], []);
|
||||
}
|
40
CringePlugins/CringePlugins.csproj
Normal file
40
CringePlugins/CringePlugins.csproj
Normal file
@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NuGet\NuGet.csproj" />
|
||||
<ProjectReference Include="..\SharedCringe\SharedCringe.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_plugins" />
|
||||
<Publicize Include="VRage:VRage.Plugins.MyPlugins.m_handleInputPlugins" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net80" Version="1.7.9" />
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net80Windows" Version="1.7.9" />
|
||||
<PackageReference Include="NLog" Version="5.3.4" />
|
||||
<PackageReference Include="ImGui.NET.DirectX" Version="1.91.0.1" />
|
||||
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="dnlib" Version="4.4.0" />
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="splash.gif" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
3
CringePlugins/Globals.cs
Normal file
3
CringePlugins/Globals.cs
Normal file
@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("CringeLauncher")]
|
37
CringePlugins/Loader/PluginAssemblyLoadContext.cs
Normal file
37
CringePlugins/Loader/PluginAssemblyLoadContext.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using SharedCringe.Loader;
|
||||
|
||||
namespace CringePlugins.Loader;
|
||||
|
||||
internal class PluginAssemblyLoadContext : DerivedAssemblyLoadContext
|
||||
{
|
||||
private readonly string _entrypointPath;
|
||||
private readonly AssemblyDependencyResolver _dependencyResolver;
|
||||
private Assembly? _assembly;
|
||||
|
||||
internal PluginAssemblyLoadContext(ICoreLoadContext parentContext, string entrypointPath) : base(parentContext, $"Plugin Context {Path.GetFileNameWithoutExtension(entrypointPath)}")
|
||||
{
|
||||
_entrypointPath = entrypointPath;
|
||||
_dependencyResolver = new(entrypointPath);
|
||||
}
|
||||
|
||||
public Assembly LoadEntrypoint() => _assembly ??= LoadFromAssemblyPath(_entrypointPath);
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
if (_dependencyResolver.ResolveAssemblyToPath(assemblyName) is { } path)
|
||||
return LoadFromAssemblyPath(path);
|
||||
|
||||
return base.Load(assemblyName);
|
||||
}
|
||||
|
||||
protected override nint LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
if (_dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName) is { } path)
|
||||
return LoadUnmanagedDllFromPath(path);
|
||||
|
||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
||||
}
|
55
CringePlugins/Loader/PluginInstance.cs
Normal file
55
CringePlugins/Loader/PluginInstance.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System.Runtime.Loader;
|
||||
using CringeBootstrap.Abstractions;
|
||||
using CringePlugins.Utils;
|
||||
using VRage.Plugins;
|
||||
|
||||
namespace CringePlugins.Loader;
|
||||
|
||||
internal sealed class PluginInstance
|
||||
{
|
||||
private readonly string _entrypointPath;
|
||||
private PluginAssemblyLoadContext? _context;
|
||||
private IPlugin? _instance;
|
||||
private IHandleInputPlugin? _handleInputInstance;
|
||||
public PluginMetadata Metadata { get; }
|
||||
|
||||
public PluginInstance(PluginMetadata metadata, string entrypointPath)
|
||||
{
|
||||
_entrypointPath = entrypointPath;
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public PluginInstance(string entrypointPath) : this(PluginMetadata.ReadFromEntrypoint(entrypointPath), entrypointPath)
|
||||
{
|
||||
}
|
||||
|
||||
public void Instantiate()
|
||||
{
|
||||
if (AssemblyLoadContext.GetLoadContext(typeof(PluginInstance).Assembly) is not ICoreLoadContext parentContext)
|
||||
throw new NotSupportedException("Plugin instantiation is not supported in this context");
|
||||
|
||||
_context = new PluginAssemblyLoadContext(parentContext, _entrypointPath);
|
||||
|
||||
var entrypoint = _context.LoadEntrypoint();
|
||||
|
||||
var plugins = IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(entrypoint.GetMainModule()).ToArray();
|
||||
|
||||
if (plugins.Length == 0)
|
||||
throw new InvalidOperationException("Entrypoint does not contain any plugins");
|
||||
if (plugins.Length > 1)
|
||||
throw new InvalidOperationException("Entrypoint contains multiple plugins");
|
||||
|
||||
_instance = (IPlugin) Activator.CreateInstance(plugins[0])!;
|
||||
_handleInputInstance = _instance as IHandleInputPlugin;
|
||||
}
|
||||
|
||||
public void RegisterLifetime()
|
||||
{
|
||||
if (_instance is null)
|
||||
throw new InvalidOperationException("Must call Instantiate first");
|
||||
|
||||
MyPlugins.m_plugins.Add(_instance);
|
||||
if (_handleInputInstance is not null)
|
||||
MyPlugins.m_handleInputPlugins.Add(_handleInputInstance);
|
||||
}
|
||||
}
|
21
CringePlugins/Loader/PluginMetadata.cs
Normal file
21
CringePlugins/Loader/PluginMetadata.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace CringePlugins.Loader;
|
||||
|
||||
public record PluginMetadata(string Name, Version Version)
|
||||
{
|
||||
public static PluginMetadata ReadFromEntrypoint(string entrypointPath)
|
||||
{
|
||||
var module = ModuleDefMD.Load(entrypointPath);
|
||||
|
||||
var titleAttribute = module.CustomAttributes.Find(typeof(AssemblyTitleAttribute).FullName);
|
||||
var versionAttribute = module.CustomAttributes.Find(typeof(AssemblyVersionAttribute).FullName);
|
||||
|
||||
var name = titleAttribute?.ConstructorArguments[0].Value as string ?? module.FullName;
|
||||
if (!Version.TryParse(versionAttribute?.ConstructorArguments[0].Value as string ?? "0.0.0.0", out var version))
|
||||
version = new();
|
||||
|
||||
return new(name, version);
|
||||
}
|
||||
}
|
142
CringePlugins/Loader/PluginsLifetime.cs
Normal file
142
CringePlugins/Loader/PluginsLifetime.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using CringePlugins.Config;
|
||||
using CringePlugins.Resolver;
|
||||
using CringePlugins.Splash;
|
||||
using NLog;
|
||||
using NuGet;
|
||||
using NuGet.Deps;
|
||||
using NuGet.Frameworks;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace CringePlugins.Loader;
|
||||
|
||||
public class PluginsLifetime : ILoadingStage
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public string Name => "Loading Plugins";
|
||||
|
||||
private ImmutableArray<PluginInstance> _plugins = [];
|
||||
private readonly DirectoryInfo _dir = Directory.CreateDirectory(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CringeLauncher"));
|
||||
private readonly NuGetFramework _runtimeFramework = NuGetFramework.ParseFolder("net8.0-windows10.0.19041.0");
|
||||
|
||||
public async ValueTask Load(ISplashProgress progress)
|
||||
{
|
||||
progress.DefineStepsCount(6);
|
||||
|
||||
progress.Report("Discovering local plugins");
|
||||
|
||||
DiscoverLocalPlugins(_dir.CreateSubdirectory("plugins"));
|
||||
|
||||
progress.Report("Loading config");
|
||||
|
||||
PackagesConfig? packagesConfig = null;
|
||||
var configPath = Path.Join(_dir.FullName, "packages.json");
|
||||
if (File.Exists(configPath))
|
||||
await using (var stream = File.OpenRead(configPath))
|
||||
packagesConfig = JsonSerializer.Deserialize<PackagesConfig>(stream, NuGetClient.SerializerOptions)!;
|
||||
|
||||
if (packagesConfig == null)
|
||||
{
|
||||
packagesConfig = PackagesConfig.Default;
|
||||
await using var stream = File.Create(configPath);
|
||||
await JsonSerializer.SerializeAsync(stream, packagesConfig, NuGetClient.SerializerOptions);
|
||||
}
|
||||
|
||||
progress.Report("Resolving packages");
|
||||
|
||||
var sourceMapping = new PackageSourceMapping(packagesConfig.Sources);
|
||||
var resolver = new PackageResolver(_runtimeFramework, packagesConfig.Packages, sourceMapping);
|
||||
|
||||
var packages = await resolver.ResolveAsync();
|
||||
|
||||
progress.Report("Downloading packages");
|
||||
|
||||
var cachedPackages = await resolver.DownloadPackagesAsync(_dir.CreateSubdirectory("cache"), packages, progress);
|
||||
|
||||
progress.Report("Loading plugins");
|
||||
|
||||
await LoadPlugins(cachedPackages, sourceMapping);
|
||||
|
||||
progress.Report("Registering plugins");
|
||||
|
||||
RegisterLifetime();
|
||||
}
|
||||
|
||||
private void RegisterLifetime()
|
||||
{
|
||||
foreach (var instance in _plugins)
|
||||
{
|
||||
try
|
||||
{
|
||||
instance.Instantiate();
|
||||
instance.RegisterLifetime();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed to instantiate plugin {Plugin}", instance.Metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPlugins(IReadOnlySet<CachedPackage> packages, PackageSourceMapping sourceMapping)
|
||||
{
|
||||
var plugins = _plugins.ToBuilder();
|
||||
|
||||
var packageVersions = BuiltInPackages.GetPackages(_runtimeFramework)
|
||||
.ToImmutableDictionary(b => b.Package.Id, b => b.Package.Version,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
packageVersions = packageVersions.AddRange(packages.Select(b =>
|
||||
new KeyValuePair<string, NuGetVersion>(b.Package.Id, b.Package.Version)));
|
||||
|
||||
var manifestBuilder = new DependencyManifestBuilder(_dir.CreateSubdirectory("cache"), sourceMapping,
|
||||
dependency => packageVersions.TryGetValue(dependency.Id, out var version) && version.Major != 99
|
||||
? version
|
||||
: dependency.Range.MinVersion ?? dependency.Range.MaxVersion);
|
||||
|
||||
foreach (var package in packages)
|
||||
{
|
||||
var dir = Path.Join(package.Directory.FullName, "lib", package.ResolvedFramework.GetShortFolderName());
|
||||
|
||||
await using (var stream = File.Create(Path.Join(dir, $"{package.Package.Id}.deps.json")))
|
||||
await manifestBuilder.WriteDependencyManifestAsync(stream, package.Entry, _runtimeFramework);
|
||||
|
||||
LoadComponent(plugins, Path.Join(dir, $"{package.Package.Id}.dll"));
|
||||
}
|
||||
|
||||
_plugins = plugins.ToImmutable();
|
||||
}
|
||||
|
||||
private void DiscoverLocalPlugins(DirectoryInfo dir)
|
||||
{
|
||||
var plugins = ImmutableArray<PluginInstance>.Empty.ToBuilder();
|
||||
|
||||
foreach (var directory in dir.EnumerateDirectories())
|
||||
{
|
||||
var files = directory.GetFiles("*.deps.json");
|
||||
|
||||
if (files.Length != 1) continue;
|
||||
|
||||
var path = files[0].FullName[..^".deps.json".Length] + ".dll";
|
||||
|
||||
LoadComponent(plugins, path);
|
||||
}
|
||||
|
||||
_plugins = plugins.ToImmutable();
|
||||
}
|
||||
|
||||
private static void LoadComponent(ImmutableArray<PluginInstance>.Builder plugins, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
plugins.Add(new PluginInstance(path));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed to load plugin {PluginPath}", path);
|
||||
}
|
||||
}
|
||||
}
|
48
CringePlugins/Render/RenderHandler.cs
Normal file
48
CringePlugins/Render/RenderHandler.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Collections.Concurrent;
|
||||
using CringePlugins.Abstractions;
|
||||
using NLog;
|
||||
|
||||
namespace CringePlugins.Render;
|
||||
|
||||
public sealed class RenderHandler : IRootRenderComponent
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private static RenderHandler? _current;
|
||||
public static RenderHandler Current => _current ?? throw new InvalidOperationException("Render is not yet initialized");
|
||||
|
||||
private readonly ConcurrentBag<ComponentRegistration> _components = [];
|
||||
|
||||
internal RenderHandler()
|
||||
{
|
||||
_current = this;
|
||||
}
|
||||
|
||||
public void RegisterComponent<TComponent>(TComponent instance) where TComponent : IRenderComponent
|
||||
{
|
||||
_components.Add(new ComponentRegistration(typeof(TComponent), instance));
|
||||
}
|
||||
|
||||
void IRenderComponent.OnFrame()
|
||||
{
|
||||
foreach (var (instanceType, renderComponent) in _components)
|
||||
{
|
||||
try
|
||||
{
|
||||
renderComponent.OnFrame();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Component {TypeName} failed to render a new frame", instanceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record ComponentRegistration(Type InstanceType, IRenderComponent Instance);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_current = null;
|
||||
_components.Clear();
|
||||
}
|
||||
}
|
78
CringePlugins/Resolver/BuiltInPackages.cs
Normal file
78
CringePlugins/Resolver/BuiltInPackages.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using Basic.Reference.Assemblies;
|
||||
using CringePlugins.Loader;
|
||||
using CringePlugins.Utils;
|
||||
using dnlib.DotNet;
|
||||
using ImGuiNET;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using NLog;
|
||||
using NuGet.Frameworks;
|
||||
using NuGet.Models;
|
||||
using NuGet.Versioning;
|
||||
using Sandbox.Game;
|
||||
using SpaceEngineers.Game;
|
||||
using VRage.Utils;
|
||||
|
||||
namespace CringePlugins.Resolver;
|
||||
|
||||
public static class BuiltInPackages
|
||||
{
|
||||
private const string SeReferenceAssemblies = "SpaceEngineersDedicated.ReferenceAssemblies";
|
||||
private const string ImGui = "ImGui.NET.DirectX";
|
||||
|
||||
public static ImmutableArray<ResolvedPackage> GetPackages(NuGetFramework runtimeFramework)
|
||||
{
|
||||
var nlog = FromAssembly<LogFactory>(runtimeFramework, version: new(5, 3, 4));
|
||||
Version seVersion = new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion!.Value);
|
||||
|
||||
var se = FromAssembly<SpaceEngineersGame>(runtimeFramework, [
|
||||
nlog.AsDependency()
|
||||
], SeReferenceAssemblies, new(seVersion));
|
||||
var imGui = FromAssembly<ImGuiKey>(runtimeFramework, id: ImGui);
|
||||
|
||||
BuiltInSdkPackage MapSdkPackage(
|
||||
(string FileName, byte[] ImageBytes, PortableExecutableReference Reference, Guid Mvid) r)
|
||||
{
|
||||
var def = ModuleDefMD.Load(r.ImageBytes, IntrospectionContext.Global.Context);
|
||||
var attribute = def.CustomAttributes.Find(typeof(AssemblyFileVersionAttribute).FullName);
|
||||
var version = attribute is null ? new(99, 0, 0) : NuGetVersion.Parse((string)attribute.ConstructorArguments[0].Value);
|
||||
|
||||
return new BuiltInSdkPackage(
|
||||
new(0, Path.GetFileNameWithoutExtension(r.FileName), version, []), runtimeFramework,
|
||||
new(Path.GetFileNameWithoutExtension(r.FileName), version, [new(runtimeFramework, [])], null, []));
|
||||
}
|
||||
|
||||
return
|
||||
[
|
||||
..Net80.ReferenceInfos.AllValues.Select(MapSdkPackage),
|
||||
..Net80Windows.ReferenceInfos.AllValues.Select(MapSdkPackage),
|
||||
nlog,
|
||||
se,
|
||||
imGui,
|
||||
FromAssembly<PluginsLifetime>(runtimeFramework, [se.AsDependency(), imGui.AsDependency()]),
|
||||
];
|
||||
}
|
||||
|
||||
private static Dependency AsDependency(this ResolvedPackage package) => new(package.Package.Id, new(package.Package.Version));
|
||||
|
||||
private static BuiltInPackage FromAssembly<T>(NuGetFramework runtimeFramework, ImmutableArray<Dependency>? dependencies = null, string? id = null, NuGetVersion? version = null)
|
||||
{
|
||||
var assembly = typeof(T).Assembly.GetName();
|
||||
id ??= assembly.Name!;
|
||||
version ??= new NuGetVersion(assembly.Version ?? new(0, 0, 0));
|
||||
dependencies ??= [];
|
||||
|
||||
return new(
|
||||
new(0, id, version, [..dependencies.Value.Select(b => b.Id)]),
|
||||
runtimeFramework,
|
||||
new(id, version, [
|
||||
new(runtimeFramework, dependencies.Value)
|
||||
], null, [])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record BuiltInPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
|
||||
|
||||
public record BuiltInSdkPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry) : BuiltInPackage(Package, ResolvedFramework, Entry);
|
144
CringePlugins/Resolver/PackageResolver.cs
Normal file
144
CringePlugins/Resolver/PackageResolver.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.IO.Compression;
|
||||
using NuGet;
|
||||
using NuGet.Frameworks;
|
||||
using NuGet.Models;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace CringePlugins.Resolver;
|
||||
|
||||
public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<PackageReference> references, PackageSourceMapping packageSources)
|
||||
{
|
||||
public async Task<ImmutableHashSet<ResolvedPackage>> ResolveAsync()
|
||||
{
|
||||
var order = 0;
|
||||
var packages = new SortedDictionary<Package, CatalogEntry>();
|
||||
|
||||
foreach (var reference in references)
|
||||
{
|
||||
var client = await packageSources.GetClientAsync(reference.Id);
|
||||
|
||||
var registrationRoot = await client.GetPackageRegistrationRootAsync(reference.Id);
|
||||
|
||||
var items = registrationRoot.Items.SelectMany(page =>
|
||||
page.Items.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"]))
|
||||
.ToImmutableDictionary(b => b.CatalogEntry.Version);
|
||||
|
||||
var version = reference.Range.FindBestMatch(items.Values.Select(b => b.CatalogEntry.Version));
|
||||
|
||||
if (version is null)
|
||||
throw new Exception($"Unable to find version for package {reference.Id}");
|
||||
|
||||
var package = new Package(order, reference.Id, version, []); // todo resolve dependencies
|
||||
|
||||
if (packages.TryAdd(package, items[version].CatalogEntry))
|
||||
continue;
|
||||
|
||||
if (!packages.TryGetValue(package, out _))
|
||||
throw new Exception($"Duplicate package {package.Id}");
|
||||
|
||||
var existingPackage = packages.Keys.First(b => b.Version == package.Version && b.Id == package.Id);
|
||||
|
||||
if (package.Version < existingPackage.Version)
|
||||
throw new Exception($"Package reference {package.Id} has lower version {package.Version} than already resolved {existingPackage.Version}");
|
||||
|
||||
if (package.Version == existingPackage.Version)
|
||||
continue;
|
||||
|
||||
packages.Remove(existingPackage);
|
||||
packages.Add(package with
|
||||
{
|
||||
Order = ++order
|
||||
}, items[version].CatalogEntry);
|
||||
}
|
||||
|
||||
var set = ImmutableHashSet<ResolvedPackage>.Empty.ToBuilder();
|
||||
foreach (var (package, catalogEntry) in packages)
|
||||
{
|
||||
var client = await packageSources.GetClientAsync(package.Id);
|
||||
|
||||
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, runtimeFramework,
|
||||
g => g.TargetFramework);
|
||||
|
||||
if (nearestGroup is null)
|
||||
throw new Exception($"Unable to find compatible dependency group for package {package.Id}");
|
||||
|
||||
set.Add(new RemotePackage(package, nearestGroup.TargetFramework, client, catalogEntry));
|
||||
}
|
||||
|
||||
return set.ToImmutable();
|
||||
}
|
||||
|
||||
public async Task<ImmutableHashSet<CachedPackage>> DownloadPackagesAsync(DirectoryInfo baseDirectory,
|
||||
IReadOnlySet<ResolvedPackage> resolvedPackages, IProgress<float>? progress = null)
|
||||
{
|
||||
var packages = ImmutableHashSet<CachedPackage>.Empty.ToBuilder();
|
||||
|
||||
var i = 0f;
|
||||
foreach (var package in resolvedPackages)
|
||||
{
|
||||
switch (package)
|
||||
{
|
||||
case RemotePackage remotePackage:
|
||||
{
|
||||
var dir = new DirectoryInfo(Path.Join(baseDirectory.FullName, package.Package.Id, package.Package.Version.ToString()));
|
||||
if (!dir.Exists)
|
||||
{
|
||||
dir.Create();
|
||||
|
||||
await using var stream = await remotePackage.Client.GetPackageContentStreamAsync(remotePackage.Package.Id, remotePackage.Package.Version);
|
||||
using var memStream = new MemoryStream();
|
||||
await stream.CopyToAsync(memStream);
|
||||
memStream.Position = 0;
|
||||
using var archive = new ZipArchive(memStream, ZipArchiveMode.Read);
|
||||
archive.ExtractToDirectory(dir.FullName);
|
||||
}
|
||||
|
||||
packages.Add(new CachedPackage(package.Package, package.ResolvedFramework, dir, package.Entry));
|
||||
break;
|
||||
}
|
||||
case CachedPackage cachedPackage:
|
||||
packages.Add(cachedPackage);
|
||||
break;
|
||||
}
|
||||
|
||||
progress?.Report(i++ / resolvedPackages.Count);
|
||||
}
|
||||
|
||||
return packages.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
public record CachedPackage(Package Package, NuGetFramework ResolvedFramework, DirectoryInfo Directory, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
|
||||
public record RemotePackage(Package Package, NuGetFramework ResolvedFramework, NuGetClient Client, CatalogEntry Entry) : ResolvedPackage(Package, ResolvedFramework, Entry);
|
||||
|
||||
public abstract record ResolvedPackage(Package Package, NuGetFramework ResolvedFramework, CatalogEntry Entry);
|
||||
|
||||
public record Package(int Order, string Id, NuGetVersion Version, ImmutableArray<string> Dependencies) : IComparable<Package>, IComparable
|
||||
{
|
||||
public int CompareTo(Package? other)
|
||||
{
|
||||
if (ReferenceEquals(this, other)) return 0;
|
||||
if (other is null) return 1;
|
||||
var orderComparison = Order.CompareTo(other.Order);
|
||||
if (orderComparison != 0) return orderComparison;
|
||||
return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (obj is null) return 1;
|
||||
if (ReferenceEquals(this, obj)) return 0;
|
||||
return obj is Package other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Package)}");
|
||||
}
|
||||
|
||||
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Id);
|
||||
|
||||
public virtual bool Equals(Package? other)
|
||||
{
|
||||
if (other is null) return false;
|
||||
return Id.Equals(other.Id, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public record PackageReference(string Id, VersionRange Range);
|
13
CringePlugins/Splash/ISplashProgress.cs
Normal file
13
CringePlugins/Splash/ISplashProgress.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace CringePlugins.Splash;
|
||||
|
||||
public interface ISplashProgress : IProgress<ProgressInfo>, IProgress<float>
|
||||
{
|
||||
void DefineStage(ILoadingStage stage);
|
||||
void DefineStepsCount(int count);
|
||||
}
|
||||
|
||||
public interface ILoadingStage
|
||||
{
|
||||
string Name { get; }
|
||||
ValueTask Load(ISplashProgress progress);
|
||||
}
|
9
CringePlugins/Splash/ProgressInfo.cs
Normal file
9
CringePlugins/Splash/ProgressInfo.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace CringePlugins.Splash;
|
||||
|
||||
public record ProgressInfo(string Text)
|
||||
{
|
||||
public static implicit operator ProgressInfo(string s) => new(s);
|
||||
public static implicit operator ProgressInfo((string, float) s) => new PercentProgressInfo(s.Item1, s.Item2);
|
||||
}
|
||||
|
||||
public record PercentProgressInfo(string Text, float Percent = 0) : ProgressInfo(Text);
|
29
CringePlugins/Splash/Splash.cs
Normal file
29
CringePlugins/Splash/Splash.cs
Normal file
@ -0,0 +1,29 @@
|
||||
namespace CringePlugins.Splash;
|
||||
|
||||
public class Splash : ISplashProgress
|
||||
{
|
||||
private readonly List<ILoadingStage> _loadingStages = [];
|
||||
|
||||
public void Report(ProgressInfo value)
|
||||
{
|
||||
}
|
||||
|
||||
public void Report(float value)
|
||||
{
|
||||
}
|
||||
|
||||
public void DefineStage(ILoadingStage stage) => _loadingStages.Add(stage);
|
||||
|
||||
public void DefineStepsCount(int count)
|
||||
{
|
||||
}
|
||||
|
||||
public void ExecuteLoadingStages()
|
||||
{
|
||||
foreach (var loadingStage in _loadingStages)
|
||||
{
|
||||
// todo sync context
|
||||
loadingStage.Load(this).AsTask().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
49
CringePlugins/Utils/IntrospectionContext.cs
Normal file
49
CringePlugins/Utils/IntrospectionContext.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace CringePlugins.Utils;
|
||||
|
||||
public class IntrospectionContext
|
||||
{
|
||||
public static IntrospectionContext Global { get; } = new();
|
||||
|
||||
internal readonly ModuleContext Context = ModuleDef.CreateModuleContext();
|
||||
|
||||
public IEnumerable<Type> CollectAttributedTypes<TAttribute>(Module module, bool allowAbstract = false) where TAttribute : Attribute
|
||||
{
|
||||
var moduleDef = ModuleDefMD.Load(module, Context);
|
||||
|
||||
return moduleDef.GetTypes()
|
||||
.Where(b => b.CustomAttributes.IsDefined(typeof(TAttribute).FullName) && (allowAbstract || !b.IsAbstract))
|
||||
.Select(b => module.GetType(b.FullName, true, false)!);
|
||||
}
|
||||
|
||||
public IEnumerable<Type> CollectDerivedTypes<T>(Module module, bool allowAbstract = false)
|
||||
{
|
||||
var moduleDef = ModuleDefMD.Load(module, Context);
|
||||
|
||||
var token = moduleDef.ImportAsTypeSig(typeof(T));
|
||||
|
||||
return moduleDef.GetTypes()
|
||||
.Where(b => (typeof(T).IsInterface
|
||||
? b.Interfaces.Any(i => i.Interface.FullName == token.FullName)
|
||||
: MatchBaseType(b, token)) && (allowAbstract || !b.IsAbstract))
|
||||
.Select(b => module.GetType(b.FullName, true, false)!);
|
||||
}
|
||||
|
||||
private static bool MatchBaseType(ITypeDefOrRef? defOrRef, TypeSig token)
|
||||
{
|
||||
while ((defOrRef = defOrRef.GetBaseType()) != null)
|
||||
{
|
||||
if (defOrRef.FullName == token.FullName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AssemblyExtensions
|
||||
{
|
||||
public static Module GetMainModule(this Assembly assembly) => assembly.GetModule(assembly.GetName().Name! + ".dll") ?? assembly.GetModules()[0];
|
||||
}
|
158
CringePlugins/packages.lock.json
Normal file
158
CringePlugins/packages.lock.json
Normal file
@ -0,0 +1,158 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0-windows7.0": {
|
||||
"Basic.Reference.Assemblies.Net80": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.7.9, )",
|
||||
"resolved": "1.7.9",
|
||||
"contentHash": "1wbS9ZJLFVrKD2jqv27gekIrpjpLffR9sitLQh5drWoG9KbyR/CgrAhw5I0c8Eq3zFMOToCmrpZi3VpRoInCgg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Common": "4.11.0"
|
||||
}
|
||||
},
|
||||
"Basic.Reference.Assemblies.Net80Windows": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.7.9, )",
|
||||
"resolved": "1.7.9",
|
||||
"contentHash": "98GFm8MC+pv37rTHaxBm5KFucqdJj0jK0XRHSGt2sXK9HNqtGImIFCFahxjUzskQjiUkPAzVhTou2OYZOuhhEg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Common": "4.11.0"
|
||||
}
|
||||
},
|
||||
"dnlib": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.4.0, )",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ=="
|
||||
},
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.91.0.1, )",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Krafs.Publicizer": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.2.1, )",
|
||||
"resolved": "2.2.1",
|
||||
"contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q=="
|
||||
},
|
||||
"NLog": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.3.4, )",
|
||||
"resolved": "5.3.4",
|
||||
"contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A=="
|
||||
},
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.*, )",
|
||||
"resolved": "1.205.25",
|
||||
"contentHash": "+70s6nJnBxEFYZY1qwKfM7FgYBYY6YDSPvbltEXrn7CVAeiWIxbtdcUZ4nDBGVTYqUWEI/r3zbLP1zlcNE27Dg==",
|
||||
"dependencies": {
|
||||
"SharpDX": "4.2.0-keen-cringe",
|
||||
"protobuf-net": "1.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.3.4",
|
||||
"contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g=="
|
||||
},
|
||||
"Microsoft.CodeAnalysis.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.11.0",
|
||||
"contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
|
||||
"System.Collections.Immutable": "8.0.0",
|
||||
"System.Reflection.Metadata": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NuGet.Frameworks": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg=="
|
||||
},
|
||||
"NuGet.Versioning": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg=="
|
||||
},
|
||||
"protobuf-net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
|
||||
},
|
||||
"SharpDX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.2.0-keen-cringe",
|
||||
"contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw=="
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"cringebootstrap.abstractions": {
|
||||
"type": "Project"
|
||||
},
|
||||
"nuget": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"NuGet.Frameworks": "[6.11.1, )",
|
||||
"NuGet.Versioning": "[6.11.1, )"
|
||||
}
|
||||
},
|
||||
"sharedcringe": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"CringeBootstrap.Abstractions": "[1.0.0, )",
|
||||
"NLog": "[5.3.4, )",
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.91.0.1, )",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
CringePlugins/splash.gif
Normal file
BIN
CringePlugins/splash.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 515 KiB |
6
NuGet.config
Normal file
6
NuGet.config
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="zznty" value="https://nuget.storage.yandexcloud.net/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
59
NuGet/Converters/FrameworkJsonConverter.cs
Normal file
59
NuGet/Converters/FrameworkJsonConverter.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace NuGet.Converters;
|
||||
|
||||
public class FrameworkJsonConverter(FrameworkNameFormat format) : JsonConverter<NuGetFramework>
|
||||
{
|
||||
public override NuGetFramework Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
throw new JsonException("Invalid framework string");
|
||||
|
||||
var s = reader.GetString()!;
|
||||
return format switch
|
||||
{
|
||||
FrameworkNameFormat.ShortFolderName => NuGetFramework.ParseFolder(s),
|
||||
FrameworkNameFormat.FrameworkName => NuGetFramework.ParseFrameworkName(s,
|
||||
DefaultFrameworkNameProvider.Instance),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, NuGetFramework value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(FormatValue(value));
|
||||
}
|
||||
|
||||
private string FormatValue(NuGetFramework value)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
FrameworkNameFormat.ShortFolderName => value.GetShortFolderName(),
|
||||
FrameworkNameFormat.FrameworkName => value.DotNetFrameworkName,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public override NuGetFramework ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert,
|
||||
JsonSerializerOptions options) => Read(ref reader, typeToConvert, options);
|
||||
|
||||
public override void WriteAsPropertyName(Utf8JsonWriter writer, NuGetFramework value,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
writer.WritePropertyName(FormatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
public enum FrameworkNameFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// The short folder name format (net8.0)
|
||||
/// </summary>
|
||||
ShortFolderName,
|
||||
/// <summary>
|
||||
/// Full framework name (.NETCoreApp,Version=v8.0)
|
||||
/// </summary>
|
||||
FrameworkName
|
||||
}
|
30
NuGet/Converters/ManifestPackageKeyJsonConverter.cs
Normal file
30
NuGet/Converters/ManifestPackageKeyJsonConverter.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Deps;
|
||||
|
||||
namespace NuGet.Converters;
|
||||
|
||||
public class ManifestPackageKeyJsonConverter : JsonConverter<ManifestPackageKey>
|
||||
{
|
||||
public override ManifestPackageKey Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
throw new JsonException("Invalid package key string");
|
||||
|
||||
return ManifestPackageKey.Parse(reader.GetString()!);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ManifestPackageKey value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
|
||||
public override ManifestPackageKey ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert,
|
||||
JsonSerializerOptions options) => Read(ref reader, typeToConvert, options);
|
||||
|
||||
public override void WriteAsPropertyName(Utf8JsonWriter writer, ManifestPackageKey value,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
writer.WritePropertyName(value.ToString());
|
||||
}
|
||||
}
|
21
NuGet/Converters/ResourceTypeJsonConverter.cs
Normal file
21
NuGet/Converters/ResourceTypeJsonConverter.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Models;
|
||||
|
||||
namespace NuGet.Converters;
|
||||
|
||||
public class ResourceTypeJsonConverter : JsonConverter<ResourceType>
|
||||
{
|
||||
public override ResourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
throw new JsonException("Invalid resource type");
|
||||
|
||||
return ResourceType.Parse(reader.GetString()!);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ResourceType value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
21
NuGet/Converters/VersionJsonConverter.cs
Normal file
21
NuGet/Converters/VersionJsonConverter.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Converters;
|
||||
|
||||
public class VersionJsonConverter : JsonConverter<NuGetVersion>
|
||||
{
|
||||
public override NuGetVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
throw new JsonException("Invalid version string");
|
||||
|
||||
return NuGetVersion.Parse(reader.GetString()!);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, NuGetVersion value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
21
NuGet/Converters/VersionRangeJsonConverter.cs
Normal file
21
NuGet/Converters/VersionRangeJsonConverter.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Converters;
|
||||
|
||||
public class VersionRangeJsonConverter : JsonConverter<VersionRange>
|
||||
{
|
||||
public override VersionRange? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
throw new JsonException("Invalid version range");
|
||||
|
||||
return VersionRange.Parse(reader.GetString()!);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, VersionRange value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
155
NuGet/Deps/DependenciesManifest.cs
Normal file
155
NuGet/Deps/DependenciesManifest.cs
Normal file
@ -0,0 +1,155 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Converters;
|
||||
using NuGet.Frameworks;
|
||||
using NuGet.Models;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Deps;
|
||||
|
||||
public record DependenciesManifest(RuntimeTarget RuntimeTarget,
|
||||
ImmutableDictionary<NuGetFramework, string> CompilationOptions,
|
||||
ImmutableDictionary<NuGetFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>> Targets,
|
||||
ImmutableDictionary<ManifestPackageKey, DependencyLibrary> Libraries);
|
||||
|
||||
public record DependencyLibrary(
|
||||
LibraryType Type,
|
||||
string Sha512 = "",
|
||||
bool Serviceable = false,
|
||||
ManifestPackageKey? Path = null,
|
||||
string? HashPath = null);
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<LibraryType>))]
|
||||
public enum LibraryType
|
||||
{
|
||||
Project,
|
||||
Package
|
||||
}
|
||||
|
||||
public record DependencyTarget(ImmutableDictionary<string, NuGetVersion>? Dependencies,
|
||||
// key is file path relative to package root
|
||||
ImmutableDictionary<string, RuntimeDependency>? Runtime,
|
||||
// key is file path relative to package root
|
||||
ImmutableDictionary<string, Dependency>? Native);
|
||||
|
||||
public record Dependency(Version? FileVersion = null);
|
||||
|
||||
public record RuntimeDependency(Version? AssemblyVersion = null, Version? FileVersion = null) : Dependency(FileVersion);
|
||||
|
||||
public record RuntimeTarget([property: JsonPropertyName("name")] NuGetFramework Framework, string Signature = "");
|
||||
|
||||
[JsonConverter(typeof(ManifestPackageKeyJsonConverter))]
|
||||
public record ManifestPackageKey(string Id, NuGetVersion Version)
|
||||
{
|
||||
public static ManifestPackageKey Parse(string str)
|
||||
{
|
||||
var index = str.IndexOf('/');
|
||||
if (index < 0)
|
||||
throw new FormatException("Invalid package key: " + str);
|
||||
|
||||
return new ManifestPackageKey(str[..index], NuGetVersion.Parse(str[(index + 1)..]));
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Id}/{Version}";
|
||||
}
|
||||
|
||||
public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSourceMapping packageSources, Func<Models.Dependency, NuGetVersion?> versionResolver)
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = true,
|
||||
Converters =
|
||||
{
|
||||
new FrameworkJsonConverter(FrameworkNameFormat.FrameworkName),
|
||||
new VersionJsonConverter()
|
||||
}
|
||||
};
|
||||
|
||||
public async ValueTask WriteDependencyManifestAsync(Stream stream, CatalogEntry catalogEntry, NuGetFramework targetFramework)
|
||||
{
|
||||
var runtimeTarget = new RuntimeTarget(targetFramework);
|
||||
|
||||
var targets = ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Empty.ToBuilder();
|
||||
|
||||
await MapCatalogEntryAsync(catalogEntry, targetFramework, targets);
|
||||
|
||||
var manifest = new DependenciesManifest(runtimeTarget, ImmutableDictionary<NuGetFramework, string>.Empty,
|
||||
ImmutableDictionary<NuGetFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>>.Empty
|
||||
.Add(targetFramework, targets.ToImmutable()),
|
||||
ImmutableDictionary<ManifestPackageKey, DependencyLibrary>.Empty);
|
||||
|
||||
await JsonSerializer.SerializeAsync(stream, manifest, SerializerOptions);
|
||||
}
|
||||
|
||||
private async Task MapCatalogEntryAsync(CatalogEntry catalogEntry, NuGetFramework targetFramework,
|
||||
ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Builder targets)
|
||||
{
|
||||
if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)))
|
||||
return;
|
||||
|
||||
var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, targetFramework,
|
||||
group => group.TargetFramework);
|
||||
|
||||
if (nearest is null)
|
||||
return;
|
||||
|
||||
targets.Add(new(catalogEntry.Id, catalogEntry.Version),
|
||||
await MapEntryAsync(catalogEntry, nearest));
|
||||
|
||||
foreach (var dependency in nearest.Dependencies ?? [])
|
||||
{
|
||||
var client = await packageSources.GetClientAsync(dependency.Id);
|
||||
var (url, entry) = await client.GetPackageRegistrationAsync(dependency.Id, versionResolver(dependency)!);
|
||||
|
||||
entry ??= await client.GetPackageCatalogEntryAsync(url);
|
||||
|
||||
await MapCatalogEntryAsync(entry, targetFramework, targets);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<DependencyTarget> MapEntryAsync(CatalogEntry entry, DependencyGroup group)
|
||||
{
|
||||
var packageEntries = entry.PackageEntries ?? await GetPackageContent(entry);
|
||||
|
||||
return new(group.Dependencies?.ToImmutableDictionary(b => b.Id, versionResolver) ?? ImmutableDictionary<string, NuGetVersion>.Empty,
|
||||
packageEntries.Where(b => b.FullName.StartsWith($"lib/{group.TargetFramework.GetShortFolderName()}/"))
|
||||
.ToImmutableDictionary(b => b.FullName, _ => new RuntimeDependency()),
|
||||
packageEntries.Where(b =>
|
||||
b.FullName.StartsWith($"runtimes/{RuntimeInformation.RuntimeIdentifier}/native/"))
|
||||
.ToImmutableDictionary(b => b.FullName, _ => new Dependency()));
|
||||
}
|
||||
|
||||
private async Task<ImmutableArray<CatalogPackageEntry>> GetPackageContent(CatalogEntry entry)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var dir = new DirectoryInfo(Path.Join(cacheDirectory.FullName, entry.Id, entry.Version.ToString()));
|
||||
|
||||
if (dir.Exists)
|
||||
{
|
||||
return
|
||||
[
|
||||
..dir.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
.Select(b => new CatalogPackageEntry(b.Name, b.FullName, b.Length, b.Length))
|
||||
];
|
||||
}
|
||||
|
||||
var client = await packageSources.GetClientAsync(entry.Id);
|
||||
|
||||
dir.Create();
|
||||
|
||||
{
|
||||
await using var stream = await client.GetPackageContentStreamAsync(entry.Id, entry.Version);
|
||||
using var memStream = new MemoryStream();
|
||||
await stream.CopyToAsync(memStream);
|
||||
memStream.Position = 0;
|
||||
using var archive = new ZipArchive(memStream, ZipArchiveMode.Read);
|
||||
archive.ExtractToDirectory(dir.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
NuGet/Models/CatalogEntry.cs
Normal file
9
NuGet/Models/CatalogEntry.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Collections.Immutable;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record CatalogEntry(string Id, NuGetVersion Version, ImmutableArray<DependencyGroup> DependencyGroups, ImmutableArray<string>? PackageTypes,
|
||||
ImmutableArray<CatalogPackageEntry>? PackageEntries);
|
||||
|
||||
public record CatalogPackageEntry(string Name, string FullName, long CompressedLength, long Length);
|
5
NuGet/Models/Dependency.cs
Normal file
5
NuGet/Models/Dependency.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record Dependency(string Id, VersionRange Range);
|
6
NuGet/Models/DependencyGroup.cs
Normal file
6
NuGet/Models/DependencyGroup.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using System.Collections.Immutable;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record DependencyGroup(NuGetFramework TargetFramework, ImmutableArray<Dependency>? Dependencies = null);
|
5
NuGet/Models/NuGetIndex.cs
Normal file
5
NuGet/Models/NuGetIndex.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record NuGetIndex(NuGetVersion Version, Resource[] Resources);
|
6
NuGet/Models/Registration.cs
Normal file
6
NuGet/Models/Registration.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record Registration([property: JsonPropertyName("catalogEntry")] string CatalogEntryUrl,
|
||||
[property: JsonPropertyName("sleet:catalogEntry")] CatalogEntry? SleetEntry);
|
3
NuGet/Models/RegistrationEntry.cs
Normal file
3
NuGet/Models/RegistrationEntry.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record RegistrationEntry(CatalogEntry CatalogEntry);
|
5
NuGet/Models/RegistrationPage.cs
Normal file
5
NuGet/Models/RegistrationPage.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record RegistrationPage(int Count, NuGetVersion Lower, NuGetVersion Upper, RegistrationEntry[] Items);
|
3
NuGet/Models/RegistrationRoot.cs
Normal file
3
NuGet/Models/RegistrationRoot.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record RegistrationRoot(int Count, RegistrationPage[] Items);
|
5
NuGet/Models/Resource.cs
Normal file
5
NuGet/Models/Resource.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
public record Resource([property: JsonPropertyName("@id")] string Url, [property: JsonPropertyName("@type")] ResourceType Type, string? Comment);
|
26
NuGet/Models/ResourceType.cs
Normal file
26
NuGet/Models/ResourceType.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using NuGet.Converters;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet.Models;
|
||||
|
||||
[JsonConverter(typeof(ResourceTypeJsonConverter))]
|
||||
public record ResourceType(string Id, NuGetVersion? Version)
|
||||
{
|
||||
public static ResourceType Parse(string typeString)
|
||||
{
|
||||
var slash = typeString.IndexOf('/');
|
||||
|
||||
if (slash < 0)
|
||||
return new ResourceType(typeString, null);
|
||||
|
||||
var id = typeString[..slash];
|
||||
var versionStr = typeString[(slash + 1)..];
|
||||
|
||||
return NuGetVersion.TryParse(versionStr, out var version)
|
||||
? new ResourceType(id, version)
|
||||
: new ResourceType(id, null);
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Id}/{Version}";
|
||||
}
|
16
NuGet/NuGet.csproj
Normal file
16
NuGet/NuGet.csproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NuGet.Frameworks" Version="6.11.1" />
|
||||
<PackageReference Include="NuGet.Versioning" Version="6.11.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
83
NuGet/NuGetClient.cs
Normal file
83
NuGet/NuGetClient.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using NuGet.Converters;
|
||||
using NuGet.Models;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace NuGet;
|
||||
|
||||
public class NuGetClient
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly Uri _packageBaseAddress;
|
||||
private readonly Uri _registration;
|
||||
|
||||
public static JsonSerializerOptions SerializerOptions { get; } = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
new VersionJsonConverter(),
|
||||
new VersionRangeJsonConverter(),
|
||||
new FrameworkJsonConverter(FrameworkNameFormat.ShortFolderName),
|
||||
},
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
private NuGetClient(HttpClient client, Uri packageBaseAddress, Uri registration)
|
||||
{
|
||||
_client = client;
|
||||
_packageBaseAddress = packageBaseAddress;
|
||||
_registration = registration;
|
||||
}
|
||||
|
||||
public Task<Stream> GetPackageContentStreamAsync(string id, NuGetVersion version)
|
||||
{
|
||||
id = id.ToLower();
|
||||
return _client.GetStreamAsync(new Uri(_packageBaseAddress,
|
||||
new Uri($"{id}/{version}/{id}.{version}.nupkg", UriKind.Relative)));
|
||||
}
|
||||
|
||||
public Task<Registration> GetPackageRegistrationAsync(string id, NuGetVersion version)
|
||||
{
|
||||
return _client.GetFromJsonAsync<Registration>(
|
||||
new Uri(_registration,
|
||||
new Uri($"{id.ToLower()}/{version}.json", UriKind.Relative)),
|
||||
SerializerOptions
|
||||
)!;
|
||||
}
|
||||
|
||||
public Task<RegistrationRoot> GetPackageRegistrationRootAsync(string id)
|
||||
{
|
||||
return _client.GetFromJsonAsync<RegistrationRoot>(
|
||||
new Uri(_registration,
|
||||
new Uri($"{id.ToLower()}/index.json", UriKind.Relative)),
|
||||
SerializerOptions
|
||||
)!;
|
||||
}
|
||||
|
||||
public Task<CatalogEntry> GetPackageCatalogEntryAsync(string url)
|
||||
{
|
||||
return _client.GetFromJsonAsync<CatalogEntry>(url, SerializerOptions)!;
|
||||
}
|
||||
|
||||
public static async Task<NuGetClient> CreateFromIndexUrlAsync(string indexUrl)
|
||||
{
|
||||
var client = new HttpClient(new HttpClientHandler
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.All
|
||||
});
|
||||
|
||||
var index = await client.GetFromJsonAsync<NuGetIndex>(indexUrl, SerializerOptions);
|
||||
|
||||
var (packageBaseAddress, _, _) = index!.Resources.First(b => b.Type.Id == "PackageBaseAddress");
|
||||
var (registration, _, _) = index!.Resources.First(b => b.Type.Id == "RegistrationsBaseUrl");
|
||||
|
||||
if (!packageBaseAddress.EndsWith('/'))
|
||||
packageBaseAddress += '/';
|
||||
if (!registration.EndsWith('/'))
|
||||
registration += '/';
|
||||
|
||||
return new NuGetClient(client, new Uri(packageBaseAddress), new Uri(registration));
|
||||
}
|
||||
}
|
17
NuGet/PackageSourceMapping.cs
Normal file
17
NuGet/PackageSourceMapping.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace NuGet;
|
||||
|
||||
public class PackageSourceMapping(ImmutableArray<PackageSource> sources)
|
||||
{
|
||||
private readonly ImmutableArray<(string pattern, Task<NuGetClient> client)> _clients = [
|
||||
..sources.Select(b =>
|
||||
(b.Pattern,
|
||||
NuGetClient.CreateFromIndexUrlAsync(b.Url)))
|
||||
];
|
||||
|
||||
public Task<NuGetClient> GetClientAsync(string packageId) =>
|
||||
_clients.FirstOrDefault(b => packageId.StartsWith(b.pattern)).client;
|
||||
}
|
||||
|
||||
public record PackageSource(string Pattern, string Url);
|
20
NuGet/packages.lock.json
Normal file
20
NuGet/packages.lock.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"NuGet.Frameworks": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.11.1, )",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg=="
|
||||
},
|
||||
"NuGet.Versioning": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.11.1, )",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg=="
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
14
SharedCringe/Loader/DerivedAssemblyLoadContext.cs
Normal file
14
SharedCringe/Loader/DerivedAssemblyLoadContext.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using CringeBootstrap.Abstractions;
|
||||
|
||||
namespace SharedCringe.Loader;
|
||||
|
||||
public abstract class DerivedAssemblyLoadContext(ICoreLoadContext parentContext, string name)
|
||||
: AssemblyLoadContext(name, true)
|
||||
{
|
||||
protected readonly ICoreLoadContext ParentContext = parentContext;
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName) => ParentContext.ResolveFromAssemblyName(assemblyName);
|
||||
protected override nint LoadUnmanagedDll(string unmanagedDllName) => ParentContext.ResolveUnmanagedDll(unmanagedDllName);
|
||||
}
|
20
SharedCringe/SharedCringe.csproj
Normal file
20
SharedCringe/SharedCringe.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NLog" Version="5.3.4" />
|
||||
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.*" ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CringeBootstrap.Abstractions\CringeBootstrap.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
37
SharedCringe/packages.lock.json
Normal file
37
SharedCringe/packages.lock.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"NLog": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.3.4, )",
|
||||
"resolved": "5.3.4",
|
||||
"contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A=="
|
||||
},
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.*, )",
|
||||
"resolved": "1.205.24",
|
||||
"contentHash": "cgzWJWflVITp+fY5OPgffcoJ08KL5YHMQrMwaDAkUxfRwPgTzU8qOADsrMqq25vXsEbznU1DzNWwTPviYYi7UA==",
|
||||
"dependencies": {
|
||||
"SharpDX": "4.2.0-keen-cringe",
|
||||
"protobuf-net": "1.0.0"
|
||||
}
|
||||
},
|
||||
"protobuf-net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
|
||||
},
|
||||
"SharpDX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.2.0-keen-cringe",
|
||||
"contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw=="
|
||||
},
|
||||
"cringebootstrap.abstractions": {
|
||||
"type": "Project"
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
25
TestPlugin/Plugin.cs
Normal file
25
TestPlugin/Plugin.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using CringePlugins.Render;
|
||||
using NLog;
|
||||
using VRage.Plugins;
|
||||
|
||||
namespace TestPlugin;
|
||||
|
||||
public class Plugin : IPlugin
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(object gameInstance)
|
||||
{
|
||||
Log.Info("Test Plugin init");
|
||||
|
||||
RenderHandler.Current.RegisterComponent(new TestRenderComponent());
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
}
|
||||
}
|
26
TestPlugin/TestPlugin.csproj
Normal file
26
TestPlugin/TestPlugin.csproj
Normal file
@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<PackageType>CringePlugin</PackageType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NLog" Version="5.3.4" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.*" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="ImGui.NET.DirectX" Version="1.91.0.1" ExcludeAssets="runtime; native"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="CringePlugins">
|
||||
<HintPath>..\CringePlugins\bin\Debug\net8.0-windows\win-x64\CringePlugins.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
17
TestPlugin/TestRenderComponent.cs
Normal file
17
TestPlugin/TestRenderComponent.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using CringePlugins.Abstractions;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace TestPlugin;
|
||||
|
||||
public class TestRenderComponent : IRenderComponent
|
||||
{
|
||||
public void OnFrame()
|
||||
{
|
||||
if (ImGui.Begin("Test Window"))
|
||||
{
|
||||
ImGui.Button("Test");
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
}
|
115
TestPlugin/packages.lock.json
Normal file
115
TestPlugin/packages.lock.json
Normal file
@ -0,0 +1,115 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0-windows7.0": {
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.91.0.1, )",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.3.4, )",
|
||||
"resolved": "5.3.4",
|
||||
"contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A=="
|
||||
},
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.*, )",
|
||||
"resolved": "1.205.24",
|
||||
"contentHash": "cgzWJWflVITp+fY5OPgffcoJ08KL5YHMQrMwaDAkUxfRwPgTzU8qOADsrMqq25vXsEbznU1DzNWwTPviYYi7UA==",
|
||||
"dependencies": {
|
||||
"SharpDX": "4.2.0-keen-cringe",
|
||||
"protobuf-net": "1.0.0"
|
||||
}
|
||||
},
|
||||
"dnlib": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "cKHI720q+zfEEvzklWVGt6B0TH3AibAyJbpUJl4U6KvTP13tycfnqJpkGHRZ/oQ45BTIoIxIwltHIJVDN+iCqQ=="
|
||||
},
|
||||
"NuGet.Frameworks": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "plTZ3ariSWQVsFn2mk83SsdmSg1VpgIMTSZpP/eSE/NNQF02p+M9ItxAYeUZBMX+cQ2nFkSwxQRJ0/fkaV9Hbg=="
|
||||
},
|
||||
"NuGet.Versioning": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.11.1",
|
||||
"contentHash": "YNn3BB71F+guJW42TbAhGcMh3gpyqFMZcPVD9pm5vcvGivTALtRely/VCPWQQ6JQ5PfwIrjPaJMO7VnqyeK3rg=="
|
||||
},
|
||||
"protobuf-net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "kTGOK0E87473sOImOjgZOnz3kTC2aMLffoRWQLYNuBLJnwNNmjanF9IkevZ9Q7yYLeABQfcF3BpeepuMntMVNw=="
|
||||
},
|
||||
"SharpDX": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.2.0-keen-cringe",
|
||||
"contentHash": "LaJN3h1Gi1FWVdef2I5WtOH9gwzKCBniH0CragarbkN2QheYY6Lqm+91PcOfp1w/4wdVb+k8Kjv3sO393Tphtw=="
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"cringebootstrap.abstractions": {
|
||||
"type": "Project"
|
||||
},
|
||||
"cringeplugins": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"NLog": "[5.3.4, )",
|
||||
"NuGet": "[1.0.0, )",
|
||||
"SharedCringe": "[1.0.0, )",
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )",
|
||||
"dnlib": "[4.4.0, )"
|
||||
}
|
||||
},
|
||||
"nuget": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"NuGet.Frameworks": "[6.11.1, )",
|
||||
"NuGet.Versioning": "[6.11.1, )"
|
||||
}
|
||||
},
|
||||
"sharedcringe": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"CringeBootstrap.Abstractions": "[1.0.0, )",
|
||||
"NLog": "[5.3.4, )",
|
||||
"SpaceEngineersDedicated.ReferenceAssemblies": "[1.*, )"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
"ImGui.NET.DirectX": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.91.0.1, )",
|
||||
"resolved": "1.91.0.1",
|
||||
"contentHash": "PpW1gQ9g97h6Hm/h/tkSBOmsBYgGwN8wKNmlJomcQFD/zRY1HPkJZz18XRSfRLHPmH2eeh4hhhZv1KHug7dF9g==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.0",
|
||||
"rollForward": "latestFeature",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user