Your storage is showing

- Depots are now viable as Mounted Storage and dynamically display their contents
This commit is contained in:
simibubi 2025-01-12 14:50:39 +01:00
parent b2891263d6
commit 316e372894
10 changed files with 289 additions and 50 deletions

View file

@ -169,6 +169,7 @@ import com.simibubi.create.content.logistics.crate.CreativeCrateBlock;
import com.simibubi.create.content.logistics.depot.DepotBlock;
import com.simibubi.create.content.logistics.depot.EjectorBlock;
import com.simibubi.create.content.logistics.depot.EjectorItem;
import com.simibubi.create.content.logistics.depot.MountedDepotInteractionBehaviour;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelBlock;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelBlockItem;
import com.simibubi.create.content.logistics.factoryBoard.FactoryPanelModel;
@ -793,6 +794,7 @@ public class AllBlocks {
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p)))
.onRegister(assignDataBehaviour(new ItemNameDisplaySource(), "combine_item_names"))
.onRegister(interactionBehaviour(new MountedDepotInteractionBehaviour()))
.item()
.transform(customItemModel("_", "block"))
.register();

View file

@ -32,6 +32,7 @@ import com.simibubi.create.content.contraptions.minecart.capability.MinecartCont
import com.simibubi.create.content.contraptions.sync.ClientMotionPacket;
import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket;
import com.simibubi.create.content.contraptions.sync.ContraptionInteractionPacket;
import com.simibubi.create.content.contraptions.sync.ContraptionItemPacket;
import com.simibubi.create.content.contraptions.sync.ContraptionSeatMappingPacket;
import com.simibubi.create.content.contraptions.sync.LimbSwingUpdatePacket;
import com.simibubi.create.content.contraptions.wrench.RadialWrenchMenuSubmitPacket;
@ -218,6 +219,7 @@ public enum AllPackets {
MINECART_CONTROLLER(MinecartControllerUpdatePacket.class, MinecartControllerUpdatePacket::new, PLAY_TO_CLIENT),
FLUID_SPLASH(FluidSplashPacket.class, FluidSplashPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_FLUID(ContraptionFluidPacket.class, ContraptionFluidPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_ITEM(ContraptionItemPacket.class, ContraptionItemPacket::new, PLAY_TO_CLIENT),
GANTRY_UPDATE(GantryContraptionUpdatePacket.class, GantryContraptionUpdatePacket::new, PLAY_TO_CLIENT),
BLOCK_HIGHLIGHT(HighlightPacket.class, HighlightPacket::new, PLAY_TO_CLIENT),
TUNNEL_FLAP(TunnelFlapPacket.class, TunnelFlapPacket::new, PLAY_TO_CLIENT),

View file

@ -1459,6 +1459,10 @@ public abstract class Contraption {
public IFluidHandler getSharedFluidTanks() {
return storage.getFluids();
}
public MountedStorageManager getStorageManager() {
return storage;
}
public RenderedBlocks getRenderedBlocks() {
return new RenderedBlocks(pos -> {
@ -1485,6 +1489,10 @@ public abstract class Contraption {
public void handleContraptionFluidPacket(BlockPos localPos, FluidStack containedFluid) {
storage.updateContainedFluid(localPos, containedFluid);
}
public void handleContraptionItemPacket(BlockPos localPos, List<ItemStack> containedItems) {
storage.updateContainedItem(localPos, containedItems);
}
public static class ContraptionInvWrapper extends CombinedInvWrapper {
protected final boolean isExternal;

View file

@ -1,16 +1,26 @@
package com.simibubi.create.content.contraptions;
import java.util.List;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllPackets;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.content.contraptions.sync.ContraptionItemPacket;
import com.simibubi.create.content.equipment.toolbox.ToolboxInventory;
import com.simibubi.create.content.kinetics.belt.transport.TransportedItemStack;
import com.simibubi.create.content.kinetics.crafter.MechanicalCrafterBlockEntity;
import com.simibubi.create.content.logistics.crate.BottomlessItemHandler;
import com.simibubi.create.content.logistics.depot.DepotBehaviour;
import com.simibubi.create.content.logistics.depot.DepotBlockEntity;
import com.simibubi.create.content.logistics.vault.ItemVaultBlockEntity;
import com.simibubi.create.content.processing.recipe.ProcessingInventory;
import net.createmod.catnip.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BarrelBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -22,6 +32,7 @@ import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.registries.ForgeRegistries;
public class MountedStorage {
@ -31,8 +42,11 @@ public class MountedStorage {
ItemStackHandler handler;
boolean noFuel;
boolean valid;
private int packetCooldown = 0;
private boolean sendPacket = false;
private BlockEntity blockEntity;
BlockEntity blockEntity;
public static boolean canUseAsStorage(BlockEntity be) {
if (be == null)
@ -49,6 +63,8 @@ public class MountedStorage {
return true;
if (be instanceof ItemVaultBlockEntity)
return true;
if (be instanceof DepotBlockEntity)
return true;
try {
LazyOptional<IItemHandler> capability = be.getCapability(ForgeCapabilities.ITEM_HANDLER);
@ -85,9 +101,17 @@ public class MountedStorage {
public void removeStorageFromWorld() {
valid = false;
sendPacket = false;
if (blockEntity == null)
return;
if (blockEntity instanceof DepotBlockEntity depot) {
handler = new SyncedMountedItemStackHandler(1);
handler.setStackInSlot(0, depot.getHeldItem());
valid = true;
return;
}
if (blockEntity instanceof ChestBlockEntity) {
CompoundTag tag = blockEntity.saveWithFullMetadata();
if (tag.contains("LootTable", 8))
@ -140,6 +164,13 @@ public class MountedStorage {
if (handler instanceof BottomlessItemHandler)
return;
if (be instanceof DepotBlockEntity depot) {
if (handler.getSlots() > 0)
depot.getBehaviour(DepotBehaviour.TYPE)
.setCenteredHeldItem(new TransportedItemStack(handler.getStackInSlot(0)));
return;
}
if (be instanceof ChestBlockEntity) {
CompoundTag tag = be.saveWithFullMetadata();
tag.remove("Items");
@ -166,6 +197,29 @@ public class MountedStorage {
inv.setStackInSlot(slot, handler.getStackInSlot(slot));
}
public void tick(Entity entity, BlockPos pos, boolean isRemote) {
if (isRemote)
return;
if (packetCooldown > 0) {
packetCooldown--;
return;
}
if (sendPacket) {
sendPacket = false;
AllPackets.getChannel()
.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity),
new ContraptionItemPacket(entity.getId(), pos, handler));
packetCooldown = 8;
}
}
public void updateItems(List<ItemStack> containedItems) {
for (int i = 0; i < Math.min(containedItems.size(), handler.getSlots()); i++)
handler.setStackInSlot(i, containedItems.get(i));
if (blockEntity instanceof DepotBlockEntity depot)
depot.setHeldItem(handler.getStackInSlot(0));
}
public IItemHandlerModifiable getItemHandler() {
return handler;
}
@ -179,6 +233,9 @@ public class MountedStorage {
NBTHelper.putMarker(tag, "NoFuel");
if (handler instanceof ToolboxInventory)
NBTHelper.putMarker(tag, "Toolbox");
if (needsSync())
NBTHelper.putMarker(tag, "Synced");
if (!(handler instanceof BottomlessItemHandler))
return tag;
@ -195,6 +252,8 @@ public class MountedStorage {
return storage;
if (nbt.contains("Toolbox"))
storage.handler = new ToolboxInventory(null);
if (nbt.contains("Synced"))
storage.handler = storage.new SyncedMountedItemStackHandler(1);
storage.valid = true;
storage.noFuel = nbt.contains("NoFuel");
@ -216,5 +275,22 @@ public class MountedStorage {
public boolean canUseForFuel() {
return !noFuel;
}
public boolean needsSync() {
return handler instanceof SyncedMountedItemStackHandler;
}
public class SyncedMountedItemStackHandler extends ItemStackHandler {
public SyncedMountedItemStackHandler(int i) {
super(i);
}
@Override
protected void onContentsChanged(int slot) {
sendPacket = true;
}
}
}

View file

@ -5,12 +5,14 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.simibubi.create.content.contraptions.Contraption.ContraptionInvWrapper;
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
import com.simibubi.create.content.logistics.depot.DepotBlockEntity;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import net.createmod.catnip.utility.NBTHelper;
@ -53,7 +55,9 @@ public class MountedStorageManager {
}
public void entityTick(AbstractContraptionEntity entity) {
fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, entity.level().isClientSide));
boolean isClientSide = entity.level().isClientSide;
storage.forEach((pos, mfs) -> mfs.tick(entity, pos, isClientSide));
fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, isClientSide));
}
public void createHandlers() {
@ -99,7 +103,7 @@ public class MountedStorageManager {
.put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedFluidStorage.deserialize(c.getCompound("Data"))));
if (clientPacket && presentBlockEntities != null)
bindTanks(presentBlockEntities);
bindTanksAndDepots(presentBlockEntities);
List<IItemHandlerModifiable> handlers = new ArrayList<>();
List<IItemHandlerModifiable> fuelHandlers = new ArrayList<>();
@ -118,33 +122,41 @@ public class MountedStorageManager {
.toList());
}
public void bindTanks(Map<BlockPos, BlockEntity> presentBlockEntities) {
fluidStorage.forEach((pos, mfs) -> {
BlockEntity blockEntity = presentBlockEntities.get(pos);
if (!(blockEntity instanceof FluidTankBlockEntity))
public void bindTanksAndDepots(Map<BlockPos, BlockEntity> presentBlockEntities) {
for (Entry<BlockPos, MountedStorage> entry : storage.entrySet()) {
BlockEntity blockEntity = presentBlockEntities.get(entry.getKey());
if (!(blockEntity instanceof DepotBlockEntity depot))
return;
depot.setHeldItem(entry.getValue().handler.getStackInSlot(0));
entry.getValue().blockEntity = depot;
}
for (Entry<BlockPos, MountedFluidStorage> entry : fluidStorage.entrySet()) {
BlockEntity blockEntity = presentBlockEntities.get(entry.getKey());
if (!(blockEntity instanceof FluidTankBlockEntity tank))
return;
FluidTankBlockEntity tank = (FluidTankBlockEntity) blockEntity;
IFluidTank tankInventory = tank.getTankInventory();
MountedFluidStorage mfs = entry.getValue();
if (tankInventory instanceof FluidTank)
((FluidTank) tankInventory).setFluid(mfs.tank.getFluid());
tank.getFluidLevel()
.startWithValue(tank.getFillState());
mfs.assignBlockEntity(tank);
});
}
}
public void write(CompoundTag nbt, boolean clientPacket) {
ListTag storageNBT = new ListTag();
if (!clientPacket)
for (BlockPos pos : storage.keySet()) {
CompoundTag c = new CompoundTag();
MountedStorage mountedStorage = storage.get(pos);
if (!mountedStorage.isValid())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
storageNBT.add(c);
}
for (BlockPos pos : storage.keySet()) {
CompoundTag c = new CompoundTag();
MountedStorage mountedStorage = storage.get(pos);
if (!mountedStorage.isValid())
continue;
if (clientPacket && !mountedStorage.needsSync())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
storageNBT.add(c);
}
ListTag fluidStorageNBT = new ListTag();
for (BlockPos pos : fluidStorage.keySet()) {
@ -196,6 +208,12 @@ public class MountedStorageManager {
mountedFluidStorage.updateFluid(containedFluid);
}
public void updateContainedItem(BlockPos localPos, List<ItemStack> containedItems) {
MountedStorage mountedStorage = storage.get(localPos);
if (mountedStorage != null)
mountedStorage.updateItems(containedItems);
}
public void attachExternal(IItemHandlerModifiable externalStorage) {
inventory = new ContraptionInvWrapper(externalStorage, inventory);
fuelInventory = new ContraptionInvWrapper(externalStorage, fuelInventory);
@ -212,6 +230,14 @@ public class MountedStorageManager {
public IFluidHandler getFluids() {
return fluidInventory;
}
public Map<BlockPos, MountedStorage> getMountedItemStorage() {
return storage;
}
public Map<BlockPos, MountedFluidStorage> getMountedFluidStorage() {
return fluidStorage;
}
public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) {
if (player.level().isClientSide()) {

View file

@ -0,0 +1,60 @@
package com.simibubi.create.content.contraptions.sync;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.network.NetworkEvent.Context;
public class ContraptionItemPacket extends SimplePacketBase {
private int entityId;
private BlockPos localPos;
private List<ItemStack> containedItems;
public ContraptionItemPacket(int entityId, BlockPos localPos, ItemStackHandler handler) {
this.entityId = entityId;
this.localPos = localPos;
this.containedItems = new ArrayList<>(handler.getSlots());
for (int i = 0; i < handler.getSlots(); i++)
containedItems.add(handler.getStackInSlot(i));
}
public ContraptionItemPacket(FriendlyByteBuf buffer) {
entityId = buffer.readInt();
localPos = buffer.readBlockPos();
int count = buffer.readVarInt();
containedItems = new ArrayList<>();
for (int i = 0; i < count; i++)
containedItems.add(buffer.readItem());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(entityId);
buffer.writeBlockPos(localPos);
buffer.writeVarInt(containedItems.size());
for (ItemStack stack : containedItems)
buffer.writeItem(stack);
}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> {
Entity entityByID = Minecraft.getInstance().level.getEntity(entityId);
if (!(entityByID instanceof AbstractContraptionEntity))
return;
AbstractContraptionEntity contraptionEntity = (AbstractContraptionEntity) entityByID;
contraptionEntity.getContraption().handleContraptionItemPacket(localPos, containedItems);
});
return true;
}
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.depot;
import java.util.List;
import com.simibubi.create.content.kinetics.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
@ -38,4 +39,12 @@ public class DepotBlockEntity extends SmartBlockEntity {
public ItemStack getHeldItem() {
return depotBehaviour.getHeldItemStack();
}
public void setHeldItem(ItemStack item) {
TransportedItemStack newStack = new TransportedItemStack(item);
if (depotBehaviour.heldItem != null)
newStack.angle = depotBehaviour.heldItem.angle;
depotBehaviour.setHeldItem(newStack);
}
}

View file

@ -0,0 +1,51 @@
package com.simibubi.create.content.logistics.depot;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.MountedStorage;
import com.simibubi.create.content.contraptions.MountedStorageManager;
import com.simibubi.create.content.contraptions.behaviour.MovingInteractionBehaviour;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.items.IItemHandlerModifiable;
public class MountedDepotInteractionBehaviour extends MovingInteractionBehaviour {
@Override
public boolean handlePlayerInteraction(Player player, InteractionHand activeHand, BlockPos localPos,
AbstractContraptionEntity contraptionEntity) {
ItemStack itemInHand = player.getItemInHand(activeHand);
if (activeHand == InteractionHand.OFF_HAND)
return false;
if (player.level().isClientSide)
return true;
MountedStorageManager storageManager = contraptionEntity.getContraption()
.getStorageManager();
if (storageManager == null)
return false;
MountedStorage mountedStorage = storageManager.getMountedItemStorage()
.get(localPos);
if (mountedStorage == null)
return false;
IItemHandlerModifiable itemHandler = mountedStorage.getItemHandler();
ItemStack itemOnDepot = itemHandler.getStackInSlot(0);
if (itemOnDepot.isEmpty() && itemInHand.isEmpty())
return true;
itemHandler.setStackInSlot(0, itemInHand.copy());
player.setItemInHand(activeHand, itemOnDepot.copy());
AllSoundEvents.DEPOT_PLOP.playOnServer(player.level(),
BlockPos.containing(contraptionEntity.toGlobalVector(Vec3.atCenterOf(localPos), 0)));
return true;
}
}

View file

@ -333,6 +333,11 @@ public class CarriageContraption extends Contraption {
public void handleContraptionFluidPacket(BlockPos localPos, FluidStack containedFluid) {
storage.updateContainedFluid(localPos, containedFluid);
}
@Override
public MountedStorageManager getStorageManager() {
return storageProxy;
}
@Override
public void tickStorage(AbstractContraptionEntity entity) {

View file

@ -186,6 +186,37 @@ public class GlobalStation extends SingleBlockEntityEdgePoint {
if (carriageInventory == null)
continue;
// Import from station
for (Entry<BlockPos, GlobalPackagePort> entry : connectedPorts.entrySet()) {
GlobalPackagePort port = entry.getValue();
BlockPos pos = entry.getKey();
PostboxBlockEntity box = null;
IItemHandlerModifiable postboxInventory = port.offlineBuffer;
if (level != null && level.isLoaded(pos)
&& level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) {
postboxInventory = ppbe.inventory;
box = ppbe;
}
for (int slot = 0; slot < postboxInventory.getSlots(); slot++) {
ItemStack stack = postboxInventory.getStackInSlot(slot);
if (!PackageItem.isPackage(stack))
continue;
if (PackageItem.matchAddress(stack, port.address))
continue;
ItemStack result = ItemHandlerHelper.insertItemStacked(carriageInventory, stack, false);
if (!result.isEmpty())
continue;
postboxInventory.setStackInSlot(slot, ItemStack.EMPTY);
Create.RAILWAYS.markTracksDirty();
if (box != null)
box.spawnParticles();
}
}
// Export to station
for (int slot = 0; slot < carriageInventory.getSlots(); slot++) {
ItemStack stack = carriageInventory.getStackInSlot(slot);
@ -220,37 +251,6 @@ public class GlobalStation extends SingleBlockEntityEdgePoint {
}
}
// Import from station
for (Entry<BlockPos, GlobalPackagePort> entry : connectedPorts.entrySet()) {
GlobalPackagePort port = entry.getValue();
BlockPos pos = entry.getKey();
PostboxBlockEntity box = null;
IItemHandlerModifiable postboxInventory = port.offlineBuffer;
if (level != null && level.isLoaded(pos)
&& level.getBlockEntity(pos) instanceof PostboxBlockEntity ppbe) {
postboxInventory = ppbe.inventory;
box = ppbe;
}
for (int slot = 0; slot < postboxInventory.getSlots(); slot++) {
ItemStack stack = postboxInventory.getStackInSlot(slot);
if (!PackageItem.isPackage(stack))
continue;
if (PackageItem.matchAddress(stack, port.address))
continue;
ItemStack result = ItemHandlerHelper.insertItemStacked(carriageInventory, stack, false);
if (!result.isEmpty())
continue;
postboxInventory.setStackInSlot(slot, ItemStack.EMPTY);
Create.RAILWAYS.markTracksDirty();
if (box != null)
box.spawnParticles();
}
}
}
}