103 lines
4.3 KiB
C#
103 lines
4.3 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(MyWorkshop), nameof(MyWorkshop.DownloadModsAsync))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> DownloadModsAsyncTranspiler(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var getCount = AccessTools.PropertyGetter(typeof(List<MyObjectBuilder_Checkpoint.ModItem>), nameof(List<MyObjectBuilder_Checkpoint.ModItem>.Count));
|
|
return new CodeMatcher(instructions)
|
|
.SearchForward(b => b.Calls(getCount))
|
|
.Advance(1)
|
|
.Insert(
|
|
CodeInstruction.LoadField(typeof(ModInjector), nameof(Mods)),
|
|
new(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(HashSet<ulong>), nameof(HashSet<ulong>.Count))),
|
|
new(OpCodes.Add)
|
|
)
|
|
.Instructions();
|
|
}
|
|
|
|
[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")));
|
|
}
|
|
} |