PrettyPipes/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalBlockEntity.java

318 lines
14 KiB
Java
Raw Normal View History

2020-05-07 18:30:40 +02:00
package de.ellpeck.prettypipes.terminal;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.EquatableItemStack;
2021-03-03 01:56:19 +01:00
import de.ellpeck.prettypipes.misc.ItemEquality;
import de.ellpeck.prettypipes.network.NetworkItem;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
2020-05-07 18:30:40 +02:00
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.packets.PacketNetworkItems;
2020-10-17 14:29:37 +02:00
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
2021-12-02 14:44:26 +01:00
import de.ellpeck.prettypipes.pipe.PipeBlockEntity;
2020-05-07 18:30:40 +02:00
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
2021-12-02 14:44:26 +01:00
import net.minecraft.ChatFormatting;
2020-09-22 19:14:07 +02:00
import net.minecraft.block.BlockState;
2021-12-02 14:44:26 +01:00
import net.minecraft.core.Direction;
2020-05-07 18:30:40 +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;
2021-12-02 14:44:26 +01:00
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Player;
2021-12-02 12:31:04 +01:00
import net.minecraft.world.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
2020-05-07 18:30:40 +02:00
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
2020-05-07 21:10:29 +02:00
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
2020-05-07 18:30:40 +02:00
import net.minecraft.util.text.TranslationTextComponent;
2020-10-14 23:39:11 +02:00
import net.minecraft.world.World;
2021-12-02 14:44:26 +01:00
import net.minecraft.world.level.block.entity.BlockEntity;
2020-10-17 14:29:37 +02:00
import net.minecraftforge.common.capabilities.Capability;
2020-05-09 12:57:25 +02:00
import net.minecraftforge.common.util.Constants.NBT;
2020-10-17 14:29:37 +02:00
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.ItemHandlerHelper;
2020-05-07 18:30:40 +02:00
import net.minecraftforge.items.ItemStackHandler;
2020-05-07 21:10:29 +02:00
import org.apache.commons.lang3.tuple.Pair;
2020-05-07 18:30:40 +02:00
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;
2020-05-07 18:30:40 +02:00
import java.util.stream.Collectors;
2021-12-02 14:44:26 +01:00
public class ItemTerminalBlockEntity extends BlockEntity implements IPipeConnectable {
2020-05-07 18:30:40 +02:00
public final ItemStackHandler items = new ItemStackHandler(12) {
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
2020-05-07 21:10:29 +02:00
return true;
2020-05-07 18:30:40 +02:00
}
};
2020-10-15 04:15:52 +02:00
protected Map<EquatableItemStack, NetworkItem> networkItems;
private final Queue<NetworkLock> existingRequests = new LinkedList<>();
2020-10-19 21:31:23 +02:00
private final LazyOptional<IPipeConnectable> lazyThis = LazyOptional.of(() -> this);
2020-05-07 18:30:40 +02:00
2021-12-02 14:44:26 +01:00
// TODO tick
/* @Override
2020-05-07 18:30:40 +02:00
public void tick() {
2021-12-02 14:44:26 +01:00
if (this.level.isRemote)
2020-05-07 18:30:40 +02:00
return;
2021-12-02 14:44:26 +01:00
PipeNetwork network = PipeNetwork.get(this.level);
PipeBlockEntity pipe = this.getConnectedPipe();
2020-05-07 18:30:40 +02:00
if (pipe == null)
return;
boolean update = false;
int interval = pipe.pressurizer != null ? 2 : 10;
2021-12-02 14:44:26 +01:00
if (this.level.getGameTime() % interval == 0) {
2020-05-07 18:30:40 +02:00
for (int i = 6; i < 12; i++) {
ItemStack extracted = this.items.extractItem(i, Integer.MAX_VALUE, true);
if (extracted.isEmpty())
continue;
2020-10-16 22:36:44 +02:00
ItemStack remain = network.routeItem(pipe.getPos(), this.pos, extracted, true);
if (remain.getCount() == extracted.getCount())
2020-05-07 18:30:40 +02:00
continue;
this.items.extractItem(i, extracted.getCount() - remain.getCount(), false);
2020-05-07 18:30:40 +02:00
break;
}
2020-05-07 21:10:29 +02:00
2020-10-14 23:39:11 +02:00
if (!this.existingRequests.isEmpty()) {
NetworkLock request = this.existingRequests.remove();
network.resolveNetworkLock(request);
2021-03-03 01:56:19 +01:00
network.requestExistingItem(request.location, pipe.getPos(), this.pos, request, request.stack, ItemEquality.NBT);
update = true;
2020-05-07 21:10:29 +02:00
}
2020-05-07 18:30:40 +02:00
}
2021-12-02 14:44:26 +01:00
if (this.level.getGameTime() % 100 == 0 || update) {
2020-05-07 21:10:29 +02:00
PlayerEntity[] lookingPlayers = this.getLookingPlayers();
2020-05-07 18:30:40 +02:00
if (lookingPlayers.length > 0)
this.updateItems(lookingPlayers);
}
2021-12-02 14:44:26 +01:00
}*/
2020-05-07 18:30:40 +02:00
@Override
2021-12-02 14:44:26 +01:00
public void setRemoved() {
super.setRemoved();
PipeNetwork network = PipeNetwork.get(this.level);
2020-10-14 23:39:11 +02:00
for (NetworkLock lock : this.existingRequests)
network.resolveNetworkLock(lock);
2020-10-19 21:31:23 +02:00
this.lazyThis.invalidate();
}
public String getInvalidTerminalReason() {
2021-12-02 14:44:26 +01:00
PipeNetwork network = PipeNetwork.get(this.level);
long pipes = Arrays.stream(Direction.values())
2021-12-02 14:44:26 +01:00
.map(d -> network.getPipe(this.worldPosition.relative(d)))
.filter(Objects::nonNull).count();
if (pipes <= 0)
return "info." + PrettyPipes.ID + ".no_pipe_connected";
if (pipes > 1)
return "info." + PrettyPipes.ID + ".too_many_pipes_connected";
return null;
}
2021-12-02 14:44:26 +01:00
public PipeBlockEntity getConnectedPipe() {
PipeNetwork network = PipeNetwork.get(this.level);
2020-05-07 18:30:40 +02:00
for (Direction dir : Direction.values()) {
2021-12-02 14:44:26 +01:00
PipeBlockEntity pipe = network.getPipe(this.worldPosition.relative(dir));
2020-05-07 18:30:40 +02:00
if (pipe != null)
return pipe;
}
return null;
}
2021-12-02 14:44:26 +01:00
public void updateItems(Player... playersToSync) {
PipeBlockEntity pipe = this.getConnectedPipe();
if (pipe == null)
2020-05-07 23:06:35 +02:00
return;
2021-03-03 01:56:19 +01:00
this.networkItems = this.collectItems(ItemEquality.NBT);
2020-05-07 21:10:29 +02:00
if (playersToSync.length > 0) {
List<ItemStack> clientItems = this.networkItems.values().stream().map(NetworkItem::asStack).collect(Collectors.toList());
2021-12-02 14:44:26 +01:00
List<ItemStack> clientCraftables = PipeNetwork.get(this.level).getAllCraftables(pipe.getPos()).stream().map(Pair::getRight).collect(Collectors.toList());
List<ItemStack> currentlyCrafting = this.getCurrentlyCrafting().stream().sorted(Comparator.comparingInt(ItemStack::getCount).reversed()).collect(Collectors.toList());
2021-12-02 14:44:26 +01:00
for (Player player : playersToSync) {
if (!(player.containerMenu instanceof ItemTerminalContainer container))
2020-05-07 21:10:29 +02:00
continue;
2021-12-02 14:44:26 +01:00
if (container.tile != this)
2020-05-07 21:10:29 +02:00
continue;
PacketHandler.sendTo(player, new PacketNetworkItems(clientItems, clientCraftables, currentlyCrafting));
2020-05-07 21:10:29 +02:00
}
2020-05-07 18:30:40 +02:00
}
}
2021-12-02 14:44:26 +01:00
public void requestItem(Player player, ItemStack stack) {
PipeNetwork network = PipeNetwork.get(this.level);
2020-05-07 21:10:29 +02:00
network.startProfile("terminal_request_item");
this.updateItems();
int requested = this.requestItemImpl(stack, onItemUnavailable(player));
2020-05-09 13:40:46 +02:00
if (requested > 0) {
2021-12-02 14:44:26 +01:00
player.sendMessage(new TranslatableComponent("info." + PrettyPipes.ID + ".sending", requested, stack.getDisplayName()).setStyle(Style.EMPTY.setFormatting(TextFormatting.GREEN)), UUID.randomUUID());
2020-05-09 13:40:46 +02:00
} else {
onItemUnavailable(player).accept(stack);
2020-05-09 13:40:46 +02:00
}
network.endProfile();
}
public int requestItemImpl(ItemStack stack, Consumer<ItemStack> unavailableConsumer) {
2021-03-03 01:56:19 +01:00
NetworkItem item = this.networkItems.get(new EquatableItemStack(stack, ItemEquality.NBT));
2020-10-14 23:39:11 +02:00
Collection<NetworkLocation> locations = item == null ? Collections.emptyList() : item.getLocations();
2021-12-02 14:44:26 +01:00
Pair<List<NetworkLock>, ItemStack> ret = requestItemLater(this.level, this.getConnectedPipe().getPos(), locations, unavailableConsumer, stack, new Stack<>(), ItemEquality.NBT);
2020-10-15 01:19:22 +02:00
this.existingRequests.addAll(ret.getLeft());
return stack.getCount() - ret.getRight().getCount();
2020-05-07 21:10:29 +02:00
}
2021-12-02 14:44:26 +01:00
protected Player[] getLookingPlayers() {
return this.level.getPlayers().stream()
2020-05-07 21:10:29 +02:00
.filter(p -> p.openContainer instanceof ItemTerminalContainer)
.filter(p -> ((ItemTerminalContainer) p.openContainer).tile == this)
.toArray(PlayerEntity[]::new);
}
2021-03-03 01:56:19 +01:00
private Map<EquatableItemStack, NetworkItem> collectItems(ItemEquality... equalityTypes) {
2021-12-02 14:44:26 +01:00
PipeNetwork network = PipeNetwork.get(this.level);
2020-05-07 18:30:40 +02:00
network.startProfile("terminal_collect_items");
2021-12-02 14:44:26 +01:00
PipeBlockEntity pipe = this.getConnectedPipe();
2020-05-07 18:30:40 +02:00
Map<EquatableItemStack, NetworkItem> items = new HashMap<>();
for (NetworkLocation location : network.getOrderedNetworkItems(pipe.getPos())) {
2021-12-02 14:44:26 +01:00
for (Map.Entry<Integer, ItemStack> entry : location.getItems(this.level).entrySet()) {
2020-11-28 02:15:29 +01:00
// make sure we can extract from this slot to display it
2021-12-02 14:44:26 +01:00
if (!location.canExtract(this.level, entry.getKey()))
2020-11-28 02:15:29 +01:00
continue;
EquatableItemStack equatable = new EquatableItemStack(entry.getValue(), equalityTypes);
2020-05-07 18:30:40 +02:00
NetworkItem item = items.computeIfAbsent(equatable, NetworkItem::new);
2020-11-28 02:15:29 +01:00
item.add(location, entry.getValue());
2020-05-07 18:30:40 +02:00
}
}
network.endProfile();
2020-05-07 21:10:29 +02:00
return items;
2020-05-07 18:30:40 +02:00
}
private List<ItemStack> getCurrentlyCrafting() {
2021-12-02 14:44:26 +01:00
PipeNetwork network = PipeNetwork.get(this.level);
PipeBlockEntity pipe = this.getConnectedPipe();
if (pipe == null)
return Collections.emptyList();
List<Pair<BlockPos, ItemStack>> crafting = network.getCurrentlyCrafting(pipe.getPos());
return crafting.stream().map(Pair::getRight).collect(Collectors.toList());
}
public void cancelCrafting() {
2021-12-02 14:44:26 +01:00
PipeNetwork network = PipeNetwork.get(this.level);
PipeBlockEntity pipe = this.getConnectedPipe();
if (pipe == null)
return;
for (Pair<BlockPos, ItemStack> craftable : network.getAllCraftables(pipe.getPos())) {
2021-12-02 14:44:26 +01:00
PipeBlockEntity otherPipe = network.getPipe(craftable.getLeft());
if (otherPipe != null) {
for (NetworkLock lock : otherPipe.craftIngredientRequests)
network.resolveNetworkLock(lock);
otherPipe.craftIngredientRequests.clear();
otherPipe.craftResultRequests.clear();
}
}
PlayerEntity[] lookingPlayers = this.getLookingPlayers();
if (lookingPlayers.length > 0)
this.updateItems(lookingPlayers);
}
2020-05-07 18:30:40 +02:00
@Override
2021-12-02 12:31:04 +01:00
public CompoundTag write(CompoundTag compound) {
2020-05-07 18:30:40 +02:00
compound.put("items", this.items.serializeNBT());
2020-10-14 23:39:11 +02:00
compound.put("requests", Utility.serializeAll(this.existingRequests));
2020-05-07 18:30:40 +02:00
return super.write(compound);
}
@Override
2021-12-02 12:31:04 +01:00
public void read(BlockState state, CompoundTag compound) {
2020-05-07 18:30:40 +02:00
this.items.deserializeNBT(compound.getCompound("items"));
2020-10-14 23:39:11 +02:00
this.existingRequests.clear();
this.existingRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new));
2020-09-22 19:14:07 +02:00
super.read(state, compound);
2020-05-07 18:30:40 +02:00
}
@Override
public ITextComponent getDisplayName() {
return new TranslationTextComponent("container." + PrettyPipes.ID + ".item_terminal");
}
@Nullable
@Override
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new ItemTerminalContainer(Registry.itemTerminalContainer, window, player, this.pos);
}
2020-10-14 23:39:11 +02:00
2020-10-17 14:29:37 +02:00
@Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == Registry.pipeConnectableCapability)
2020-10-19 21:31:23 +02:00
return this.lazyThis.cast();
2020-10-17 14:29:37 +02:00
return LazyOptional.empty();
}
@Override
public ConnectionType getConnectionType(BlockPos pipePos, Direction direction) {
return ConnectionType.CONNECTED;
}
@Override
public ItemStack insertItem(BlockPos pipePos, Direction direction, ItemStack stack, boolean simulate) {
BlockPos pos = pipePos.offset(direction);
2021-12-02 14:44:26 +01:00
ItemTerminalBlockEntity tile = Utility.getBlockEntity(ItemTerminalBlockEntity.class, world, pos);
2020-10-17 14:29:37 +02:00
if (tile != null)
return ItemHandlerHelper.insertItemStacked(tile.items, stack, simulate);
return stack;
}
@Override
public boolean allowsModules(BlockPos pipePos, Direction direction) {
return true;
}
2021-03-03 01:56:19 +01:00
public static Pair<List<NetworkLock>, ItemStack> requestItemLater(World world, BlockPos destPipe, Collection<NetworkLocation> locations, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
2020-10-14 23:39:11 +02:00
List<NetworkLock> requests = new ArrayList<>();
2020-10-15 01:19:22 +02:00
ItemStack remain = stack.copy();
2020-10-14 23:39:11 +02:00
PipeNetwork network = PipeNetwork.get(world);
// check for existing items
for (NetworkLocation location : locations) {
int amount = location.getItemAmount(world, stack, equalityTypes);
2020-10-14 23:39:11 +02:00
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack, null, equalityTypes);
2020-10-14 23:39:11 +02:00
if (amount > 0) {
2020-10-15 01:19:22 +02:00
if (remain.getCount() < amount)
amount = remain.getCount();
remain.shrink(amount);
2020-10-14 23:39:11 +02:00
while (amount > 0) {
ItemStack copy = stack.copy();
copy.setCount(Math.min(stack.getMaxStackSize(), amount));
NetworkLock lock = new NetworkLock(location, copy);
network.createNetworkLock(lock);
requests.add(lock);
amount -= copy.getCount();
}
2020-10-15 01:19:22 +02:00
if (remain.isEmpty())
2020-10-14 23:39:11 +02:00
break;
}
}
// check for craftable items
2020-10-15 01:19:22 +02:00
if (!remain.isEmpty())
remain = network.requestCraftedItem(destPipe, unavailableConsumer, remain, dependencyChain, equalityTypes);
2020-10-15 01:19:22 +02:00
return Pair.of(requests, remain);
2020-10-14 23:39:11 +02:00
}
2021-12-02 14:44:26 +01:00
public static Consumer<ItemStack> onItemUnavailable(Player player) {
return s -> player.sendMessage(new TranslatableComponent("info." + PrettyPipes.ID + ".not_found", s.getDisplayName()).setStyle(Style.EMPTY.applyFormat(ChatFormatting.RED)), UUID.randomUUID());
}
2020-05-07 18:30:40 +02:00
}