Added options to disable launcher/plugin auto updates
All checks were successful
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 4m4s
Build / Build Nuget package (NuGet) (push) Successful in 4m7s
Build / Build Nuget package (SharedCringe) (push) Successful in 4m5s
Build / Build Nuget package (CringePlugins) (push) Successful in 4m25s
Build / Build Launcher (push) Successful in 5m12s

Also ran cleanup
This commit is contained in:
2025-06-06 01:35:09 -04:00
parent bc88f0c28a
commit 94fc8a55c0
48 changed files with 381 additions and 267 deletions

View File

@@ -42,7 +42,7 @@ public class GameDirectoryAssemblyLoadContext : AssemblyLoadContext, ICoreLoadCo
if (key.StartsWith("System.") || ReferenceAssemblies.Contains(key)) if (key.StartsWith("System.") || ReferenceAssemblies.Contains(key))
return; return;
_assemblyNames.TryAdd(key, file); _assemblyNames.TryAdd(key, file);
} }
@@ -51,9 +51,9 @@ public class GameDirectoryAssemblyLoadContext : AssemblyLoadContext, ICoreLoadCo
AddOverride(new(name), Path.Join(AppContext.BaseDirectory, name + ".dll")); AddOverride(new(name), Path.Join(AppContext.BaseDirectory, name + ".dll"));
} }
protected override Assembly? Load(AssemblyName name) protected override Assembly? Load(AssemblyName assemblyName)
{ {
var key = name.Name ?? name.FullName[..',']; var key = assemblyName.Name ?? assemblyName.FullName[..','];
try try
{ {
@@ -79,11 +79,11 @@ public class GameDirectoryAssemblyLoadContext : AssemblyLoadContext, ICoreLoadCo
var path = Path.Join(dir, unmanagedDllName); var path = Path.Join(dir, unmanagedDllName);
if (!Path.HasExtension(path)) if (!Path.HasExtension(path))
path += ".dll"; path += ".dll";
if (File.Exists(path)) if (File.Exists(path))
return LoadUnmanagedDllFromPath(path); return LoadUnmanagedDllFromPath(path);
} }
throw new DllNotFoundException($"Unable to load {unmanagedDllName}, module not found in valid locations"); throw new DllNotFoundException($"Unable to load {unmanagedDllName}, module not found in valid locations");
} }

View File

@@ -16,7 +16,7 @@ if (args.Length == 0)
{ {
var path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CringeLauncher", var path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CringeLauncher",
"current", "CringeBootstrap.exe"); "current", "CringeBootstrap.exe");
Console.Write("Set your Launch Options under "); Console.Write("Set your Launch Options under ");
Console.ForegroundColor = ConsoleColor.Cyan; Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("Space Engineers -> Properties -> Launch Options"); Console.Write("Space Engineers -> Properties -> Launch Options");
@@ -38,7 +38,7 @@ AssemblyLoadContext.Default.Resolving += (loadContext, name) =>
return null; return null;
}; };
#endif #endif
var dir = Path.GetDirectoryName(args[0])!; var dir = Path.GetDirectoryName(args[0])!;
var context = new GameDirectoryAssemblyLoadContext(dir); var context = new GameDirectoryAssemblyLoadContext(dir);

View File

@@ -66,7 +66,7 @@ internal sealed class ImGuiHandler : IGuiHandler, IDisposable
ImGui_ImplWin32_Init(windowHandle); ImGui_ImplWin32_Init(windowHandle);
ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer); ImGui_ImplDX11_Init(device.NativePointer, deviceContext.NativePointer);
_init = true; _init = true;
_imageService.Initialize(); _imageService.Initialize();
} }
@@ -123,7 +123,7 @@ internal sealed class ImGuiHandler : IGuiHandler, IDisposable
UpdatePlatformWindows(); UpdatePlatformWindows();
RenderPlatformWindowsDefault(); RenderPlatformWindowsDefault();
_imageService.Update(); _imageService.Update();
} }

View File

@@ -1,9 +1,4 @@
using System.Net; using CringeBootstrap.Abstractions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using CringeBootstrap.Abstractions;
using CringeLauncher.Utils; using CringeLauncher.Utils;
using CringePlugins.Config; using CringePlugins.Config;
using CringePlugins.Loader; using CringePlugins.Loader;
@@ -25,6 +20,12 @@ using Sandbox.Game;
using SpaceEngineers.Game; using SpaceEngineers.Game;
using SpaceEngineers.Game.Achievements; using SpaceEngineers.Game.Achievements;
using SpaceEngineers.Game.GUI; using SpaceEngineers.Game.GUI;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Text.Json;
using Velopack; using Velopack;
using VRage; using VRage;
using VRage.Audio; using VRage.Audio;
@@ -58,7 +59,7 @@ public class Launcher : ICorePlugin
{ {
if (Type.GetType("GameAnalyticsSDK.Net.Logging.GALogger, GameAnalytics.Mono") is { } gaLoggerType) if (Type.GetType("GameAnalyticsSDK.Net.Logging.GALogger, GameAnalytics.Mono") is { } gaLoggerType)
RuntimeHelpers.RunClassConstructor(gaLoggerType.TypeHandle); RuntimeHelpers.RunClassConstructor(gaLoggerType.TypeHandle);
LogManager.Setup() LogManager.Setup()
.LoadConfigurationFromFile() .LoadConfigurationFromFile()
.SetupExtensions(s => .SetupExtensions(s =>
@@ -75,19 +76,21 @@ public class Launcher : ICorePlugin
var logger = LogManager.GetLogger("CringeBootstrap"); var logger = LogManager.GetLogger("CringeBootstrap");
logger.Info("Bootstrapping"); logger.Info("Bootstrapping");
//environment variable for viktor's plugins //environment variable for viktor's plugins
Environment.SetEnvironmentVariable("SE_PLUGIN_DISABLE_METHOD_VERIFICATION", "True"); Environment.SetEnvironmentVariable("SE_PLUGIN_DISABLE_METHOD_VERIFICATION", "True");
#if !DEBUG #if !DEBUG
CheckUpdates(args, logger).GetAwaiter().GetResult(); CheckUpdates(args, logger).GetAwaiter().GetResult();
#else
logger.Info("Updates disabled: {Flag}", CheckUpdatesDisabledAsync(logger).GetAwaiter().GetResult());
#endif #endif
// hook up steam as we ship it inside base context as an override // hook up steam as we ship it inside base context as an override
if (AssemblyLoadContext.GetLoadContext(typeof(Launcher).Assembly) is ICoreLoadContext coreLoadContext) if (AssemblyLoadContext.GetLoadContext(typeof(Launcher).Assembly) is ICoreLoadContext coreLoadContext)
NativeLibrary.SetDllImportResolver(typeof(Steamworks.Constants).Assembly, (name, _, _) => coreLoadContext.ResolveUnmanagedDll(name)); NativeLibrary.SetDllImportResolver(typeof(Steamworks.Constants).Assembly, (name, _, _) => coreLoadContext.ResolveUnmanagedDll(name));
NativeLibrary.SetDllImportResolver(typeof(EosService).Assembly, (name, _, _) => NativeLibrary.Load(Path.Join(AppContext.BaseDirectory, name))); NativeLibrary.SetDllImportResolver(typeof(EosService).Assembly, (name, _, _) => NativeLibrary.Load(Path.Join(AppContext.BaseDirectory, name)));
_harmony.PatchAll(typeof(Launcher).Assembly); _harmony.PatchAll(typeof(Launcher).Assembly);
MyFileSystem.ExePath = Path.GetDirectoryName(args.ElementAtOrDefault(0) ?? Assembly.GetExecutingAssembly().Location)!; MyFileSystem.ExePath = Path.GetDirectoryName(args.ElementAtOrDefault(0) ?? Assembly.GetExecutingAssembly().Location)!;
@@ -105,12 +108,12 @@ public class Launcher : ICorePlugin
MyShaderCompiler.Init(MyShaderCompiler.TargetPlatform.PC, false); MyShaderCompiler.Init(MyShaderCompiler.TargetPlatform.PC, false);
MyVRageWindows.Init(MyPerGameSettings.BasicGameInfo.ApplicationName, MySandboxGame.Log, MyVRageWindows.Init(MyPerGameSettings.BasicGameInfo.ApplicationName, MySandboxGame.Log,
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
MyPerGameSettings.BasicGameInfo.ApplicationName), MyPerGameSettings.BasicGameInfo.ApplicationName),
false, false); false, false);
MyPlatformGameSettings.SAVE_TO_CLOUD_OPTION_AVAILABLE = true; MyPlatformGameSettings.SAVE_TO_CLOUD_OPTION_AVAILABLE = true;
MyXAudio2.DEVICE_DETAILS_SUPPORTED = false; MyXAudio2.DEVICE_DETAILS_SUPPORTED = false;
if (MyVRage.Platform.System.SimulationQuality == SimulationQuality.Normal) if (MyVRage.Platform.System.SimulationQuality == SimulationQuality.Normal)
{ {
MyPlatformGameSettings.SIMPLIFIED_SIMULATION_OVERRIDE = false; MyPlatformGameSettings.SIMPLIFIED_SIMULATION_OVERRIDE = false;
@@ -133,17 +136,17 @@ public class Launcher : ICorePlugin
_renderComponent = new(); _renderComponent = new();
_renderComponent.Start(new(), () => InitEarlyWindow(splash), MyVideoSettingsManager.Initialize(), MyPerGameSettings.MaxFrameRate); _renderComponent.Start(new(), () => InitEarlyWindow(splash), MyVideoSettingsManager.Initialize(), MyPerGameSettings.MaxFrameRate);
_renderComponent.RenderThread.BeforeDraw += MyFpsManager.Update; _renderComponent.RenderThread.BeforeDraw += MyFpsManager.Update;
// this technically should wait for render thread init, but who cares // this technically should wait for render thread init, but who cares
splash.ExecuteLoadingStages(); splash.ExecuteLoadingStages();
InitUgc(); InitUgc();
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString()); MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
_lifetime.RegisterLifetime(); _lifetime.RegisterLifetime();
WaitForDevice(); WaitForDevice();
_game = new(args) _game = new(args)
{ {
GameRenderComponent = _renderComponent, GameRenderComponent = _renderComponent,
@@ -164,14 +167,14 @@ public class Launcher : ICorePlugin
var retryPolicy = HttpPolicyExtensions.HandleTransientHttpError() var retryPolicy = HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(5, _ => TimeSpan.FromSeconds(1)); .WaitAndRetryAsync(5, _ => TimeSpan.FromSeconds(1));
services.AddHttpClient<PluginsLifetime>() services.AddHttpClient<PluginsLifetime>()
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{ {
AutomaticDecompression = DecompressionMethods.All AutomaticDecompression = DecompressionMethods.All
}) })
.AddPolicyHandler(retryPolicy); .AddPolicyHandler(retryPolicy);
services.AddHttpClient<ImGuiImageService>() services.AddHttpClient<ImGuiImageService>()
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{ {
@@ -208,25 +211,55 @@ public class Launcher : ICorePlugin
private IVRageWindow InitEarlyWindow(Splash splash) private IVRageWindow InitEarlyWindow(Splash splash)
{ {
ImGuiHandler.Instance = new(_configDir); ImGuiHandler.Instance = new(_configDir);
RenderHandler.Current.RegisterComponent(splash); RenderHandler.Current.RegisterComponent(splash);
MyVRage.Platform.Windows.CreateWindow("Cringe Launcher", MyPerGameSettings.GameIcon, null); MyVRage.Platform.Windows.CreateWindow("Cringe Launcher", MyPerGameSettings.GameIcon, null);
MyVRage.Platform.Windows.Window.OnExit += MySandboxGame.ExitThreadSafe; MyVRage.Platform.Windows.Window.OnExit += MySandboxGame.ExitThreadSafe;
MyRenderProxy.RenderThread = _renderComponent!.RenderThread; MyRenderProxy.RenderThread = _renderComponent!.RenderThread;
MyVRage.Platform.Windows.Window.ShowAndFocus(); MyVRage.Platform.Windows.Window.ShowAndFocus();
return MyVRage.Platform.Windows.Window; return MyVRage.Platform.Windows.Window;
} }
private async Task<bool> CheckUpdatesDisabledAsync(Logger logger)
{
var path = Path.Join(_configDir.FullName, "launcher.json");
if (!File.Exists(path))
return false;
try
{
await using var stream = File.OpenRead(path);
var conf = await JsonSerializer.DeserializeAsync<LauncherConfig>(stream, ConfigHandler.SerializerOptions);
return conf?.DisableLauncherUpdates ?? false;
}
catch (Exception ex)
{
logger.Error(ex, "Error reading launcher config");
}
return false;
}
private async Task CheckUpdates(string[] args, Logger logger) private async Task CheckUpdates(string[] args, Logger logger)
{ {
if (await CheckUpdatesDisabledAsync(logger))
{
logger.Warn("Updates Disabled (may break from keen update)");
return;
}
logger.Info("Checking for updates..."); logger.Info("Checking for updates...");
var mgr = new UpdateManager("https://dl.zznty.ru/CringeLauncher/"); var mgr = new UpdateManager("https://dl.zznty.ru/CringeLauncher/");
// check for new version // check for new version
var newVersion = await mgr.CheckForUpdatesAsync(); var newVersion = await mgr.CheckForUpdatesAsync();
if (newVersion == null) if (newVersion == null)
@@ -234,7 +267,7 @@ public class Launcher : ICorePlugin
logger.Info("Up to date"); logger.Info("Up to date");
return; // no update available return; // no update available
} }
// print update info // print update info
Console.ForegroundColor = ConsoleColor.Cyan; Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"New version available: {mgr.CurrentVersion} -> {newVersion.TargetFullRelease.Version}"); Console.WriteLine($"New version available: {mgr.CurrentVersion} -> {newVersion.TargetFullRelease.Version}");
@@ -250,7 +283,7 @@ public class Launcher : ICorePlugin
// download new version // download new version
await mgr.DownloadUpdatesAsync(newVersion); await mgr.DownloadUpdatesAsync(newVersion);
logger.Info("Done! Restarting..."); logger.Info("Done! Restarting...");
// install new version and restart app // install new version and restart app
@@ -278,11 +311,11 @@ public class Launcher : ICorePlugin
var textsPath = Path.Combine(MyFileSystem.RootPath, @"Content\Data\Localization\CoreTexts"); var textsPath = Path.Combine(MyFileSystem.RootPath, @"Content\Data\Localization\CoreTexts");
var hashSet = new HashSet<MyLanguagesEnum>(); var hashSet = new HashSet<MyLanguagesEnum>();
MyTexts.LoadSupportedLanguages(textsPath, hashSet); MyTexts.LoadSupportedLanguages(textsPath, hashSet);
if (!MyTexts.Languages.TryGetValue(MyLanguage.Instance.GetOsLanguageCurrentOfficial(), out var description) && if (!MyTexts.Languages.TryGetValue(MyLanguage.Instance.GetOsLanguageCurrentOfficial(), out var description) &&
!MyTexts.Languages.TryGetValue(MyLanguagesEnum.English, out description)) !MyTexts.Languages.TryGetValue(MyLanguagesEnum.English, out description))
return; return;
MyTexts.LoadTexts(textsPath, description.CultureName, description.SubcultureName); MyTexts.LoadTexts(textsPath, description.CultureName, description.SubcultureName);
} }
@@ -290,20 +323,20 @@ public class Launcher : ICorePlugin
{ {
var steamGameService = MySteamGameService.Create(false, AppId); var steamGameService = MySteamGameService.Create(false, AppId);
MyServiceManager.Instance.AddService(steamGameService); MyServiceManager.Instance.AddService(steamGameService);
var aggregator = new MyServerDiscoveryAggregator(); var aggregator = new MyServerDiscoveryAggregator();
MySteamGameService.InitNetworking(false, steamGameService, MyPerGameSettings.GameName, aggregator); MySteamGameService.InitNetworking(false, steamGameService, MyPerGameSettings.GameName, aggregator);
EosService.InitNetworking(false, false, MyPerGameSettings.GameName, steamGameService, "xyza7891964JhtVD93nm3nZp8t1MbnhC", EosService.InitNetworking(false, false, MyPerGameSettings.GameName, steamGameService, "xyza7891964JhtVD93nm3nZp8t1MbnhC",
"AKGM16qoFtct0IIIA8RCqEIYG4d4gXPPDNpzGuvlhLA", "24b1cd652a18461fa9b3d533ac8d6b5b", "AKGM16qoFtct0IIIA8RCqEIYG4d4gXPPDNpzGuvlhLA", "24b1cd652a18461fa9b3d533ac8d6b5b",
"1958fe26c66d4151a327ec162e4d49c8", "07c169b3b641401496d352cad1c905d6", "1958fe26c66d4151a327ec162e4d49c8", "07c169b3b641401496d352cad1c905d6",
"https://retail.epicgames.com/", EosService.CreatePlatform(), "https://retail.epicgames.com/", EosService.CreatePlatform(),
MyPlatformGameSettings.VERBOSE_NETWORK_LOGGING, ArraySegment<string>.Empty, aggregator, MyPlatformGameSettings.VERBOSE_NETWORK_LOGGING, [], aggregator,
MyMultiplayer.Channels); MyMultiplayer.Channels);
MyServiceManager.Instance.AddService<IMyServerDiscovery>(aggregator); MyServiceManager.Instance.AddService<IMyServerDiscovery>(aggregator);
MyServiceManager.Instance.AddService(MySteamGameService.CreateMicrophone()); MyServiceManager.Instance.AddService(MySteamGameService.CreateMicrophone());
MyGameService.WorkshopService.AddAggregate(MySteamUgcService.Create(AppId, steamGameService)); MyGameService.WorkshopService.AddAggregate(MySteamUgcService.Create(AppId, steamGameService));
var modUgc = MyModIoService.Create(MyServiceManager.Instance.GetService<IMyGameService>(), "spaceengineers", var modUgc = MyModIoService.Create(MyServiceManager.Instance.GetService<IMyGameService>(), "spaceengineers",
@@ -313,7 +346,7 @@ public class Launcher : ICorePlugin
MyPlatformGameSettings.MODIO_PORTAL); MyPlatformGameSettings.MODIO_PORTAL);
modUgc.IsConsentGiven = MySandboxGame.Config.ModIoConsent; modUgc.IsConsentGiven = MySandboxGame.Config.ModIoConsent;
MyGameService.WorkshopService.AddAggregate(modUgc); MyGameService.WorkshopService.AddAggregate(modUgc);
MySpaceEngineersAchievements.Initialize(); MySpaceEngineersAchievements.Initialize();
} }
@@ -321,13 +354,13 @@ public class Launcher : ICorePlugin
{ {
var renderQualityHint = MyVRage.Platform.Render.GetRenderQualityHint(); var renderQualityHint = MyVRage.Platform.Render.GetRenderQualityHint();
var preset = MyGuiScreenOptionsGraphics.GetPreset(renderQualityHint); var preset = MyGuiScreenOptionsGraphics.GetPreset(renderQualityHint);
MyRenderProxy.Settings.User = MyVideoSettingsManager MyRenderProxy.Settings.User = MyVideoSettingsManager
.GetGraphicsSettingsFromConfig(ref preset, renderQualityHint > MyRenderPresetEnum.CUSTOM) .GetGraphicsSettingsFromConfig(ref preset, renderQualityHint > MyRenderPresetEnum.CUSTOM)
.PerformanceSettings.RenderSettings; .PerformanceSettings.RenderSettings;
MyRenderProxy.Settings.EnableAnsel = MyPlatformGameSettings.ENABLE_ANSEL; MyRenderProxy.Settings.EnableAnsel = MyPlatformGameSettings.ENABLE_ANSEL;
MyRenderProxy.Settings.EnableAnselWithSprites = MyPlatformGameSettings.ENABLE_ANSEL_WITH_SPRITES; MyRenderProxy.Settings.EnableAnselWithSprites = MyPlatformGameSettings.ENABLE_ANSEL_WITH_SPRITES;
var graphicsRenderer = MySandboxGame.Config.GraphicsRenderer; var graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
MySandboxGame.Config.GraphicsRenderer = graphicsRenderer; MySandboxGame.Config.GraphicsRenderer = graphicsRenderer;

View File

@@ -16,15 +16,15 @@ public static class EosInitPatch
var ins = instructions.ToList(); var ins = instructions.ToList();
var stIndex = ins.FindIndex(b => b.opcode == OpCodes.Stloc_1); var stIndex = ins.FindIndex(b => b.opcode == OpCodes.Stloc_1);
ins.InsertRange(stIndex, new [] ins.InsertRange(stIndex,
{ [
new CodeInstruction(OpCodes.Dup), new CodeInstruction(OpCodes.Dup),
new(OpCodes.Ldc_I4_2), // PlatformFlags.DisableOverlay new(OpCodes.Ldc_I4_2), // PlatformFlags.DisableOverlay
new(OpCodes.Conv_I8), new(OpCodes.Conv_I8),
CodeInstruction.Call("Epic.OnlineServices.Platform.Options:set_Flags"), CodeInstruction.Call("Epic.OnlineServices.Platform.Options:set_Flags"),
}); ]);
return ins; return ins;
} }
} }

