Files
ClientModLoader/Plugin.ClientModLoader/ModInjector.cs
zznty 947fcde9ca 1
2025-05-11 02:37:57 +07:00

87 lines
3.5 KiB
C#

using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using HarmonyLib;
using Sandbox.Definitions;
using Sandbox.Engine.Networking;
using Sandbox.Game.World;
using VRage.Game;
namespace Plugin.ClientModLoader;
[HarmonyPatch]
internal static class ModInjector
{
public static HashSet<ulong> Mods = [];
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> AdditionalFilledModItems = [];
[HarmonyPatch(typeof(MyWorkshop), nameof(MyWorkshop.DownloadWorldModsBlockingInternal))]
[HarmonyPrefix]
private static void DownloadModsBlockingPrefix(ref List<MyObjectBuilder_Checkpoint.ModItem> mods, ref List<MyObjectBuilder_Checkpoint.ModItem> __state)
{
AdditionalFilledModItems.Clear();
__state = mods;
AppendToList(ref mods);
}
[HarmonyPatch(typeof(MyWorkshop), nameof(MyWorkshop.DownloadWorldModsBlockingInternal))]
[HarmonyPostfix]
private static void DownloadModsBlockingPostfix(List<MyObjectBuilder_Checkpoint.ModItem> mods, List<MyObjectBuilder_Checkpoint.ModItem> __state)
{
foreach (var mod in mods)
{
var index = __state.FindIndex(b =>
b.PublishedFileId == mod.PublishedFileId && b.PublishedServiceName == mod.PublishedServiceName);
if (index != -1)
{
var stateMod = __state[index];
stateMod.SetModData(mod.GetModData());
__state[index] = stateMod;
}
else if (Mods.Contains(mod.PublishedFileId))
AdditionalFilledModItems.Add(mod);
else
__state.Add(mod);
}
}
[HarmonyPatch(typeof(MyDefinitionManager), nameof(MyDefinitionManager.LoadData))]
[HarmonyPrefix]
private static void LoadDefinitionsPrefix(ref List<MyObjectBuilder_Checkpoint.ModItem> mods) => AppendToList(ref mods);
[HarmonyPatch(typeof(MyScriptManager), nameof(MyScriptManager.LoadData))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> LoadScriptsTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var staticGetter = AccessTools.PropertyGetter(typeof(MySession), nameof(MySession.Static));
var modsGetter = AccessTools.PropertyGetter(typeof(MySession), nameof(MySession.Mods));
return new CodeMatcher(instructions, generator)
.Start()
.DeclareLocal(typeof(List<MyObjectBuilder_Checkpoint.ModItem>), out var modsLocal)
.CreateLabel(out var start)
.InsertAndAdvance(
new(OpCodes.Call, staticGetter),
new(OpCodes.Call, modsGetter),
new(OpCodes.Stloc, modsLocal),
new(OpCodes.Ldloc, modsLocal),
new(OpCodes.Brfalse, start),
new(OpCodes.Ldloca, modsLocal),
CodeInstruction.Call(typeof(ModInjector), nameof(AppendToList))
)
.MatchStartForward(CodeMatch.Calls(staticGetter), CodeMatch.Calls(modsGetter))
.Repeat(a => a
.SetAndAdvance(OpCodes.Ldloc, modsLocal)
.SetAndAdvance(OpCodes.Nop, null))
.Instructions();
}
private static void AppendToList(ref List<MyObjectBuilder_Checkpoint.ModItem> mods)
{
// copy
mods = mods.ToList();
mods.AddRange(AdditionalFilledModItems.Count > 0
? AdditionalFilledModItems
: Mods.Select(mod => new MyObjectBuilder_Checkpoint.ModItem(mod, "Steam")));
}
}