455 lines
17 KiB
Java
455 lines
17 KiB
Java
package net.minecraft.world.level.block.entity;
|
|
|
|
import java.util.List;
|
|
import java.util.function.BooleanSupplier;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.NonNullList;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.world.Container;
|
|
import net.minecraft.world.ContainerHelper;
|
|
import net.minecraft.world.WorldlyContainer;
|
|
import net.minecraft.world.WorldlyContainerHolder;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
import net.minecraft.world.entity.player.Inventory;
|
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
import net.minecraft.world.inventory.HopperMenu;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.ChestBlock;
|
|
import net.minecraft.world.level.block.HopperBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.phys.AABB;
|
|
|
|
public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper {
|
|
public static final int MOVE_ITEM_SPEED = 8;
|
|
public static final int HOPPER_CONTAINER_SIZE = 5;
|
|
private static final int[][] CACHED_SLOTS = new int[54][];
|
|
private static final int NO_COOLDOWN_TIME = -1;
|
|
private NonNullList<ItemStack> items = NonNullList.withSize(5, ItemStack.EMPTY);
|
|
private int cooldownTime = -1;
|
|
private long tickedGameTime;
|
|
private Direction facing;
|
|
|
|
public HopperBlockEntity(BlockPos p_155550_, BlockState p_155551_) {
|
|
super(BlockEntityType.HOPPER, p_155550_, p_155551_);
|
|
this.facing = p_155551_.getValue(HopperBlock.FACING);
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(CompoundTag p_331195_, HolderLookup.Provider p_329407_) {
|
|
super.loadAdditional(p_331195_, p_329407_);
|
|
this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
|
if (!this.tryLoadLootTable(p_331195_)) {
|
|
ContainerHelper.loadAllItems(p_331195_, this.items, p_329407_);
|
|
}
|
|
|
|
this.cooldownTime = p_331195_.getIntOr("TransferCooldown", -1);
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag p_187502_, HolderLookup.Provider p_334921_) {
|
|
super.saveAdditional(p_187502_, p_334921_);
|
|
if (!this.trySaveLootTable(p_187502_)) {
|
|
ContainerHelper.saveAllItems(p_187502_, this.items, p_334921_);
|
|
}
|
|
|
|
p_187502_.putInt("TransferCooldown", this.cooldownTime);
|
|
}
|
|
|
|
@Override
|
|
public int getContainerSize() {
|
|
return this.items.size();
|
|
}
|
|
|
|
@Override
|
|
public ItemStack removeItem(int p_59309_, int p_59310_) {
|
|
this.unpackLootTable(null);
|
|
return ContainerHelper.removeItem(this.getItems(), p_59309_, p_59310_);
|
|
}
|
|
|
|
@Override
|
|
public void setItem(int p_59315_, ItemStack p_59316_) {
|
|
this.unpackLootTable(null);
|
|
this.getItems().set(p_59315_, p_59316_);
|
|
p_59316_.limitSize(this.getMaxStackSize(p_59316_));
|
|
}
|
|
|
|
@Override
|
|
public void setBlockState(BlockState p_334323_) {
|
|
super.setBlockState(p_334323_);
|
|
this.facing = p_334323_.getValue(HopperBlock.FACING);
|
|
}
|
|
|
|
@Override
|
|
protected Component getDefaultName() {
|
|
return Component.translatable("container.hopper");
|
|
}
|
|
|
|
public static void pushItemsTick(Level p_155574_, BlockPos p_155575_, BlockState p_155576_, HopperBlockEntity p_155577_) {
|
|
p_155577_.cooldownTime--;
|
|
p_155577_.tickedGameTime = p_155574_.getGameTime();
|
|
if (!p_155577_.isOnCooldown()) {
|
|
p_155577_.setCooldown(0);
|
|
tryMoveItems(p_155574_, p_155575_, p_155576_, p_155577_, () -> suckInItems(p_155574_, p_155577_));
|
|
}
|
|
}
|
|
|
|
private static boolean tryMoveItems(Level p_155579_, BlockPos p_155580_, BlockState p_155581_, HopperBlockEntity p_155582_, BooleanSupplier p_155583_) {
|
|
if (p_155579_.isClientSide) {
|
|
return false;
|
|
} else {
|
|
if (!p_155582_.isOnCooldown() && p_155581_.getValue(HopperBlock.ENABLED)) {
|
|
boolean flag = false;
|
|
if (!p_155582_.isEmpty()) {
|
|
flag = ejectItems(p_155579_, p_155580_, p_155582_);
|
|
}
|
|
|
|
if (!p_155582_.inventoryFull()) {
|
|
flag |= p_155583_.getAsBoolean();
|
|
}
|
|
|
|
if (flag) {
|
|
p_155582_.setCooldown(8);
|
|
setChanged(p_155579_, p_155580_, p_155581_);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private boolean inventoryFull() {
|
|
for (ItemStack itemstack : this.items) {
|
|
if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static boolean ejectItems(Level p_155563_, BlockPos p_155564_, HopperBlockEntity p_329427_) {
|
|
Container container = getAttachedContainer(p_155563_, p_155564_, p_329427_);
|
|
if (container == null) {
|
|
return false;
|
|
} else {
|
|
Direction direction = p_329427_.facing.getOpposite();
|
|
if (isFullContainer(container, direction)) {
|
|
return false;
|
|
} else {
|
|
for (int i = 0; i < p_329427_.getContainerSize(); i++) {
|
|
ItemStack itemstack = p_329427_.getItem(i);
|
|
if (!itemstack.isEmpty()) {
|
|
int j = itemstack.getCount();
|
|
ItemStack itemstack1 = addItem(p_329427_, container, p_329427_.removeItem(i, 1), direction);
|
|
if (itemstack1.isEmpty()) {
|
|
container.setChanged();
|
|
return true;
|
|
}
|
|
|
|
itemstack.setCount(j);
|
|
if (j == 1) {
|
|
p_329427_.setItem(i, itemstack);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int[] getSlots(Container p_59340_, Direction p_59341_) {
|
|
if (p_59340_ instanceof WorldlyContainer worldlycontainer) {
|
|
return worldlycontainer.getSlotsForFace(p_59341_);
|
|
} else {
|
|
int i = p_59340_.getContainerSize();
|
|
if (i < CACHED_SLOTS.length) {
|
|
int[] aint = CACHED_SLOTS[i];
|
|
if (aint != null) {
|
|
return aint;
|
|
} else {
|
|
int[] aint1 = createFlatSlots(i);
|
|
CACHED_SLOTS[i] = aint1;
|
|
return aint1;
|
|
}
|
|
} else {
|
|
return createFlatSlots(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int[] createFlatSlots(int p_329697_) {
|
|
int[] aint = new int[p_329697_];
|
|
int i = 0;
|
|
|
|
while (i < aint.length) {
|
|
aint[i] = i++;
|
|
}
|
|
|
|
return aint;
|
|
}
|
|
|
|
private static boolean isFullContainer(Container p_59386_, Direction p_59387_) {
|
|
int[] aint = getSlots(p_59386_, p_59387_);
|
|
|
|
for (int i : aint) {
|
|
ItemStack itemstack = p_59386_.getItem(i);
|
|
if (itemstack.getCount() < itemstack.getMaxStackSize()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static boolean suckInItems(Level p_155553_, Hopper p_155554_) {
|
|
BlockPos blockpos = BlockPos.containing(p_155554_.getLevelX(), p_155554_.getLevelY() + 1.0, p_155554_.getLevelZ());
|
|
BlockState blockstate = p_155553_.getBlockState(blockpos);
|
|
Container container = getSourceContainer(p_155553_, p_155554_, blockpos, blockstate);
|
|
if (container != null) {
|
|
Direction direction = Direction.DOWN;
|
|
|
|
for (int i : getSlots(container, direction)) {
|
|
if (tryTakeInItemFromSlot(p_155554_, container, i, direction)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
boolean flag = p_155554_.isGridAligned() && blockstate.isCollisionShapeFullBlock(p_155553_, blockpos) && !blockstate.is(BlockTags.DOES_NOT_BLOCK_HOPPERS);
|
|
if (!flag) {
|
|
for (ItemEntity itementity : getItemsAtAndAbove(p_155553_, p_155554_)) {
|
|
if (addItem(p_155554_, itementity)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static boolean tryTakeInItemFromSlot(Hopper p_59355_, Container p_59356_, int p_59357_, Direction p_59358_) {
|
|
ItemStack itemstack = p_59356_.getItem(p_59357_);
|
|
if (!itemstack.isEmpty() && canTakeItemFromContainer(p_59355_, p_59356_, itemstack, p_59357_, p_59358_)) {
|
|
int i = itemstack.getCount();
|
|
ItemStack itemstack1 = addItem(p_59356_, p_59355_, p_59356_.removeItem(p_59357_, 1), null);
|
|
if (itemstack1.isEmpty()) {
|
|
p_59356_.setChanged();
|
|
return true;
|
|
}
|
|
|
|
itemstack.setCount(i);
|
|
if (i == 1) {
|
|
p_59356_.setItem(p_59357_, itemstack);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static boolean addItem(Container p_59332_, ItemEntity p_59333_) {
|
|
boolean flag = false;
|
|
ItemStack itemstack = p_59333_.getItem().copy();
|
|
ItemStack itemstack1 = addItem(null, p_59332_, itemstack, null);
|
|
if (itemstack1.isEmpty()) {
|
|
flag = true;
|
|
p_59333_.setItem(ItemStack.EMPTY);
|
|
p_59333_.discard();
|
|
} else {
|
|
p_59333_.setItem(itemstack1);
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
public static ItemStack addItem(@Nullable Container p_59327_, Container p_59328_, ItemStack p_59329_, @Nullable Direction p_59330_) {
|
|
if (p_59328_ instanceof WorldlyContainer worldlycontainer && p_59330_ != null) {
|
|
int[] aint = worldlycontainer.getSlotsForFace(p_59330_);
|
|
|
|
for (int k = 0; k < aint.length && !p_59329_.isEmpty(); k++) {
|
|
p_59329_ = tryMoveInItem(p_59327_, p_59328_, p_59329_, aint[k], p_59330_);
|
|
}
|
|
} else {
|
|
int i = p_59328_.getContainerSize();
|
|
|
|
for (int j = 0; j < i && !p_59329_.isEmpty(); j++) {
|
|
p_59329_ = tryMoveInItem(p_59327_, p_59328_, p_59329_, j, p_59330_);
|
|
}
|
|
}
|
|
|
|
return p_59329_;
|
|
}
|
|
|
|
private static boolean canPlaceItemInContainer(Container p_59335_, ItemStack p_59336_, int p_59337_, @Nullable Direction p_59338_) {
|
|
return !p_59335_.canPlaceItem(p_59337_, p_59336_)
|
|
? false
|
|
: !(p_59335_ instanceof WorldlyContainer worldlycontainer && !worldlycontainer.canPlaceItemThroughFace(p_59337_, p_59336_, p_59338_));
|
|
}
|
|
|
|
private static boolean canTakeItemFromContainer(Container p_273433_, Container p_273542_, ItemStack p_273400_, int p_273519_, Direction p_273088_) {
|
|
return !p_273542_.canTakeItem(p_273433_, p_273519_, p_273400_)
|
|
? false
|
|
: !(p_273542_ instanceof WorldlyContainer worldlycontainer && !worldlycontainer.canTakeItemThroughFace(p_273519_, p_273400_, p_273088_));
|
|
}
|
|
|
|
private static ItemStack tryMoveInItem(@Nullable Container p_59321_, Container p_59322_, ItemStack p_59323_, int p_59324_, @Nullable Direction p_59325_) {
|
|
ItemStack itemstack = p_59322_.getItem(p_59324_);
|
|
if (canPlaceItemInContainer(p_59322_, p_59323_, p_59324_, p_59325_)) {
|
|
boolean flag = false;
|
|
boolean flag1 = p_59322_.isEmpty();
|
|
if (itemstack.isEmpty()) {
|
|
p_59322_.setItem(p_59324_, p_59323_);
|
|
p_59323_ = ItemStack.EMPTY;
|
|
flag = true;
|
|
} else if (canMergeItems(itemstack, p_59323_)) {
|
|
int i = p_59323_.getMaxStackSize() - itemstack.getCount();
|
|
int j = Math.min(p_59323_.getCount(), i);
|
|
p_59323_.shrink(j);
|
|
itemstack.grow(j);
|
|
flag = j > 0;
|
|
}
|
|
|
|
if (flag) {
|
|
if (flag1 && p_59322_ instanceof HopperBlockEntity hopperblockentity1 && !hopperblockentity1.isOnCustomCooldown()) {
|
|
int k = 0;
|
|
if (p_59321_ instanceof HopperBlockEntity hopperblockentity && hopperblockentity1.tickedGameTime >= hopperblockentity.tickedGameTime) {
|
|
k = 1;
|
|
}
|
|
|
|
hopperblockentity1.setCooldown(8 - k);
|
|
}
|
|
|
|
p_59322_.setChanged();
|
|
}
|
|
}
|
|
|
|
return p_59323_;
|
|
}
|
|
|
|
@Nullable
|
|
private static Container getAttachedContainer(Level p_155593_, BlockPos p_155594_, HopperBlockEntity p_331744_) {
|
|
return getContainerAt(p_155593_, p_155594_.relative(p_331744_.facing));
|
|
}
|
|
|
|
@Nullable
|
|
private static Container getSourceContainer(Level p_155597_, Hopper p_155598_, BlockPos p_330370_, BlockState p_334668_) {
|
|
return getContainerAt(p_155597_, p_330370_, p_334668_, p_155598_.getLevelX(), p_155598_.getLevelY() + 1.0, p_155598_.getLevelZ());
|
|
}
|
|
|
|
public static List<ItemEntity> getItemsAtAndAbove(Level p_155590_, Hopper p_155591_) {
|
|
AABB aabb = p_155591_.getSuckAabb().move(p_155591_.getLevelX() - 0.5, p_155591_.getLevelY() - 0.5, p_155591_.getLevelZ() - 0.5);
|
|
return p_155590_.getEntitiesOfClass(ItemEntity.class, aabb, EntitySelector.ENTITY_STILL_ALIVE);
|
|
}
|
|
|
|
@Nullable
|
|
public static Container getContainerAt(Level p_59391_, BlockPos p_59392_) {
|
|
return getContainerAt(p_59391_, p_59392_, p_59391_.getBlockState(p_59392_), p_59392_.getX() + 0.5, p_59392_.getY() + 0.5, p_59392_.getZ() + 0.5);
|
|
}
|
|
|
|
@Nullable
|
|
private static Container getContainerAt(Level p_59348_, BlockPos p_330520_, BlockState p_334938_, double p_59349_, double p_59350_, double p_59351_) {
|
|
Container container = getBlockContainer(p_59348_, p_330520_, p_334938_);
|
|
if (container == null) {
|
|
container = getEntityContainer(p_59348_, p_59349_, p_59350_, p_59351_);
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
@Nullable
|
|
private static Container getBlockContainer(Level p_329847_, BlockPos p_329170_, BlockState p_328169_) {
|
|
Block block = p_328169_.getBlock();
|
|
if (block instanceof WorldlyContainerHolder) {
|
|
return ((WorldlyContainerHolder)block).getContainer(p_328169_, p_329847_, p_329170_);
|
|
} else if (p_328169_.hasBlockEntity() && p_329847_.getBlockEntity(p_329170_) instanceof Container container) {
|
|
if (container instanceof ChestBlockEntity && block instanceof ChestBlock) {
|
|
container = ChestBlock.getContainer((ChestBlock)block, p_328169_, p_329847_, p_329170_, true);
|
|
}
|
|
|
|
return container;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static Container getEntityContainer(Level p_328239_, double p_335152_, double p_336273_, double p_330059_) {
|
|
List<Entity> list = p_328239_.getEntities(
|
|
(Entity)null,
|
|
new AABB(p_335152_ - 0.5, p_336273_ - 0.5, p_330059_ - 0.5, p_335152_ + 0.5, p_336273_ + 0.5, p_330059_ + 0.5),
|
|
EntitySelector.CONTAINER_ENTITY_SELECTOR
|
|
);
|
|
return !list.isEmpty() ? (Container)list.get(p_328239_.random.nextInt(list.size())) : null;
|
|
}
|
|
|
|
private static boolean canMergeItems(ItemStack p_59345_, ItemStack p_59346_) {
|
|
return p_59345_.getCount() <= p_59345_.getMaxStackSize() && ItemStack.isSameItemSameComponents(p_59345_, p_59346_);
|
|
}
|
|
|
|
@Override
|
|
public double getLevelX() {
|
|
return this.worldPosition.getX() + 0.5;
|
|
}
|
|
|
|
@Override
|
|
public double getLevelY() {
|
|
return this.worldPosition.getY() + 0.5;
|
|
}
|
|
|
|
@Override
|
|
public double getLevelZ() {
|
|
return this.worldPosition.getZ() + 0.5;
|
|
}
|
|
|
|
@Override
|
|
public boolean isGridAligned() {
|
|
return true;
|
|
}
|
|
|
|
private void setCooldown(int p_59396_) {
|
|
this.cooldownTime = p_59396_;
|
|
}
|
|
|
|
private boolean isOnCooldown() {
|
|
return this.cooldownTime > 0;
|
|
}
|
|
|
|
private boolean isOnCustomCooldown() {
|
|
return this.cooldownTime > 8;
|
|
}
|
|
|
|
@Override
|
|
protected NonNullList<ItemStack> getItems() {
|
|
return this.items;
|
|
}
|
|
|
|
@Override
|
|
protected void setItems(NonNullList<ItemStack> p_59371_) {
|
|
this.items = p_59371_;
|
|
}
|
|
|
|
public static void entityInside(Level p_155568_, BlockPos p_155569_, BlockState p_155570_, Entity p_155571_, HopperBlockEntity p_155572_) {
|
|
if (p_155571_ instanceof ItemEntity itementity
|
|
&& !itementity.getItem().isEmpty()
|
|
&& p_155571_.getBoundingBox().move(-p_155569_.getX(), -p_155569_.getY(), -p_155569_.getZ()).intersects(p_155572_.getSuckAabb())) {
|
|
tryMoveItems(p_155568_, p_155569_, p_155570_, p_155572_, () -> addItem(p_155572_, itementity));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected AbstractContainerMenu createMenu(int p_59312_, Inventory p_59313_) {
|
|
return new HopperMenu(p_59312_, p_59313_, this);
|
|
}
|
|
} |