383 lines
15 KiB
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);
|
|
}
|
|
}
|