diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/ContainerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/ContainerMixin.java index cb2b8288..b20f8cf6 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/ContainerMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/ContainerMixin.java @@ -1,8 +1,8 @@ package io.izzel.arclight.common.mixin.core.world; import io.izzel.arclight.common.bridge.core.inventory.IInventoryBridge; +import io.izzel.arclight.common.mod.inventory.SideViewingTracker; import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; import org.bukkit.Location; import org.bukkit.craftbukkit.v.entity.CraftHumanEntity; @@ -10,28 +10,24 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.InventoryHolder; import org.spongepowered.asm.mixin.Mixin; -import java.util.ArrayList; import java.util.List; @Mixin(Container.class) public interface ContainerMixin extends IInventoryBridge { - @Override - default List getContents() { - return new ArrayList<>(); - } - @Override default void onOpen(CraftHumanEntity who) { + SideViewingTracker.onOpen((Container) this, who); } @Override default void onClose(CraftHumanEntity who) { + SideViewingTracker.onClose((Container) this, who); } @Override default List getViewers() { - return new ArrayList<>(); + return SideViewingTracker.getViewers((Container) this); } @Override diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/inventory/SideViewingTracker.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/inventory/SideViewingTracker.java new file mode 100644 index 00000000..df33468f --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/inventory/SideViewingTracker.java @@ -0,0 +1,27 @@ +package io.izzel.arclight.common.mod.inventory; + +import net.minecraft.world.Container; +import org.bukkit.entity.HumanEntity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +public class SideViewingTracker { + + private static final Map> VIEWERS = Collections.synchronizedMap(new WeakHashMap<>()); + + public static void onOpen(Container container, HumanEntity humanEntity) { + VIEWERS.computeIfAbsent(container, k -> new ArrayList<>()).add(humanEntity); + } + + public static void onClose(Container container, HumanEntity humanEntity) { + VIEWERS.computeIfAbsent(container, k -> new ArrayList<>()).remove(humanEntity); + } + + public static List getViewers(Container container) { + return VIEWERS.computeIfAbsent(container, k -> new ArrayList<>()); + } +} diff --git a/arclight-forge/src/main/java/io/izzel/arclight/boot/asm/InventoryImplementer.java b/arclight-forge/src/main/java/io/izzel/arclight/boot/asm/InventoryImplementer.java index 92d72505..20c8d388 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/boot/asm/InventoryImplementer.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/boot/asm/InventoryImplementer.java @@ -1,15 +1,10 @@ package io.izzel.arclight.boot.asm; -import com.google.common.io.ByteStreams; import cpw.mods.modlauncher.serviceapi.ILaunchPluginService; -import io.izzel.arclight.api.ArclightVersion; -import io.izzel.arclight.i18n.LocalizedException; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; @@ -19,149 +14,48 @@ 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.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; public class InventoryImplementer implements Implementer { private static final Marker MARKER = MarkerManager.getMarker("INVENTORY"); - private static final String INV_TYPE = "net/minecraft/world/Container"; private static final String BRIDGE_TYPE = "io/izzel/arclight/common/bridge/core/inventory/IInventoryBridge"; - private final Map map = new ConcurrentHashMap<>(); - public InventoryImplementer() { - map.put(INV_TYPE, 1); - map.put("java/lang/Object", 0); } @Override public boolean processClass(ClassNode node, ILaunchPluginService.ITransformerLoader transformerLoader) { - try { - if (Modifier.isInterface(node.access) || node.interfaces.contains(BRIDGE_TYPE)) { - return false; - } - if (isInventoryClass(node, transformerLoader)) { - return tryImplement(node); - } else return false; - } catch (Throwable t) { - if (t instanceof LocalizedException) { - ArclightImplementer.LOGGER.error(MARKER, ((LocalizedException) t).node(), ((LocalizedException) t).args()); - } else { - ArclightImplementer.LOGGER.error(MARKER, "Error processing class", t); - } + if (Modifier.isInterface(node.access) || node.interfaces.contains(BRIDGE_TYPE)) { return false; } - } - - private boolean isInventoryClass(ClassNode node, ILaunchPluginService.ITransformerLoader transformerLoader) throws Throwable { - if (node == null) { // maybe runtime defined class - return false; - } - 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, 2); - return true; - } else { - boolean b = isInventoryClass(findClass(node.superName, transformerLoader), transformerLoader); - if (b) { - map.put(node.name, map.get(node.superName) + 1); - } else { - map.put(node.name, 0); - } - return b; - } - } - - private ClassNode findClass(String typeName, ILaunchPluginService.ITransformerLoader transformerLoader) throws Exception { - try { - byte[] array; - InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(typeName + ".class"); - if (stream != null) { - array = ByteStreams.toByteArray(stream); - } else { - array = transformerLoader.buildTransformedClassNodeFor(Type.getObjectType(typeName).getClassName()); - } - ClassNode node = new ClassNode(); - new ClassReader(array).accept(node, ClassReader.SKIP_CODE); - return node; - } catch (ClassNotFoundException e) { - ArclightImplementer.LOGGER.debug("implementer.not-found", typeName); - return null; - } + return tryImplement(node); } private boolean tryImplement(ClassNode node) { - Set methods = new HashSet<>(); MethodNode stackLimitMethod = null; for (MethodNode method : node.methods) { - String desc = method.name + method.desc; - methods.add(desc); - if (desc.equals("m_6893_()I")) { + if (!Modifier.isAbstract(method.access) && method.name.equals("m_6893_") && method.desc.equals("()I")) { // getMaxStackSize stackLimitMethod = method; + break; } } - if (methods.contains("getViewers()Ljava/util/List;")) { - ArclightImplementer.LOGGER.debug(MARKER, "Found implemented class {}", node.name); - node.interfaces.add(BRIDGE_TYPE); + if (stackLimitMethod == null) { return false; } else { - ArclightImplementer.LOGGER.debug(MARKER, "Implementing inventory for class {} in {}", node.name, map.get(node.name)); - FieldNode transaction = new FieldNode(Opcodes.ACC_PRIVATE, "$transaction", Type.getType(List.class).getDescriptor(), null, null); - FieldNode maxStack = new FieldNode(Opcodes.ACC_PRIVATE, "$maxStack", Type.getType(Integer.class).getDescriptor(), null, null); - node.fields.add(transaction); + for (MethodNode method : node.methods) { + if (method.name.equals("setMaxStackSize") && method.desc.equals("(I)V")) { + ArclightImplementer.LOGGER.debug(MARKER, "Found implemented class {}", node.name); + return false; + } + } + + ArclightImplementer.LOGGER.debug(MARKER, "Implementing inventory for class {}", node.name); + FieldNode maxStack = new FieldNode(Opcodes.ACC_PRIVATE, "arclight$maxStack", Type.getType(Integer.class).getDescriptor(), null, null); node.fields.add(maxStack); node.interfaces.add(BRIDGE_TYPE); - { - 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(); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, transaction.name, transaction.desc)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add", "(Ljava/lang/Object;)Z", true)); - insnList.add(new InsnNode(Opcodes.POP)); - insnList.add(new InsnNode(Opcodes.RETURN)); - methodNode.instructions = insnList; - node.methods.add(methodNode); - } - { - MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "onClose", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getObjectType("org/bukkit/craftbukkit/" + ArclightVersion.current().packageName() + "/entity/CraftHumanEntity")), null, null); - InsnList insnList = new InsnList(); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 0)); - insnList.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, transaction.name, transaction.desc)); - insnList.add(new VarInsnNode(Opcodes.ALOAD, 1)); - insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "remove", "(Ljava/lang/Object;)Z", true)); - insnList.add(new InsnNode(Opcodes.POP)); - insnList.add(new InsnNode(Opcodes.RETURN)); - methodNode.instructions = insnList; - node.methods.add(methodNode); - } - { - MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "getViewers", 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, transaction.name, transaction.desc)); - insnList.add(new InsnNode(Opcodes.ARETURN)); - methodNode.instructions = insnList; - node.methods.add(methodNode); - } InsnList list = new InsnList(); LabelNode labelNode = new LabelNode(); list.add(new VarInsnNode(Opcodes.ALOAD, 0)); @@ -172,24 +66,7 @@ public class InventoryImplementer implements Implementer { list.add(new InsnNode(Opcodes.IRETURN)); list.add(labelNode); list.add(new InsnNode(Opcodes.POP)); - if (stackLimitMethod != null && !Modifier.isAbstract(stackLimitMethod.access)) { - stackLimitMethod.instructions.insert(list); - } else { - MethodNode methodNode = stackLimitMethod == null - ? new MethodNode(0, "m_6893_()I", "()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); - } + stackLimitMethod.instructions.insert(list); { MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "setMaxStackSize", "(I)V", null, null); InsnList insnList = new InsnList(); @@ -201,23 +78,6 @@ public class InventoryImplementer implements Implementer { methodNode.instructions = insnList; node.methods.add(methodNode); } - { - for (MethodNode methodNode : node.methods) { - if (methodNode.name.equals("")) { - AbstractInsnNode initNode = methodNode.instructions.getFirst(); - while (!(initNode.getOpcode() == Opcodes.INVOKESPECIAL && ((MethodInsnNode) initNode).name.equals(""))) { - initNode = initNode.getNext(); - } - 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), "", "()V", false)); - insnList.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, transaction.name, transaction.desc)); - methodNode.instructions.insert(initNode, insnList); - } - } - } return true; } }