Mod config migration from plugin hub
All checks were successful
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (NuGet) (push) Successful in 50s
Build / Build Nuget package (SharedCringe) (push) Successful in 58s
Build / Build Nuget package (CringePlugins) (push) Successful in 1m11s
Build / Build Launcher (push) Successful in 1m42s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 3m5s

Update ui when migrating pluginhub config
Added mod loader as a default plugin
Temp fix for pbs
This commit is contained in:
2025-05-13 04:04:22 -04:00
parent 3295afc447
commit 78880d2a78
4 changed files with 138 additions and 24 deletions

View File

@@ -12,7 +12,7 @@ using VRage.Scripting;
namespace CringeLauncher.Patches; namespace CringeLauncher.Patches;
[HarmonyPatch] [HarmonyPatch]
public static class ModAssemblyLoadContextPatches public static class ModAssemblyLoadContextPatches //todo: use ModScriptCompilerPatch
{ {
private static ModAssemblyLoadContext? _currentSessionContext; private static ModAssemblyLoadContext? _currentSessionContext;
private static readonly MyConcurrentHashSet<string> AssemblyNames = []; private static readonly MyConcurrentHashSet<string> AssemblyNames = [];
@@ -30,7 +30,7 @@ public static class ModAssemblyLoadContextPatches
.InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target")) .InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target"))
.SetInstruction(CodeInstruction.CallClosure((byte[] assembly, byte[] symbols, MyApiTarget target) => .SetInstruction(CodeInstruction.CallClosure((byte[] assembly, byte[] symbols, MyApiTarget target) =>
{ {
if (target is not MyApiTarget.Mod) return Assembly.Load(assembly, symbols); //if (target is not MyApiTarget.Mod) return Assembly.Load(assembly, symbols);
ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context"); ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context");
return _currentSessionContext.LoadFromStream(new MemoryStream(assembly), new MemoryStream(symbols)); return _currentSessionContext.LoadFromStream(new MemoryStream(assembly), new MemoryStream(symbols));
})) }))
@@ -39,7 +39,7 @@ public static class ModAssemblyLoadContextPatches
.InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target")) .InsertAndAdvance(new(OpCodes.Ldarg_0), CodeInstruction.LoadField(original.DeclaringType, "target"))
.SetInstruction(CodeInstruction.CallClosure((byte[] assembly, MyApiTarget target) => .SetInstruction(CodeInstruction.CallClosure((byte[] assembly, MyApiTarget target) =>
{ {
if (target is not MyApiTarget.Mod) return Assembly.Load(assembly); //if (target is not MyApiTarget.Mod) return Assembly.Load(assembly);
ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context"); ArgumentNullException.ThrowIfNull(_currentSessionContext, "No session context");
return _currentSessionContext.LoadFromStream(new MemoryStream(assembly)); return _currentSessionContext.LoadFromStream(new MemoryStream(assembly));
})) }))

View File

