reflection part 2

This commit is contained in:
Tyfon
2024-07-11 21:58:20 -07:00
parent 8ad0b8c5f7
commit 3d8c9cc232
10 changed files with 178 additions and 84 deletions

View File

@@ -12,9 +12,23 @@ global using Stackable = GClass2751;
global using RagfairSearch = GClass3219;
global using CurrencyInfo = GClass2531;
global using Scheme = GClass1939;
global using ItemFilterExtensions = GClass2524;
global using QuickBindCommandMap = GClass3032;
global using DiscardResult = GClass2799;
global using BindOperation = GClass2818;
// State machine states
global using FirearmReadyState = EFT.Player.FirearmController.GClass1619;
global using FirearmAddingModState = EFT.Player.FirearmController.Class1039;
// JSON
global using LocationJsonParser = GClass1496;
global using JsonItem = GClass1198;
// Errors
global using DestroyError = GClass3344;
global using GridNoRoomError = StashGridClass.GClass3310;
global using GridSpaceTakenError = StashGridClass.GClass3311;
global using GridModificationsUnavailableError = StashGridClass.GClass3315;
global using NoRoomError = GClass3316;
global using NoPossibleActionsError = GClass3317;

View File

@@ -3,6 +3,7 @@ using EFT.InputSystem;
using HarmonyLib;
using SPT.Reflection.Patching;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace UIFixes
@@ -28,7 +29,7 @@ namespace UIFixes
protected override MethodBase GetTargetMethod()
{
StateMachineArray = AccessTools.Field(typeof(KeyCombination), "keyCombinationState_1");
return AccessTools.Constructor(typeof(ToggleKeyCombination), [typeof(EGameKey), typeof(ECommand), typeof(ECommand), typeof(int)]);
return AccessTools.GetDeclaredConstructors(typeof(ToggleKeyCombination)).Single();
}
[PatchPostfix]

View File

@@ -4,7 +4,6 @@ using EFT.UI;
using EFT.UI.DragAndDrop;
using HarmonyLib;
using SPT.Reflection.Patching;
using SPT.Reflection.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -17,56 +16,12 @@ namespace UIFixes
{
public static class ContextMenuPatches
{
private static Type InventoryRootInteractionsType;
private static Type TradingRootInteractionsType;
private static FieldInfo TradingRootInteractionsItemField;
private static InsuranceInteractions CurrentInsuranceInteractions = null;
private static RepairInteractions CurrentRepairInteractions = null;
private static string CreatedButtonInteractionId = null;
private static readonly HashSet<EItemInfoButton> TradingRootInteractions =
[
EItemInfoButton.Inspect,
EItemInfoButton.Uninstall,
EItemInfoButton.Examine,
EItemInfoButton.Open,
EItemInfoButton.Insure,
EItemInfoButton.Repair,
EItemInfoButton.Modding,
EItemInfoButton.EditBuild,
EItemInfoButton.FilterSearch,
EItemInfoButton.LinkedSearch,
EItemInfoButton.NeededSearch,
EItemInfoButton.Tag,
EItemInfoButton.ResetTag,
EItemInfoButton.TurnOn,
EItemInfoButton.TurnOff,
EItemInfoButton.Fold,
EItemInfoButton.Unfold,
EItemInfoButton.Disassemble,
EItemInfoButton.Discard
];
public static void Enable()
{
// The context menus in the inventory and the trading screen inventory are *completely different code*
InventoryRootInteractionsType = PatchConstants.EftTypes.Single(t => t.GetField("HIDEOUT_WEAPON_MODIFICATION_REQUIRED") != null); // GClass3045
// GClass3054 - this is nuts to find, have to inspect a static enum array
TradingRootInteractionsType = PatchConstants.EftTypes.Single(t =>
{
var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static);
if (enumerableField != null)
{
var enumerable = (IEnumerable<EItemInfoButton>)enumerableField.GetValue(null);
return TradingRootInteractions.SetEquals(enumerable);
}
return false;
});
TradingRootInteractionsItemField = AccessTools.Field(TradingRootInteractionsType, "item_0");
new ContextMenuNamesPatch().Enable();
new PositionSubMenuPatch().Enable();
new PositionInsuranceSubMenuPatch().Enable();
@@ -170,7 +125,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(InventoryRootInteractionsType, "get_SubInteractions");
return AccessTools.Method(R.InventoryInteractions.Type, "get_SubInteractions");
}
[PatchPostfix]
@@ -186,7 +141,7 @@ namespace UIFixes
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(InventoryRootInteractionsType, "CreateSubInteractions");
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
}
[PatchPrefix]
@@ -236,7 +191,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(TradingRootInteractionsType, "get_SubInteractions");
return AccessTools.Method(R.TradingInteractions.Type, "get_SubInteractions");
}
[PatchPostfix]
@@ -252,7 +207,7 @@ namespace UIFixes
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(TradingRootInteractionsType, "CreateSubInteractions");
return AccessTools.Method(R.TradingInteractions.Type, "CreateSubInteractions");
}
[PatchPrefix]
@@ -261,12 +216,14 @@ namespace UIFixes
// Clear this, since something else should be active (even a different mouseover of the insurance button)
LoadingInsuranceActions = false;
var wrappedInstance = new R.TradingInteractions(__instance);
if (parentInteraction == EItemInfoButton.Insure)
{
int playerRubles = GetPlayerRubles(___itemUiContext_0);
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032
Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance);
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
Item item = wrappedInstance.Item;
CurrentInsuranceInteractions = new(item, ___itemUiContext_0, playerRubles);
CurrentInsuranceInteractions = MultiSelect.Active ?
@@ -292,8 +249,8 @@ namespace UIFixes
{
int playerRubles = GetPlayerRubles(___itemUiContext_0);
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032
Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance);
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
Item item = wrappedInstance.Item;
CurrentRepairInteractions = new(item, ___itemUiContext_0, playerRubles);
subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions);
@@ -469,7 +426,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(InventoryRootInteractionsType, "CreateSubInteractions");
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
}
// Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left.

