diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/play/ServerPlayNetHandlerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/play/ServerPlayNetHandlerMixin.java index 243e6e51..ce03783f 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/play/ServerPlayNetHandlerMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/play/ServerPlayNetHandlerMixin.java @@ -1229,7 +1229,9 @@ public abstract class ServerPlayNetHandlerMixin implements ServerPlayNetHandlerB if (packet.getSlotId() < -1 && packet.getSlotId() != -999) { return; } + ArclightCaptures.captureContainerOwner(this.player); InventoryView inventory = ((ContainerBridge) this.player.openContainer).bridge$getBukkitView(); + ArclightCaptures.resetContainerOwner(); InventoryType.SlotType type = inventory.getSlotType(packet.getSlotId()); org.bukkit.event.inventory.ClickType click = org.bukkit.event.inventory.ClickType.UNKNOWN; InventoryAction action = InventoryAction.UNKNOWN; diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/forge/NetworkHooksMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/forge/NetworkHooksMixin.java new file mode 100644 index 00000000..07fcd47c --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/forge/NetworkHooksMixin.java @@ -0,0 +1,35 @@ +package io.izzel.arclight.common.mixin.forge; + +import io.izzel.arclight.common.bridge.inventory.container.ContainerBridge; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.INamedContainerProvider; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkHooks; +import org.bukkit.craftbukkit.v.event.CraftEventFactory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.function.Consumer; + +@Mixin(NetworkHooks.class) +public class NetworkHooksMixin { + + @Inject(method = "openGui(Lnet/minecraft/entity/player/ServerPlayerEntity;Lnet/minecraft/inventory/container/INamedContainerProvider;Ljava/util/function/Consumer;)V", + cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/container/Container;getType()Lnet/minecraft/inventory/container/ContainerType;")) + private static void arclight$openContainer(ServerPlayerEntity player, INamedContainerProvider containerSupplier, Consumer extraDataWriter, CallbackInfo ci, + int currentId, PacketBuffer extraData, PacketBuffer output, Container container) { + ((ContainerBridge) container).bridge$setTitle(containerSupplier.getDisplayName()); + container = CraftEventFactory.callInventoryOpenEvent(player, container); + if (container == null) { + if (containerSupplier instanceof IInventory) { + ((IInventory) containerSupplier).closeInventory(player); + } + ci.cancel(); + } + } +} 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 index e21e7b4a..b17e9b43 100644 --- 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 @@ -2,29 +2,37 @@ 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.bridge.inventory.IInventoryBridge; +import io.izzel.arclight.common.bridge.inventory.container.PosContainerBridge; 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.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; 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.Location; +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.entity.HumanEntity; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; public class ArclightContainer { @@ -76,50 +84,157 @@ public class ArclightContainer { // todo check this public static InventoryView createInvView(Container container) { - PlayerEntity candidate = null; - Set set = new HashSet<>(); - for (Slot slot : container.inventorySlots) { + PlayerEntity candidate = ArclightCaptures.getContainerOwner(); + int bottomBegin = -1, bottomEnd = -1; + for (ListIterator iterator = container.inventorySlots.listIterator(); iterator.hasNext(); ) { + Slot slot = iterator.next(); 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; + 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; + if (bottomBegin == -1 || bottomBegin < bottomEnd) { + bottomBegin = iterator.previousIndex(); + } + } else { + if (bottomEnd < bottomBegin) { + bottomEnd = iterator.previousIndex(); } - set.add(inventory); } } if (candidate == null) { - if (ArclightCaptures.getContainerOwner() != null) { - candidate = ArclightCaptures.getContainerOwner(); - } else { - throw new RuntimeException("candidate cannot be null, " + container + "/" + container.getClass()); + throw new RuntimeException("candidate cannot be null, " + container + "/" + container.getClass()); + } + if (bottomBegin < bottomEnd) { + bottomBegin = container.inventorySlots.size(); + } + Inventory viewing = new CraftInventory(new ContainerInvWrapper(container, bottomBegin, candidate)); + return new CraftInventoryView(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), viewing, container); + } + + private static class ContainerInvWrapper implements IInventory, IInventoryBridge { + + private final Container container; + private final int size; + private InventoryHolder owner; + private final List viewers = new ArrayList<>(); + + public ContainerInvWrapper(Container container, int size, PlayerEntity owner) { + this.container = container; + this.size = size; + this.owner = ((PlayerEntityBridge) owner).bridge$getBukkitEntity(); + } + + @Override + public int getSizeInventory() { + return size; + } + + @Override + public boolean isEmpty() { + for (Slot slot : container.inventorySlots) { + if (!slot.getStack().isEmpty()) return false; + } + return true; + } + + @Override + public @NotNull ItemStack getStackInSlot(int index) { + if (index >= size) return ItemStack.EMPTY; + return container.getSlot(index).getStack(); + } + + @Override + public @NotNull ItemStack decrStackSize(int index, int count) { + if (index >= size) return ItemStack.EMPTY; + return container.getSlot(index).decrStackSize(count); + } + + @Override + public @NotNull ItemStack removeStackFromSlot(int index) { + if (index >= size) return ItemStack.EMPTY; + return container.getSlot(index).decrStackSize(Integer.MAX_VALUE); + } + + @Override + public void setInventorySlotContents(int index, @NotNull ItemStack stack) { + if (index >= size) return; + container.putStackInSlot(index, stack); + } + + @Override + public int getInventoryStackLimit() { + if (size <= 0) return 0; + return container.getSlot(0).getSlotStackLimit(); + } + + @Override + public void markDirty() { + } + + @Override + public boolean isUsableByPlayer(@NotNull PlayerEntity player) { + return this.container.canInteractWith(player); + } + + @Override + public void clear() { + for (Slot slot : this.container.inventorySlots) { + slot.decrStackSize(Integer.MAX_VALUE); } } - CraftResultInventory resultCandidate = null; - IInventory mainCandidate = null; - for (IInventory inventory : set) { - if (inventory instanceof CraftResultInventory) { - resultCandidate = (CraftResultInventory) inventory; - } else { - mainCandidate = inventory; + + @Override + public List getContents() { + container.detectAndSendChanges(); + return container.inventoryItemStacks.subList(0, size); + } + + @Override + public void onOpen(CraftHumanEntity who) { + viewers.add(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + viewers.remove(who); + } + + @Override + public List getViewers() { + return viewers; + } + + @Override + public InventoryHolder getOwner() { + return owner; + } + + @Override + public void setOwner(InventoryHolder owner) { + this.owner = owner; + } + + @Override + public void setMaxStackSize(int size) { + } + + @Override + public Location getLocation() { + if (container instanceof PosContainerBridge) { + return ((PosContainerBridge) container).bridge$getWorldLocation(); } + return null; } - Inventory inv; - if (mainCandidate == null && resultCandidate != null) { - mainCandidate = resultCandidate; - resultCandidate = null; + + @Override + public IRecipe getCurrentRecipe() { + return 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); + + @Override + public void setCurrentRecipe(IRecipe recipe) { } - return new CraftInventoryView(((PlayerEntityBridge) candidate).bridge$getBukkitEntity(), inv, container); } } diff --git a/arclight-common/src/main/resources/mixins.arclight.forge.json b/arclight-common/src/main/resources/mixins.arclight.forge.json index 86c1e65f..799ed00f 100644 --- a/arclight-common/src/main/resources/mixins.arclight.forge.json +++ b/arclight-common/src/main/resources/mixins.arclight.forge.json @@ -8,6 +8,7 @@ "mixins": [ "ForgeEventFactoryMixin", "ForgeHooksMixin", - "ForgeInternalHandlerMixin" + "ForgeInternalHandlerMixin", + "NetworkHooksMixin" ] } \ No newline at end of file