@@ -9,24 +9,39 @@ namespace CringePlugins.Compatability;
[XmlType("PluginConfig")] [XmlType("PluginConfig")]
public class PluginLoaderConfig public class PluginLoaderConfig
{ {
[XmlArrayItem("Id")] /// <summary>
public string[] Plugins { get; set; } = []; /// Raw plugin and mod ids
/// </summary>
[XmlArrayItem("Id")] public string[] Plugins { get; set; } = [];
[XmlArrayItem("Profile")] /// <summary>
public PluginLoaderProfile[] Profiles { get; set; } = []; /// Raw profiles
/// </summary>
[XmlArrayItem("Profile")] public PluginLoaderProfile[] Profiles { get; set; } = [];
public PackagesConfig Migrate(PackagesConfig old) public PackagesConfig MigratePlugins(PackagesConfig old)
{ {
//ensure defaults are installed
var defaultConfig = PackagesConfig.Default;
var sources = old.Sources.ToBuilder();
foreach (var source in defaultConfig.Sources)
{
if (!sources.Contains(source))
sources.Add(source);
}
var pluginsBuilder = ImmutableArray.CreateBuilder<PackageReference>(); var pluginsBuilder = ImmutableArray.CreateBuilder<PackageReference>();
var defaultVersion = new NuGetVersion(1, 0, 0); var defaultVersion = new NuGetVersion(1, 0, 0);
foreach (var plugin in Plugins) foreach (var plugin in GetPlugins())
{ {
if (!IsValidId(plugin))
continue;
pluginsBuilder.Add(new PackageReference($"Plugin.{plugin.Replace('/', '.')}", pluginsBuilder.Add(new PackageReference($"Plugin.{plugin.Replace('/', '.')}",
new(defaultVersion))); new(defaultVersion)));
} }
foreach (var package in defaultConfig.Packages)
{
if (!pluginsBuilder.Any(x => x.Id == package.Id))
pluginsBuilder.Add(package);
}
var profiles = new Dictionary<string, ImmutableArray<PackageReference>>(); var profiles = new Dictionary<string, ImmutableArray<PackageReference>>();
foreach (var profile in Profiles) foreach (var profile in Profiles)
@@ -34,24 +49,85 @@ public class PluginLoaderConfig
var builder = ImmutableArray.CreateBuilder<PackageReference>(); var builder = ImmutableArray.CreateBuilder<PackageReference>();
foreach (var plugin in profile.Plugins) foreach (var plugin in profile.Plugins)
{ {
if (!IsValidId(plugin)) if (!IsValidPluginId(plugin))
continue; continue;
builder.Add(new PackageReference($"Plugin.{plugin.Replace('/', '.')}", builder.Add(new PackageReference($"Plugin.{plugin.Replace('/', '.')}",
new(defaultVersion))); new(defaultVersion)));
} }
foreach (var package in defaultConfig.Packages)
{
if (!builder.Any(x => x.Id == package.Id))
builder.Add(package);
}
profiles[profile.Name] = builder.ToImmutable(); profiles[profile.Name] = builder.ToImmutable();
} }
return old with return old with
{ {
Packages = pluginsBuilder.ToImmutableArray(), Packages = pluginsBuilder.ToImmutable(),
Profiles = profiles Profiles = profiles,
Sources = sources.ToImmutable()
}; };
} }
private static bool IsValidId(string pluginId) public HashSet<string> GetPlugins() => GetPlugins(Plugins);
public HashSet<ulong> GetMods() => GetMods(Plugins);
public Dictionary<string, HashSet<string>> GetPluginProfiles()
{
var dict = new Dictionary<string, HashSet<string>>(Profiles.Length);
foreach (var profile in Profiles)
{
dict[profile.Name] = GetPlugins(profile.Plugins);
}
return dict;
}
public Dictionary<string, HashSet<ulong>> GetModProfiles()
{
var dict = new Dictionary<string, HashSet<ulong>>(Profiles.Length);
foreach (var profile in Profiles)
{
dict[profile.Name] = GetMods(profile.Plugins);
}
return dict;
}
private static HashSet<string> GetPlugins(string[] mixed)
{
var plugins = new HashSet<string>();
foreach (var plugin in mixed)
{
if (!IsValidPluginId(plugin))
continue;
plugins.Add(plugin);
}
return plugins;
}
private static HashSet<ulong> GetMods(string[] mixed)
{
var mods = new HashSet<ulong>();
foreach (var plugin in mixed)
{
if (!ulong.TryParse(plugin, out var modId))
continue;
mods.Add(modId);
}
return mods;
}
private static bool IsValidPluginId(string pluginId)
{ {
var count = 0; var count = 0;
foreach (var c in pluginId) foreach (var c in pluginId)

View File

@@ -10,5 +10,9 @@ public record PackagesConfig(ImmutableArray<PackageSource> Sources, ImmutableArr
public static PackagesConfig Default { get; } = new([ public static PackagesConfig Default { get; } = new([
new("zznty", @"^SpaceEngineersDedicated\.ReferenceAssemblies$|^ImGui\.NET\.DirectX$|^NuGet$|^Cringe.+$|^SharedCringe$|^Plugin.+$", "https://ng.zznty.ru/v3/index.json"), new("zznty", @"^SpaceEngineersDedicated\.ReferenceAssemblies$|^ImGui\.NET\.DirectX$|^NuGet$|^Cringe.+$|^SharedCringe$|^Plugin.+$", "https://ng.zznty.ru/v3/index.json"),
new("nuget.org", string.Empty, "https://api.nuget.org/v3/index.json") new("nuget.org", string.Empty, "https://api.nuget.org/v3/index.json")
], [], []); //todo: default profile with recommended plugins? ],
[
new PackageReference("Plugin.ClientModLoader", new(new(0,0,0)))
],
[]); //todo: default profile with recommended plugins?
} }

View File

@@ -33,6 +33,7 @@ internal class PluginListComponent : IRenderComponent
private bool _open = true; private bool _open = true;
private PackagesConfig _packagesConfig; private PackagesConfig _packagesConfig;
private readonly PackageSourceMapping _sourceMapping; private readonly PackageSourceMapping _sourceMapping;
private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web);
private ImmutableHashSet<PackageSource>? _selectedSources; private ImmutableHashSet<PackageSource>? _selectedSources;
private readonly string _configPath; private readonly string _configPath;
private readonly string _gameFolder; private readonly string _gameFolder;
@@ -255,17 +256,50 @@ internal class PluginListComponent : IRenderComponent
if (BeginTabItem("Settings")) if (BeginTabItem("Settings"))
{ {
var oldConfigPath = Path.Join(_gameFolder, "Plugins", "config.xml"); var oldConfigPath = Path.Join(_gameFolder, "Plugins", "config.xml");
if (File.Exists(oldConfigPath) && Button("Migrate PluginLoader Config")) if (File.Exists(oldConfigPath))
{ {
var configSerializer = new XmlSerializer(typeof(PluginLoaderConfig)); if (Button("Migrate PluginLoader Plugins"))
using var fs = File.OpenRead(oldConfigPath);
if (configSerializer.Deserialize(fs) is PluginLoaderConfig oldConfig)
{ {
_packagesConfig = oldConfig.Migrate(_packagesConfig); var configSerializer = new XmlSerializer(typeof(PluginLoaderConfig));
using var fs = File.OpenRead(oldConfigPath);
if (configSerializer.Deserialize(fs) is PluginLoaderConfig oldConfig)
{
_packagesConfig = oldConfig.MigratePlugins(_packagesConfig);
Save(false); Save(false);
_packages = _packagesConfig.Packages.ToImmutableDictionary(b => b.Id, b => b.Range, StringComparer.OrdinalIgnoreCase);
}
}
var hasModLodaer = _packages.ContainsKey("Plugin.ClientModLoader");
if (!hasModLodaer)
BeginDisabled();
if (Button("Migrate Pluginloader Mods"))
{
var configSerializer = new XmlSerializer(typeof(PluginLoaderConfig));
using var fs = File.OpenRead(oldConfigPath);
if (configSerializer.Deserialize(fs) is PluginLoaderConfig plConfig)
{
var dir = new DirectoryInfo(Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"CringeLauncher"));
var file = Path.Join(dir.FullName, "mods.json");
using var modsFile = File.Create(file);
JsonSerializer.Serialize(modsFile, plConfig.GetMods(), _serializerOptions);
}
}
if (!hasModLodaer)
{
if (IsItemHovered())
SetTooltip("Requires Plugin.ClientModLoader");
EndDisabled();
} }
} }
EndTabItem(); EndTabItem();