View File

@@ -42,23 +42,26 @@ public static class IntrospectionPatches
//mods need to look for specific derived types //mods need to look for specific derived types
Debug.WriteLine($"Getting special types for {__instance.FullName}"); Debug.WriteLine($"Getting special types for {__instance.FullName}");
var module = __instance.GetMainModule(); var module = __instance.GetMainModule();
__result = IntrospectionContext.Global.CollectDerivedTypes<MyObjectBuilder_Base>(module) __result =
.Concat(IntrospectionContext.Global.CollectDerivedTypes<MyStatLogic>(module)) [
.Concat(IntrospectionContext.Global.CollectAttributedTypes<MyObjectBuilderDefinitionAttribute>(module)) .. IntrospectionContext.Global.CollectDerivedTypes<MyObjectBuilder_Base>(module)
.Concat(IntrospectionContext.Global.CollectDerivedTypes<MyComponentBase>(module)) ,
.Concat(IntrospectionContext.Global.CollectAttributedTypes<MyComponentBuilderAttribute>(module)) .. IntrospectionContext.Global.CollectDerivedTypes<MyStatLogic>(module),
.Concat(IntrospectionContext.Global.CollectDerivedTypes<IMyTextSurfaceScript>(module)) .. IntrospectionContext.Global.CollectAttributedTypes<MyObjectBuilderDefinitionAttribute>(module),
.Concat(IntrospectionContext.Global.CollectDerivedTypes<IMyUseObject>(module)) .. IntrospectionContext.Global.CollectDerivedTypes<MyComponentBase>(module),
.Concat(IntrospectionContext.Global.CollectDerivedTypes<IMyHudStat>(module)) .. IntrospectionContext.Global.CollectAttributedTypes<MyComponentBuilderAttribute>(module),
.Concat(IntrospectionContext.Global.CollectAttributedTypes<MySessionComponentDescriptor>(module)) .. IntrospectionContext.Global.CollectDerivedTypes<IMyTextSurfaceScript>(module),
.ToArray(); .. IntrospectionContext.Global.CollectDerivedTypes<IMyUseObject>(module),
.. IntrospectionContext.Global.CollectDerivedTypes<IMyHudStat>(module),
.. IntrospectionContext.Global.CollectAttributedTypes<MySessionComponentDescriptor>(module),
];
return false; return false;
} }
Debug.WriteLine($"Blocking GetTypes for {__instance.FullName}"); Debug.WriteLine($"Blocking GetTypes for {__instance.FullName}");
__result = []; __result = [];
return false; return false;
} }
@@ -74,10 +77,9 @@ public static class IntrospectionPatches
__result = []; __result = [];
return false; return false;
} }
// static classes are abstract // static classes are abstract
__result = IntrospectionContext.Global.CollectAttributedTypes<HarmonyAttribute>(assembly.GetMainModule(), true) __result = [.. IntrospectionContext.Global.CollectAttributedTypes<HarmonyAttribute>(assembly.GetMainModule(), true)];
.ToArray();
return false; return false;
} }
@@ -101,13 +103,13 @@ public static class IntrospectionPatches
.Concat(PluginsLifetime.Contexts.SelectMany(x => x.Assemblies)) ?? []; .Concat(PluginsLifetime.Contexts.SelectMany(x => x.Assemblies)) ?? [];
return false; return false;
} }
[HarmonyPrefix, HarmonyPatch(typeof(MySession), "PrepareBaseSession", typeof(MyObjectBuilder_Checkpoint), typeof(MyObjectBuilder_Sector))] [HarmonyPrefix, HarmonyPatch(typeof(MySession), "PrepareBaseSession", typeof(MyObjectBuilder_Checkpoint), typeof(MyObjectBuilder_Sector))]
private static void PrepareSessionPrefix() private static void PrepareSessionPrefix()
{ {
// i hate keen for that in MyUseObjectFactory..cctor // i hate keen for that in MyUseObjectFactory..cctor
// MyUseObjectFactory.RegisterAssemblyTypes(Assembly.LoadFrom(Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll"))); // MyUseObjectFactory.RegisterAssemblyTypes(Assembly.LoadFrom(Path.Combine(MyFileSystem.ExePath, "Sandbox.Game.dll")));
MyUseObjectFactory.RegisterAssemblyTypes(MyPlugins.SandboxGameAssembly); MyUseObjectFactory.RegisterAssemblyTypes(MyPlugins.SandboxGameAssembly);
} }
@@ -117,10 +119,10 @@ public static class IntrospectionPatches
foreach (var type in assemblies.SelectMany(b => IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(b.GetMainModule()))) foreach (var type in assemblies.SelectMany(b => IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(b.GetMainModule())))
{ {
var instance = Activator.CreateInstance(type); var instance = Activator.CreateInstance(type);
if (instance is IPlugin plugin) if (instance is IPlugin plugin)
___m_plugins.Add(plugin); ___m_plugins.Add(plugin);
if (instance is IHandleInputPlugin handleInputPlugin) if (instance is IHandleInputPlugin handleInputPlugin)
___m_handleInputPlugins.Add(handleInputPlugin); ___m_handleInputPlugins.Add(handleInputPlugin);
} }
@@ -138,22 +140,22 @@ public static class IntrospectionPatches
if (assembly?.GetType($"Microsoft.Xml.Serialization.GeneratedAssembly.{typeName}Serializer") is not { } type) if (assembly?.GetType($"Microsoft.Xml.Serialization.GeneratedAssembly.{typeName}Serializer") is not { } type)
return false; return false;
__result = Activator.CreateInstance(type) as XmlSerializer; __result = Activator.CreateInstance(type) as XmlSerializer;
return false; return false;
} }
[HarmonyPrefix, HarmonyPatch(typeof(MyXmlSerializerManager), nameof(MyXmlSerializerManager.RegisterFromAssembly))] [HarmonyPrefix, HarmonyPatch(typeof(MyXmlSerializerManager), nameof(MyXmlSerializerManager.RegisterFromAssembly))]
private static bool XmlManagerRegisterPrefix(Assembly assembly) => AssemblyLoadContext.GetLoadContext(assembly) is ICoreLoadContext; private static bool XmlManagerRegisterPrefix(Assembly assembly) => AssemblyLoadContext.GetLoadContext(assembly) is ICoreLoadContext;
[HarmonyPatch] [HarmonyPatch]
private static class GameAssembliesPatch private static class GameAssembliesPatch
{ {
private static IEnumerable<MethodInfo> TargetMethods() private static IEnumerable<MethodInfo> TargetMethods()
{ {
return AccessTools.GetDeclaredMethods(typeof(MyPlugins)) return AccessTools.GetDeclaredMethods(typeof(MyPlugins))
.Where(b => b.Name.StartsWith("Register") && .Where(b => b.Name.StartsWith("Register") &&
b.GetParameters() is [var param] && param.ParameterType == typeof(string)); b.GetParameters() is [var param] && param.ParameterType == typeof(string));
} }

View File

@@ -13,7 +13,7 @@ public static class PluginNamePatch
// to be just $"Plugin Init: {plugin}" // to be just $"Plugin Init: {plugin}"
// so you could override .ToString // so you could override .ToString
// doesn't change default behavior since base .ToString is .GetType().ToString() // doesn't change default behavior since base .ToString is .GetType().ToString()
return new CodeMatcher(instructions) return new CodeMatcher(instructions)
.SearchForward(b => b.Is(OpCodes.Ldstr, "Plugin Init: ")) .SearchForward(b => b.Is(OpCodes.Ldstr, "Plugin Init: "))
.Advance(2) .Advance(2)

View File

@@ -10,7 +10,7 @@ namespace CringeLauncher.Patches;
public static class ScriptCompilationSettingsPatch public static class ScriptCompilationSettingsPatch
{ {
private static readonly CSharpParseOptions Options = new(LanguageVersion.Latest, DocumentationMode.None); private static readonly CSharpParseOptions Options = new(LanguageVersion.Latest, DocumentationMode.None);
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
var field = AccessTools.Field(typeof(MyScriptCompiler), nameof(MyScriptCompiler.m_conditionalParseOptions)); var field = AccessTools.Field(typeof(MyScriptCompiler), nameof(MyScriptCompiler.m_conditionalParseOptions));

View File

@@ -14,7 +14,7 @@ public static class ScriptCompilerInitializationPatch
return AccessTools.Method(Type.GetType("VRage.Scripting.MyVRageScriptingInternal, VRage.Scripting", true), return AccessTools.Method(Type.GetType("VRage.Scripting.MyVRageScriptingInternal, VRage.Scripting", true),
"Initialize"); "Initialize");
} }
private static bool Prefix(Thread updateThread, Type[] referencedTypes, string[] symbols) private static bool Prefix(Thread updateThread, Type[] referencedTypes, string[] symbols)
{ {
MyModWatchdog.Init(updateThread); MyModWatchdog.Init(updateThread);

View File

@@ -18,10 +18,10 @@ public static class SwapChainPatch
WindowHandle = windowHandle; WindowHandle = windowHandle;
MyPlatformRender.DisposeSwapChain(); MyPlatformRender.DisposeSwapChain();
MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain"); MyPlatformRender.Log.WriteLine("CreateDeviceInternal create swapchain");
if (MyPlatformRender.m_swapchain != null) if (MyPlatformRender.m_swapchain != null)
return false; return false;
var chainDescription = new SwapChainDescription var chainDescription = new SwapChainDescription
{ {
BufferCount = 2, BufferCount = 2,
@@ -39,7 +39,7 @@ public static class SwapChainPatch
Usage = Usage.ShaderInput | Usage.RenderTargetOutput, Usage = Usage.ShaderInput | Usage.RenderTargetOutput,
SwapEffect = SwapEffect.Discard SwapEffect = SwapEffect.Discard
}; };
var factory = MyPlatformRender.GetFactory(); var factory = MyPlatformRender.GetFactory();
try try
{ {
@@ -62,7 +62,7 @@ public static class SwapChainPatch
{ {
ImGuiHandler.Instance?.Init(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext); ImGuiHandler.Instance?.Init(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
} }
[HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))] [HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))]
private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB) private static bool SwapChainBBPrefix(MyBackbuffer __instance, SharpDX.Direct3D11.Resource swapChainBB)
{ {
@@ -73,13 +73,13 @@ public static class SwapChainPatch
Dimension = RenderTargetViewDimension.Texture2D, Dimension = RenderTargetViewDimension.Texture2D,
}); });
__instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB); __instance.m_srv = new ShaderResourceView(MyRender11.DeviceInstance, swapChainBB);
ImGuiHandler.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new() ImGuiHandler.Rtv = new RenderTargetView(MyRender11.DeviceInstance, swapChainBB, new()
{ {
Format = Format.R8G8B8A8_UNorm, Format = Format.R8G8B8A8_UNorm,
Dimension = RenderTargetViewDimension.Texture2D, Dimension = RenderTargetViewDimension.Texture2D,
}); });
return false; return false;
} }
@@ -87,7 +87,7 @@ public static class SwapChainPatch
private static void SwapChainBBReleasePrefix(MyBackbuffer __instance) private static void SwapChainBBReleasePrefix(MyBackbuffer __instance)
{ {
if (ImGuiHandler.Rtv is null) return; if (ImGuiHandler.Rtv is null) return;
ImGuiHandler.Rtv.Dispose(); ImGuiHandler.Rtv.Dispose();
ImGuiHandler.Rtv = null; ImGuiHandler.Rtv = null;
} }

