PrettyPipes/src/main/java/de/ellpeck/prettypipes/pipe/PipeTileEntity.java

382 lines
16 KiB
Java
Raw Normal View History

2020-04-16 04:42:42 +02:00
package de.ellpeck.prettypipes.pipe;
2020-04-14 01:38:48 +02:00
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
2020-05-09 12:57:25 +02:00
import de.ellpeck.prettypipes.Utility;
2020-04-16 00:39:53 +02:00
import de.ellpeck.prettypipes.items.IModule;
2020-10-14 23:39:11 +02:00
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
2020-04-14 17:14:24 +02:00
import de.ellpeck.prettypipes.network.PipeItem;
2020-04-17 17:18:25 +02:00
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.containers.MainPipeContainer;
2020-10-13 18:11:40 +02:00
import de.ellpeck.prettypipes.pressurizer.PressurizerTileEntity;
import net.minecraft.block.BlockState;
2020-10-17 00:58:31 +02:00
import net.minecraft.block.Blocks;
import net.minecraft.block.ChestBlock;
2020-04-14 01:38:48 +02:00
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
2020-04-16 00:39:53 +02:00
import net.minecraft.item.Item;
2020-04-14 01:38:48 +02:00
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
2020-10-15 01:19:22 +02:00
import net.minecraft.nbt.ListNBT;
2020-10-17 00:58:31 +02:00
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
2020-04-16 00:39:53 +02:00
import net.minecraft.profiler.IProfiler;
import net.minecraft.tileentity.ChestTileEntity;
2020-04-14 17:14:24 +02:00
import net.minecraft.tileentity.ITickableTileEntity;
2020-04-14 01:38:48 +02:00
import net.minecraft.tileentity.TileEntity;
2020-08-28 00:29:44 +02:00
import net.minecraft.tileentity.TileEntityType;
2020-04-14 17:14:24 +02:00
import net.minecraft.util.Direction;
2020-10-17 00:58:31 +02:00
import net.minecraft.util.math.AxisAlignedBB;
2020-04-14 17:14:24 +02:00
import net.minecraft.util.math.BlockPos;
2020-04-14 01:38:48 +02:00
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
2020-10-17 00:58:31 +02:00
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
2020-05-09 12:57:25 +02:00
import net.minecraftforge.common.util.Constants.NBT;
2020-04-14 17:14:24 +02:00
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
2020-04-14 01:38:48 +02:00
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.InvWrapper;
2020-04-16 04:42:42 +02:00
import org.apache.commons.lang3.tuple.Pair;
2020-10-15 01:19:22 +02:00
import org.apache.commons.lang3.tuple.Triple;
2020-04-14 01:38:48 +02:00
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
2020-04-16 04:42:42 +02:00
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
2020-04-16 00:39:53 +02:00
import java.util.stream.Stream;
2020-04-14 01:38:48 +02:00
2020-04-16 04:42:42 +02:00
public class PipeTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity {
2020-04-14 01:38:48 +02:00
2020-04-16 00:39:53 +02:00
public final ItemStackHandler modules = new ItemStackHandler(3) {
2020-04-14 01:38:48 +02:00
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
Item item = stack.getItem();
if (!(item instanceof IModule))
return false;
IModule module = (IModule) item;
2020-04-16 04:42:42 +02:00
return PipeTileEntity.this.streamModules().allMatch(m -> module.isCompatible(stack, PipeTileEntity.this, m.getRight()) && m.getRight().isCompatible(m.getLeft(), PipeTileEntity.this, module));
}
@Override
public int getSlotLimit(int slot) {
return 1;
2020-04-14 01:38:48 +02:00
}
};
public final Queue<NetworkLock> craftIngredientRequests = new LinkedList<>();
2020-10-15 04:15:52 +02:00
public final List<Pair<BlockPos, ItemStack>> craftResultRequests = new ArrayList<>();
public PressurizerTileEntity pressurizer;
2020-10-17 00:58:31 +02:00
public BlockState cover;
public int moduleDropCheck;
2020-08-28 17:14:29 +02:00
protected List<PipeItem> items;
2020-05-03 15:33:56 +02:00
private int lastItemAmount;
2020-04-17 17:18:25 +02:00
private int priority;
2020-04-14 01:38:48 +02:00
public PipeTileEntity() {
2020-08-28 00:29:44 +02:00
this(Registry.pipeTileEntity);
}
protected PipeTileEntity(TileEntityType<?> type) {
super(type);
2020-04-14 01:38:48 +02:00
}
@Override
public CompoundNBT write(CompoundNBT compound) {
2020-04-16 00:39:53 +02:00
compound.put("modules", this.modules.serializeNBT());
compound.putInt("module_drop_check", this.moduleDropCheck);
2020-10-14 23:39:11 +02:00
compound.put("requests", Utility.serializeAll(this.craftIngredientRequests));
2020-10-17 00:58:31 +02:00
if (this.cover != null)
compound.put("cover", NBTUtil.writeBlockState(this.cover));
2020-10-15 01:19:22 +02:00
ListNBT results = new ListNBT();
2020-10-15 04:15:52 +02:00
for (Pair<BlockPos, ItemStack> triple : this.craftResultRequests) {
2020-10-15 01:19:22 +02:00
CompoundNBT nbt = new CompoundNBT();
nbt.putLong("dest_pipe", triple.getLeft().toLong());
nbt.put("item", triple.getRight().serializeNBT());
results.add(nbt);
}
compound.put("craft_results", results);
2020-04-14 01:38:48 +02:00
return super.write(compound);
}
@Override
2020-09-22 19:14:07 +02:00
public void read(BlockState state, CompoundNBT compound) {
2020-04-16 00:39:53 +02:00
this.modules.deserializeNBT(compound.getCompound("modules"));
this.moduleDropCheck = compound.getInt("module_drop_check");
2020-10-17 00:58:31 +02:00
this.cover = compound.contains("cover") ? NBTUtil.readBlockState(compound.getCompound("cover")) : null;
2020-10-14 23:39:11 +02:00
this.craftIngredientRequests.clear();
this.craftIngredientRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new));
2020-10-15 01:19:22 +02:00
this.craftResultRequests.clear();
ListNBT results = compound.getList("craft_results", NBT.TAG_COMPOUND);
for (int i = 0; i < results.size(); i++) {
CompoundNBT nbt = results.getCompound(i);
2020-10-15 04:15:52 +02:00
this.craftResultRequests.add(Pair.of(
2020-10-15 01:19:22 +02:00
BlockPos.fromLong(nbt.getLong("dest_pipe")),
ItemStack.read(nbt.getCompound("item"))));
}
2020-09-22 19:14:07 +02:00
super.read(state, compound);
2020-04-14 01:38:48 +02:00
}
2020-04-14 17:14:24 +02:00
2020-04-14 21:04:41 +02:00
@Override
public CompoundNBT getUpdateTag() {
// sync pipe items on load
CompoundNBT nbt = this.write(new CompoundNBT());
2020-05-09 12:57:25 +02:00
nbt.put("items", Utility.serializeAll(this.getItems()));
return nbt;
}
@Override
2020-09-22 19:14:07 +02:00
public void handleUpdateTag(BlockState state, CompoundNBT nbt) {
this.read(state, nbt);
List<PipeItem> items = this.getItems();
items.clear();
2020-10-16 22:36:44 +02:00
items.addAll(Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), PipeItem::load));
2020-04-14 21:04:41 +02:00
}
2020-10-17 00:58:31 +02:00
@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
this.read(this.getBlockState(), pkt.getNbtCompound());
}
2020-04-14 17:14:24 +02:00
@Override
public void tick() {
// invalidate our pressurizer reference if it was removed
if (this.pressurizer != null && this.pressurizer.isRemoved())
this.pressurizer = null;
2020-04-14 21:04:41 +02:00
if (!this.world.isAreaLoaded(this.pos, 1))
return;
2020-04-16 00:39:53 +02:00
IProfiler profiler = this.world.getProfiler();
2020-04-14 21:04:41 +02:00
if (!this.world.isRemote) {
// drop modules here to give a bit of time for blocks to update (iron -> gold chest etc.)
if (this.moduleDropCheck > 0) {
this.moduleDropCheck--;
if (this.moduleDropCheck <= 0 && !this.canHaveModules())
Utility.dropInventory(this, this.modules);
}
profiler.startSection("ticking_modules");
int prio = 0;
Iterator<Pair<ItemStack, IModule>> modules = this.streamModules().iterator();
while (modules.hasNext()) {
Pair<ItemStack, IModule> module = modules.next();
module.getRight().tick(module.getLeft(), this);
prio += module.getRight().getPriority(module.getLeft(), this);
}
if (prio != this.priority) {
this.priority = prio;
// clear the cache so that it's reevaluated based on priority
PipeNetwork.get(this.world).clearDestinationCache(this.pos);
}
profiler.endSection();
2020-04-17 17:18:25 +02:00
}
2020-04-16 00:39:53 +02:00
profiler.startSection("ticking_items");
List<PipeItem> items = this.getItems();
for (int i = items.size() - 1; i >= 0; i--)
items.get(i).updateInPipe(this);
2020-05-03 15:33:56 +02:00
if (items.size() != this.lastItemAmount) {
this.lastItemAmount = items.size();
this.world.updateComparatorOutputLevel(this.pos, this.getBlockState().getBlock());
}
2020-04-16 00:39:53 +02:00
profiler.endSection();
}
2020-04-14 17:14:24 +02:00
public List<PipeItem> getItems() {
if (this.items == null)
this.items = PipeNetwork.get(this.world).getItemsInPipe(this.pos);
return this.items;
}
2020-10-13 18:11:40 +02:00
public void addNewItem(PipeItem item) {
// an item might be re-routed from a previous location, but it should still count as a new item then
if (!this.getItems().contains(item))
this.getItems().add(item);
if (this.pressurizer != null)
2020-10-13 18:11:40 +02:00
this.pressurizer.pressurizeItem(item.stack, false);
}
2020-04-16 00:39:53 +02:00
public boolean isConnected(Direction dir) {
return this.getBlockState().get(PipeBlock.DIRECTIONS.get(dir)).isConnected();
2020-04-14 17:14:24 +02:00
}
public Pair<BlockPos, ItemStack> getAvailableDestination(ItemStack stack, boolean force, boolean preventOversending) {
2020-05-03 15:51:16 +02:00
if (!this.canWork())
return null;
2020-05-07 21:10:29 +02:00
if (!force && this.streamModules().anyMatch(m -> !m.getRight().canAcceptItem(m.getLeft(), this, stack)))
2020-04-16 00:39:53 +02:00
return null;
2020-04-14 17:14:24 +02:00
for (Direction dir : Direction.values()) {
2020-10-15 01:19:22 +02:00
IItemHandler handler = this.getItemHandler(dir);
2020-04-14 17:14:24 +02:00
if (handler == null)
continue;
ItemStack remain = ItemHandlerHelper.insertItem(handler, stack, true);
// did we insert anything?
if (remain.getCount() == stack.getCount())
2020-04-16 00:39:53 +02:00
continue;
ItemStack toInsert = stack.copy();
toInsert.shrink(remain.getCount());
// limit to the max amount that modules allow us to insert
2020-04-18 22:30:14 +02:00
int maxAmount = this.streamModules().mapToInt(m -> m.getRight().getMaxInsertionAmount(m.getLeft(), this, stack, handler)).min().orElse(Integer.MAX_VALUE);
if (maxAmount < toInsert.getCount())
toInsert.setCount(maxAmount);
BlockPos offset = this.pos.offset(dir);
2020-04-18 22:30:14 +02:00
if (preventOversending || maxAmount < Integer.MAX_VALUE) {
PipeNetwork network = PipeNetwork.get(this.world);
// these are the items that are currently in the pipes, going to this inventory
int onTheWay = network.getItemsOnTheWay(offset, null);
2020-04-17 22:29:02 +02:00
if (onTheWay > 0) {
if (maxAmount < Integer.MAX_VALUE) {
// these are the items on the way, limited to items of the same type as stack
int onTheWaySame = network.getItemsOnTheWay(offset, stack);
// check if any modules are limiting us
if (toInsert.getCount() + onTheWaySame > maxAmount)
toInsert.setCount(maxAmount - onTheWaySame);
}
2020-04-17 22:29:02 +02:00
ItemStack copy = stack.copy();
copy.setCount(copy.getMaxStackSize());
// totalSpace will be the amount of items that fit into the attached container
int totalSpace = 0;
for (int i = 0; i < handler.getSlots(); i++) {
// this is an inaccurate check since it ignores the fact that some slots might
// have space for items of other types, but it'll be good enough for us
ItemStack left = handler.insertItem(i, copy, true);
totalSpace += copy.getMaxStackSize() - left.getCount();
2020-04-17 22:29:02 +02:00
}
// if the items on the way plus the items we're trying to move are too much, reduce
if (onTheWay + toInsert.getCount() > totalSpace)
toInsert.setCount(totalSpace - onTheWay);
2020-04-17 22:29:02 +02:00
}
}
// we return the item that can actually be inserted, NOT the remainder!
if (!toInsert.isEmpty())
return Pair.of(offset, toInsert);
2020-04-14 17:14:24 +02:00
}
return null;
}
2020-04-15 02:16:23 +02:00
public int getPriority() {
2020-04-17 17:18:25 +02:00
return this.priority;
2020-04-15 02:16:23 +02:00
}
2020-10-13 18:11:40 +02:00
public float getItemSpeed(ItemStack stack) {
float moduleSpeed = (float) this.streamModules().mapToDouble(m -> m.getRight().getItemSpeedIncrease(m.getLeft(), this)).sum();
float pressureSpeed = this.pressurizer != null && this.pressurizer.pressurizeItem(stack, true) ? 0.45F : 0;
2020-10-13 18:11:40 +02:00
return 0.05F + moduleSpeed + pressureSpeed;
2020-04-16 23:40:35 +02:00
}
2020-05-03 15:51:16 +02:00
public boolean canWork() {
return this.streamModules().allMatch(m -> m.getRight().canPipeWork(m.getLeft(), this));
}
2020-10-15 04:15:52 +02:00
public List<ItemStack> getAllCraftables() {
return this.streamModules()
2020-10-15 04:15:52 +02:00
.flatMap(m -> m.getRight().getAllCraftables(m.getLeft(), this).stream())
.collect(Collectors.toList());
}
public int getCraftableAmount(Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
2020-10-15 04:15:52 +02:00
return this.streamModules()
.mapToInt(m -> m.getRight().getCraftableAmount(m.getLeft(), this, unavailableConsumer, stack))
2020-10-15 04:15:52 +02:00
.sum();
}
public ItemStack craft(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
2020-10-14 23:39:11 +02:00
Iterator<Pair<ItemStack, IModule>> modules = this.streamModules().iterator();
while (modules.hasNext()) {
Pair<ItemStack, IModule> module = modules.next();
stack = module.getRight().craft(module.getLeft(), this, destPipe, unavailableConsumer, stack);
2020-10-14 23:39:11 +02:00
if (stack.isEmpty())
break;
}
return stack;
}
2020-10-15 01:19:22 +02:00
public IItemHandler getItemHandler(Direction dir) {
2020-04-16 00:39:53 +02:00
if (!this.isConnected(dir))
2020-04-14 17:14:24 +02:00
return null;
2020-05-07 21:10:29 +02:00
BlockPos pos = this.pos.offset(dir);
TileEntity tile = this.world.getTileEntity(pos);
if (tile != null) {
// if we don't do this, then chests get really weird
if (tile instanceof ChestTileEntity) {
BlockState state = this.world.getBlockState(tile.getPos());
if (state.getBlock() instanceof ChestBlock)
2020-09-22 19:14:07 +02:00
return new InvWrapper(ChestBlock.getChestInventory((ChestBlock) state.getBlock(), state, this.world, tile.getPos(), true));
2020-05-07 21:10:29 +02:00
}
2020-10-15 01:19:22 +02:00
return tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, dir.getOpposite()).orElse(null);
}
return null;
}
public IPipeConnectable getPipeConnectable(Direction dir) {
BlockState state = this.world.getBlockState(this.pos.offset(dir));
if (state.getBlock() instanceof IPipeConnectable)
return (IPipeConnectable) state.getBlock();
2020-05-07 21:10:29 +02:00
return null;
2020-04-14 17:14:24 +02:00
}
2020-04-15 18:35:00 +02:00
public boolean isConnectedInventory(Direction dir) {
2020-10-15 01:19:22 +02:00
return this.getItemHandler(dir) != null;
2020-04-15 18:35:00 +02:00
}
public boolean canHaveModules() {
for (Direction dir : Direction.values()) {
if (this.isConnectedInventory(dir))
return true;
IPipeConnectable connectable = this.getPipeConnectable(dir);
if (connectable != null && connectable.allowsModules(this.world, this.pos, dir))
return true;
}
return false;
2020-04-15 18:35:00 +02:00
}
2020-04-16 00:39:53 +02:00
2020-04-17 22:29:02 +02:00
public boolean canNetworkSee() {
return this.streamModules().allMatch(m -> m.getRight().canNetworkSee(m.getLeft(), this));
}
2020-05-02 18:56:54 +02:00
public Stream<Pair<ItemStack, IModule>> streamModules() {
2020-04-16 04:42:42 +02:00
Stream.Builder<Pair<ItemStack, IModule>> builder = Stream.builder();
2020-04-16 00:39:53 +02:00
for (int i = 0; i < this.modules.getSlots(); i++) {
ItemStack stack = this.modules.getStackInSlot(i);
if (stack.isEmpty())
continue;
2020-04-16 04:42:42 +02:00
builder.accept(Pair.of(stack, (IModule) stack.getItem()));
2020-04-16 00:39:53 +02:00
}
return builder.build();
}
@Override
public void remove() {
super.remove();
this.getItems().clear();
2020-10-14 23:39:11 +02:00
PipeNetwork network = PipeNetwork.get(this.world);
for (NetworkLock lock : this.craftIngredientRequests)
network.resolveNetworkLock(lock);
}
2020-04-16 04:42:42 +02:00
@Override
public ITextComponent getDisplayName() {
return new TranslationTextComponent("container." + PrettyPipes.ID + ".pipe");
}
@Nullable
@Override
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new MainPipeContainer(Registry.pipeContainer, window, player, PipeTileEntity.this.pos);
}
2020-10-17 00:58:31 +02:00
@Override
@OnlyIn(Dist.CLIENT)
public AxisAlignedBB getRenderBoundingBox() {
// our render bounding box should always be the full block in case we're covered
return new AxisAlignedBB(this.pos);
}
2020-04-14 01:38:48 +02:00
}