Skip to content

Commit

Permalink
Move Trade and Loot Modifier to method handle helper
Browse files Browse the repository at this point in the history
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
  • Loading branch information
TheSilkMiner committed Apr 24, 2021
1 parent 3c642ab commit 3a3c4e4
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 61 deletions.
@@ -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<String, MethodHandle> 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));
Expand Down Expand Up @@ -62,11 +44,7 @@ public static float getPriceMult(VillagerTrades.ITrade trade) {

private static <T> T invoke(final VillagerTrades.ITrade trade, final TradeFunction<T> 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!");
}
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.
*
Expand Down Expand Up @@ -213,20 +191,27 @@ public void removeAll() {
@SuppressWarnings("unchecked")
private Map<ResourceLocation, IGlobalLootModifier> getLmmMap() {
try {
final LootModifierManager lmm = (LootModifierManager) LMM_GETTER.invokeExact();
Map<ResourceLocation, IGlobalLootModifier> map = (Map<ResourceLocation, IGlobalLootModifier>) LMM_MAP_GETTER.invokeExact(lmm);
final LootModifierManager lmm = MethodHandleHelper.invoke(() -> (LootModifierManager) LMM_GETTER.invokeExact());
Map<ResourceLocation, IGlobalLootModifier> map = MethodHandleHelper.invoke(() -> (Map<ResourceLocation, IGlobalLootModifier>) 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<ResourceLocation, IGlobalLootModifier> 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<ResourceLocation, IGlobalLootModifier> map) throws Throwable {
LMM_MAP_SETTER.invokeExact(lmm, map);
}

private ResourceLocation fromName(final String name) {
return NameUtils.fromFixedName(
Expand Down

0 comments on commit 3a3c4e4

Please sign in to comment.