View File

@@ -10,7 +10,7 @@ public static class WhitelistAllowPatch
private static void Prefix(ref MemberInfo[] members) private static void Prefix(ref MemberInfo[] members)
{ {
if (members.Any(b => b is null)) if (members.Any(b => b is null))
members = members.Where(b => b is { }).ToArray(); members = [.. members.Where(b => b is { })];
} }
private static Exception? Finalizer(Exception __exception) private static Exception? Finalizer(Exception __exception)

View File

@@ -14,7 +14,7 @@ public static class WhitelistPatch
private static void Prefix(MyScriptCompiler scriptCompiler) private static void Prefix(MyScriptCompiler scriptCompiler)
{ {
var baseDir = new FileInfo(typeof(Type).Assembly.Location).DirectoryName!; var baseDir = new FileInfo(typeof(Type).Assembly.Location).DirectoryName!;
scriptCompiler.AddReferencedAssemblies( scriptCompiler.AddReferencedAssemblies(
typeof(Type).Assembly.Location, typeof(Type).Assembly.Location,
typeof(LinkedList<>).Assembly.Location, typeof(LinkedList<>).Assembly.Location,

View File

@@ -9,7 +9,7 @@ public static class WhitelistTypeResolutionPatch
{ {
[HarmonyReversePatch] [HarmonyReversePatch]
private static INamedTypeSymbol ResolveTypeSymbol(MyScriptWhitelist.Batch batch, Type type) => throw null!; private static INamedTypeSymbol ResolveTypeSymbol(MyScriptWhitelist.Batch batch, Type type) => throw null!;
// cant be assed to write a transpiler so heres a prefix // cant be assed to write a transpiler so heres a prefix
private static bool Prefix(MyScriptWhitelist.Batch __instance, Type type, ref INamedTypeSymbol __result) private static bool Prefix(MyScriptWhitelist.Batch __instance, Type type, ref INamedTypeSymbol __result)
{ {
@@ -26,18 +26,18 @@ public static class WhitelistTypeResolutionPatch
// if type is not generic or constructed generic, run regular lookup // if type is not generic or constructed generic, run regular lookup
if (!type.IsGenericType || !type.IsConstructedGenericType) if (!type.IsGenericType || !type.IsConstructedGenericType)
return ResolveTypeSymbol(batch, type); return ResolveTypeSymbol(batch, type);
var unconstructedSymbol = ResolveTypeSymbol(batch, type.GetGenericTypeDefinition()); var unconstructedSymbol = ResolveTypeSymbol(batch, type.GetGenericTypeDefinition());
var typeArguments = type.GetGenericArguments(); var typeArguments = type.GetGenericArguments();
var typeSymbolArguments = new ITypeSymbol[typeArguments.Length]; var typeSymbolArguments = new ITypeSymbol[typeArguments.Length];
for (var i = 0; i < typeArguments.Length; i++) for (var i = 0; i < typeArguments.Length; i++)
{ {
// recursively resolve (possibly) generic arguments // recursively resolve (possibly) generic arguments
typeSymbolArguments[i] = ResolveGenericTypeSymbol(batch, typeArguments[i]); typeSymbolArguments[i] = ResolveGenericTypeSymbol(batch, typeArguments[i]);
} }
return unconstructedSymbol.Construct(typeSymbolArguments); return unconstructedSymbol.Construct(typeSymbolArguments);
} }
} }

View File

@@ -11,26 +11,26 @@ public static class XmlRootWriterPatch
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
var ins = instructions.ToList(); var ins = instructions.ToList();
var index = ins.FindIndex(b => var index = ins.FindIndex(b =>
b.opcode == OpCodes.Ldstr && b.operand is "xsi:type"); b.opcode == OpCodes.Ldstr && b.operand is "xsi:type");
ins[index].operand = "xsi"; ins[index].operand = "xsi";
ins.InsertRange(index + 1, new[] ins.InsertRange(index + 1,
{ [
new CodeInstruction(OpCodes.Ldstr, "type"), new CodeInstruction(OpCodes.Ldstr, "type"),
new CodeInstruction(OpCodes.Ldstr, "http://www.w3.org/2001/XMLSchema-instance") new CodeInstruction(OpCodes.Ldstr, "http://www.w3.org/2001/XMLSchema-instance")
}); ]);
var instruction = ins[ins.FindIndex(b => b.opcode == OpCodes.Callvirt)]; var instruction = ins[ins.FindIndex(b => b.opcode == OpCodes.Callvirt)];
instruction.operand = AccessTools.Method(typeof(XmlWriter), "WriteAttributeString", new[] instruction.operand = AccessTools.Method(typeof(XmlWriter), "WriteAttributeString",
{ [
typeof(string), typeof(string),
typeof(string), typeof(string),
typeof(string), typeof(string),
typeof(string) typeof(string)
}); ]);
return ins; return ins;
} }
} }

View File

