Fix mod inventory size limit implementer (#227 #251)

This commit is contained in:
IzzelAliz 2021-05-09 21:46:38 +08:00
parent d22c78f007
commit 1c0c9116d5
3 changed files with 120 additions and 56 deletions

View File

@ -15,13 +15,15 @@ import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
@ -29,8 +31,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
public class InventoryImplementer implements Implementer {
@ -38,17 +38,17 @@ public class InventoryImplementer implements Implementer {
private static final String INV_TYPE = "net/minecraft/inventory/IInventory";
private static final String BRIDGE_TYPE = "io/izzel/arclight/common/bridge/inventory/IInventoryBridge";
private final Map<String, Boolean> map = new ConcurrentHashMap<>();
private final Map<String, Integer> map = new ConcurrentHashMap<>();
public InventoryImplementer() {
map.put(INV_TYPE, true);
map.put("java/lang/Object", false);
map.put(INV_TYPE, 1);
map.put("java/lang/Object", 0);
}
@Override
public boolean processClass(ClassNode node, ILaunchPluginService.ITransformerLoader transformerLoader) {
try {
if (node.interfaces.contains(BRIDGE_TYPE)) {
if (Modifier.isInterface(node.access) || node.interfaces.contains(BRIDGE_TYPE)) {
return false;
}
if (isInventoryClass(node, transformerLoader)) {
@ -65,17 +65,26 @@ public class InventoryImplementer implements Implementer {
}
private boolean isInventoryClass(ClassNode node, ILaunchPluginService.ITransformerLoader transformerLoader) throws Throwable {
Integer ret = map.get(node.name);
if (ret != null) return ret > 1;
Integer i = map.get(node.superName);
if (i != null) {
if (i > 1) {
map.put(node.name, i + 1);
return true;
}
}
if (node.interfaces.contains(INV_TYPE)) {
map.put(node.name, true);
map.put(node.name, 2);
return true;
} else {
Boolean b = map.get(node.superName);
if (b != null) {
map.put(node.name, b);
return b;
boolean b = isInventoryClass(findClass(node.superName, transformerLoader), transformerLoader);
if (b) {
map.put(node.name, map.get(node.superName) + 1);
} else {
return isInventoryClass(findClass(node.superName, transformerLoader), transformerLoader);
map.put(node.name, 0);
}
return b;
}
}
@ -114,29 +123,12 @@ public class InventoryImplementer implements Implementer {
node.interfaces.add(BRIDGE_TYPE);
return false;
} else {
List<FieldNode> list = findPossibleList(node);
if (list.size() != 1) {
if (list.size() > 1) {
ArclightImplementer.LOGGER.warn(MARKER, "Found multiple possible fields in class {}: {}", node.name, list.stream().map(it -> it.name + it.desc).collect(Collectors.joining(", ")));
} else return false;
}
ArclightImplementer.LOGGER.debug(MARKER, "Implementing inventory for class {}", node.name);
FieldNode stackList = list.get(0);
FieldNode transaction = new FieldNode(Opcodes.ACC_PRIVATE, "transaction", Type.getType(List.class).getDescriptor(), null, null);
FieldNode maxStack = new FieldNode(Opcodes.ACC_PRIVATE, "maxStack", "I", null, 64);
FieldNode transaction = new FieldNode(Opcodes.ACC_PRIVATE, "$transaction", Type.getType(List.class).getDescriptor(), null, null);
FieldNode maxStack = new FieldNode(Opcodes.ACC_PRIVATE, "$maxStack", "I", null, null);
node.fields.add(transaction);
node.fields.add(maxStack);
node.interfaces.add(BRIDGE_TYPE);
InsnList initInsn = new InsnList();
{
MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "getContents", Type.getMethodDescriptor(Type.getType(List.class)), null, null);
InsnList insnList = new InsnList();
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
insnList.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, stackList.name, stackList.desc));
insnList.add(new InsnNode(Opcodes.ARETURN));
methodNode.instructions = insnList;
node.methods.add(methodNode);
}
{
MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "onOpen", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getObjectType("org/bukkit/craftbukkit/" + ArclightVersion.current().packageName() + "/entity/CraftHumanEntity")), null, null);
InsnList insnList = new InsnList();
@ -170,25 +162,33 @@ public class InventoryImplementer implements Implementer {
methodNode.instructions = insnList;
node.methods.add(methodNode);
}
{
MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "func_70297_j_", "()I", null, null);
InsnList insnList = new InsnList();
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
insnList.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, maxStack.name, maxStack.desc));
insnList.add(new InsnNode(Opcodes.IRETURN));
methodNode.instructions = insnList;
node.methods.add(methodNode);
}
if (stackLimitMethod == null) {
initInsn.add(new VarInsnNode(Opcodes.ALOAD, 0));
initInsn.add(new IntInsnNode(Opcodes.BIPUSH, 64));
initInsn.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, maxStack.name, maxStack.desc));
InsnList list = new InsnList();
LabelNode labelNode = new LabelNode();
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
list.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, maxStack.name, maxStack.desc));
list.add(new InsnNode(Opcodes.ICONST_M1));
list.add(new JumpInsnNode(Opcodes.IF_ICMPEQ, labelNode));
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
list.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, maxStack.name, maxStack.desc));
list.add(new InsnNode(Opcodes.IRETURN));
list.add(labelNode);
if (stackLimitMethod != null && !Modifier.isAbstract(stackLimitMethod.access)) {
stackLimitMethod.instructions.insert(list);
} else {
stackLimitMethod.name += "$" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
initInsn.add(new VarInsnNode(Opcodes.ALOAD, 0));
initInsn.add(new InsnNode(Opcodes.DUP));
initInsn.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, node.name, stackLimitMethod.name, stackLimitMethod.desc, false));
initInsn.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, maxStack.name, maxStack.desc));
MethodNode methodNode = stackLimitMethod == null
? new MethodNode(0, "func_70297_j_", "()I", null, null)
: stackLimitMethod;
methodNode.access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
int level = map.get(node.name);
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
if (level > 2) {
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, node.superName, methodNode.name, methodNode.desc, false));
} else {
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, INV_TYPE, methodNode.name, methodNode.desc, true));
}
list.add(new InsnNode(Opcodes.IRETURN));
methodNode.instructions.insert(list);
node.methods.add(methodNode);
}
{
MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "setMaxStackSize", "(I)V", null, null);
@ -207,14 +207,15 @@ public class InventoryImplementer implements Implementer {
while (!(initNode.getOpcode() == Opcodes.INVOKESPECIAL && ((MethodInsnNode) initNode).name.equals("<init>"))) {
initNode = initNode.getNext();
}
MethodNode mn = new MethodNode();
initInsn.accept(mn);
InsnList insnList = mn.instructions;
InsnList insnList = new InsnList();
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>", "()V", false));
insnList.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, transaction.name, transaction.desc));
insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
insnList.add(new InsnNode(Opcodes.ICONST_M1));
insnList.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, maxStack.name, maxStack.desc));
methodNode.instructions.insert(initNode, insnList);
}
}

