diff --git a/src/main/java/com/blamejared/crafttweaker/api/villagers/BasicTradeExposer.java b/src/main/java/com/blamejared/crafttweaker/api/villagers/BasicTradeExposer.java index cbc72dc09..9f8f4b773 100644 --- a/src/main/java/com/blamejared/crafttweaker/api/villagers/BasicTradeExposer.java +++ b/src/main/java/com/blamejared/crafttweaker/api/villagers/BasicTradeExposer.java @@ -1,40 +1,22 @@ package com.blamejared.crafttweaker.api.villagers; -import com.blamejared.crafttweaker.api.util.StringUtils; +import com.blamejared.crafttweaker.api.util.MethodHandleHelper; import net.minecraft.entity.merchant.villager.VillagerTrades; import net.minecraft.item.ItemStack; import net.minecraftforge.common.BasicTrade; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.util.function.Function; /** * Class holding helper methods to expose fields in {@link net.minecraftforge.common.BasicTrade} */ public class BasicTradeExposer { - - private static final Function MAKE_HANDLE = s -> { - - try { - final MethodHandles.Lookup lookup = MethodHandles.lookup(); - final Class forgeInternalHandlerClass = BasicTrade.class; - final Field field = forgeInternalHandlerClass.getDeclaredField(s); - field.setAccessible(true); - - return lookup.unreflectGetter(field); - } catch(ReflectiveOperationException e) { - throw new RuntimeException("Unable to reflect into Basic Trade to get: " + StringUtils.quoteAndEscape(s)); - } - }; - - private static final MethodHandle PRICE_GETTER = MAKE_HANDLE.apply("price"); - private static final MethodHandle PRICE2_GETTER = MAKE_HANDLE.apply("price2"); - private static final MethodHandle FOR_SALE_GETTER = MAKE_HANDLE.apply("forSale"); - private static final MethodHandle MAX_TRADES_GETTER = MAKE_HANDLE.apply("maxTrades"); - private static final MethodHandle XP_GETTER = MAKE_HANDLE.apply("xp"); - private static final MethodHandle PRICE_MULT_GETTER = MAKE_HANDLE.apply("priceMult"); + private static final MethodHandle PRICE_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "price"); + private static final MethodHandle PRICE2_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "price2"); + private static final MethodHandle FOR_SALE_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "forSale"); + private static final MethodHandle MAX_TRADES_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "maxTrades"); + private static final MethodHandle XP_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "xp"); + private static final MethodHandle PRICE_MULT_GETTER = MethodHandleHelper.linkGetter(BasicTrade.class, "priceMult"); public static ItemStack getPrice(VillagerTrades.ITrade trade) { return invoke(trade, it -> (ItemStack) PRICE_GETTER.invokeExact(it)); @@ -62,11 +44,7 @@ public static float getPriceMult(VillagerTrades.ITrade trade) { private static T invoke(final VillagerTrades.ITrade trade, final TradeFunction function) { if(trade instanceof BasicTrade) { - try { - return function.x((BasicTrade) trade); - } catch(Throwable throwable) { - throw new RuntimeException(throwable); - } + return MethodHandleHelper.invoke(() -> function.x((BasicTrade) trade)); } throw new IllegalArgumentException(trade.getClass() + " is not of type BasicTrade!"); } diff --git a/src/main/java/com/blamejared/crafttweaker/impl/loot/modifiers/CTLootModifierManager.java b/src/main/java/com/blamejared/crafttweaker/impl/loot/modifiers/CTLootModifierManager.java index 378301118..5f8348c4a 100644 --- a/src/main/java/com/blamejared/crafttweaker/impl/loot/modifiers/CTLootModifierManager.java +++ b/src/main/java/com/blamejared/crafttweaker/impl/loot/modifiers/CTLootModifierManager.java @@ -5,6 +5,7 @@ import com.blamejared.crafttweaker.api.annotations.ZenRegister; import com.blamejared.crafttweaker.api.loot.conditions.ILootCondition; import com.blamejared.crafttweaker.api.loot.modifiers.ILootModifier; +import com.blamejared.crafttweaker.api.util.MethodHandleHelper; import com.blamejared.crafttweaker.impl.actions.loot.ActionRegisterLootModifier; import com.blamejared.crafttweaker.impl.actions.loot.ActionRemoveLootModifier; import com.blamejared.crafttweaker.impl.loot.conditions.CTLootConditionBuilder; @@ -18,9 +19,6 @@ import org.openzen.zencode.java.ZenCodeType; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -46,32 +44,12 @@ public final class CTLootModifierManager { public static final CTLootModifierManager LOOT_MODIFIER_MANAGER = new CTLootModifierManager(); - private static final MethodHandle LMM_GETTER; - private static final MethodHandle LMM_MAP_GETTER; - private static final MethodHandle LMM_MAP_SETTER; + private static final MethodHandle LMM_GETTER = MethodHandleHelper.link(ForgeInternalHandler.class, "getLootModifierManager"); + private static final MethodHandle LMM_MAP_GETTER = MethodHandleHelper.linkGetter(LootModifierManager.class, "registeredLootModifiers"); + private static final MethodHandle LMM_MAP_SETTER = MethodHandleHelper.linkSetter(LootModifierManager.class, "registeredLootModifiers"); private CTLootModifierManager() {} - static { - try { - final MethodHandles.Lookup lookup = MethodHandles.lookup(); - - final Class forgeInternalHandlerClass = ForgeInternalHandler.class; - final Method lmmGetterMethod = forgeInternalHandlerClass.getDeclaredMethod("getLootModifierManager"); - lmmGetterMethod.setAccessible(true); - - final Class lmmClass = LootModifierManager.class; - final Field registeredLootModifiersField = lmmClass.getDeclaredField("registeredLootModifiers"); - registeredLootModifiersField.setAccessible(true); - - LMM_GETTER = lookup.unreflect(lmmGetterMethod); - LMM_MAP_GETTER = lookup.unreflectGetter(registeredLootModifiersField); - LMM_MAP_SETTER = lookup.unreflectSetter(registeredLootModifiersField); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Unable to reflect into the loot modifier manager", e); - } - } - /** * Registers a new global loot modifier with the given name. * @@ -213,20 +191,27 @@ public void removeAll() { @SuppressWarnings("unchecked") private Map getLmmMap() { try { - final LootModifierManager lmm = (LootModifierManager) LMM_GETTER.invokeExact(); - Map map = (Map) LMM_MAP_GETTER.invokeExact(lmm); + final LootModifierManager lmm = MethodHandleHelper.invoke(() -> (LootModifierManager) LMM_GETTER.invokeExact()); + Map map = MethodHandleHelper.invoke(() -> (Map) LMM_MAP_GETTER.invokeExact(lmm)); if (map instanceof ImmutableMap) { map = new HashMap<>(map); - LMM_MAP_SETTER.invokeExact(lmm, map); // Let's "mutabilize" the map + final Map finalMap = map; + MethodHandleHelper.invokeVoid(() -> this.setLmmMap(lmm, finalMap)); // Let's "mutabilize" the map } return map; } catch (final IllegalStateException e) { // LMM_GETTER.invokeExact() throws ISE if we're on the client and playing multiplayer return Collections.emptyMap(); - } catch (final Throwable e) { - throw new RuntimeException(e); } } + + // Note for the dev: this is needed because in expression lambdas, the compiler incorrectly infers the method to + // be (Lnet/minecraftforge/common/loot/LootModifierManager;Ljava/util/Map;)Ljava/lang/Object; even if the return + // type of the lambda is void. The return value is simply popped. The presence of another method works around the + // issue. + private void setLmmMap(final LootModifierManager lmm, final Map map) throws Throwable { + LMM_MAP_SETTER.invokeExact(lmm, map); + } private ResourceLocation fromName(final String name) { return NameUtils.fromFixedName(