@@ -9,11 +9,11 @@ namespace CringeLauncher.Utils;
public static class ExceptionFormatter public static class ExceptionFormatter
{ {
private static readonly AccessTools.FieldRef<Exception, string> StackTraceField = AccessTools.FieldRefAccess<Exception, string>("_remoteStackTraceString"); private static readonly AccessTools.FieldRef<Exception, string> StackTraceField = AccessTools.FieldRefAccess<Exception, string>("_remoteStackTraceString");
public static void FormatStackTrace(this Exception exception) public static void FormatStackTrace(this Exception exception)
{ {
var stackTrace = new StackTrace(exception, true); var stackTrace = new StackTrace(exception, true);
var sb = new StringBuilder(); var sb = new StringBuilder();
var i = 0; var i = 0;
@@ -22,12 +22,12 @@ public static class ExceptionFormatter
var method = frame.GetMethod(); var method = frame.GetMethod();
if (method is null) if (method is null)
continue; continue;
sb.Append("at "); sb.Append("at ");
if (method.DeclaringType is { } declaringType && if (method.DeclaringType is { } declaringType &&
AssemblyLoadContext.GetLoadContext(declaringType.Assembly) is { } assemblyLoadContext) AssemblyLoadContext.GetLoadContext(declaringType.Assembly) is { } assemblyLoadContext)
sb.Append(assemblyLoadContext).Append("//"); sb.Append(assemblyLoadContext).Append("//");
if (method.IsStatic) if (method.IsStatic)
sb.Append("static "); sb.Append("static ");
@@ -35,7 +35,7 @@ public static class ExceptionFormatter
sb.Append(methodInfo.ReturnType, false); sb.Append(methodInfo.ReturnType, false);
else else
sb.Append("new"); sb.Append("new");
sb.Append(' '); sb.Append(' ');
if (method.DeclaringType is null) if (method.DeclaringType is null)
@@ -48,7 +48,7 @@ public static class ExceptionFormatter
sb.Append('.'); sb.Append('.');
sb.Append(method.Name); sb.Append(method.Name);
} }
if (method.ContainsGenericParameters) if (method.ContainsGenericParameters)
sb.Append(method.GetGenericArguments(), false); sb.Append(method.GetGenericArguments(), false);
@@ -63,7 +63,7 @@ public static class ExceptionFormatter
if (j < parameters.Length - 1) if (j < parameters.Length - 1)
sb.Append(", "); sb.Append(", ");
} }
sb.Append(')'); sb.Append(')');
if (frame.GetFileName() is { } fileName) if (frame.GetFileName() is { } fileName)
@@ -75,7 +75,7 @@ public static class ExceptionFormatter
} }
ref var stackTraceString = ref StackTraceField(exception); ref var stackTraceString = ref StackTraceField(exception);
stackTraceString = sb.ToString(); stackTraceString = sb.ToString();
} }
@@ -84,9 +84,9 @@ public static class ExceptionFormatter
if (fullName && !string.IsNullOrEmpty(type.Namespace)) if (fullName && !string.IsNullOrEmpty(type.Namespace))
sb.Append(type.Namespace).Append('.'); sb.Append(type.Namespace).Append('.');
sb.Append(type.Name); sb.Append(type.Name);
if (type.ContainsGenericParameters) if (type.ContainsGenericParameters)
sb.Append(type.GetGenericArguments(), fullName); sb.Append(type.GetGenericArguments(), fullName);
return sb; return sb;
} }

View File

@@ -8,7 +8,7 @@ public static class MethodTools
public static MethodInfo AsyncMethodBody(MethodInfo method) public static MethodInfo AsyncMethodBody(MethodInfo method)
{ {
var (_, operand) = PatchProcessor.ReadMethodBody(method).First(); var (_, operand) = PatchProcessor.ReadMethodBody(method).First();
if (operand is not LocalVariableInfo localVar) if (operand is not LocalVariableInfo localVar)
throw new InvalidOperationException($"Method {method.FullDescription()} does not contain a valid async state machine"); throw new InvalidOperationException($"Method {method.FullDescription()} does not contain a valid async state machine");

View File

@@ -9,16 +9,18 @@ namespace CringePlugins.Config;
public sealed class ConfigHandler public sealed class ConfigHandler
{ {
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); public static readonly JsonSerializerOptions SerializerOptions = new(NuGetClient.SerializerOptions)
private readonly DirectoryInfo _configDirectory;
private readonly JsonSerializerOptions _serializerOptions = new(NuGetClient.SerializerOptions)
{ {
WriteIndented = true, WriteIndented = true,
AllowTrailingCommas = true, AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip ReadCommentHandling = JsonCommentHandling.Skip
}; };
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private readonly DirectoryInfo _configDirectory;
private readonly EvaluationOptions _evaluationOptions = new() private readonly EvaluationOptions _evaluationOptions = new()
{ {
OutputFormat = OutputFormat.List, OutputFormat = OutputFormat.List,
@@ -67,7 +69,7 @@ public sealed class ConfigHandler
T instance; T instance;
try try
{ {
instance = jsonNode.Deserialize<T>(_serializerOptions)!; instance = jsonNode.Deserialize<T>(SerializerOptions)!;
} }
catch (JsonException e) catch (JsonException e)
{ {
@@ -84,7 +86,7 @@ public sealed class ConfigHandler
{ {
var spec = IConfigurationSpecProvider.FromType(typeof(T)); var spec = IConfigurationSpecProvider.FromType(typeof(T));
var jsonNode = JsonSerializer.SerializeToNode(newValue, _serializerOptions)!; var jsonNode = JsonSerializer.SerializeToNode(newValue, SerializerOptions)!;
if (spec != null && !TryValidate(name, spec, jsonNode)) if (spec != null && !TryValidate(name, spec, jsonNode))
throw new JsonException($"Supplied config value for {name} is invalid"); throw new JsonException($"Supplied config value for {name} is invalid");
@@ -96,7 +98,7 @@ public sealed class ConfigHandler
{ {
Indented = true Indented = true
}); });
jsonNode.WriteTo(writer, _serializerOptions); jsonNode.WriteTo(writer, SerializerOptions);
ConfigReloaded?.Invoke(this, new ConfigValue<T>(name, newValue)); ConfigReloaded?.Invoke(this, new ConfigValue<T>(name, newValue));
} }

View File

@@ -0,0 +1,5 @@
namespace CringePlugins.Config;
public sealed record LauncherConfig(bool DisableLauncherUpdates, bool DisablePluginUpdates)
{
public static LauncherConfig Default => new(false, false);
}

View File

@@ -51,7 +51,7 @@ internal class PluginAssemblyLoadContext : DerivedAssemblyLoadContext
{ {
if (_dependencyResolver.ResolveAssemblyToPath(assemblyName) is { } path) if (_dependencyResolver.ResolveAssemblyToPath(assemblyName) is { } path)
return LoadFromAssemblyPath(path); return LoadFromAssemblyPath(path);
return base.Load(assemblyName); return base.Load(assemblyName);
} }
@@ -59,7 +59,7 @@ internal class PluginAssemblyLoadContext : DerivedAssemblyLoadContext
{ {
if (_dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName) is { } path) if (_dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName) is { } path)
return LoadUnmanagedDllFromPath(path); return LoadUnmanagedDllFromPath(path);
return base.LoadUnmanagedDll(unmanagedDllName); return base.LoadUnmanagedDll(unmanagedDllName);
} }

View File

@@ -29,14 +29,14 @@ internal sealed class PluginInstance(PluginMetadata metadata, string entrypointP
{ {
if (AssemblyLoadContext.GetLoadContext(typeof(PluginInstance).Assembly) is not ICoreLoadContext parentContext) if (AssemblyLoadContext.GetLoadContext(typeof(PluginInstance).Assembly) is not ICoreLoadContext parentContext)
throw new NotSupportedException("Plugin instantiation is not supported in this context"); throw new NotSupportedException("Plugin instantiation is not supported in this context");
_context = new PluginAssemblyLoadContext(parentContext, entrypointPath); _context = new PluginAssemblyLoadContext(parentContext, entrypointPath);
contextBuilder.Add(_context); contextBuilder.Add(_context);
var entrypoint = _context.LoadEntrypoint(); var entrypoint = _context.LoadEntrypoint();
var plugins = IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(entrypoint.GetMainModule()).ToArray(); var plugins = IntrospectionContext.Global.CollectDerivedTypes<IPlugin>(entrypoint.GetMainModule()).ToArray();
if (plugins.Length == 0) if (plugins.Length == 0)
throw new InvalidOperationException("Entrypoint does not contain any plugins"); throw new InvalidOperationException("Entrypoint does not contain any plugins");
if (plugins.Length > 1) if (plugins.Length > 1)
@@ -66,7 +66,7 @@ internal sealed class PluginInstance(PluginMetadata metadata, string entrypointP
{ {
if (_instance is null) if (_instance is null)
throw new InvalidOperationException("Must call Instantiate first"); throw new InvalidOperationException("Must call Instantiate first");
MyPlugins.m_plugins.Add(WrappedInstance); MyPlugins.m_plugins.Add(WrappedInstance);
if (_instance is IHandleInputPlugin) if (_instance is IHandleInputPlugin)
MyPlugins.m_handleInputPlugins.Add(WrappedInstance); MyPlugins.m_handleInputPlugins.Add(WrappedInstance);

View File

@@ -19,7 +19,7 @@ public record PluginMetadata(string Name, NuGetVersion Version, string Source)
(versionAttribute ?? fileVersionAttribute)?.ConstructorArguments[0].Value as UTF8String ?? "0.0.0.0", (versionAttribute ?? fileVersionAttribute)?.ConstructorArguments[0].Value as UTF8String ?? "0.0.0.0",
out var version)) out var version))
version = new(0, 0, 0, 0); version = new(0, 0, 0, 0);
return new(name, version, "Local"); return new(name, version, "Local");
} }
} }

View File

@@ -14,7 +14,7 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I
public Type InstanceType => plugin.GetType(); public Type InstanceType => plugin.GetType();
private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
private readonly IHandleInputPlugin? _handleInputPlugin = plugin as IHandleInputPlugin; private readonly IHandleInputPlugin? _handleInputPlugin = plugin as IHandleInputPlugin;
private const float ErrorShowTime = 10f; private const float ErrorShowTime = 10f;

View File

@@ -20,14 +20,15 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
public static ImmutableArray<DerivedAssemblyLoadContext> Contexts { get; private set; } = []; public static ImmutableArray<DerivedAssemblyLoadContext> Contexts { get; private set; } = [];
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string Name => "Loading Plugins"; public string Name => "Loading Plugins";
private ImmutableArray<PluginInstance> _plugins = []; private ImmutableArray<PluginInstance> _plugins = [];
private readonly DirectoryInfo _dir = Directory.CreateDirectory(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CringeLauncher")); private readonly DirectoryInfo _dir = Directory.CreateDirectory(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CringeLauncher"));
private readonly NuGetRuntimeFramework _runtimeFramework = new(NuGetFramework.ParseFolder("net9.0-windows10.0.19041.0"), RuntimeInformation.RuntimeIdentifier); private readonly NuGetRuntimeFramework _runtimeFramework = new(NuGetFramework.ParseFolder("net9.0-windows10.0.19041.0"), RuntimeInformation.RuntimeIdentifier);
private ConfigReference<PackagesConfig>? _configReference; private ConfigReference<PackagesConfig>? _configReference;
private ConfigReference<LauncherConfig>? _launcherConfig;
public async ValueTask Load(ISplashProgress progress) public async ValueTask Load(ISplashProgress progress)
{ {
@@ -40,7 +41,9 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
progress.Report("Loading config"); progress.Report("Loading config");
_configReference = configHandler.RegisterConfig("packages", PackagesConfig.Default); _configReference = configHandler.RegisterConfig("packages", PackagesConfig.Default);
_launcherConfig = configHandler.RegisterConfig("launcher", LauncherConfig.Default);
var packagesConfig = _configReference.Value; var packagesConfig = _configReference.Value;
var launcherConfig = _launcherConfig.Value;
progress.Report("Resolving packages"); progress.Report("Resolving packages");
@@ -48,12 +51,14 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
// TODO take into account the target framework runtime identifier // TODO take into account the target framework runtime identifier
var resolver = new PackageResolver(_runtimeFramework.Framework, packagesConfig.Packages, sourceMapping); var resolver = new PackageResolver(_runtimeFramework.Framework, packagesConfig.Packages, sourceMapping);
var packages = await resolver.ResolveAsync(); var cacheDir = _dir.CreateSubdirectory("cache");
var packages = await resolver.ResolveAsync(cacheDir, launcherConfig.DisablePluginUpdates);
progress.Report("Downloading packages"); progress.Report("Downloading packages");
var builtInPackages = await BuiltInPackages.GetPackagesAsync(_runtimeFramework); var builtInPackages = await BuiltInPackages.GetPackagesAsync(_runtimeFramework);
var cachedPackages = await resolver.DownloadPackagesAsync(_dir.CreateSubdirectory("cache"), packages, builtInPackages.Keys.ToHashSet(), progress); var cachedPackages = await PackageResolver.DownloadPackagesAsync(cacheDir, packages, builtInPackages.Keys.ToHashSet(), progress);
progress.Report("Loading plugins"); progress.Report("Loading plugins");
@@ -62,7 +67,7 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
await LoadPlugins(cachedPackages, sourceMapping, packagesConfig, builtInPackages); await LoadPlugins(cachedPackages, sourceMapping, packagesConfig, builtInPackages);
RenderHandler.Current.RegisterComponent(new PluginListComponent(_configReference, sourceMapping, MyFileSystem.ExePath, _plugins)); RenderHandler.Current.RegisterComponent(new PluginListComponent(_configReference, _launcherConfig, sourceMapping, MyFileSystem.ExePath, _plugins));
} }
public void RegisterLifetime() public void RegisterLifetime()
@@ -87,7 +92,7 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
PackagesConfig packagesConfig, ImmutableDictionary<string, ResolvedPackage> builtInPackages) PackagesConfig packagesConfig, ImmutableDictionary<string, ResolvedPackage> builtInPackages)
{ {
var plugins = _plugins.ToBuilder(); var plugins = _plugins.ToBuilder();
var resolvedPackages = builtInPackages.ToDictionary(); var resolvedPackages = builtInPackages.ToDictionary();
foreach (var package in packages) foreach (var package in packages)
{ {
@@ -100,14 +105,14 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
resolvedPackages.TryGetValue(dependency.Id, out var package); resolvedPackages.TryGetValue(dependency.Id, out var package);
return package?.Entry; return package?.Entry;
}); });
foreach (var package in packages) foreach (var package in packages)
{ {
if (builtInPackages.ContainsKey(package.Package.Id)) continue; if (builtInPackages.ContainsKey(package.Package.Id)) continue;
var client = await sourceMapping.GetClientAsync(package.Package.Id); var packageClient = await sourceMapping.GetClientAsync(package.Package.Id);
if (client == null) if (packageClient == null)
{ {
Log.Warn("Client not found for {Package}", package.Package.Id); Log.Warn("Client not found for {Package}", package.Package.Id);
continue; continue;
@@ -133,29 +138,29 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
} }
} }
var sourceName = packagesConfig.Sources.First(b => b.Url == client.ToString()).Name; var sourceName = packagesConfig.Sources.First(b => b.Url == packageClient.ToString()).Name;
LoadComponent(plugins, Path.Join(dir, $"{package.Package.Id}.dll"), LoadComponent(plugins, Path.Join(dir, $"{package.Package.Id}.dll"),
new(package.Package.Id, package.Package.Version, sourceName)); new(package.Package.Id, package.Package.Version, sourceName));
} }
_plugins = plugins.ToImmutable(); _plugins = plugins.ToImmutable();
} }
private void DiscoverLocalPlugins(DirectoryInfo dir) private void DiscoverLocalPlugins(DirectoryInfo dir)
{ {
var plugins = ImmutableArray<PluginInstance>.Empty.ToBuilder(); var plugins = ImmutableArray<PluginInstance>.Empty.ToBuilder();
foreach (var directory in dir.EnumerateDirectories()) foreach (var directory in dir.EnumerateDirectories())
{ {
var files = directory.GetFiles("*.deps.json"); var files = directory.GetFiles("*.deps.json");
if (files.Length != 1) continue; if (files.Length != 1) continue;
var path = files[0].FullName[..^".deps.json".Length] + ".dll"; var path = files[0].FullName[..^".deps.json".Length] + ".dll";
LoadComponent(plugins, path); LoadComponent(plugins, path);
} }
_plugins = plugins.ToImmutable(); _plugins = plugins.ToImmutable();
} }
@@ -166,7 +171,7 @@ internal class PluginsLifetime(ConfigHandler configHandler, HttpClient client) :
plugins.Add(metadata is null ? new PluginInstance(path) : new(metadata, path)); plugins.Add(metadata is null ? new PluginInstance(path) : new(metadata, path));
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e, "Failed to load plugin {PluginPath}", path); Log.Error(e, "Failed to load plugin {PluginPath}", path);
} }
} }

