diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java index 1e69e8a6..6e842346 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java @@ -4,11 +4,13 @@ import com.google.common.collect.ImmutableMap; import io.izzel.arclight.common.bridge.block.FireBlockBridge; import io.izzel.arclight.common.bridge.bukkit.MaterialBridge; import io.izzel.arclight.common.mod.ArclightMod; +import io.izzel.arclight.common.mod.server.block.ArclightTileInventory; import io.izzel.arclight.i18n.LocalizedException; import io.izzel.arclight.i18n.conf.MaterialPropertySpec; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.FallingBlock; +import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; @@ -334,6 +336,7 @@ public abstract class MaterialMixin implements MaterialBridge { this.setupBlockStateFunc(); } + @SuppressWarnings({"rawtypes", "unchecked"}) private void setupBlockStateFunc() { if (arclight$spec.blockStateClass != null) { try { @@ -368,6 +371,9 @@ public abstract class MaterialMixin implements MaterialBridge { if (this.arclight$stateFunc == null) { this.arclight$stateFunc = b -> { TileEntity tileEntity = b.getCraftWorld().getHandle().getTileEntity(b.getPosition()); + if (tileEntity instanceof IInventory) { + return new ArclightTileInventory(b, tileEntity.getClass()); + } return tileEntity == null ? new CraftBlockState(b) : new CraftBlockEntityState<>(b, tileEntity.getClass()); }; } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/inventory/container/ContainerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/inventory/container/ContainerMixin.java index 2499da86..2466e115 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/inventory/container/ContainerMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/inventory/container/ContainerMixin.java @@ -1,18 +1,15 @@ package io.izzel.arclight.common.mixin.core.inventory.container; import com.google.common.base.Preconditions; -import io.izzel.arclight.common.bridge.entity.player.PlayerEntityBridge; import io.izzel.arclight.common.bridge.inventory.IInventoryBridge; import io.izzel.arclight.common.bridge.inventory.container.ContainerBridge; import io.izzel.arclight.common.bridge.inventory.container.SlotBridge; import io.izzel.arclight.common.mod.ArclightMod; -import io.izzel.arclight.common.mod.util.ArclightCaptures; +import io.izzel.arclight.common.mod.server.ArclightContainer; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.inventory.CraftResultInventory; -import net.minecraft.inventory.IInventory; import net.minecraft.inventory.container.ClickType; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; @@ -25,13 +22,10 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.craftbukkit.v.entity.CraftHumanEntity; import org.bukkit.craftbukkit.v.inventory.CraftInventory; -import org.bukkit.craftbukkit.v.inventory.CraftInventoryCustom; -import org.bukkit.craftbukkit.v.inventory.CraftInventoryView; import org.bukkit.craftbukkit.v.inventory.CraftItemStack; import org.bukkit.event.Event; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; @@ -42,7 +36,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -75,54 +68,9 @@ public abstract class ContainerMixin implements ContainerBridge { public boolean checkReachable = true; private InventoryView bukkitView; - // todo check this public InventoryView getBukkitView() { if (bukkitView == null) { - PlayerEntity candidate = null; - Set set = new HashSet<>(); - for (Slot slot : this.inventorySlots) { - if (slot.inventory != null) { - if (slot.inventory instanceof PlayerInventory) { - if (candidate != null && ((PlayerInventory) slot.inventory).player != candidate) { - ArclightMod.LOGGER.warn("Duplicate PlayerInventory inside {}, previous {}, new {}", this, candidate, slot.inventory); - } - candidate = ((PlayerInventory) slot.inventory).player; - } else { - set.add(slot.inventory); - } - } - } - if (candidate == null) { - if (ArclightCaptures.getContainerOwner() != null) { - candidate = ArclightCaptures.getContainerOwner(); - } else { - throw new RuntimeException("candidate cannot be null"); - } - } - CraftResultInventory resultCandidate = null; - IInventory mainCandidate = null; - for (IInventory inventory : set) { - if (inventory instanceof CraftResultInventory) { - resultCandidate = (CraftResultInventory) inventory; - } else { - mainCandidate = inventory; - } - } - Inventory inv; - if (mainCandidate == null && resultCandidate != null) { - mainCandidate = resultCandidate; - resultCandidate = null; - } - if (mainCandidate != null) { - if (resultCandidate != null) { - inv = new org.bukkit.craftbukkit.v.inventory.CraftResultInventory(mainCandidate, resultCandidate); - } else { - inv = new CraftInventory(mainCandidate); - } - } else { // container has no slots - inv = new CraftInventoryCustom(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), 0); - } - bukkitView = new CraftInventoryView(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), inv, (Container) (Object) this); + bukkitView = ArclightContainer.createInvView((Container) (Object) this); } return bukkitView; } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightContainer.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightContainer.java new file mode 100644 index 00000000..e21e7b4a --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/ArclightContainer.java @@ -0,0 +1,125 @@ +package io.izzel.arclight.common.mod.server; + +import io.izzel.arclight.api.Unsafe; +import io.izzel.arclight.common.bridge.entity.player.PlayerEntityBridge; +import io.izzel.arclight.common.mod.ArclightMod; +import io.izzel.arclight.common.mod.util.ArclightCaptures; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.CraftResultInventory; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.Slot; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.SlotItemHandler; +import net.minecraftforge.items.wrapper.CombinedInvWrapper; +import net.minecraftforge.items.wrapper.InvWrapper; +import net.minecraftforge.items.wrapper.RangedWrapper; +import org.bukkit.craftbukkit.v.inventory.CraftInventory; +import org.bukkit.craftbukkit.v.inventory.CraftInventoryCustom; +import org.bukkit.craftbukkit.v.inventory.CraftInventoryView; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; + +public class ArclightContainer { + + private static final long HANDLERS_OFFSET; + private static final long COMPOSE_OFFSET; + + static { + try { + Unsafe.ensureClassInitialized(CombinedInvWrapper.class); + Field itemHandler = CombinedInvWrapper.class.getDeclaredField("itemHandler"); + HANDLERS_OFFSET = Unsafe.objectFieldOffset(itemHandler); + Unsafe.ensureClassInitialized(RangedWrapper.class); + Field compose = RangedWrapper.class.getDeclaredField("compose"); + COMPOSE_OFFSET = Unsafe.objectFieldOffset(compose); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private static IInventory getActualInventoryForSlot(Slot slot) { + if (slot instanceof SlotItemHandler) { + return getInventoryFromWrapper(((SlotItemHandler) slot).getItemHandler()); + } else { + return slot.inventory; + } + } + + private static IInventory getInventoryFromWrapper(IItemHandler handler) { + if (handler instanceof CombinedInvWrapper) { + IItemHandlerModifiable[] handlers = ((IItemHandlerModifiable[]) Unsafe.getObject(handler, HANDLERS_OFFSET)); + IInventory last = null; + for (IItemHandlerModifiable modifiable : handlers) { + IInventory inventory = getInventoryFromWrapper(modifiable); + if (inventory instanceof PlayerInventory) { + return inventory; + } else { + last = inventory; + } + } + return last; + } else if (handler instanceof InvWrapper) { + return ((InvWrapper) handler).getInv(); + } else if (handler instanceof RangedWrapper) { + return getInventoryFromWrapper(((IItemHandler) Unsafe.getObject(handler, COMPOSE_OFFSET))); + } else { + return null; + } + } + + // todo check this + public static InventoryView createInvView(Container container) { + PlayerEntity candidate = null; + Set set = new HashSet<>(); + for (Slot slot : container.inventorySlots) { + IInventory inventory = getActualInventoryForSlot(slot); + if (inventory != null) { + if (inventory instanceof PlayerInventory) { + if (candidate != null && ((PlayerInventory) inventory).player != candidate) { + ArclightMod.LOGGER.warn("Multiple player found in {}/{}, previous {}, new {}", container, container.getClass(), candidate, ((PlayerInventory) inventory).player); + } + candidate = ((PlayerInventory) inventory).player; + } + set.add(inventory); + } + } + if (candidate == null) { + if (ArclightCaptures.getContainerOwner() != null) { + candidate = ArclightCaptures.getContainerOwner(); + } else { + throw new RuntimeException("candidate cannot be null, " + container + "/" + container.getClass()); + } + } + CraftResultInventory resultCandidate = null; + IInventory mainCandidate = null; + for (IInventory inventory : set) { + if (inventory instanceof CraftResultInventory) { + resultCandidate = (CraftResultInventory) inventory; + } else { + mainCandidate = inventory; + } + } + Inventory inv; + if (mainCandidate == null && resultCandidate != null) { + mainCandidate = resultCandidate; + resultCandidate = null; + } + if (mainCandidate != null) { + if (resultCandidate != null) { + inv = new org.bukkit.craftbukkit.v.inventory.CraftResultInventory(mainCandidate, resultCandidate); + } else { + inv = new CraftInventory(mainCandidate); + } + } else { // container has no slots + inv = new CraftInventoryCustom(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), 0); + } + return new CraftInventoryView(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), inv, container); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/block/ArclightTileInventory.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/block/ArclightTileInventory.java new file mode 100644 index 00000000..ec6ede34 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/block/ArclightTileInventory.java @@ -0,0 +1,22 @@ +package io.izzel.arclight.common.mod.server.block; + +import net.minecraft.inventory.IInventory; +import net.minecraft.tileentity.TileEntity; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v.block.CraftBlockEntityState; +import org.bukkit.craftbukkit.v.inventory.CraftInventory; +import org.bukkit.inventory.BlockInventoryHolder; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; + +public class ArclightTileInventory extends CraftBlockEntityState implements BlockInventoryHolder { + + public ArclightTileInventory(Block block, Class tileEntityClass) { + super(block, tileEntityClass); + } + + @Override + public @NotNull Inventory getInventory() { + return new CraftInventory(this.getTileEntity()); + } +}