View File

@ -1,6 +1,7 @@
package io.izzel.arclight.common.bridge.inventory;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.common.mod.util.WrappedContents;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
@ -17,7 +18,9 @@ public interface IInventoryBridge {
int MAX_STACK = 64;
List<ItemStack> getContents();
default List<ItemStack> getContents() {
return new WrappedContents((IInventory) this);
}
void onOpen(CraftHumanEntity who);
@ -33,9 +36,12 @@ public interface IInventoryBridge {
Location getLocation();
IRecipe<?> getCurrentRecipe();
default IRecipe<?> getCurrentRecipe() {
return null;
}
void setCurrentRecipe(IRecipe<?> recipe);
default void setCurrentRecipe(IRecipe<?> recipe) {
}
default Inventory getOwnerInventory() {
InventoryHolder owner = this.getOwner();

View File

@ -0,0 +1,57 @@
package io.izzel.arclight.common.mod.util;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import org.jetbrains.annotations.NotNull;
import javax.annotation.ParametersAreNonnullByDefault;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class WrappedContents extends NonNullList<ItemStack> {
private final IInventory inventory;
public WrappedContents(IInventory inventory) {
this.inventory = inventory;
}
@NotNull
@Override
public ItemStack get(int i) {
return inventory.getStackInSlot(i);
}
@Override
public ItemStack set(int i, ItemStack stack) {
ItemStack ret = inventory.getStackInSlot(i);
inventory.setInventorySlotContents(i, stack);
return ret;
}
@Override
public void add(int i, ItemStack stack) {
if (inventory.getStackInSlot(i).isEmpty()) {
inventory.setInventorySlotContents(i, stack);
} else {
throw new UnsupportedOperationException();
}
}
@Override
public ItemStack remove(int i) {
return inventory.removeStackFromSlot(i);
}
@Override
public int size() {
return inventory.getSizeInventory();
}
@Override
public void clear() {
inventory.clear();
}
}