From 2b91d7cd0c230292b2ac3b41fe7aac219f3bd7b8 Mon Sep 17 00:00:00 2001 From: TropheusJ Date: Sat, 25 Jan 2025 07:04:59 -0500 Subject: [PATCH] rework synced storage - ported depot support --- .../java/com/simibubi/create/AllBlocks.java | 2 + .../create/AllMountedStorageTypes.java | 2 + .../java/com/simibubi/create/AllPackets.java | 7 +- .../storage/SyncedMountedStorage.java | 25 +++ .../AbstractContraptionEntity.java | 2 +- .../content/contraptions/Contraption.java | 9 +- .../contraptions/MountedFluidStorage.java | 174 --------------- .../contraptions/MountedStorageManager.java | 211 +++++++++++++++--- .../MountedStorageSyncPacket.java | 63 ++++++ .../elevator/ElevatorContraption.java | 4 +- .../minecart/TrainCargoManager.java | 21 +- .../sync/ContraptionFluidPacket.java | 49 ---- .../sync/ContraptionItemPacket.java | 60 ----- .../tank/FluidTankMovementBehavior.java | 54 +---- .../tank/storage/FluidTankMountedStorage.java | 85 +++++-- .../MountedDepotInteractionBehaviour.java | 22 +- .../depot/storage/DepotMountedStorage.java | 97 ++++++++ .../storage/DepotMountedStorageType.java | 27 +++ .../content/trains/entity/Carriage.java | 5 +- .../DeliverPackagesInstruction.java | 3 +- .../foundation/utility/CreateCodecs.java | 16 +- 21 files changed, 497 insertions(+), 441 deletions(-) create mode 100644 src/main/java/com/simibubi/create/api/contraption/storage/SyncedMountedStorage.java delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/MountedFluidStorage.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/MountedStorageSyncPacket.java delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionFluidPacket.java delete mode 100644 src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionItemPacket.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorage.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorageType.java diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index d7aabfad38..28786c917a 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -332,6 +332,7 @@ import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; + import net.minecraftforge.client.model.generators.ConfiguredModel; import net.minecraftforge.client.model.generators.ModelFile; import net.minecraftforge.common.Tags; @@ -799,6 +800,7 @@ public class AllBlocks { .blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p))) .onRegister(assignDataBehaviour(new ItemNameDisplaySource(), "combine_item_names")) .onRegister(interactionBehaviour(new MountedDepotInteractionBehaviour())) + .transform(mountedItemStorage(AllMountedStorageTypes.DEPOT)) .item() .transform(customItemModel("_", "block")) .register(); diff --git a/src/main/java/com/simibubi/create/AllMountedStorageTypes.java b/src/main/java/com/simibubi/create/AllMountedStorageTypes.java index 9d04993f7a..aba02b31c9 100644 --- a/src/main/java/com/simibubi/create/AllMountedStorageTypes.java +++ b/src/main/java/com/simibubi/create/AllMountedStorageTypes.java @@ -13,6 +13,7 @@ import com.simibubi.create.content.equipment.toolbox.ToolboxMountedStorageType; import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorageType; import com.simibubi.create.content.fluids.tank.storage.creative.CreativeFluidTankMountedStorageType; import com.simibubi.create.content.logistics.crate.CreativeCrateMountedStorageType; +import com.simibubi.create.content.logistics.depot.storage.DepotMountedStorageType; import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorageType; import com.simibubi.create.impl.contraption.storage.FallbackMountedStorageType; import com.tterrag.registrate.util.entry.RegistryEntry; @@ -27,6 +28,7 @@ public class AllMountedStorageTypes { public static final RegistryEntry CREATIVE_CRATE = simpleItem("creative_crate", CreativeCrateMountedStorageType::new); public static final RegistryEntry VAULT = simpleItem("vault", ItemVaultMountedStorageType::new); public static final RegistryEntry TOOLBOX = simpleItem("toolbox", ToolboxMountedStorageType::new); + public static final RegistryEntry DEPOT = simpleItem("depot", DepotMountedStorageType::new); public static final RegistryEntry FLUID_TANK = simpleFluid("fluid_tank", FluidTankMountedStorageType::new); public static final RegistryEntry CREATIVE_FLUID_TANK = simpleFluid("creative_fluid_tank", CreativeFluidTankMountedStorageType::new); diff --git a/src/main/java/com/simibubi/create/AllPackets.java b/src/main/java/com/simibubi/create/AllPackets.java index cf771352b4..04a05d3100 100644 --- a/src/main/java/com/simibubi/create/AllPackets.java +++ b/src/main/java/com/simibubi/create/AllPackets.java @@ -16,6 +16,7 @@ import com.simibubi.create.content.contraptions.ContraptionColliderLockPacket.Co import com.simibubi.create.content.contraptions.ContraptionDisassemblyPacket; import com.simibubi.create.content.contraptions.ContraptionRelocationPacket; import com.simibubi.create.content.contraptions.ContraptionStallPacket; +import com.simibubi.create.content.contraptions.MountedStorageSyncPacket; import com.simibubi.create.content.contraptions.TrainCollisionPacket; import com.simibubi.create.content.contraptions.actors.contraptionControls.ContraptionDisableActorPacket; import com.simibubi.create.content.contraptions.actors.trainControls.ControlsInputPacket; @@ -30,9 +31,7 @@ import com.simibubi.create.content.contraptions.glue.SuperGlueSelectionPacket; import com.simibubi.create.content.contraptions.minecart.CouplingCreationPacket; import com.simibubi.create.content.contraptions.minecart.capability.MinecartControllerUpdatePacket; 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; @@ -123,6 +122,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; + import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkEvent.Context; import net.minecraftforge.network.NetworkRegistry; @@ -218,8 +218,7 @@ public enum AllPackets { LIMBSWING_UPDATE(LimbSwingUpdatePacket.class, LimbSwingUpdatePacket::new, PLAY_TO_CLIENT), 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), + MOUNTED_STORAGE_SYNC(MountedStorageSyncPacket.class, MountedStorageSyncPacket::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), diff --git a/src/main/java/com/simibubi/create/api/contraption/storage/SyncedMountedStorage.java b/src/main/java/com/simibubi/create/api/contraption/storage/SyncedMountedStorage.java new file mode 100644 index 0000000000..035ed5d94f --- /dev/null +++ b/src/main/java/com/simibubi/create/api/contraption/storage/SyncedMountedStorage.java @@ -0,0 +1,25 @@ +package com.simibubi.create.api.contraption.storage; + +import com.simibubi.create.content.contraptions.Contraption; + +import net.minecraft.core.BlockPos; + +/** + * Optional interface for mounted storage that is synced with the client. + */ +public interface SyncedMountedStorage { + /** + * @return true if this storage needs to be synced. + */ + boolean isDirty(); + + /** + * Called after this storage has been synced. + */ + void markClean(); + + /** + * Called on the client side after this storage has been synced from the server. + */ + void afterSync(Contraption contraption, BlockPos localPos); +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java index 1ae3f8c6b6..aed5425cd6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java @@ -376,7 +376,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit if (!initialized) contraptionInitialize(); - contraption.tick(this); + contraption.tickStorage(this); tickContraption(); super.tick(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java index 17466c94c0..155b78415a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java @@ -732,7 +732,7 @@ public abstract class Contraption { }); }); - storage.read(nbt); + storage.read(nbt, spawnData, this); actors.clear(); nbt.getList("Actors", Tag.TAG_COMPOUND) @@ -1447,10 +1447,6 @@ public abstract class Contraption { return this.storage; } - public MountedStorageManager getStorageManager() { - return storage; - } - public RenderedBlocks getRenderedBlocks() { return new RenderedBlocks(pos -> { StructureBlockInfo info = blocks.get(pos); @@ -1473,7 +1469,8 @@ public abstract class Contraption { return simplifiedEntityColliders; } - public void tick(AbstractContraptionEntity entity) { + public void tickStorage(AbstractContraptionEntity entity) { + this.storage.tick(entity); } public boolean containsBlockBreakers() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/MountedFluidStorage.java b/src/main/java/com/simibubi/create/content/contraptions/MountedFluidStorage.java deleted file mode 100644 index f0024dce60..0000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/MountedFluidStorage.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.simibubi.create.content.contraptions; - -import com.simibubi.create.AllPackets; -import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket; -import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity; -import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity.CreativeSmartFluidTank; -import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity; -import com.simibubi.create.foundation.fluid.SmartFluidTank; - -import net.createmod.catnip.animation.LerpedFloat; -import net.createmod.catnip.animation.LerpedFloat.Chaser; -import net.createmod.catnip.nbt.NBTHelper; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.block.entity.BlockEntity; - -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.IFluidTank; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.network.PacketDistributor; - -public class MountedFluidStorage { - - SmartFluidTank tank; - private boolean valid; - private BlockEntity blockEntity; - - private int packetCooldown = 0; - private boolean sendPacket = false; - - public static boolean canUseAsStorage(BlockEntity be) { - if (be instanceof FluidTankBlockEntity) - return ((FluidTankBlockEntity) be).isController(); - return false; - } - - public MountedFluidStorage(BlockEntity be) { - assignBlockEntity(be); - } - - public void assignBlockEntity(BlockEntity be) { - this.blockEntity = be; - tank = createMountedTank(be); - } - - private SmartFluidTank createMountedTank(BlockEntity be) { - if (be instanceof CreativeFluidTankBlockEntity) - return new CreativeSmartFluidTank( - ((FluidTankBlockEntity) be).getTotalTankSize() * FluidTankBlockEntity.getCapacityMultiplier(), $ -> { - }); - if (be instanceof FluidTankBlockEntity) - return new SmartFluidTank( - ((FluidTankBlockEntity) be).getTotalTankSize() * FluidTankBlockEntity.getCapacityMultiplier(), - this::onFluidStackChanged); - return null; - } - - public void tick(Entity entity, BlockPos pos, boolean isRemote) { - if (!isRemote) { - if (packetCooldown > 0) - packetCooldown--; - else if (sendPacket) { - sendPacket = false; - AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), - new ContraptionFluidPacket(entity.getId(), pos, tank.getFluid())); - packetCooldown = 8; - } - return; - } - - if (!(blockEntity instanceof FluidTankBlockEntity)) - return; - FluidTankBlockEntity tank = (FluidTankBlockEntity) blockEntity; - tank.getFluidLevel() - .tickChaser(); - } - - public void updateFluid(FluidStack fluid) { - tank.setFluid(fluid); - if (!(blockEntity instanceof FluidTankBlockEntity)) - return; - float fillState = tank.getFluidAmount() / (float) tank.getCapacity(); - FluidTankBlockEntity tank = (FluidTankBlockEntity) blockEntity; - if (tank.getFluidLevel() == null) - tank.setFluidLevel(LerpedFloat.linear() - .startWithValue(fillState)); - tank.getFluidLevel() - .chase(fillState, 0.5, Chaser.EXP); - IFluidTank tankInventory = tank.getTankInventory(); - if (tankInventory instanceof SmartFluidTank) - ((SmartFluidTank) tankInventory).setFluid(fluid); - } - - public void removeStorageFromWorld() { - valid = false; - if (blockEntity == null) - return; - - IFluidHandler teHandler = blockEntity.getCapability(ForgeCapabilities.FLUID_HANDLER) - .orElse(null); - if (!(teHandler instanceof SmartFluidTank)) - return; - SmartFluidTank smartTank = (SmartFluidTank) teHandler; - tank.setFluid(smartTank.getFluid()); - sendPacket = false; - valid = true; - } - - private void onFluidStackChanged(FluidStack fs) { - sendPacket = true; - } - - public void addStorageToWorld(BlockEntity be) { - if (tank instanceof CreativeSmartFluidTank) - return; - - LazyOptional capability = be.getCapability(ForgeCapabilities.FLUID_HANDLER); - IFluidHandler teHandler = capability.orElse(null); - if (!(teHandler instanceof SmartFluidTank)) - return; - - SmartFluidTank inv = (SmartFluidTank) teHandler; - inv.setFluid(tank.getFluid() - .copy()); - } - - public IFluidHandler getFluidHandler() { - return tank; - } - - public CompoundTag serialize() { - if (!valid) - return null; - CompoundTag tag = tank.writeToNBT(new CompoundTag()); - tag.putInt("Capacity", tank.getCapacity()); - - if (tank instanceof CreativeSmartFluidTank) { - NBTHelper.putMarker(tag, "Bottomless"); - tag.put("ProvidedStack", tank.getFluid() - .writeToNBT(new CompoundTag())); - } - return tag; - } - - public static MountedFluidStorage deserialize(CompoundTag nbt) { - MountedFluidStorage storage = new MountedFluidStorage(null); - if (nbt == null) - return storage; - - int capacity = nbt.getInt("Capacity"); - storage.tank = new SmartFluidTank(capacity, storage::onFluidStackChanged); - storage.valid = true; - - if (nbt.contains("Bottomless")) { - FluidStack providedStack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("ProvidedStack")); - CreativeSmartFluidTank creativeSmartFluidTank = new CreativeSmartFluidTank(capacity, $ -> { - }); - creativeSmartFluidTank.setContainedFluid(providedStack); - storage.tank = creativeSmartFluidTank; - return storage; - } - - storage.tank.readFromNBT(nbt); - return storage; - } - - public boolean isValid() { - return valid; - } - -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/MountedStorageManager.java b/src/main/java/com/simibubi/create/content/contraptions/MountedStorageManager.java index e7bf0e30b4..51770ca729 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/MountedStorageManager.java +++ b/src/main/java/com/simibubi/create/content/contraptions/MountedStorageManager.java @@ -1,16 +1,24 @@ package com.simibubi.create.content.contraptions; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; import com.mojang.datafixers.util.Pair; +import com.simibubi.create.AllPackets; import com.simibubi.create.Create; import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry; +import com.simibubi.create.api.contraption.storage.SyncedMountedStorage; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageWrapper; @@ -24,7 +32,7 @@ import com.simibubi.create.content.logistics.crate.CreativeCrateMountedStorage; import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorage; import com.simibubi.create.impl.contraption.storage.FallbackMountedStorage; -import net.createmod.catnip.utility.NBTHelper; +import net.createmod.catnip.nbt.NBTHelper; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -38,25 +46,39 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; + import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.wrapper.CombinedInvWrapper; +import net.minecraftforge.network.PacketDistributor; public class MountedStorageManager { // builders used during assembly, null afterward - private ImmutableMap.Builder itemsBuilder; - private ImmutableMap.Builder fluidsBuilder; + // ImmutableMap.Builder is not used because it will throw with duplicate keys, not override them + private Map itemsBuilder; + private Map fluidsBuilder; + private Map syncedItemsBuilder; + private Map syncedFluidsBuilder; // built data structures after assembly, null before - protected ImmutableMap allItemStorages; + private ImmutableMap allItemStorages; // different from allItemStorages, does not contain internal ones protected MountedItemStorageWrapper items; @Nullable protected MountedItemStorageWrapper fuelItems; protected MountedFluidStorageWrapper fluids; + private ImmutableMap syncedItems; + private ImmutableMap syncedFluids; + private List externalHandlers; - protected CombinedInvWrapper allItems; + private CombinedInvWrapper allItems; + + // ticks until storage can sync again + private int syncCooldown; + + // client-side: not all storages are synced, this determines which interactions are valid + private Set interactablePositions; public MountedStorageManager() { this.reset(); @@ -67,7 +89,7 @@ public class MountedStorageManager { throw new IllegalStateException("Mounted storage has already been initialized"); } - this.allItemStorages = this.itemsBuilder.build(); + this.allItemStorages = ImmutableMap.copyOf(this.itemsBuilder); this.items = new MountedItemStorageWrapper(subMap( this.allItemStorages, storage -> !storage.isInternal() @@ -81,9 +103,14 @@ public class MountedStorageManager { ); this.fuelItems = fuelMap.isEmpty() ? null : new MountedItemStorageWrapper(fuelMap); - ImmutableMap fluids = this.fluidsBuilder.build(); + ImmutableMap fluids = ImmutableMap.copyOf(this.fluidsBuilder); this.fluids = new MountedFluidStorageWrapper(fluids); this.fluidsBuilder = null; + + this.syncedItems = ImmutableMap.copyOf(this.syncedItemsBuilder); + this.syncedItemsBuilder = null; + this.syncedFluids = ImmutableMap.copyOf(this.syncedFluidsBuilder); + this.syncedFluidsBuilder = null; } private boolean isInitialized() { @@ -103,8 +130,11 @@ public class MountedStorageManager { this.fluids = null; this.externalHandlers = new ArrayList<>(); this.allItems = null; - this.itemsBuilder = ImmutableMap.builder(); - this.fluidsBuilder = ImmutableMap.builder(); + this.itemsBuilder = new HashMap<>(); + this.fluidsBuilder = new HashMap<>(); + this.syncedItemsBuilder = new HashMap<>(); + this.syncedFluidsBuilder = new HashMap<>(); + // interactablePositions intentionally not reset } public void addBlock(Level level, BlockState state, BlockPos globalPos, BlockPos localPos, @Nullable BlockEntity be) { @@ -113,6 +143,9 @@ public class MountedStorageManager { MountedItemStorage storage = itemType.mount(level, state, globalPos, be); if (storage != null) { this.itemsBuilder.put(localPos, storage); + if (storage instanceof SyncedMountedStorage synced) { + this.syncedItemsBuilder.put(localPos, synced); + } } } @@ -121,6 +154,9 @@ public class MountedStorageManager { MountedFluidStorage storage = fluidType.mount(level, state, globalPos, be); if (storage != null) { this.fluidsBuilder.put(localPos, storage); + if (storage instanceof SyncedMountedStorage synced) { + this.syncedFluidsBuilder.put(localPos, synced); + } } } } @@ -146,7 +182,70 @@ public class MountedStorageManager { } } - public void read(CompoundTag nbt) { + public void tick(AbstractContraptionEntity entity) { + if (this.syncCooldown > 0) { + this.syncCooldown--; + return; + } + + Map items = new HashMap<>(); + Map fluids = new HashMap<>(); + this.syncedItems.forEach((pos, storage) -> { + if (storage.isDirty()) { + items.put(pos, (MountedItemStorage) storage); + storage.markClean(); + } + }); + this.syncedFluids.forEach((pos, storage) -> { + if (storage.isDirty()) { + fluids.put(pos, (MountedFluidStorage) storage); + storage.markClean(); + } + }); + + if (!items.isEmpty() || !fluids.isEmpty()) { + MountedStorageSyncPacket packet = new MountedStorageSyncPacket(entity.getId(), items, fluids); + AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), packet); + this.syncCooldown = 8; + } + } + + public void handleSync(MountedStorageSyncPacket packet, AbstractContraptionEntity entity) { + // packet only contains changed storages, grab existing ones before resetting + ImmutableMap items = this.getAllItemStorages(); + MountedFluidStorageWrapper fluids = this.getFluids(); + this.reset(); + + // track freshly synced storages + Map syncedStorages = new IdentityHashMap<>(); + + try { + // re-add existing ones + this.itemsBuilder.putAll(items); + this.fluidsBuilder.putAll(fluids.storages); + // add newly synced ones, overriding existing ones if present + packet.items.forEach((pos, storage) -> { + this.itemsBuilder.put(pos, storage); + syncedStorages.put((SyncedMountedStorage) storage, pos); + }); + packet.fluids.forEach((pos, storage) -> { + this.fluidsBuilder.put(pos, storage); + syncedStorages.put((SyncedMountedStorage) storage, pos); + }); + } catch (Throwable t) { + // an exception will leave the manager in an invalid state + Create.LOGGER.error("An error occurred while syncing a MountedStorageManager", t); + } + + this.initialize(); + + // call all afterSync methods + Contraption contraption = entity.getContraption(); + syncedStorages.forEach((storage, pos) -> storage.afterSync(contraption, pos)); + } + + // contraption is provided on the client for initial afterSync storage callbacks + public void read(CompoundTag nbt, boolean clientPacket, @Nullable Contraption contraption) { this.reset(); try { @@ -169,42 +268,79 @@ public class MountedStorageManager { }); this.readLegacy(nbt); + + if (nbt.contains("interactable_positions")) { + this.interactablePositions = new HashSet<>(); + NBTHelper.iterateCompoundList(nbt.getList("interactable_positions", Tag.TAG_COMPOUND), tag -> { + BlockPos pos = NbtUtils.readBlockPos(tag); + this.interactablePositions.add(pos); + }); + } } catch (Throwable t) { Create.LOGGER.error("Error deserializing mounted storage", t); // an exception will leave the manager in an invalid state, initialize must be called } this.initialize(); + + // for client sync, run initial afterSync callbacks + if (!clientPacket || contraption == null) + return; + + this.getAllItemStorages().forEach((pos, storage) -> { + if (storage instanceof SyncedMountedStorage synced) { + synced.afterSync(contraption, pos); + } + }); + this.getFluids().storages.forEach((pos, storage) -> { + if (storage instanceof SyncedMountedStorage synced) { + synced.afterSync(contraption, pos); + } + }); } public void write(CompoundTag nbt, boolean clientPacket) { - if (!this.getAllItemStorages().isEmpty()) { - ListTag items = new ListTag(); - this.getAllItemStorages().forEach( - (pos, storage) -> MountedItemStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage) - .result().ifPresent(encoded -> { + ListTag items = new ListTag(); + this.getAllItemStorages().forEach((pos, storage) -> { + if (!clientPacket || storage instanceof SyncedMountedStorage) { + MountedItemStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage).result().ifPresent(encoded -> { CompoundTag tag = new CompoundTag(); tag.put("pos", NbtUtils.writeBlockPos(pos)); tag.put("storage", encoded); items.add(tag); - }) - ); + }); + } + } + ); + if (!items.isEmpty()) { nbt.put("items", items); } - if (!this.getFluids().storages.isEmpty()) { - ListTag fluids = new ListTag(); - this.getFluids().storages.forEach( - (pos, storage) -> MountedFluidStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage) - .result().ifPresent(encoded -> { + ListTag fluids = new ListTag(); + this.getFluids().storages.forEach((pos, storage) -> { + if (!clientPacket || storage instanceof SyncedMountedStorage) { + MountedFluidStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage).result().ifPresent(encoded -> { CompoundTag tag = new CompoundTag(); tag.put("pos", NbtUtils.writeBlockPos(pos)); tag.put("storage", encoded); fluids.add(tag); - }) - ); + }); + } + } + ); + if (!fluids.isEmpty()) { nbt.put("fluids", fluids); } + + if (clientPacket) { + // let the client know of all non-synced ones too + SetView positions = Sets.union(this.getAllItemStorages().keySet(), this.getFluids().storages.keySet()); + ListTag list = new ListTag(); + for (BlockPos pos : positions) { + list.add(NbtUtils.writeBlockPos(pos)); + } + nbt.put("interactable_positions", list); + } } public void attachExternal(IItemHandlerModifiable externalStorage) { @@ -218,9 +354,18 @@ public class MountedStorageManager { this.allItems = new CombinedInvWrapper(all); } + /** + * The primary way to access a contraption's inventory. Includes all + * non-internal mounted storages as well as all external storage. + */ + public CombinedInvWrapper getAllItems() { + this.assertInitialized(); + return this.allItems; + } + /** * Gets a map of all MountedItemStorages in the contraption, irrelevant of them - * being internal or providing fuel. The methods below are likely more useful. + * being internal or providing fuel. * @see MountedItemStorage#isInternal() * @see MountedItemStorage#providesFuel() */ @@ -250,22 +395,18 @@ public class MountedStorageManager { } /** - * Gets an item handler wrapping all non-internal mounted storages and all external storages. - * Non-internal storages are mounted storages that are intended to be exposed to the entire - * contraption. External storages are non-mounted storages that are still part of a contraption's - * inventory, such as the inventories of chest minecarts. + * Gets a fluid handler wrapping all mounted fluid storages. */ - public CombinedInvWrapper getAllItems() { - this.assertInitialized(); - return this.allItems; - } - public MountedFluidStorageWrapper getFluids() { this.assertInitialized(); return this.fluids; } public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) { + if (!(player instanceof ServerPlayer serverPlayer)) { + return this.interactablePositions != null && this.interactablePositions.contains(localPos); + } + StructureBlockInfo info = contraption.getBlocks().get(localPos); if (info == null) return false; @@ -274,7 +415,7 @@ public class MountedStorageManager { MountedItemStorage storage = storageManager.getAllItemStorages().get(localPos); if (storage != null) { - return !(player instanceof ServerPlayer serverPlayer) || storage.handleInteraction(serverPlayer, contraption, info); + return storage.handleInteraction(serverPlayer, contraption, info); } else { return false; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/MountedStorageSyncPacket.java b/src/main/java/com/simibubi/create/content/contraptions/MountedStorageSyncPacket.java new file mode 100644 index 0000000000..751990c21a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/MountedStorageSyncPacket.java @@ -0,0 +1,63 @@ +package com.simibubi.create.content.contraptions; + +import java.util.Map; + +import com.mojang.serialization.Codec; +import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage; +import com.simibubi.create.api.contraption.storage.item.MountedItemStorage; +import com.simibubi.create.foundation.networking.SimplePacketBase; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.NbtOps; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.Entity; + +import net.minecraftforge.network.NetworkEvent.Context; + +public class MountedStorageSyncPacket extends SimplePacketBase { + public final int contraptionId; + public final Map items; + public final Map fluids; + + public MountedStorageSyncPacket(int contraptionId, Map items, Map fluids) { + this.contraptionId = contraptionId; + this.items = items; + this.fluids = fluids; + } + + public MountedStorageSyncPacket(FriendlyByteBuf buf) { + this.contraptionId = buf.readVarInt(); + this.items = read(buf, MountedItemStorage.CODEC); + this.fluids = read(buf, MountedFluidStorage.CODEC); + } + + @Override + public void write(FriendlyByteBuf buffer) { + buffer.writeVarInt(this.contraptionId); + write(buffer, this.items, MountedItemStorage.CODEC); + write(buffer, this.fluids, MountedFluidStorage.CODEC); + } + + @SuppressWarnings("deprecation") + private static void write(FriendlyByteBuf buf, Map map, Codec codec) { + buf.writeMap(map, FriendlyByteBuf::writeBlockPos, (b, t) -> b.writeWithCodec(NbtOps.INSTANCE, codec, t)); + } + + @SuppressWarnings("deprecation") + private static Map read(FriendlyByteBuf buf, Codec codec) { + return buf.readMap(FriendlyByteBuf::readBlockPos, (b) -> b.readWithCodec(NbtOps.INSTANCE, codec)); + } + + @Override + public boolean handle(Context context) { + context.enqueueWork(() -> { + Entity entity = Minecraft.getInstance().level.getEntity(this.contraptionId); + if (!(entity instanceof AbstractContraptionEntity contraption)) + return; + + contraption.getContraption().getStorage().handleSync(this, contraption); + }); + return true; + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/elevator/ElevatorContraption.java b/src/main/java/com/simibubi/create/content/contraptions/elevator/ElevatorContraption.java index 27f24b42a8..b3c5dfdf0e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/elevator/ElevatorContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/elevator/ElevatorContraption.java @@ -56,8 +56,8 @@ public class ElevatorContraption extends PulleyContraption { } @Override - public void tick(AbstractContraptionEntity entity) { - super.tick(entity); + public void tickStorage(AbstractContraptionEntity entity) { + super.tickStorage(entity); if (entity.tickCount % 10 != 0) return; diff --git a/src/main/java/com/simibubi/create/content/contraptions/minecart/TrainCargoManager.java b/src/main/java/com/simibubi/create/content/contraptions/minecart/TrainCargoManager.java index 99a461e29d..33a9fadce8 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/minecart/TrainCargoManager.java +++ b/src/main/java/com/simibubi/create/content/contraptions/minecart/TrainCargoManager.java @@ -2,14 +2,17 @@ package com.simibubi.create.content.contraptions.minecart; import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.Nullable; + +import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageWrapper; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageWrapper; +import com.simibubi.create.content.contraptions.Contraption; import com.simibubi.create.content.contraptions.MountedStorageManager; -import com.simibubi.create.foundation.fluid.CombinedTankWrapper; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; + import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler; public class TrainCargoManager extends MountedStorageManager { @@ -28,6 +31,7 @@ public class TrainCargoManager extends MountedStorageManager { if (this.fuelItems != null) { this.fuelItems = new CargoInvWrapper(this.fuelItems); } + this.fluids = new CargoTankWrapper(this.fluids); } @Override @@ -37,8 +41,8 @@ public class TrainCargoManager extends MountedStorageManager { } @Override - public void read(CompoundTag nbt) { - super.read(nbt); + public void read(CompoundTag nbt, boolean clientPacket, @Nullable Contraption contraption) { + super.read(nbt, clientPacket, contraption); ticksSinceLastExchange = nbt.getInt("TicksSinceLastExchange"); } @@ -64,7 +68,7 @@ public class TrainCargoManager extends MountedStorageManager { } class CargoInvWrapper extends MountedItemStorageWrapper { - public CargoInvWrapper(MountedItemStorageWrapper wrapped) { + CargoInvWrapper(MountedItemStorageWrapper wrapped) { super(wrapped.storages); } @@ -93,10 +97,9 @@ public class TrainCargoManager extends MountedStorageManager { } - class CargoTankWrapper extends CombinedTankWrapper { - - public CargoTankWrapper(IFluidHandler... fluidHandler) { - super(fluidHandler); + class CargoTankWrapper extends MountedFluidStorageWrapper { + CargoTankWrapper(MountedFluidStorageWrapper wrapped) { + super(wrapped.storages); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionFluidPacket.java b/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionFluidPacket.java deleted file mode 100644 index 4b0555b8e7..0000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionFluidPacket.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.simibubi.create.content.contraptions.sync; - -import com.simibubi.create.content.contraptions.AbstractContraptionEntity; -import com.simibubi.create.content.fluids.tank.FluidTankMovementBehavior; -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.minecraftforge.fluids.FluidStack; -import net.minecraftforge.network.NetworkEvent.Context; - -public class ContraptionFluidPacket extends SimplePacketBase { - - private int entityId; - private BlockPos localPos; - private FluidStack containedFluid; - - public ContraptionFluidPacket(int entityId, BlockPos localPos, FluidStack containedFluid) { - this.entityId = entityId; - this.localPos = localPos; - this.containedFluid = containedFluid; - } - - public ContraptionFluidPacket(FriendlyByteBuf buffer) { - entityId = buffer.readInt(); - localPos = buffer.readBlockPos(); - containedFluid = FluidStack.readFromPacket(buffer); - } - - @Override - public void write(FriendlyByteBuf buffer) { - buffer.writeInt(entityId); - buffer.writeBlockPos(localPos); - containedFluid.writeToPacket(buffer); - } - - @Override - public boolean handle(Context context) { - context.enqueueWork(() -> { - Entity entityByID = Minecraft.getInstance().level.getEntity(entityId); - if (entityByID instanceof AbstractContraptionEntity entity) { - FluidTankMovementBehavior.handlePacket(entity, this.localPos, this.containedFluid); - } - }); - return true; - } -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionItemPacket.java b/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionItemPacket.java deleted file mode 100644 index 6f18fc1e58..0000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/sync/ContraptionItemPacket.java +++ /dev/null @@ -1,60 +0,0 @@ -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 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; - } -} diff --git a/src/main/java/com/simibubi/create/content/fluids/tank/FluidTankMovementBehavior.java b/src/main/java/com/simibubi/create/content/fluids/tank/FluidTankMovementBehavior.java index 66f0515e90..9f57a9deb5 100644 --- a/src/main/java/com/simibubi/create/content/fluids/tank/FluidTankMovementBehavior.java +++ b/src/main/java/com/simibubi/create/content/fluids/tank/FluidTankMovementBehavior.java @@ -1,21 +1,11 @@ package com.simibubi.create.content.fluids.tank; -import com.simibubi.create.AllPackets; -import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage; -import com.simibubi.create.content.contraptions.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour; import com.simibubi.create.content.contraptions.behaviour.MovementContext; -import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket; -import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorage; -import net.createmod.catnip.utility.animation.LerpedFloat; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.templates.FluidTank; -import net.minecraftforge.network.PacketDistributor; -// the contents of the mounted storage need to be synced to the block entity for rendering +// The fluid level needs to be ticked to animate smoothly public class FluidTankMovementBehavior implements MovementBehaviour { @Override public boolean mustTickWhileDisabled() { @@ -24,51 +14,11 @@ public class FluidTankMovementBehavior implements MovementBehaviour { @Override public void tick(MovementContext context) { - if (!context.world.isClientSide) { - MountedFluidStorage storage = context.getFluidStorage(); - if (!(storage instanceof FluidTankMountedStorage tank)) - return; - - FluidStack fluid = tank.getTank().getFluid(); - if (!(context.temporaryData instanceof SyncState)) - context.temporaryData = new SyncState(); - - SyncState state = (SyncState) context.temporaryData; - if (state.cooldown > 0) { - state.cooldown--; - } else if (!fluid.isFluidStackIdentical(state.stack)) { - // fluid has changed, sync it - state.cooldown = 8; - state.stack = fluid.copy(); - - AbstractContraptionEntity entity = context.contraption.entity; - ContraptionFluidPacket packet = new ContraptionFluidPacket(entity.getId(), context.localPos, fluid); - AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), packet); - } - } else { + if (context.world.isClientSide) { BlockEntity be = context.contraption.presentBlockEntities.get(context.localPos); if (be instanceof FluidTankBlockEntity tank) { tank.getFluidLevel().tickChaser(); } } } - - public static void handlePacket(AbstractContraptionEntity entity, BlockPos localPos, FluidStack fluid) { - BlockEntity be = entity.getContraption().presentBlockEntities.get(localPos); - if (!(be instanceof FluidTankBlockEntity tank)) - return; - - FluidTank inv = tank.getTankInventory(); - inv.setFluid(fluid); - float fillLevel = inv.getFluidAmount() / (float) inv.getCapacity(); - if (tank.getFluidLevel() == null) { - tank.setFluidLevel(LerpedFloat.linear().startWithValue(fillLevel)); - } - tank.getFluidLevel().chase(fillLevel, 0.5, LerpedFloat.Chaser.EXP); - } - - private static final class SyncState { - private int cooldown = 0; - private FluidStack stack = FluidStack.EMPTY; - } } diff --git a/src/main/java/com/simibubi/create/content/fluids/tank/storage/FluidTankMountedStorage.java b/src/main/java/com/simibubi/create/content/fluids/tank/storage/FluidTankMountedStorage.java index c13e31be10..a686bfb59d 100644 --- a/src/main/java/com/simibubi/create/content/fluids/tank/storage/FluidTankMountedStorage.java +++ b/src/main/java/com/simibubi/create/content/fluids/tank/storage/FluidTankMountedStorage.java @@ -3,30 +3,41 @@ package com.simibubi.create.content.fluids.tank.storage; import org.jetbrains.annotations.Nullable; import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import com.simibubi.create.AllMountedStorageTypes; +import com.simibubi.create.api.contraption.storage.SyncedMountedStorage; import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType; import com.simibubi.create.api.contraption.storage.fluid.WrapperMountedFluidStorage; +import com.simibubi.create.content.contraptions.Contraption; import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity; -import com.simibubi.create.foundation.utility.CreateCodecs; +import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorage.Handler; +import net.createmod.catnip.animation.LerpedFloat; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; + +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.templates.FluidTank; -public class FluidTankMountedStorage extends WrapperMountedFluidStorage { - public static final Codec CODEC = CreateCodecs.FLUID_TANK.xmap( - FluidTankMountedStorage::new, storage -> storage.wrapped - ); +public class FluidTankMountedStorage extends WrapperMountedFluidStorage implements SyncedMountedStorage { + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("capacity").forGetter(FluidTankMountedStorage::getCapacity), + FluidStack.CODEC.fieldOf("fluid").forGetter(FluidTankMountedStorage::getFluid) + ).apply(i, FluidTankMountedStorage::new)); - protected FluidTankMountedStorage(MountedFluidStorageType type, FluidTank wrapped) { - super(type, wrapped); + private boolean dirty; + + protected FluidTankMountedStorage(MountedFluidStorageType type, int capacity, FluidStack stack) { + super(type, new Handler(capacity, stack)); + this.wrapped.onChange = () -> this.dirty = true; } - protected FluidTankMountedStorage(FluidTank wrapped) { - this(AllMountedStorageTypes.FLUID_TANK.get(), wrapped); + protected FluidTankMountedStorage(int capacity, FluidStack stack) { + this(AllMountedStorageTypes.FLUID_TANK.get(), capacity, stack); } @Override @@ -38,22 +49,62 @@ public class FluidTankMountedStorage extends WrapperMountedFluidStorage {}; + + public Handler(int capacity, FluidStack stack) { + super(capacity); + this.setFluid(stack); + } + + @Override + protected void onContentsChanged() { + this.onChange.run(); + } } } diff --git a/src/main/java/com/simibubi/create/content/logistics/depot/MountedDepotInteractionBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/depot/MountedDepotInteractionBehaviour.java index e4ebc9ad01..06cd5d2615 100644 --- a/src/main/java/com/simibubi/create/content/logistics/depot/MountedDepotInteractionBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/depot/MountedDepotInteractionBehaviour.java @@ -1,17 +1,17 @@ package com.simibubi.create.content.logistics.depot; import com.simibubi.create.AllSoundEvents; +import com.simibubi.create.api.contraption.storage.item.MountedItemStorage; 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 com.simibubi.create.content.logistics.depot.storage.DepotMountedStorage; 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 { @@ -24,23 +24,17 @@ public class MountedDepotInteractionBehaviour extends MovingInteractionBehaviour if (player.level().isClientSide) return true; - MountedStorageManager storageManager = contraptionEntity.getContraption() - .getStorageManager(); - if (storageManager == null) + MountedStorageManager manager = contraptionEntity.getContraption().getStorage(); + + MountedItemStorage storage = manager.getAllItemStorages().get(localPos); + if (!(storage instanceof DepotMountedStorage depot)) return false; - MountedStorage mountedStorage = storageManager.getMountedItemStorage() - .get(localPos); - if (mountedStorage == null) - return false; - - IItemHandlerModifiable itemHandler = mountedStorage.getItemHandler(); - ItemStack itemOnDepot = itemHandler.getStackInSlot(0); - + ItemStack itemOnDepot = depot.getItem(); if (itemOnDepot.isEmpty() && itemInHand.isEmpty()) return true; - itemHandler.setStackInSlot(0, itemInHand.copy()); + depot.setItem(itemInHand.copy()); player.setItemInHand(activeHand, itemOnDepot.copy()); AllSoundEvents.DEPOT_PLOP.playOnServer(player.level(), BlockPos.containing(contraptionEntity.toGlobalVector(Vec3.atCenterOf(localPos), 0))); diff --git a/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorage.java b/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorage.java new file mode 100644 index 0000000000..3312d514e3 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorage.java @@ -0,0 +1,97 @@ +package com.simibubi.create.content.logistics.depot.storage; + +import org.jetbrains.annotations.Nullable; + +import com.mojang.serialization.Codec; +import com.simibubi.create.AllMountedStorageTypes; +import com.simibubi.create.api.contraption.storage.SyncedMountedStorage; +import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; +import com.simibubi.create.api.contraption.storage.item.WrapperMountedItemStorage; +import com.simibubi.create.content.contraptions.Contraption; +import com.simibubi.create.content.logistics.depot.DepotBlockEntity; +import com.simibubi.create.content.logistics.depot.storage.DepotMountedStorage.Handler; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; + +import net.minecraftforge.items.ItemStackHandler; + +public class DepotMountedStorage extends WrapperMountedItemStorage implements SyncedMountedStorage { + public static final Codec CODEC = ItemStack.CODEC.xmap( + DepotMountedStorage::new, DepotMountedStorage::getItem + ); + + private boolean dirty; + + protected DepotMountedStorage(ItemStack stack) { + this(AllMountedStorageTypes.DEPOT.get(), stack); + } + + protected DepotMountedStorage(MountedItemStorageType type, ItemStack stack) { + super(type, new Handler(stack)); + this.wrapped.onChange = () -> this.dirty = true; + } + + @Override + public void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) { + if (be instanceof DepotBlockEntity depot) { + depot.setHeldItem(this.getStackInSlot(0)); + } + } + + @Override + public boolean handleInteraction(ServerPlayer player, Contraption contraption, StructureBlockInfo info) { + // interaction is handled in the Interaction Behavior, swaps items with the player + return false; + } + + @Override + public boolean isDirty() { + return this.dirty; + } + + @Override + public void markClean() { + this.dirty = false; + } + + @Override + public void afterSync(Contraption contraption, BlockPos localPos) { + BlockEntity be = contraption.presentBlockEntities.get(localPos); + if (be instanceof DepotBlockEntity depot) { + depot.setHeldItem(this.getItem()); + } + } + + public void setItem(ItemStack stack) { + this.setStackInSlot(0, stack); + } + + public ItemStack getItem() { + return this.getStackInSlot(0); + } + + public static DepotMountedStorage fromDepot(DepotBlockEntity depot) { + ItemStack held = depot.getHeldItem(); + return new DepotMountedStorage(held.copy()); + } + + public static final class Handler extends ItemStackHandler { + private Runnable onChange = () -> {}; + + private Handler(ItemStack stack) { + super(1); + this.setStackInSlot(0, stack); + } + + @Override + protected void onContentsChanged(int slot) { + this.onChange.run(); + } + } +} diff --git a/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorageType.java b/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorageType.java new file mode 100644 index 0000000000..6b47a17487 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/depot/storage/DepotMountedStorageType.java @@ -0,0 +1,27 @@ +package com.simibubi.create.content.logistics.depot.storage; + +import org.jetbrains.annotations.Nullable; + +import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; +import com.simibubi.create.content.logistics.depot.DepotBlockEntity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class DepotMountedStorageType extends MountedItemStorageType { + public DepotMountedStorageType() { + super(DepotMountedStorage.CODEC); + } + + @Override + @Nullable + public DepotMountedStorage mount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) { + if (be instanceof DepotBlockEntity depot) { + return DepotMountedStorage.fromDepot(depot); + } + + return null; + } +} diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Carriage.java b/src/main/java/com/simibubi/create/content/trains/entity/Carriage.java index 3db70fed15..3f0c36b22e 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Carriage.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Carriage.java @@ -30,9 +30,9 @@ import com.simibubi.create.foundation.advancement.AllAdvancements; import net.createmod.catnip.data.Couple; import net.createmod.catnip.data.Iterate; -import net.createmod.catnip.nbt.NBTHelper; import net.createmod.catnip.data.Pair; import net.createmod.catnip.math.VecHelper; +import net.createmod.catnip.nbt.NBTHelper; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; @@ -46,6 +46,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; + import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.DistExecutor; @@ -647,7 +648,7 @@ public class Carriage { public void read(CompoundTag tag) { cutoff = tag.getFloat("Cutoff"); discardTicks = tag.getInt("DiscardTicks"); - storage.read(tag); + storage.read(tag, false, null); if (tag.contains("Pivot")) pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null); if (positionAnchor != null) diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/destination/DeliverPackagesInstruction.java b/src/main/java/com/simibubi/create/content/trains/schedule/destination/DeliverPackagesInstruction.java index f9bafe17f1..b577f4dd17 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/destination/DeliverPackagesInstruction.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/destination/DeliverPackagesInstruction.java @@ -27,6 +27,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; + import net.minecraftforge.items.IItemHandlerModifiable; public class DeliverPackagesInstruction extends ScheduleInstruction { @@ -78,7 +79,7 @@ public class DeliverPackagesInstruction extends ScheduleInstruction { } for (Carriage carriage : train.carriages) { - IItemHandlerModifiable carriageInventory = carriage.storage.getItems(); + IItemHandlerModifiable carriageInventory = carriage.storage.getAllItems(); if (carriageInventory == null) continue; diff --git a/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java b/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java index 10c541523a..c912a8858e 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java +++ b/src/main/java/com/simibubi/create/foundation/utility/CreateCodecs.java @@ -2,12 +2,10 @@ package com.simibubi.create.foundation.utility; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; -import com.mojang.serialization.codecs.RecordCodecBuilder; import com.simibubi.create.foundation.item.ItemSlots; import net.minecraft.util.ExtraCodecs; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.templates.FluidTank; + import net.minecraftforge.items.ItemStackHandler; public class CreateCodecs { @@ -26,18 +24,6 @@ public class CreateCodecs { slots -> slots.toHandler(ItemStackHandler::new), ItemSlots::fromHandler ); - /** - * Codec for a simple FluidTank with no validator support. - */ - public static final Codec FLUID_TANK = RecordCodecBuilder.create(i -> i.group( - FluidStack.CODEC.fieldOf("fluid").forGetter(FluidTank::getFluid), - ExtraCodecs.NON_NEGATIVE_INT.fieldOf("capacity").forGetter(FluidTank::getCapacity) - ).apply(i, (fluid, capacity) -> { - FluidTank tank = new FluidTank(capacity); - tank.setFluid(fluid); - return tank; - })); - public static Codec boundedIntStr(int min) { return ExtraCodecs.validate( INT_STR,