Find players from forge wrapper classes

This commit is contained in:
IzzelAliz 2020-10-24 17:56:52 +08:00
parent 6c8d6d832a
commit 401a290c68
4 changed files with 155 additions and 54 deletions

View File

@ -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());
};
}

View File

@ -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<IInventory> 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;
}

View File

@ -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<IInventory> 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);
}
}

View File

@ -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<T extends TileEntity & IInventory> extends CraftBlockEntityState<T> implements BlockInventoryHolder {
public ArclightTileInventory(Block block, Class<T> tileEntityClass) {
super(block, tileEntityClass);
}
@Override
public @NotNull Inventory getInventory() {
return new CraftInventory(this.getTileEntity());
}
}