minecraft-source/src/net/minecraft/world/level/block/entity/HopperBlockEntity.java

383 lines
15 KiB
Java

package net.minecraft.world.level.block.entity;
import java.util.stream.Stream;
import net.minecraft.world.inventory.HopperMenu;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.WorldlyContainerHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Predicate;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import javax.annotation.Nullable;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.WorldlyContainer;
import java.util.stream.IntStream;
import net.minecraft.world.Container;
import net.minecraft.core.Direction;
import java.util.Iterator;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.HopperBlock;
import java.util.function.Supplier;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import java.util.List;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.ContainerHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.NonNullList;
public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, TickableBlockEntity {
private NonNullList<ItemStack> items;
private int cooldownTime;
private long tickedGameTime;
public HopperBlockEntity() {
super(BlockEntityType.HOPPER);
this.items = NonNullList.<ItemStack>withSize(5, ItemStack.EMPTY);
this.cooldownTime = -1;
}
@Override
public void load(final CompoundTag jt) {
super.load(jt);
this.items = NonNullList.<ItemStack>withSize(this.getContainerSize(), ItemStack.EMPTY);
if (!this.tryLoadLootTable(jt)) {
ContainerHelper.loadAllItems(jt, this.items);
}
this.cooldownTime = jt.getInt("TransferCooldown");
}
@Override
public CompoundTag save(final CompoundTag jt) {
super.save(jt);
if (!this.trySaveLootTable(jt)) {
ContainerHelper.saveAllItems(jt, this.items);
}
jt.putInt("TransferCooldown", this.cooldownTime);
return jt;
}
@Override
public int getContainerSize() {
return this.items.size();
}
@Override
public ItemStack removeItem(final int integer1, final int integer2) {
this.unpackLootTable(null);
return ContainerHelper.removeItem(this.getItems(), integer1, integer2);
}
@Override
public void setItem(final int integer, final ItemStack bek) {
this.unpackLootTable(null);
this.getItems().set(integer, bek);
if (bek.getCount() > this.getMaxStackSize()) {
bek.setCount(this.getMaxStackSize());
}
}
@Override
protected Component getDefaultName() {
return new TranslatableComponent("container.hopper", new Object[0]);
}
@Override
public void tick() {
if (this.level == null || this.level.isClientSide) {
return;
}
--this.cooldownTime;
this.tickedGameTime = this.level.getGameTime();
if (!this.isOnCooldown()) {
this.setCooldown(0);
this.tryMoveItems(() -> suckInItems(this));
}
}
private boolean tryMoveItems(final Supplier<Boolean> supplier) {
if (this.level == null || this.level.isClientSide) {
return false;
}
if (!this.isOnCooldown() && this.getBlockState().<Boolean>getValue((Property<Boolean>)HopperBlock.ENABLED)) {
boolean boolean3 = false;
if (!this.isEmpty()) {
boolean3 = this.ejectItems();
}
if (!this.inventoryFull()) {
boolean3 |= supplier.get();
}
if (boolean3) {
this.setCooldown(8);
this.setChanged();
return true;
}
}
return false;
}
private boolean inventoryFull() {
for (final ItemStack bek3 : this.items) {
if (bek3.isEmpty() || bek3.getCount() != bek3.getMaxStackSize()) {
return false;
}
}
return true;
}
private boolean ejectItems() {
final Container ajb2 = this.getAttachedContainer();
if (ajb2 == null) {
return false;
}
final Direction fp3 = this.getBlockState().<Direction>getValue((Property<Direction>)HopperBlock.FACING).getOpposite();
if (this.isFullContainer(ajb2, fp3)) {
return false;
}
for (int integer4 = 0; integer4 < this.getContainerSize(); ++integer4) {
if (!this.getItem(integer4).isEmpty()) {
final ItemStack bek5 = this.getItem(integer4).copy();
final ItemStack bek6 = addItem(this, ajb2, this.removeItem(integer4, 1), fp3);
if (bek6.isEmpty()) {
ajb2.setChanged();
return true;
}
this.setItem(integer4, bek5);
}
}
return false;
}
private static IntStream getSlots(final Container ajb, final Direction fp) {
if (ajb instanceof WorldlyContainer) {
return IntStream.of(((WorldlyContainer)ajb).getSlotsForFace(fp));
}
return IntStream.range(0, ajb.getContainerSize());
}
private boolean isFullContainer(final Container ajb, final Direction fp) {
final ItemStack bek3;
return getSlots(ajb, fp).allMatch(integer -> {
bek3 = ajb.getItem(integer);
return bek3.getCount() >= bek3.getMaxStackSize();
});
}
private static boolean isEmptyContainer(final Container ajb, final Direction fp) {
return getSlots(ajb, fp).allMatch(integer -> ajb.getItem(integer).isEmpty());
}
public static boolean suckInItems(final Hopper bww) {
final Container ajb2 = getSourceContainer(bww);
if (ajb2 != null) {
final Direction fp3 = Direction.DOWN;
return !isEmptyContainer(ajb2, fp3) && getSlots(ajb2, fp3).anyMatch(integer -> tryTakeInItemFromSlot(bww, ajb2, integer, fp3));
}
for (final ItemEntity avy4 : getItemsAtAndAbove(bww)) {
if (addItem(bww, avy4)) {
return true;
}
}
return false;
}
private static boolean tryTakeInItemFromSlot(final Hopper bww, final Container ajb, final int integer, final Direction fp) {
final ItemStack bek5 = ajb.getItem(integer);
if (!bek5.isEmpty() && canTakeItemFromContainer(ajb, bek5, integer, fp)) {
final ItemStack bek6 = bek5.copy();
final ItemStack bek7 = addItem(ajb, bww, ajb.removeItem(integer, 1), null);
if (bek7.isEmpty()) {
ajb.setChanged();
return true;
}
ajb.setItem(integer, bek6);
}
return false;
}
public static boolean addItem(final Container ajb, final ItemEntity avy) {
boolean boolean3 = false;
final ItemStack bek4 = avy.getItem().copy();
final ItemStack bek5 = addItem(null, ajb, bek4, null);
if (bek5.isEmpty()) {
boolean3 = true;
avy.remove();
}
else {
avy.setItem(bek5);
}
return boolean3;
}
public static ItemStack addItem(@Nullable final Container ajb1, final Container ajb2, ItemStack bek, @Nullable final Direction fp) {
if (ajb2 instanceof WorldlyContainer && fp != null) {
final WorldlyContainer ajr5 = (WorldlyContainer)ajb2;
final int[] arr6 = ajr5.getSlotsForFace(fp);
for (int integer7 = 0; integer7 < arr6.length && !bek.isEmpty(); bek = tryMoveInItem(ajb1, ajb2, bek, arr6[integer7], fp), ++integer7) {}
}
else {
for (int integer8 = ajb2.getContainerSize(), integer9 = 0; integer9 < integer8 && !bek.isEmpty(); bek = tryMoveInItem(ajb1, ajb2, bek, integer9, fp), ++integer9) {}
}
return bek;
}
private static boolean canPlaceItemInContainer(final Container ajb, final ItemStack bek, final int integer, @Nullable final Direction fp) {
return ajb.canPlaceItem(integer, bek) && (!(ajb instanceof WorldlyContainer) || ((WorldlyContainer)ajb).canPlaceItemThroughFace(integer, bek, fp));
}
private static boolean canTakeItemFromContainer(final Container ajb, final ItemStack bek, final int integer, final Direction fp) {
return !(ajb instanceof WorldlyContainer) || ((WorldlyContainer)ajb).canTakeItemThroughFace(integer, bek, fp);
}
private static ItemStack tryMoveInItem(@Nullable final Container ajb1, final Container ajb2, ItemStack bek, final int integer, @Nullable final Direction fp) {
final ItemStack bek2 = ajb2.getItem(integer);
if (canPlaceItemInContainer(ajb2, bek, integer, fp)) {
boolean boolean7 = false;
final boolean boolean8 = ajb2.isEmpty();
if (bek2.isEmpty()) {
ajb2.setItem(integer, bek);
bek = ItemStack.EMPTY;
boolean7 = true;
}
else if (canMergeItems(bek2, bek)) {
final int integer2 = bek.getMaxStackSize() - bek2.getCount();
final int integer3 = Math.min(bek.getCount(), integer2);
bek.shrink(integer3);
bek2.grow(integer3);
boolean7 = (integer3 > 0);
}
if (boolean7) {
if (boolean8 && ajb2 instanceof HopperBlockEntity) {
final HopperBlockEntity bwx9 = (HopperBlockEntity)ajb2;
if (!bwx9.isOnCustomCooldown()) {
int integer3 = 0;
if (ajb1 instanceof HopperBlockEntity) {
final HopperBlockEntity bwx10 = (HopperBlockEntity)ajb1;
if (bwx9.tickedGameTime >= bwx10.tickedGameTime) {
integer3 = 1;
}
}
bwx9.setCooldown(8 - integer3);
}
}
ajb2.setChanged();
}
}
return bek;
}
@Nullable
private Container getAttachedContainer() {
final Direction fp2 = this.getBlockState().<Direction>getValue((Property<Direction>)HopperBlock.FACING);
return getContainerAt(this.getLevel(), this.worldPosition.relative(fp2));
}
@Nullable
public static Container getSourceContainer(final Hopper bww) {
return getContainerAt(bww.getLevel(), bww.getLevelX(), bww.getLevelY() + 1.0, bww.getLevelZ());
}
public static List<ItemEntity> getItemsAtAndAbove(final Hopper bww) {
return bww.getSuckShape().toAabbs().stream().flatMap(cvc -> bww.getLevel().<Entity>getEntitiesOfClass(ItemEntity.class, cvc.move(bww.getLevelX() - 0.5, bww.getLevelY() - 0.5, bww.getLevelZ() - 0.5), EntitySelector.ENTITY_STILL_ALIVE).stream()).collect(Collectors.toList());
}
@Nullable
public static Container getContainerAt(final Level bjt, final BlockPos fk) {
return getContainerAt(bjt, fk.getX() + 0.5, fk.getY() + 0.5, fk.getZ() + 0.5);
}
@Nullable
public static Container getContainerAt(final Level bjt, final double double2, final double double3, final double double4) {
Container ajb8 = null;
final BlockPos fk9 = new BlockPos(double2, double3, double4);
final BlockState byg10 = bjt.getBlockState(fk9);
final Block bpe11 = byg10.getBlock();
if (bpe11 instanceof WorldlyContainerHolder) {
ajb8 = ((WorldlyContainerHolder)bpe11).getContainer(byg10, bjt, fk9);
}
else if (bpe11.isEntityBlock()) {
final BlockEntity bwi12 = bjt.getBlockEntity(fk9);
if (bwi12 instanceof Container) {
ajb8 = (Container)bwi12;
if (ajb8 instanceof ChestBlockEntity && bpe11 instanceof ChestBlock) {
ajb8 = ChestBlock.getContainer((ChestBlock)bpe11, byg10, bjt, fk9, true);
}
}
}
if (ajb8 == null) {
final List<Entity> list12 = bjt.getEntities(null, new AABB(double2 - 0.5, double3 - 0.5, double4 - 0.5, double2 + 0.5, double3 + 0.5, double4 + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR);
if (!list12.isEmpty()) {
ajb8 = (Container)list12.get(bjt.random.nextInt(list12.size()));
}
}
return ajb8;
}
private static boolean canMergeItems(final ItemStack bek1, final ItemStack bek2) {
return bek1.getItem() == bek2.getItem() && bek1.getDamageValue() == bek2.getDamageValue() && bek1.getCount() <= bek1.getMaxStackSize() && ItemStack.tagMatches(bek1, bek2);
}
@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;
}
private void setCooldown(final int integer) {
this.cooldownTime = integer;
}
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(final NonNullList<ItemStack> fy) {
this.items = fy;
}
public void entityInside(final Entity akn) {
if (akn instanceof ItemEntity) {
final BlockPos fk3 = this.getBlockPos();
if (Shapes.joinIsNotEmpty(Shapes.create(akn.getBoundingBox().move(-fk3.getX(), -fk3.getY(), -fk3.getZ())), this.getSuckShape(), BooleanOp.AND)) {
this.tryMoveItems(() -> addItem(this, (ItemEntity)akn));
}
}
}
@Override
protected AbstractContainerMenu createMenu(final int integer, final Inventory ayf) {
return new HopperMenu(integer, ayf, this);
}
}