mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2026-03-22 15:53:28 +08:00
UXAssist: - PlanetFunctions: fix infinite loop in constructStats cleanup (i++ -> i--) - PlanetFunctions: skip entityPool slot 0 (sentinel) in DismantleAll foreach - PlanetFunctions: fix belt buffer walk infinite loop when buffer[j]==250, add cargoPool bounds check - FactoryPatch: fix UnfixProto guard condition (< 3 -> < PowerPoleIds.Length) - FactoryPatch: fix Array.Resize(index*2) -> (index+1)*2 to handle index==0 - LogisticsPatch: fix UpdateStorageMax early-exit to check both local and remote - LogisticsPatch: fix storage max scan to use Math.Max instead of last-write - LogisticsPatch: add divide-by-zero guard in station storage ratio calculation - LogisticsPatch: fix station entry right-click delegates using per-instance dictionary instead of shared static array to prevent unsubscribe mismatch - LogisticsPatch: add null checks for GameObject.Find results in InitGUI - GamePatch: log exception in previously empty catch block - GameLogic: invoke event handlers individually with try/catch so one failing subscriber does not abort subsequent ones - DysonSpherePatch/LogisticsPatch/PersistPatch/PlayerPatch: replace matcher.Labels = null with matcher.Labels = [] (5 sites) - UIConfigWindow: remove duplicate I18N.Add for 'Outgoing integration count' - UIConfigWindow: remove two orphaned y += 36f spacing increments CheatEnabler: - GamePatch: add null check for GameMain.gameScenario in AbnormalDisabler.OnEnable - FactoryPatch: add null check for GameMain.mainPlayer in TakeTailItemsPatch and GetItemCountPatch - FactoryPatch: add null check for GameMain.mainPlayer in ConstructionSystem_GameTick_Prefix - FactoryPatch: fix _portalFrom.Remove(beltId) -> Remove(v) (wrong key) - FactoryPatch: add null guard for GameMain.data before iterating factories in WindTurbinesPowerGlobalCoverage - DysonSphereFunctions: fix shell pool rebuild loop writing all shells to slot 1 (id=1); now uses j+1 per iteration - DysonSphereFunctions: replace GameMain.gameScenario.NotifyOnPlanDysonShell() with null-conditional call (?.) at 4 sites - DysonSpherePatch: fix SkipAbsorbPatch/QuickAbsorbPatch sharing _instantAbsorb: OnDisable now recalculates the flag instead of unconditionally clearing it - PlayerFunctions: add null check for GameMain.galaxy in TeleportToOuterSpace - UIConfigWindow: add null guard in UpdateButtons before accessing button fields - PlanetFunctions: add colliderId bounds check in BuryAllVeins
365 lines
16 KiB
C#
365 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection.Emit;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using UXAssist.Common;
|
|
|
|
namespace UXAssist.Patches;
|
|
|
|
public class PersistPatch : PatchImpl<PersistPatch>
|
|
{
|
|
public static void Start()
|
|
{
|
|
Enable(true);
|
|
}
|
|
|
|
public static void Uninit()
|
|
{
|
|
Enable(false);
|
|
}
|
|
|
|
// Check for noModifier while pressing hotkeys on build bar
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnUpdate))]
|
|
private static IEnumerable<CodeInstruction> UIBuildMenu__OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inScreen)))
|
|
);
|
|
matcher.Repeat(codeMatcher =>
|
|
{
|
|
var jumpPos = codeMatcher.Advance(1).Operand;
|
|
codeMatcher.Advance(-1).InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.noModifier))),
|
|
new CodeInstruction(OpCodes.Brfalse_S, jumpPos)
|
|
).Advance(2);
|
|
});
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Bring popup tip window to top layer
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIButton), nameof(UIButton.LateUpdate))]
|
|
private static IEnumerable<CodeInstruction> UIButton_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldloc || ci.opcode == OpCodes.Ldloc_S),
|
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.gameObject))),
|
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameObject), nameof(GameObject.activeSelf)))
|
|
);
|
|
var ldLocOpr = matcher.Operand;
|
|
var labels = matcher.Labels;
|
|
matcher.Labels = [];
|
|
matcher.Insert(
|
|
new CodeInstruction(OpCodes.Ldloc_S, ldLocOpr).WithLabels(labels),
|
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform))),
|
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
|
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
|
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Transform), nameof(Transform.SetAsLastSibling)))
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Sort blueprint data when pasting
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(BuildTool_BlueprintCopy), nameof(BuildTool_BlueprintCopy.UseToPasteNow))]
|
|
private static IEnumerable<CodeInstruction> BuildTool_BlueprintCopy_UseToPasteNow_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldarg_0),
|
|
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BuildTool_BlueprintCopy), nameof(BuildTool_BlueprintCopy.RefreshBlueprintData)))
|
|
).Advance(2).Insert(
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_BlueprintCopy), nameof(BuildTool_BlueprintCopy.blueprint))),
|
|
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Functions.FactoryFunctions), nameof(Functions.FactoryFunctions.SortBlueprintData)))
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Sort blueprint data when saving
|
|
[HarmonyPrefix]
|
|
[HarmonyPatch(typeof(BlueprintData), nameof(BlueprintData.SaveBlueprintData))]
|
|
private static void BlueprintData_SaveBlueprintData_Prefix(BlueprintData __instance)
|
|
{
|
|
if (!__instance.isValid) return;
|
|
Functions.FactoryFunctions.SortBlueprintData(__instance);
|
|
}
|
|
|
|
// Increase maximum value of property realizing, 2000 -> 20000
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.UpdateUIElements))]
|
|
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnRealizeButtonClick))]
|
|
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnInputValueEnd))]
|
|
private static IEnumerable<CodeInstruction> UIProductEntry_UpdateUIElements_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldc_I4, 2000)
|
|
);
|
|
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 20000); });
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnInputValueEnd))]
|
|
private static IEnumerable<CodeInstruction> UIProductEntry_OnInputValueEnd_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_R4 && ci.OperandIs(2000f))
|
|
);
|
|
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_R4, 20000f); });
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Increase capacity of player order queue, 16 -> 128
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(PlayerOrder), MethodType.Constructor, typeof(Player))]
|
|
private static IEnumerable<CodeInstruction> PlayerOrder_Constructor_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => (ci.opcode == OpCodes.Ldc_I4_S || ci.opcode == OpCodes.Ldc_I4) && ci.OperandIs(16))
|
|
);
|
|
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 128); });
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Increase Player Command Queue from 16 to 128
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(PlayerOrder), nameof(PlayerOrder._trimEnd))]
|
|
[HarmonyPatch(typeof(PlayerOrder), nameof(PlayerOrder.Enqueue))]
|
|
private static IEnumerable<CodeInstruction> PlayerOrder_ExtendCount_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => (ci.opcode == OpCodes.Ldc_I4_S || ci.opcode == OpCodes.Ldc_I4) && ci.OperandIs(16))
|
|
);
|
|
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 128); });
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Allow F11 in star map
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIGame), nameof(UIGame._OnLateUpdate))]
|
|
private static IEnumerable<CodeInstruction> UIGame__OnLateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inFullscreenGUI))),
|
|
new CodeMatch(ci => ci.opcode == OpCodes.Brfalse || ci.opcode == OpCodes.Brfalse_S)
|
|
);
|
|
var jumpPos = matcher.Advance(1).Operand;
|
|
matcher.Advance(-1).Insert(
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(UIGame), nameof(UIGame.starmap))),
|
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(ManualBehaviour), nameof(ManualBehaviour.active))),
|
|
new CodeInstruction(OpCodes.Brtrue_S, jumpPos)
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Ignore UIDFCommunicatorWindow.Determine()
|
|
[HarmonyPrefix]
|
|
[HarmonyPatch(typeof(UIDFCommunicatorWindow), nameof(UIDFCommunicatorWindow.Determine))]
|
|
private static bool UIDFCommunicatorWindow_Determine_Prefix()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Fix crash in NeutronStarHandler.OnEnable()
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(NeutronStarHandler), nameof(NeutronStarHandler.OnEnable))]
|
|
private static IEnumerable<CodeInstruction> NeutronStarHandler_OnEnable_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldarg_0),
|
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(NeutronStarHandler), nameof(NeutronStarHandler.streamRenderer))),
|
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Renderer), nameof(Renderer.sharedMaterial)))
|
|
).RemoveInstructions(3).InsertAndAdvance(
|
|
Transpilers.EmitDelegate(() =>
|
|
{
|
|
return Configs.builtin.neutronStarPrefab.streamRenderer.sharedMaterial;
|
|
})
|
|
);
|
|
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
// Disable rendering when hideAllUI0 is true
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(GameLogic), nameof(GameLogic.LateUpdate))]
|
|
[HarmonyPatch(typeof(GameLogic), nameof(GameLogic.Draw))]
|
|
[HarmonyPatch(typeof(GameLogic), nameof(GameLogic.DrawPost))]
|
|
private static IEnumerable<CodeInstruction> GameLogic_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.Start();
|
|
matcher.CreateLabel(out var label);
|
|
matcher.InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIRoot), nameof(UIRoot.instance))),
|
|
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(UIRoot), nameof(UIRoot.uiGame))),
|
|
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(UIGame), nameof(UIGame.hideAllUI0))),
|
|
new CodeInstruction(OpCodes.Brfalse, label),
|
|
new CodeInstruction(OpCodes.Ret)
|
|
);
|
|
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
static bool _preHideAllUI0 = false;
|
|
static bool _localPlanetChanged = false;
|
|
[HarmonyPrefix]
|
|
[HarmonyPatch(typeof(UIGame), nameof(UIGame._OnLateUpdate))]
|
|
private static void UIGame__OnLateUpdate_Prefix(UIGame __instance)
|
|
{
|
|
_preHideAllUI0 = __instance.hideAllUI0;
|
|
}
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(UIGame), nameof(UIGame._OnLateUpdate))]
|
|
private static void UIGame__OnLateUpdate_Postfix(UIGame __instance)
|
|
{
|
|
var hideAllUI0 = __instance.hideAllUI0;
|
|
if (_localPlanetChanged)
|
|
{
|
|
_localPlanetChanged = false;
|
|
}
|
|
else
|
|
{
|
|
if (_preHideAllUI0 == hideAllUI0) return;
|
|
}
|
|
var show = GameMain.localPlanet != null || !hideAllUI0;
|
|
GameMain.data?.mainPlayer?.effect.sailEffect.gameObject.SetActive(!hideAllUI0);
|
|
var universeSimulator = GameMain.universeSimulator;
|
|
if (universeSimulator == null) return;
|
|
universeSimulator.gameObject.SetActive(show);
|
|
var planetSimulators = universeSimulator.planetSimulators;
|
|
if (planetSimulators == null) return;
|
|
foreach (var planet in planetSimulators)
|
|
{
|
|
if (planet == null) continue;
|
|
planet.gameObject.SetActive(show);
|
|
}
|
|
}
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(UniverseSimulator), nameof(UniverseSimulator.SetPlanetSimulator))]
|
|
private static void UniverseSimulator_SetPlanetSimulator_Postfix(UniverseSimulator __instance, PlanetSimulator sim)
|
|
{
|
|
if (GameMain.localPlanet == null && UIRoot.instance.uiGame.hideAllUI0) sim.gameObject.SetActive(false);
|
|
}
|
|
|
|
[HarmonyPrefix]
|
|
[HarmonyPatch(typeof(GameData), nameof(GameData.localPlanet), MethodType.Setter)]
|
|
private static void GameData_localPlanet_Setter_Prefix(GameData __instance, PlanetData value)
|
|
{
|
|
var oldPlanet = __instance.localPlanet;
|
|
if (oldPlanet == null && value != null || oldPlanet != null && value == null) _localPlanetChanged = true;
|
|
}
|
|
|
|
#region Cluster Upload Result
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(MilkyWayWebClient), nameof(MilkyWayWebClient.OnUploadLoginErrored))]
|
|
private static void MilkyWayWebClient_OnUploadLoginErrored_Postfix(MilkyWayWebClient __instance, DSPWeb.HTTP_ERROR_TYPE errorType, string errorInfo, int errorCode)
|
|
{
|
|
switch (errorType)
|
|
{
|
|
case DSPWeb.HTTP_ERROR_TYPE.NETWORK_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-10001, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.HTTP_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-10010 - errorCode, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.USER_ABORT:
|
|
Functions.UIFunctions.AddClusterUploadResult(-10003, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.UNEXPECTED_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-10004, 0f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(MilkyWayWebClient), nameof(MilkyWayWebClient.OnUploadErrored))]
|
|
private static void MilkyWayWebClient_OnUploadErrored_Postfix(MilkyWayWebClient __instance, DSPWeb.HTTP_ERROR_TYPE errorType, string errorInfo, int errorCode)
|
|
{
|
|
switch (errorType)
|
|
{
|
|
case DSPWeb.HTTP_ERROR_TYPE.NETWORK_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-101, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.HTTP_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-110 - errorCode, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.USER_ABORT:
|
|
Functions.UIFunctions.AddClusterUploadResult(-103, 0f);
|
|
break;
|
|
case DSPWeb.HTTP_ERROR_TYPE.UNEXPECTED_ERROR:
|
|
Functions.UIFunctions.AddClusterUploadResult(-104, 0f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(MilkyWayWebClient), nameof(MilkyWayWebClient.OnUploadSucceed))]
|
|
private static void MilkyWayWebClient_OnUploadSucceed_Postfix(MilkyWayWebClient __instance, DownloadHandler handler)
|
|
{
|
|
if (!int.TryParse(handler.text, out var rcode))
|
|
rcode = -1;
|
|
Functions.UIFunctions.AddClusterUploadResult(rcode, __instance.uploadRequest == null ? 0f : (float)__instance.uploadRequest.reqTime);
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(MilkyWayCache), nameof(MilkyWayCache.LoadTopTenPlayerData))]
|
|
private static IEnumerable<CodeInstruction> MilkyWayCache_LoadTopTenPlayerData_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldarg_1),
|
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(BinaryReader), nameof(BinaryReader.ReadInt32))),
|
|
new CodeMatch(ci => ci.IsStloc())
|
|
).Advance(2).InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Dup),
|
|
Transpilers.EmitDelegate(Functions.UIFunctions.SetTopPlayerCount)
|
|
);
|
|
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldloca_S),
|
|
new CodeMatch(OpCodes.Ldarg_1),
|
|
new CodeMatch(OpCodes.Callvirt),
|
|
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(ClusterPlayerData), nameof(ClusterPlayerData.isAnon)))
|
|
);
|
|
var objloc = matcher.Operand;
|
|
matcher.Advance(4);
|
|
var iinstr = matcher.Instruction.Clone();
|
|
matcher.InsertAndAdvance(
|
|
iinstr,
|
|
new CodeInstruction(OpCodes.Ldloca_S, objloc),
|
|
Transpilers.EmitDelegate((int index, ref ClusterPlayerData playerData) =>
|
|
{
|
|
Functions.UIFunctions.SetTopPlayerData(index, ref playerData);
|
|
})
|
|
);
|
|
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(MilkyWayCache), nameof(MilkyWayCache.LoadTopTenPlayerData))]
|
|
private static void MilkyWayCache_LoadTopTenPlayerData_Postfix(MilkyWayCache __instance)
|
|
{
|
|
Functions.UIFunctions.UpdateMilkyWayTopTenPlayers();
|
|
}
|
|
|
|
#endregion
|
|
}
|