View File

@@ -33,10 +33,10 @@ public static class BuiltInPackages
(_, _, _, libraries) = await DependencyManifestSerializer.DeserializeAsync(stream); (_, _, _, libraries) = await DependencyManifestSerializer.DeserializeAsync(stream);
var framework = runtimeFramework.Framework; var framework = runtimeFramework.Framework;
var nlog = FromAssembly<LogFactory>(framework, version: libraries.Keys.Single(b => b.Id == NLog).Version); var nlog = FromAssembly<LogFactory>(framework, version: libraries.Keys.Single(b => b.Id == NLog).Version);
Version seVersion = new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion!.Value); Version seVersion = new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion!.Value);
var se = FromAssembly<SpaceEngineersGame>(framework, [ var se = FromAssembly<SpaceEngineersGame>(framework, [
nlog.AsDependency(libraries) nlog.AsDependency(libraries)
], SeReferenceAssemblies, new(seVersion)); ], SeReferenceAssemblies, new(seVersion));
@@ -50,7 +50,7 @@ public static class BuiltInPackages
var def = ModuleDefMD.Load(r.ImageBytes, IntrospectionContext.Global.Context); var def = ModuleDefMD.Load(r.ImageBytes, IntrospectionContext.Global.Context);
var attribute = def.CustomAttributes.Find(typeof(AssemblyFileVersionAttribute).FullName); var attribute = def.CustomAttributes.Find(typeof(AssemblyFileVersionAttribute).FullName);
var version = attribute is null ? new(99, 0, 0) : NuGetVersion.Parse((string)attribute.ConstructorArguments[0].Value); var version = attribute is null ? new(99, 0, 0) : NuGetVersion.Parse((string)attribute.ConstructorArguments[0].Value);
return new BuiltInSdkPackage( return new BuiltInSdkPackage(
new(0, Path.GetFileNameWithoutExtension(r.FileName), version), framework, new(0, Path.GetFileNameWithoutExtension(r.FileName), version), framework,
new(Path.GetFileNameWithoutExtension(r.FileName), version, [new(framework, [])], null, [])); new(Path.GetFileNameWithoutExtension(r.FileName), version, [new(framework, [])], null, []));
@@ -95,16 +95,16 @@ public static class BuiltInPackages
var builder = ImmutableDictionary.CreateBuilder<string, ResolvedPackage>(); var builder = ImmutableDictionary.CreateBuilder<string, ResolvedPackage>();
foreach (var package in packages) foreach (var package in packages)
builder.TryAdd(package.Package.Id, package); builder.TryAdd(package.Package.Id, package);
return builder.ToImmutable(); return builder.ToImmutable();
} }
private static Dependency AsDependency(this ResolvedPackage package, ImmutableDictionary<ManifestPackageKey, DependencyLibrary> libraries) private static Dependency AsDependency(this ResolvedPackage package, ImmutableDictionary<ManifestPackageKey, DependencyLibrary> libraries)
{ {
//ignore the SE reference because the game can update without a launcher update //ignore the SE reference because the game can update without a launcher update
if (package.Entry.Id != SeReferenceAssemblies && !libraries.ContainsKey(new(package.Package.Id, package.Package.Version))) if (package.Entry.Id != SeReferenceAssemblies && !libraries.ContainsKey(new(package.Package.Id, package.Package.Version)))
throw new KeyNotFoundException($"Package {package.Package} not found in root dependencies manifest"); throw new KeyNotFoundException($"Package {package.Package} not found in root dependencies manifest");
return new Dependency(package.Package.Id, new(package.Package.Version)); return new Dependency(package.Package.Id, new(package.Package.Version));
} }

View File

@@ -1,17 +1,17 @@
using System.Collections.Immutable; using NLog;
using System.IO.Compression;
using NLog;
using NuGet; using NuGet;
using NuGet.Frameworks; using NuGet.Frameworks;
using NuGet.Models; using NuGet.Models;
using NuGet.Versioning; using NuGet.Versioning;
using System.Collections.Immutable;
using System.IO.Compression;
namespace CringePlugins.Resolver; namespace CringePlugins.Resolver;
public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<PackageReference> references, PackageSourceMapping packageSources) public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<PackageReference> references, PackageSourceMapping packageSources)
{ {
private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
public async Task<ImmutableSortedSet<ResolvedPackage>> ResolveAsync() public async Task<ImmutableSortedSet<ResolvedPackage>> ResolveAsync(DirectoryInfo baseDir, bool disableUpdates)
{ {
var order = 0; var order = 0;
var packages = new Dictionary<Package, CatalogEntry>(); var packages = new Dictionary<Package, CatalogEntry>();
@@ -39,26 +39,43 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
page.Items!.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"])) page.Items!.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"]))
.ToImmutableDictionary(b => b.CatalogEntry.Version); .ToImmutableDictionary(b => b.CatalogEntry.Version);
var version = items.Values.Select(b => b.CatalogEntry.Version).OrderDescending().First(b => reference.Range.Satisfies(b)); var version = items.Values.Select(b => b.CatalogEntry.Version).OrderDescending().First(reference.Range.Satisfies);
if (disableUpdates)
{
if (GetLatestInstalledVersion(baseDir, reference.Id, reference.Range) is { } installedVersion && items.ContainsKey(installedVersion))
{
if (installedVersion < version)
{
Log.Warn("Using outdated version of package {Package} {InstalledVersion} instead of {AvailableVersion} due to updates being disabled",
reference.Id, installedVersion, version);
}
version = installedVersion;
}
else
{
Log.Warn("No valid installed version found for package {Package}", reference.Id);
}
}
if (version is null) if (version is null)
throw new NotSupportedException($"Unable to find version for package {reference.Id}"); throw new NotSupportedException($"Unable to find version for package {reference.Id}");
var catalogEntry = items[version].CatalogEntry; var catalogEntry = items[version].CatalogEntry;
var package = new Package(order, reference.Id, version); var package = new Package(order, reference.Id, version);
if (packages.TryAdd(package, catalogEntry)) if (packages.TryAdd(package, catalogEntry))
continue; continue;
if (!packages.TryGetValue(package, out var existingEntry)) if (!packages.TryGetValue(package, out var existingEntry))
throw new InvalidOperationException($"Duplicate package error {package.Id}"); throw new InvalidOperationException($"Duplicate package error {package.Id}");
if (package.Version < existingEntry.Version) if (package.Version < existingEntry.Version)
throw new NotSupportedException($"Package reference {package.Id} has lower version {package.Version} than already resolved {existingEntry.Version}"); throw new NotSupportedException($"Package reference {package.Id} has lower version {package.Version} than already resolved {existingEntry.Version}");
if (package.Version == existingEntry.Version) if (package.Version == existingEntry.Version)
continue; continue;
@@ -69,13 +86,13 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
foreach (var (package, catalogEntry) in packages) foreach (var (package, catalogEntry) in packages)
{ {
var client = await packageSources.GetClientAsync(package.Id); var client = await packageSources.GetClientAsync(package.Id);
if (client == null || !catalogEntry.DependencyGroups.HasValue) if (client == null || !catalogEntry.DependencyGroups.HasValue)
continue; continue;
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, runtimeFramework, var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, runtimeFramework,
g => g.TargetFramework); g => g.TargetFramework);
if (nearestGroup is null) if (nearestGroup is null)
throw new NotSupportedException($"Unable to find compatible dependency group for package {package.Id}"); throw new NotSupportedException($"Unable to find compatible dependency group for package {package.Id}");
@@ -91,14 +108,14 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
var dependencies = package.Entry.DependencyGroups var dependencies = package.Entry.DependencyGroups
?.Single(b => b.TargetFramework == package.ResolvedFramework)?.Dependencies ?? ?.Single(b => b.TargetFramework == package.ResolvedFramework)?.Dependencies ??
[]; [];
foreach (var (id, versionRange) in dependencies) foreach (var (id, versionRange) in dependencies)
{ {
var client = await packageSources.GetClientAsync(id); var client = await packageSources.GetClientAsync(id);
if (client == null) if (client == null)
continue; continue;
RegistrationRoot? registrationRoot; RegistrationRoot? registrationRoot;
try try
@@ -109,15 +126,29 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
{ {
throw new InvalidOperationException($"Failed to resolve dependency {id} for {package.Package}", ex); throw new InvalidOperationException($"Failed to resolve dependency {id} for {package.Package}", ex);
} }
var items = registrationRoot.Items.SelectMany(page => page.Items!) var items = registrationRoot.Items.SelectMany(page => page.Items!)
.ToImmutableDictionary(b => b.CatalogEntry.Version); .ToImmutableDictionary(b => b.CatalogEntry.Version);
var version = items.Values.Select(b => b.CatalogEntry.Version).OrderDescending().FirstOrDefault(versionRange.Satisfies); var version = items.Values.Select(b => b.CatalogEntry.Version).OrderDescending().FirstOrDefault(versionRange.Satisfies);
if (version is null) if (version is null)
throw new NotSupportedException($"Unable to find version for package {id} as dependency of {package.Package}"); throw new NotSupportedException($"Unable to find version for package {id} as dependency of {package.Package}");
if (disableUpdates)
{
if (GetLatestInstalledVersion(baseDir, id, versionRange) is { } installedVersion && items.ContainsKey(installedVersion))
{
if (installedVersion < version)
{
Log.Warn("Using outdated version of dependency package {Package} {InstalledVersion} instead of {AvailableVersion} due to updates being disabled",
id, installedVersion, version);
}
version = installedVersion;
}
//todo: warnings here? we'd need to check against builtin packages
}
var catalogEntry = items[version].CatalogEntry; var catalogEntry = items[version].CatalogEntry;
var dependencyPackage = new Package(i, id, version); var dependencyPackage = new Package(i, id, version);
@@ -165,10 +196,10 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
continue; continue;
} }
if (!packages.TryAdd(dependencyPackage, catalogEntry) || !dependencyVersions.TryAdd(dependencyPackage, versionRange)) if (!packages.TryAdd(dependencyPackage, catalogEntry) || !dependencyVersions.TryAdd(dependencyPackage, versionRange))
throw new InvalidOperationException($"Duplicate package {dependencyPackage.Id}"); throw new InvalidOperationException($"Duplicate package {dependencyPackage.Id}");
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups ?? [], runtimeFramework, var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups ?? [], runtimeFramework,
g => g.TargetFramework) ?? throw new NotSupportedException($"Unable to find compatible dependency group for {dependencyPackage} as dependency of {package.Package}"); g => g.TargetFramework) ?? throw new NotSupportedException($"Unable to find compatible dependency group for {dependencyPackage} as dependency of {package.Package}");
@@ -182,11 +213,30 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
return set.ToImmutable(); return set.ToImmutable();
} }
public async Task<ImmutableHashSet<CachedPackage>> DownloadPackagesAsync(DirectoryInfo baseDirectory, private static NuGetVersion? GetLatestInstalledVersion(DirectoryInfo baseDirectory, string id, VersionRange range)
{
var dir = new DirectoryInfo(Path.Join(baseDirectory.FullName, id));
if (!dir.Exists)
return null;
NuGetVersion? maxVersion = null;
foreach (var subdir in dir.GetDirectories())
{
if (NuGetVersion.TryParse(subdir.Name, out var version) && range.Satisfies(version) && (maxVersion == null || version > maxVersion))
{
maxVersion = version;
}
}
return maxVersion;
}
public static async Task<ImmutableHashSet<CachedPackage>> DownloadPackagesAsync(DirectoryInfo baseDirectory,
IReadOnlySet<ResolvedPackage> resolvedPackages, IReadOnlySet<string>? ignorePackages = null, IProgress<float>? progress = null) IReadOnlySet<ResolvedPackage> resolvedPackages, IReadOnlySet<string>? ignorePackages = null, IProgress<float>? progress = null)
{ {
var packages = ImmutableHashSet<CachedPackage>.Empty.ToBuilder(); var packages = ImmutableHashSet<CachedPackage>.Empty.ToBuilder();
var i = 0f; var i = 0f;
foreach (var package in resolvedPackages) foreach (var package in resolvedPackages)
{ {
@@ -206,26 +256,26 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
var client = (package as RemoteDependencyPackage)?.Client ?? ((RemotePackage)package).Client; var client = (package as RemoteDependencyPackage)?.Client ?? ((RemotePackage)package).Client;
await using var stream = await client.GetPackageContentStreamAsync(package.Package.Id, package.Package.Version); await using var stream = await client.GetPackageContentStreamAsync(package.Package.Id, package.Package.Version);
using var memStream = new MemoryStream(); await using var memStream = new MemoryStream();
await stream.CopyToAsync(memStream); await stream.CopyToAsync(memStream);
memStream.Position = 0; memStream.Position = 0;
using var archive = new ZipArchive(memStream, ZipArchiveMode.Read); using var archive = new ZipArchive(memStream, ZipArchiveMode.Read);
archive.ExtractToDirectory(dir.FullName); archive.ExtractToDirectory(dir.FullName);
} }
packages.Add(new CachedPackage(package.Package, package.ResolvedFramework, dir, package.Entry)); packages.Add(new CachedPackage(package.Package, package.ResolvedFramework, dir, package.Entry));
break; break;
} }
case CachedPackage cachedPackage: case CachedPackage cachedPackage:
packages.Add(cachedPackage); packages.Add(cachedPackage);
break; break;
} }
progress?.Report(i++ / resolvedPackages.Count); progress?.Report(i++ / resolvedPackages.Count);
} }
return packages.ToImmutable(); return packages.ToImmutable();
} }
} }