View File

@@ -2,6 +2,8 @@
using EFT.UI;
using HarmonyLib;
using SPT.Reflection.Patching;
using SPT.Reflection.Utils;
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -53,7 +55,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass2524), nameof(GClass2524.CheckItemFilter));
return AccessTools.Method(typeof(ItemFilterExtensions), nameof(ItemFilterExtensions.CheckItemFilter));
}
[PatchPrefix]
@@ -72,7 +74,8 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass3065), nameof(GClass3065.method_6));
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagInteraction") != null);
return AccessTools.Method(type, "method_6");
}
[PatchPrefix]
@@ -92,7 +95,8 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass3066), nameof(GClass3066.method_7));
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
return AccessTools.Method(type, "method_7");
}
[PatchPrefix]
@@ -119,7 +123,8 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass3066), nameof(GClass3066.method_6));
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
return AccessTools.Method(type, "method_6");
}
[PatchPrefix]

View File

@@ -13,14 +13,14 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GClass1855), nameof(GClass1855.method_9));
return AccessTools.Method(R.ItemReceiver.Type, "method_9"); // GClass1855
}
// The patched method can't handle new items that aren't in stash root.
// Find items that are in subcontainers and handle them first - the patched method will ignore items that have a CurrentAddress
// This is a subset of the original method - doesn't handle slots, equipment containers, etc.
[PatchPrefix]
public static void Prefix(ref GClass1198[] newItems, Profile ___profile_0, ItemFactory ___itemFactory, GClass2780 ___gclass2780_0)
public static void Prefix(object __instance, ref JsonItem[] newItems, Profile ___profile_0, ItemFactory ___itemFactory)
{
Inventory inventory = ___profile_0.Inventory;
StashClass stash = inventory.Stash;
@@ -40,10 +40,12 @@ namespace UIFixes
List<Item> stashItems = stash.GetNotMergedItems().ToList();
ItemFactory.GStruct135 tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null);
InventoryControllerClass inventoryController = new R.ItemReceiver(__instance).InventoryController;
var tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null);
foreach (Item item in tree.Items.Values.Where(i => i.CurrentAddress == null))
{
GClass1198 newItem = unhandledItems.First(i => i._id == item.Id);
var newItem = unhandledItems.First(i => i._id == item.Id);
if (newItem.parentId != null || newItem.slotId != null)
{
// Assuming here that unhandled items are trying to go into containers in the stash - find that container
@@ -52,14 +54,14 @@ namespace UIFixes
{
if (containerCollection.GetContainer(newItem.slotId) is StashGridClass grid)
{
LocationInGrid location = GClass1496.CreateItemLocation<LocationInGrid>(newItem.location);
LocationInGrid location = LocationJsonParser.CreateItemLocation<LocationInGrid>(newItem.location);
ItemAddress itemAddress = new GridItemAddress(grid, location);
GStruct414<GClass2798> operation = InteractionsHandlerClass.Add(item, itemAddress, ___gclass2780_0, false);
var operation = InteractionsHandlerClass.Add(item, itemAddress, inventoryController, false);
if (operation.Succeeded)
{
operation.Value.RaiseEvents(___gclass2780_0, CommandStatus.Begin);
operation.Value.RaiseEvents(___gclass2780_0, CommandStatus.Succeed);
operation.Value.RaiseEvents(inventoryController, CommandStatus.Begin);
operation.Value.RaiseEvents(inventoryController, CommandStatus.Succeed);
}
}
}

View File

@@ -92,7 +92,7 @@ namespace UIFixes
[PatchPrefix]
public static void Prefix(ECommand command)
{
FixVisibilityPatch.Ignorable = GClass3032.SlotBySelectCommandDictionary.ContainsKey(command);
FixVisibilityPatch.Ignorable = QuickBindCommandMap.SlotBySelectCommandDictionary.ContainsKey(command);
}
[PatchPostfix]

View File

@@ -21,7 +21,7 @@ namespace UIFixes
// This is a grenade specific event emitter that has all the info needed to do this
[PatchPostfix]
public static void Postfix(CommandStatus status, GClass2799 ___DiscardResult)
public static void Postfix(CommandStatus status, DiscardResult ___DiscardResult)
{
if (status != CommandStatus.Succeed)
{
@@ -40,7 +40,7 @@ namespace UIFixes
var nextGrenade = matchingGrenades.FirstOrDefault(g => controller.IsAtBindablePlace(g));
if (nextGrenade != null)
{
controller.TryRunNetworkTransaction(GClass2818.Run(controller, nextGrenade, index, true), null);
controller.TryRunNetworkTransaction(BindOperation.Run(controller, nextGrenade, index, true), null);
}
}
}

View File

@@ -532,7 +532,7 @@ namespace UIFixes
[PatchPrefix]
public static bool Prefix(SimpleTooltip __instance, InventoryError error)
{
if (error is StashGridClass.GClass3310 || error is StashGridClass.GClass3311)
if (error is GridNoRoomError || error is GridSpaceTakenError)
{
__instance.Close();
return false;
@@ -546,15 +546,15 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(Player.FirearmController.Class1039), nameof(Player.FirearmController.Class1039.OnModChanged));
return AccessTools.Method(typeof(FirearmAddingModState), nameof(FirearmAddingModState.OnModChanged));
}
// The firearm state machine state Class1015 is the "adding mod" state
// Unpatched, it fires off the success callback before returning to ready state (GClass1608)
// This is the state machine's "adding mod" state
// Unpatched, it fires off the success callback before returning to ready state
// Patched to not be that stupid
[PatchPrefix]
public static bool Prefix(
Player.FirearmController.Class1039 __instance,
FirearmAddingModState __instance,
bool ___bool_0,
FirearmsAnimator ___firearmsAnimator_0,
Item ___item_0,
@@ -577,7 +577,7 @@ namespace UIFixes
__instance.State = Player.EOperationState.Finished;
// Begin change (moved from bottom)
___firearmController_0.InitiateOperation<Player.FirearmController.GClass1619>().Start(null);
___firearmController_0.InitiateOperation<FirearmReadyState>().Start(null);
__instance.method_5(gameObject);
// End change

View File

@@ -1,9 +1,9 @@
using SPT.Reflection.Patching;
using Comfort.Common;
using EFT.HealthSystem;
using Comfort.Common;
using EFT.InventoryLogic;
using EFT.UI;
using HarmonyLib;
using SPT.Reflection.Patching;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -25,7 +25,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.DeclaredProperty(typeof(GClass3054), nameof(GClass3054.AvailableInteractions)).GetMethod;
return AccessTools.DeclaredProperty(R.TradingInteractions.Type, "AvailableInteractions").GetMethod;
}
[PatchPostfix]
@@ -41,7 +41,7 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.DeclaredProperty(typeof(GClass3057), nameof(GClass3057.AvailableInteractions)).GetMethod;
return AccessTools.DeclaredProperty(R.TransferInteractions.Type, "AvailableInteractions").GetMethod;
}
[PatchPostfix]
@@ -88,11 +88,12 @@ namespace UIFixes
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Constructor(typeof(ScavengerInventoryScreen.GClass3156), [typeof(GClass2780), typeof(GClass2780), typeof(IHealthController), typeof(StashClass), typeof(ISession)]);
Type type = typeof(ScavengerInventoryScreen).GetNestedTypes().Single(t => t.GetField("ScavController") != null); // ScavengerInventoryScreen.GClass3156
return AccessTools.GetDeclaredConstructors(type).Single();
}
[PatchPrefix]
public static void Prefix(GClass2780 scavController)
public static void Prefix(InventoryContainerClass scavController)
{
scavController.Inventory.Stash = null;
}