View File

@@ -48,7 +48,7 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
}); });
var srv = new ShaderResourceView(MyRender11.DeviceInstance, tex); var srv = new ShaderResourceView(MyRender11.DeviceInstance, tex);
_placeholderImage = new Image(null!, srv, new(1, 1)); _placeholderImage = new Image(null!, srv, new(1, 1));
} }
@@ -88,12 +88,12 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
try try
{ {
var request = new HttpRequestMessage(HttpMethod.Get, url); var request = new HttpRequestMessage(HttpMethod.Get, url);
if (_webCacheEtag.TryGetValue(new(url), out var existingEtag)) if (_webCacheEtag.TryGetValue(new(url), out var existingEtag))
request.Headers.IfNoneMatch.Add(existingEtag); request.Headers.IfNoneMatch.Add(existingEtag);
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
if (response.Headers.ETag is { } etag) if (response.Headers.ETag is { } etag)
_webCacheEtag[new(url)] = etag; _webCacheEtag[new(url)] = etag;
@@ -125,7 +125,7 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
if (cacheControl.MaxAge.HasValue) if (cacheControl.MaxAge.HasValue)
{ {
var responseAge = DateTimeOffset.UtcNow - cacheControl.MaxAge.Value; var responseAge = DateTimeOffset.UtcNow - cacheControl.MaxAge.Value;
return File.GetLastWriteTimeUtc(path) > responseAge; return File.GetLastWriteTimeUtc(path) > responseAge;
} }
return true; return true;
@@ -160,14 +160,14 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
CpuAccessFlags = CpuAccessFlags.None, CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None, OptionFlags = ResourceOptionFlags.None,
}, img.ToDataBox()); }, img.ToDataBox());
var srv = new ShaderResourceView(MyRender11.DeviceInstance, tex); var srv = new ShaderResourceView(MyRender11.DeviceInstance, tex);
image = new Image(identifier, srv, new(desc.Width, desc.Height)); image = new Image(identifier, srv, new(desc.Width, desc.Height));
_images.Add(identifier, image, true); _images.Add(identifier, image, true);
return image; return image;
} }
private class ImageReference(ImGuiImage placeholderImage) : ImGuiImage private class ImageReference(ImGuiImage placeholderImage) : ImGuiImage
{ {
public ImGuiImage? Image; public ImGuiImage? Image;
@@ -175,14 +175,14 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
public override nint TextureId => Image ?? ErrorImage ?? placeholderImage; public override nint TextureId => Image ?? ErrorImage ?? placeholderImage;
public override Vector2 Size => Image ?? ErrorImage ?? placeholderImage; public override Vector2 Size => Image ?? ErrorImage ?? placeholderImage;
public override void Dispose() public override void Dispose()
{ {
Image?.Dispose(); Image?.Dispose();
ErrorImage?.Dispose(); ErrorImage?.Dispose();
} }
} }
private class Image(ImageIdentifier identifier, ShaderResourceView srv, Vector2 size) : ImGuiImage private class Image(ImageIdentifier identifier, ShaderResourceView srv, Vector2 size) : ImGuiImage
{ {
private bool _disposed; private bool _disposed;
@@ -210,7 +210,7 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
private void OnUse() private void OnUse()
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
_lastUse = Stopwatch.GetTimestamp(); _lastUse = Stopwatch.GetTimestamp();
} }
@@ -223,20 +223,20 @@ internal sealed class ImGuiImageService(HttpClient client) : IImGuiImageService
public override string ToString() public override string ToString()
{ {
return $"Image {{ {identifier} {size} }}"; return $"Image {{ {identifier} {size} }}";
} }
} }
private abstract record ImageIdentifier; private abstract record ImageIdentifier;
private record WebImageIdentifier(Uri Url) : ImageIdentifier; private record WebImageIdentifier(Uri Url) : ImageIdentifier;
private record FileImageIdentifier(string Path) : ImageIdentifier; private record FileImageIdentifier(string Path) : ImageIdentifier;
} }
public abstract class ImGuiImage : IDisposable public abstract class ImGuiImage : IDisposable
{ {
public abstract nint TextureId { get; } public abstract nint TextureId { get; }
public abstract Vector2 Size { get; } public abstract Vector2 Size { get; }
public static implicit operator nint(ImGuiImage image) => image.TextureId; public static implicit operator nint(ImGuiImage image) => image.TextureId;
public static implicit operator Vector2(ImGuiImage image) => image.Size; public static implicit operator Vector2(ImGuiImage image) => image.Size;
public abstract void Dispose(); public abstract void Dispose();

View File

@@ -1,6 +1,6 @@
namespace CringePlugins.Splash; namespace CringePlugins.Splash;
public interface ISplashProgress : IProgress<ProgressInfo>, IProgress<float> public interface ISplashProgress : IProgress<ProgressInfo>, IProgress<float>
{ {
void DefineStage(ILoadingStage stage); void DefineStage(ILoadingStage stage);
void DefineStepsCount(int count); void DefineStepsCount(int count);

View File

@@ -10,16 +10,16 @@ namespace CringePlugins.Splash;
public class Splash : ISplashProgress, IRenderComponent public class Splash : ISplashProgress, IRenderComponent
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly List<ILoadingStage> _loadingStages = []; private readonly List<ILoadingStage> _loadingStages = [];
private ProgressInfo? _lastInfo; private ProgressInfo? _lastInfo;
private bool _done; private bool _done;
public void Report(ProgressInfo value) public void Report(ProgressInfo value)
{ {
_lastInfo = value; _lastInfo = value;
if (value is PercentProgressInfo percentProgressInfo) if (value is PercentProgressInfo percentProgressInfo)
Logger.Info("{Text} {Percent:P0}", percentProgressInfo.Text, percentProgressInfo.Percent); Logger.Info("{Text} {Percent:P0}", percentProgressInfo.Text, percentProgressInfo.Percent);
else else
@@ -58,7 +58,7 @@ public class Splash : ISplashProgress, IRenderComponent
public void OnFrame() public void OnFrame()
{ {
if (_done) return; if (_done) return;
SetNextWindowPos(GetMainViewport().GetCenter(), ImGuiCond.Always, new(.5f, .5f)); SetNextWindowPos(GetMainViewport().GetCenter(), ImGuiCond.Always, new(.5f, .5f));
SetNextWindowSize(new(400, GetFrameHeightWithSpacing()), ImGuiCond.Always); SetNextWindowSize(new(400, GetFrameHeightWithSpacing()), ImGuiCond.Always);
Begin("Splash", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoInputs); Begin("Splash", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoInputs);
@@ -68,13 +68,13 @@ public class Splash : ISplashProgress, IRenderComponent
{ {
const string text = "Loading..."; const string text = "Loading...";
var size = CalcTextSize(text); var size = CalcTextSize(text);
SetCursorPosX((GetWindowWidth() - size.X) * .5f); SetCursorPosX((GetWindowWidth() - size.X) * .5f);
Text(text); Text(text);
} }
else else
ProgressBar((_lastInfo as PercentProgressInfo)?.Percent ?? 0, sizeArg, _lastInfo.Text); ProgressBar((_lastInfo as PercentProgressInfo)?.Percent ?? 0, sizeArg, _lastInfo.Text);
End(); End();
} }
} }

View File

@@ -25,7 +25,6 @@ public sealed class NotificationsComponent : IRenderComponent
var lastY = _notificationSize.Y; var lastY = _notificationSize.Y;
var viewportPos = ImGui.GetMainViewport().Pos; var viewportPos = ImGui.GetMainViewport().Pos;
//todo: consider adding a limit to the number of messages that can be displayed at once //todo: consider adding a limit to the number of messages that can be displayed at once
for (var i = Notifications.Count; i-- > 0;) for (var i = Notifications.Count; i-- > 0;)
{ {
@@ -76,7 +75,7 @@ public sealed class NotificationsComponent : IRenderComponent
Notifications.RemoveAll(x => x.IsGarbage); Notifications.RemoveAll(x => x.IsGarbage);
_time += MyCommon.GetLastFrameDelta(); _time += MyCommon.GetLastFrameDelta();
} }
public static void SpawnNotification(float showTime, Action renderCallback) public static void SpawnNotification(float showTime, Action renderCallback)

View File

@@ -38,10 +38,13 @@ internal class PluginListComponent : IRenderComponent
private int _selectedProfile = -1; private int _selectedProfile = -1;
private ImmutableArray<Profile> _profiles; private ImmutableArray<Profile> _profiles;
private bool _disableUpdates;
private bool _disablePluginUpdates;
private bool _changed; private bool _restartRequired;
private bool _open = true; private bool _open = true;
private readonly ConfigReference<PackagesConfig> _packagesConfig; private readonly ConfigReference<PackagesConfig> _packagesConfig;
private readonly ConfigReference<LauncherConfig> _launcherConfig;
private readonly PackageSourceMapping _sourceMapping; private readonly PackageSourceMapping _sourceMapping;
private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web); private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web);
private ImmutableHashSet<PackageSource>? _selectedSources; private ImmutableHashSet<PackageSource>? _selectedSources;
@@ -51,10 +54,11 @@ internal class PluginListComponent : IRenderComponent
private (PackageSource source, int index)? _selectedSource; private (PackageSource source, int index)? _selectedSource;
private readonly IImGuiImageService _imageService = GameServicesExtension.GameServices.GetRequiredService<IImGuiImageService>(); private readonly IImGuiImageService _imageService = GameServicesExtension.GameServices.GetRequiredService<IImGuiImageService>();
public PluginListComponent(ConfigReference<PackagesConfig> packagesConfig, PackageSourceMapping sourceMapping, string gameFolder, public PluginListComponent(ConfigReference<PackagesConfig> packagesConfig, ConfigReference<LauncherConfig> launcherConfig,
ImmutableArray<PluginInstance> plugins) PackageSourceMapping sourceMapping, string gameFolder, ImmutableArray<PluginInstance> plugins)
{ {
_packagesConfig = packagesConfig; _packagesConfig = packagesConfig;
_launcherConfig = launcherConfig;
_sourceMapping = sourceMapping; _sourceMapping = sourceMapping;
_gameFolder = gameFolder; _gameFolder = gameFolder;
_plugins = plugins; _plugins = plugins;
@@ -62,6 +66,9 @@ internal class PluginListComponent : IRenderComponent
StringComparer.OrdinalIgnoreCase); StringComparer.OrdinalIgnoreCase);
_profiles = packagesConfig.Value.Profiles; _profiles = packagesConfig.Value.Profiles;
_disablePluginUpdates = _launcherConfig.Value.DisablePluginUpdates;
_disableUpdates = _launcherConfig.Value.DisableLauncherUpdates;
MyScreenManager.ScreenAdded += ScreenChanged; MyScreenManager.ScreenAdded += ScreenChanged;
MyScreenManager.ScreenRemoved += ScreenChanged; MyScreenManager.ScreenRemoved += ScreenChanged;
} }
@@ -83,9 +90,9 @@ internal class PluginListComponent : IRenderComponent
return; return;
} }
if (_changed) if (_restartRequired)
{ {
TextDisabled("Changes would be applied on the next restart"); TextDisabled("Changes will be applied on the next restart");
SameLine(); SameLine();
if (Button("Restart Now")) if (Button("Restart Now"))
{ {
@@ -172,7 +179,7 @@ internal class PluginListComponent : IRenderComponent
{ {
var source = _packagesConfig.Value.Sources[index]; var source = _packagesConfig.Value.Sources[index];
TableNextRow(); TableNextRow();
TableNextColumn(); TableNextColumn();
if (Selectable(source.Name, index == _selectedSource?.index, ImGuiSelectableFlags.SpanAllColumns)) if (Selectable(source.Name, index == _selectedSource?.index, ImGuiSelectableFlags.SpanAllColumns))
@@ -187,12 +194,12 @@ internal class PluginListComponent : IRenderComponent
EndTable(); EndTable();
} }
EndChild(); EndChild();
} }
SameLine(); SameLine();
BeginGroup(); BeginGroup();
BeginChild("Source View", new(0, -GetFrameHeightWithSpacing())); // Leave room for 1 line below us BeginChild("Source View", new(0, -GetFrameHeightWithSpacing())); // Leave room for 1 line below us
@@ -200,7 +207,7 @@ internal class PluginListComponent : IRenderComponent
if (_selectedSource is not null) if (_selectedSource is not null)
{ {
var (selectedSource, index) = _selectedSource.Value; var (selectedSource, index) = _selectedSource.Value;
var name = selectedSource.Name; var name = selectedSource.Name;
if (InputText("Name", ref name, 256)) if (InputText("Name", ref name, 256))
selectedSource = selectedSource with selectedSource = selectedSource with
@@ -214,7 +221,7 @@ internal class PluginListComponent : IRenderComponent
{ {
Url = url Url = url
}; };
var pattern = selectedSource.Pattern; var pattern = selectedSource.Pattern;
if (InputText("Pattern", ref pattern, 1024)) if (InputText("Pattern", ref pattern, 1024))
selectedSource = selectedSource with selectedSource = selectedSource with
@@ -237,7 +244,7 @@ internal class PluginListComponent : IRenderComponent
Save(); Save();
} }
SameLine(); SameLine();
if (Button("Delete")) if (Button("Delete"))
@@ -254,7 +261,7 @@ internal class PluginListComponent : IRenderComponent
Save(); Save();
} }
} }
EndChild(); EndChild();
} }
@@ -271,14 +278,22 @@ internal class PluginListComponent : IRenderComponent
_selectedSource = (source, array.Length - 1); _selectedSource = (source, array.Length - 1);
} }
EndGroup(); EndGroup();
EndTabItem(); EndTabItem();
} }
if (BeginTabItem("Settings")) if (BeginTabItem("Settings"))
{ {
if (Checkbox("Disable Plugin Updates", ref _disablePluginUpdates))
{
_launcherConfig.Value = _launcherConfig.Value with { DisablePluginUpdates = _disablePluginUpdates };
}
if (Checkbox("Disable Launcher Updates", ref _disableUpdates))
{
_launcherConfig.Value = _launcherConfig.Value with { DisableLauncherUpdates = _disableUpdates };
}
var oldConfigPath = Path.Join(_gameFolder, "Plugins", "config.xml"); var oldConfigPath = Path.Join(_gameFolder, "Plugins", "config.xml");
if (File.Exists(oldConfigPath)) if (File.Exists(oldConfigPath))
{ {
@@ -299,6 +314,8 @@ internal class PluginListComponent : IRenderComponent
var hasModLodaer = _packages.ContainsKey("Plugin.ClientModLoader"); var hasModLodaer = _packages.ContainsKey("Plugin.ClientModLoader");
SameLine();
if (!hasModLodaer) if (!hasModLodaer)
BeginDisabled(); BeginDisabled();
@@ -310,11 +327,12 @@ internal class PluginListComponent : IRenderComponent
if (configSerializer.Deserialize(fs) is PluginLoaderConfig plConfig) if (configSerializer.Deserialize(fs) is PluginLoaderConfig plConfig)
{ {
var dir = new DirectoryInfo(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), var dir = new DirectoryInfo(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"CringeLauncher")); "config", "CringeLauncher"));
var file = Path.Join(dir.FullName, "mods.json"); var file = Path.Join(dir.FullName, "mods.json");
using var modsFile = File.Create(file); using var modsFile = File.Create(file);
JsonSerializer.Serialize(modsFile, plConfig.GetMods(), _serializerOptions); JsonSerializer.Serialize(modsFile, plConfig.GetMods(), _serializerOptions);
_restartRequired = true;
} }
} }
@@ -535,7 +553,7 @@ internal class PluginListComponent : IRenderComponent
{ {
_selectedSources = selected _selectedSources = selected
? (_selectedSources?.Count ?? 0) + 1 == _packagesConfig.Value.Sources.Length ? null : _selectedSources?.Add(source) ? (_selectedSources?.Count ?? 0) + 1 == _packagesConfig.Value.Sources.Length ? null : _selectedSources?.Add(source)
: (_selectedSources ?? _packagesConfig.Value.Sources.ToImmutableHashSet()).Remove(source); : (_selectedSources ?? [.. _packagesConfig.Value.Sources]).Remove(source);
_searchTask = RefreshAsync(); _searchTask = RefreshAsync();
EndCombo(); EndCombo();
@@ -545,9 +563,9 @@ internal class PluginListComponent : IRenderComponent
EndCombo(); EndCombo();
} }
Spacing(); Spacing();
switch (_searchTask) switch (_searchTask)
{ {
case { IsCompleted: false }: case { IsCompleted: false }:
@@ -733,9 +751,9 @@ internal class PluginListComponent : IRenderComponent
await foreach (var source in _sourceMapping) await foreach (var source in _sourceMapping)
{ {
if (source == null || _selectedSources is not null && _selectedSources.All(b => b.Url != source.ToString())) if (source == null || _selectedSources?.All(b => b.Url != source.ToString()) == true)
continue; continue;
try try
{ {
var result = await source.SearchPackagesAsync(_searchQuery, take: 1000, packageType: "CringePlugin"); var result = await source.SearchPackagesAsync(_searchQuery, take: 1000, packageType: "CringePlugin");
@@ -758,7 +776,7 @@ internal class PluginListComponent : IRenderComponent
Packages = [.. _packages.Select(b => new PackageReference(b.Key, b.Value))] Packages = [.. _packages.Select(b => new PackageReference(b.Key, b.Value))]
} : _packagesConfig; } : _packagesConfig;
_changed = true; _restartRequired = true;
} }
private static unsafe int ComparePlugins(PluginInstance x, PluginInstance y, ImGuiTableSortSpecsPtr specs) private static unsafe int ComparePlugins(PluginInstance x, PluginInstance y, ImGuiTableSortSpecsPtr specs)

View File

@@ -13,17 +13,17 @@ public class IntrospectionContext
public IntrospectionContext() public IntrospectionContext()
{ {
var assemblyResolver = new AssemblyResolver(); var assemblyResolver = new AssemblyResolver();
assemblyResolver.PreSearchPaths.Add(AppContext.BaseDirectory); assemblyResolver.PreSearchPaths.Add(AppContext.BaseDirectory);
assemblyResolver.PreSearchPaths.Add(MyFileSystem.ExePath); assemblyResolver.PreSearchPaths.Add(MyFileSystem.ExePath);
Context = new(assemblyResolver); Context = new(assemblyResolver);
} }
public IEnumerable<Type> CollectAttributedTypes<TAttribute>(Module module, bool allowAbstract = false) where TAttribute : Attribute public IEnumerable<Type> CollectAttributedTypes<TAttribute>(Module module, bool allowAbstract = false) where TAttribute : Attribute
{ {
var moduleDef = ModuleDefMD.Load(module, Context); var moduleDef = ModuleDefMD.Load(module, Context);
var token = moduleDef.ImportAsTypeSig(typeof(TAttribute)); var token = moduleDef.ImportAsTypeSig(typeof(TAttribute));
return moduleDef.GetTypes() return moduleDef.GetTypes()
@@ -53,7 +53,7 @@ public class IntrospectionContext
if (defOrRef.FullName == token.FullName) if (defOrRef.FullName == token.FullName)
return true; return true;
} }
return false; return false;
} }
} }

View File

@@ -10,7 +10,7 @@ public class FrameworkJsonConverter(FrameworkNameFormat format) : JsonConverter<
{ {
if (reader.TokenType != JsonTokenType.String) if (reader.TokenType != JsonTokenType.String)
throw new JsonException("Invalid framework string"); throw new JsonException("Invalid framework string");
var s = reader.GetString()!; var s = reader.GetString()!;
return format switch return format switch
{ {

View File

@@ -10,7 +10,7 @@ public class ManifestPackageKeyJsonConverter : JsonConverter<ManifestPackageKey>
{ {
if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName)) if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
throw new JsonException("Invalid package key string"); throw new JsonException("Invalid package key string");
return ManifestPackageKey.Parse(reader.GetString()!); return ManifestPackageKey.Parse(reader.GetString()!);
} }

View File

@@ -19,12 +19,12 @@ public class PackageAuthorsJsonConverter : JsonConverter<PackageAuthors>
case JsonTokenType.StartArray: case JsonTokenType.StartArray:
{ {
var builder = ImmutableArray.CreateBuilder<string>(); var builder = ImmutableArray.CreateBuilder<string>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{ {
builder.Add(reader.GetString()!); builder.Add(reader.GetString()!);
} }
return new PackageAuthors(string.Join(", ", builder), builder.ToImmutable()); return new PackageAuthors(string.Join(", ", builder), builder.ToImmutable());
} }
case JsonTokenType.Null: case JsonTokenType.Null:
@@ -41,14 +41,14 @@ public class PackageAuthorsJsonConverter : JsonConverter<PackageAuthors>
writer.WriteStringValue(value.Author); writer.WriteStringValue(value.Author);
return; return;
} }
writer.WriteStartArray(); writer.WriteStartArray();
foreach (var author in value.Authors) foreach (var author in value.Authors)
{ {
writer.WriteStringValue(author); writer.WriteStringValue(author);
} }
writer.WriteEndArray(); writer.WriteEndArray();
} }
} }