116
R.cs
View File

@@ -59,6 +59,10 @@ namespace UIFixes
ContextMenuHelper.InitTypes();
RagfairNewOfferItemView.InitTypes();
TradingTableGridView.InitTypes();
ItemReceiver.InitTypes();
InventoryInteractions.InitTypes();
TradingInteractions.InitTypes();
TransferInteractions.InitTypes();
}
public abstract class Wrapper(object value)
@@ -142,7 +146,7 @@ namespace UIFixes
{
Type = typeof(EFT.Hideout.ProductionPanel);
SearchInputFieldField = AccessTools.Field(Type, "_searchInputField");
ProductionBuildsField = AccessTools.GetDeclaredFields(Type).Single(t => t.FieldType.GetElementType() == typeof(ProductionBuildAbstractClass));
ProductionBuildsField = AccessTools.GetDeclaredFields(Type).Single(f => f.FieldType.GetElementType() == typeof(ProductionBuildAbstractClass));
}
public ValidationInputField SeachInputField { get { return (ValidationInputField)SearchInputFieldField.GetValue(Value); } }
@@ -703,6 +707,116 @@ namespace UIFixes
public TraderAssortmentControllerClass TraderAssortmentController { get { return (TraderAssortmentControllerClass)TraderAssortmentControllerField.GetValue(Value); } }
}
public class ItemReceiver(object value) : Wrapper(value) // GClass1855
{
public static Type Type { get; private set; }
private static FieldInfo InventoryControllerField;
public static void InitTypes()
{
Type = PatchConstants.EftTypes.Single(t =>
{
FieldInfo field = t.GetField("localQuestControllerClass", BindingFlags.NonPublic | BindingFlags.Instance);
return field != null && field.IsInitOnly;
});
InventoryControllerField = Type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Single(f => typeof(InventoryControllerClass).IsAssignableFrom(f.FieldType));
}
public InventoryControllerClass InventoryController { get { return (InventoryControllerClass)InventoryControllerField.GetValue(Value); } }
}
public class InventoryInteractions(object value) : Wrapper(value)
{
public static Type Type { get; private set; }
public static void InitTypes()
{
Type = PatchConstants.EftTypes.Single(t => t.GetField("HIDEOUT_WEAPON_MODIFICATION_REQUIRED") != null); // GClass3045
}
}
public class TradingInteractions(object value) : Wrapper(value)
{
public static Type Type { get; private set; }
private static FieldInfo ItemField;
private static readonly HashSet<EItemInfoButton> Interactions =
[
EItemInfoButton.Inspect,
EItemInfoButton.Uninstall,
EItemInfoButton.Examine,
EItemInfoButton.Open,
EItemInfoButton.Insure,
EItemInfoButton.Repair,
EItemInfoButton.Modding,
EItemInfoButton.EditBuild,
EItemInfoButton.FilterSearch,
EItemInfoButton.LinkedSearch,
EItemInfoButton.NeededSearch,
EItemInfoButton.Tag,
EItemInfoButton.ResetTag,
EItemInfoButton.TurnOn,
EItemInfoButton.TurnOff,
EItemInfoButton.Fold,
EItemInfoButton.Unfold,
EItemInfoButton.Disassemble,
EItemInfoButton.Discard
];
public static void InitTypes()
{
// GClass3054
Type = PatchConstants.EftTypes.Single(t =>
{
var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static);
if (enumerableField != null)
{
var enumerable = (IEnumerable<EItemInfoButton>)enumerableField.GetValue(null);
return Interactions.SetEquals(enumerable);
}
return false;
});
ItemField = AccessTools.Field(Type, "item_0");
}
public Item Item { get { return (Item) ItemField.GetValue(Value); } }
}
public class TransferInteractions(object value) : Wrapper(value)
{
public static Type Type { get; private set; }
private static readonly HashSet<EItemInfoButton> Interactions =
[
EItemInfoButton.Inspect,
EItemInfoButton.Uninstall,
EItemInfoButton.Examine,
EItemInfoButton.Equip,
EItemInfoButton.Open,
EItemInfoButton.Fold,
EItemInfoButton.Unfold,
EItemInfoButton.Disassemble,
EItemInfoButton.Discard
];
public static void InitTypes()
{
// GClass3057
Type = PatchConstants.EftTypes.Single(t =>
{
var enumerableField = t.GetField("ienumerable_2", BindingFlags.NonPublic | BindingFlags.Static);
if (enumerableField != null)
{
var enumerable = (IEnumerable<EItemInfoButton>)enumerableField.GetValue(null);
return Interactions.SetEquals(enumerable);
}
return false;
});
}
}
}
public static class RExtentensions