View File

@@ -10,7 +10,7 @@ public class ResourceTypeJsonConverter : JsonConverter<ResourceType>
{ {
if (reader.TokenType != JsonTokenType.String) if (reader.TokenType != JsonTokenType.String)
throw new JsonException("Invalid resource type"); throw new JsonException("Invalid resource type");
return ResourceType.Parse(reader.GetString()!); return ResourceType.Parse(reader.GetString()!);
} }

View File

@@ -10,7 +10,7 @@ public class RuntimeFrameworkJsonConverter : JsonConverter<NuGetRuntimeFramework
{ {
if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName)) if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
throw new JsonException("Invalid runtime framework string"); throw new JsonException("Invalid runtime framework string");
return NuGetRuntimeFramework.Parse(reader.GetString()!); return NuGetRuntimeFramework.Parse(reader.GetString()!);
} }

View File

@@ -15,12 +15,12 @@ public class StringOrStringArrayConverter : JsonConverter<ImmutableArray<string>
case JsonTokenType.StartArray: case JsonTokenType.StartArray:
{ {
var builder = ImmutableArray.CreateBuilder<string>(); var builder = ImmutableArray.CreateBuilder<string>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{ {
builder.Add(reader.GetString()!); builder.Add(reader.GetString()!);
} }
return builder.ToImmutable(); return builder.ToImmutable();
} }
default: default:
@@ -35,14 +35,14 @@ public class StringOrStringArrayConverter : JsonConverter<ImmutableArray<string>
writer.WriteStringValue(value[0]); writer.WriteStringValue(value[0]);
return; return;
} }
writer.WriteStartArray(); writer.WriteStartArray();
foreach (var author in value) foreach (var author in value)
{ {
writer.WriteStringValue(author); writer.WriteStringValue(author);
} }
writer.WriteEndArray(); writer.WriteEndArray();
} }
} }

View File

@@ -10,7 +10,7 @@ public class VersionJsonConverter : JsonConverter<NuGetVersion>
{ {
if (reader.TokenType != JsonTokenType.String) if (reader.TokenType != JsonTokenType.String)
throw new JsonException("Invalid version string"); throw new JsonException("Invalid version string");
return NuGetVersion.Parse(reader.GetString()!); return NuGetVersion.Parse(reader.GetString()!);
} }

View File

@@ -10,7 +10,7 @@ public class VersionRangeJsonConverter : JsonConverter<VersionRange>
{ {
if (reader.TokenType != JsonTokenType.String) if (reader.TokenType != JsonTokenType.String)
throw new JsonException("Invalid version range"); throw new JsonException("Invalid version range");
return VersionRange.Parse(reader.GetString()!); return VersionRange.Parse(reader.GetString()!);
} }

View File

@@ -52,10 +52,10 @@ public record ManifestPackageKey(string Id, NuGetVersion Version)
var index = str.IndexOf('/'); var index = str.IndexOf('/');
if (index < 0) if (index < 0)
throw new FormatException("Invalid package key: " + str); throw new FormatException("Invalid package key: " + str);
return new ManifestPackageKey(str[..index], NuGetVersion.Parse(str[(index + 1)..])); return new ManifestPackageKey(str[..index], NuGetVersion.Parse(str[(index + 1)..]));
} }
public override string ToString() => $"{Id}/{Version}"; public override string ToString() => $"{Id}/{Version}";
} }
@@ -71,9 +71,9 @@ public static class DependencyManifestSerializer
new VersionJsonConverter() new VersionJsonConverter()
} }
}; };
public static Task SerializeAsync(Stream stream, DependenciesManifest manifest) => JsonSerializer.SerializeAsync(stream, manifest, SerializerOptions); public static Task SerializeAsync(Stream stream, DependenciesManifest manifest) => JsonSerializer.SerializeAsync(stream, manifest, SerializerOptions);
public static ValueTask<DependenciesManifest> DeserializeAsync(Stream stream) => JsonSerializer.DeserializeAsync<DependenciesManifest>(stream, SerializerOptions)!; public static ValueTask<DependenciesManifest> DeserializeAsync(Stream stream) => JsonSerializer.DeserializeAsync<DependenciesManifest>(stream, SerializerOptions)!;
} }
@@ -86,12 +86,12 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
var targets = ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Empty.ToBuilder(); var targets = ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Empty.ToBuilder();
await MapCatalogEntryAsync(catalogEntry, targetFramework, targets); await MapCatalogEntryAsync(catalogEntry, targetFramework, targets);
var manifest = new DependenciesManifest(runtimeTarget, ImmutableDictionary<NuGetRuntimeFramework, string>.Empty, var manifest = new DependenciesManifest(runtimeTarget, ImmutableDictionary<NuGetRuntimeFramework, string>.Empty,
ImmutableDictionary<NuGetRuntimeFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>>.Empty ImmutableDictionary<NuGetRuntimeFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>>.Empty
.Add(targetFramework, targets.ToImmutable()), .Add(targetFramework, targets.ToImmutable()),
ImmutableDictionary<ManifestPackageKey, DependencyLibrary>.Empty); ImmutableDictionary<ManifestPackageKey, DependencyLibrary>.Empty);
await DependencyManifestSerializer.SerializeAsync(stream, manifest); await DependencyManifestSerializer.SerializeAsync(stream, manifest);
} }
@@ -100,14 +100,14 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
{ {
if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)) || !catalogEntry.DependencyGroups.HasValue) if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)) || !catalogEntry.DependencyGroups.HasValue)
return; return;
// TODO take into account the target framework runtime identifier // TODO take into account the target framework runtime identifier
var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, targetFramework.Framework, var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, targetFramework.Framework,
group => group.TargetFramework); group => group.TargetFramework);
if (nearest is null) if (nearest is null)
return; return;
targets.Add(new(catalogEntry.Id, catalogEntry.Version), targets.Add(new(catalogEntry.Id, catalogEntry.Version),
await MapEntryAsync(catalogEntry, nearest)); await MapEntryAsync(catalogEntry, nearest));
@@ -115,7 +115,7 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
{ {
if (entry is null) if (entry is null)
continue; continue;
await MapCatalogEntryAsync(entry, targetFramework, targets); await MapCatalogEntryAsync(entry, targetFramework, targets);
} }
} }
@@ -156,7 +156,7 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
{ {
await using var stream = await client.GetPackageContentStreamAsync(entry.Id, entry.Version); await using var stream = await client.GetPackageContentStreamAsync(entry.Id, entry.Version);
using var memStream = new MemoryStream(); await using var memStream = new MemoryStream();
await stream.CopyToAsync(memStream); await stream.CopyToAsync(memStream);
memStream.Position = 0; memStream.Position = 0;
using var archive = new ZipArchive(memStream, ZipArchiveMode.Read); using var archive = new ZipArchive(memStream, ZipArchiveMode.Read);

View File

@@ -5,5 +5,5 @@ namespace NuGet.Models;
public record CatalogEntry(string Id, NuGetVersion Version, ImmutableArray<DependencyGroup>? DependencyGroups, ImmutableArray<string>? PackageTypes, public record CatalogEntry(string Id, NuGetVersion Version, ImmutableArray<DependencyGroup>? DependencyGroups, ImmutableArray<string>? PackageTypes,
ImmutableArray<CatalogPackageEntry>? PackageEntries); ImmutableArray<CatalogPackageEntry>? PackageEntries);
public record CatalogPackageEntry(string Name, string FullName, long CompressedLength, long Length); public record CatalogPackageEntry(string Name, string FullName, long CompressedLength, long Length);

View File

@@ -13,10 +13,10 @@ public record NuGetRuntimeFramework(NuGetFramework Framework, string? RuntimeIde
public static NuGetRuntimeFramework Parse(string str) public static NuGetRuntimeFramework Parse(string str)
{ {
var index = str.IndexOf('/'); var index = str.IndexOf('/');
if (index < 0) if (index < 0)
return new NuGetRuntimeFramework(NuGetFramework.Parse(str), null); return new NuGetRuntimeFramework(NuGetFramework.Parse(str), null);
return new NuGetRuntimeFramework(NuGetFramework.Parse(str[..index]), str[(index + 1)..]); return new NuGetRuntimeFramework(NuGetFramework.Parse(str[..index]), str[(index + 1)..]);
} }

View File

@@ -2,5 +2,5 @@
namespace NuGet.Models; namespace NuGet.Models;
public record Registration([property: JsonPropertyName("catalogEntry")] string CatalogEntryUrl, public record Registration([property: JsonPropertyName("catalogEntry")] string CatalogEntryUrl,
[property: JsonPropertyName("sleet:catalogEntry")] CatalogEntry? SleetEntry); [property: JsonPropertyName("sleet:catalogEntry")] CatalogEntry? SleetEntry);

View File

@@ -10,10 +10,10 @@ public record ResourceType(string Id, NuGetVersion? Version)
public static ResourceType Parse(string typeString) public static ResourceType Parse(string typeString)
{ {
var slash = typeString.IndexOf('/'); var slash = typeString.IndexOf('/');
if (slash < 0) if (slash < 0)
return new ResourceType(typeString, null); return new ResourceType(typeString, null);
var id = typeString[..slash]; var id = typeString[..slash];
var versionStr = typeString[(slash + 1)..]; var versionStr = typeString[(slash + 1)..];

View File

@@ -7,7 +7,7 @@ using NuGet.Versioning;
namespace NuGet; namespace NuGet;
public class NuGetClient public sealed class NuGetClient
{ {
private readonly Uri _index; private readonly Uri _index;
private readonly HttpClient _client; private readonly HttpClient _client;
@@ -69,7 +69,7 @@ public class NuGetClient
bool? includePrerelease = null, NuGetVersion? minVersion = null, string? packageType = null) bool? includePrerelease = null, NuGetVersion? minVersion = null, string? packageType = null)
{ {
var queryParameters = HttpUtility.ParseQueryString(string.Empty); var queryParameters = HttpUtility.ParseQueryString(string.Empty);
if (!string.IsNullOrEmpty(query)) if (!string.IsNullOrEmpty(query))
queryParameters.Add("q", query); queryParameters.Add("q", query);
@@ -78,13 +78,13 @@ public class NuGetClient
if (take.HasValue) if (take.HasValue)
queryParameters.Add("take", take.Value.ToString()); queryParameters.Add("take", take.Value.ToString());
if (includePrerelease.HasValue) if (includePrerelease.HasValue)
queryParameters.Add("prerelease", includePrerelease.Value.ToString()); queryParameters.Add("prerelease", includePrerelease.Value.ToString());
if (minVersion is not null) if (minVersion is not null)
queryParameters.Add("semVerLevel", minVersion.ToString()); queryParameters.Add("semVerLevel", minVersion.ToString());
if (!string.IsNullOrEmpty(packageType)) if (!string.IsNullOrEmpty(packageType))
queryParameters.Add("packageType", packageType); queryParameters.Add("packageType", packageType);

View File

@@ -8,7 +8,7 @@ public abstract class DerivedAssemblyLoadContext(ICoreLoadContext parentContext,
: AssemblyLoadContext(name, true) : AssemblyLoadContext(name, true)
{ {
protected readonly ICoreLoadContext ParentContext = parentContext; protected readonly ICoreLoadContext ParentContext = parentContext;
protected override Assembly? Load(AssemblyName assemblyName) => ParentContext.ResolveFromAssemblyName(assemblyName); protected override Assembly? Load(AssemblyName assemblyName) => ParentContext.ResolveFromAssemblyName(assemblyName);
protected override nint LoadUnmanagedDll(string unmanagedDllName) => ParentContext.ResolveUnmanagedDll(unmanagedDllName); protected override nint LoadUnmanagedDll(string unmanagedDllName) => ParentContext.ResolveUnmanagedDll(unmanagedDllName);
} }

View File

@@ -7,7 +7,7 @@ namespace TestPlugin;
public class Plugin : IPlugin public class Plugin : IPlugin
{ {
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public void Dispose() public void Dispose()
{ {
} }
@@ -15,7 +15,7 @@ public class Plugin : IPlugin
public void Init(object gameInstance) public void Init(object gameInstance)
{ {
Log.Info("Test Plugin init"); Log.Info("Test Plugin init");
RenderHandler.Current.RegisterComponent(new TestRenderComponent()); RenderHandler.Current.RegisterComponent(new TestRenderComponent());
} }

View File

@@ -10,7 +10,7 @@ public class TestRenderComponent : IRenderComponent
if (ImGui.Begin("Test Window")) if (ImGui.Begin("Test Window"))
{ {
ImGui.Button("Test"); ImGui.Button("Test");
ImGui.End(); ImGui.End();
} }
} }