rework synced storage

- ported depot support
This commit is contained in:
TropheusJ 2025-01-25 07:04:59 -05:00
parent 4c5a350d2b
commit 2b91d7cd0c
21 changed files with 497 additions and 441 deletions

View file

@ -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();

View file

@ -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<CreativeCrateMountedStorageType> CREATIVE_CRATE = simpleItem("creative_crate", CreativeCrateMountedStorageType::new);
public static final RegistryEntry<ItemVaultMountedStorageType> VAULT = simpleItem("vault", ItemVaultMountedStorageType::new);
public static final RegistryEntry<ToolboxMountedStorageType> TOOLBOX = simpleItem("toolbox", ToolboxMountedStorageType::new);
public static final RegistryEntry<DepotMountedStorageType> DEPOT = simpleItem("depot", DepotMountedStorageType::new);
public static final RegistryEntry<FluidTankMountedStorageType> FLUID_TANK = simpleFluid("fluid_tank", FluidTankMountedStorageType::new);
public static final RegistryEntry<CreativeFluidTankMountedStorageType> CREATIVE_FLUID_TANK = simpleFluid("creative_fluid_tank", CreativeFluidTankMountedStorageType::new);

View file

@ -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),

View file

@ -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);
}

View file

@ -376,7 +376,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
if (!initialized)
contraptionInitialize();
contraption.tick(this);
contraption.tickStorage(this);
tickContraption();
super.tick();

View file

@ -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() {

View file

@ -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<IFluidHandler> 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;
}
}

View file

@ -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<BlockPos, MountedItemStorage> itemsBuilder;
private ImmutableMap.Builder<BlockPos, MountedFluidStorage> fluidsBuilder;
// ImmutableMap.Builder is not used because it will throw with duplicate keys, not override them
private Map<BlockPos, MountedItemStorage> itemsBuilder;
private Map<BlockPos, MountedFluidStorage> fluidsBuilder;
private Map<BlockPos, SyncedMountedStorage> syncedItemsBuilder;
private Map<BlockPos, SyncedMountedStorage> syncedFluidsBuilder;
// built data structures after assembly, null before
protected ImmutableMap<BlockPos, MountedItemStorage> allItemStorages;
private ImmutableMap<BlockPos, MountedItemStorage> allItemStorages;
// different from allItemStorages, does not contain internal ones
protected MountedItemStorageWrapper items;
@Nullable
protected MountedItemStorageWrapper fuelItems;
protected MountedFluidStorageWrapper fluids;
private ImmutableMap<BlockPos, SyncedMountedStorage> syncedItems;
private ImmutableMap<BlockPos, SyncedMountedStorage> syncedFluids;
private List<IItemHandlerModifiable> 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<BlockPos> 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<BlockPos, MountedFluidStorage> fluids = this.fluidsBuilder.build();
ImmutableMap<BlockPos, MountedFluidStorage> 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<BlockPos, MountedItemStorage> items = new HashMap<>();
Map<BlockPos, MountedFluidStorage> 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<BlockPos, MountedItemStorage> items = this.getAllItemStorages();
MountedFluidStorageWrapper fluids = this.getFluids();
this.reset();
// track freshly synced storages
Map<SyncedMountedStorage, BlockPos> 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<BlockPos> 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;
}

View file

@ -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<BlockPos, MountedItemStorage> items;
public final Map<BlockPos, MountedFluidStorage> fluids;
public MountedStorageSyncPacket(int contraptionId, Map<BlockPos, MountedItemStorage> items, Map<BlockPos, MountedFluidStorage> 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 <T> void write(FriendlyByteBuf buf, Map<BlockPos, T> map, Codec<T> codec) {
buf.writeMap(map, FriendlyByteBuf::writeBlockPos, (b, t) -> b.writeWithCodec(NbtOps.INSTANCE, codec, t));
}
@SuppressWarnings("deprecation")
private static <T> Map<BlockPos, T> read(FriendlyByteBuf buf, Codec<T> 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;
}
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}
}

View file

@ -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<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

@ -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;
}
}

View file

@ -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<FluidTank> {
public static final Codec<FluidTankMountedStorage> CODEC = CreateCodecs.FLUID_TANK.xmap(
FluidTankMountedStorage::new, storage -> storage.wrapped
);
public class FluidTankMountedStorage extends WrapperMountedFluidStorage<Handler> implements SyncedMountedStorage {
public static final Codec<FluidTankMountedStorage> 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<FluidTan
}
}
public FluidTank getTank() {
return this.wrapped;
public FluidStack getFluid() {
return this.wrapped.getFluid();
}
public int getCapacity() {
return this.wrapped.getCapacity();
}
@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 FluidTankBlockEntity tank))
return;
FluidTank inv = tank.getTankInventory();
inv.setFluid(this.getFluid());
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);
}
public static FluidTankMountedStorage fromTank(FluidTankBlockEntity tank) {
// tank has update callbacks, make an isolated copy
FluidTank inventory = tank.getTankInventory();
FluidTank copy = new FluidTank(inventory.getCapacity());
copy.setFluid(inventory.getFluid());
return new FluidTankMountedStorage(copy);
return new FluidTankMountedStorage(inventory.getCapacity(), inventory.getFluid().copy());
}
public static FluidTankMountedStorage fromLegacy(CompoundTag nbt) {
int capacity = nbt.getInt("Capacity");
FluidTank tank = new FluidTank(capacity);
tank.readFromNBT(nbt);
return new FluidTankMountedStorage(tank);
FluidStack fluid = FluidStack.loadFluidStackFromNBT(nbt);
return new FluidTankMountedStorage(capacity, fluid);
}
public static final class Handler extends FluidTank {
private Runnable onChange = () -> {};
public Handler(int capacity, FluidStack stack) {
super(capacity);
this.setFluid(stack);
}
@Override
protected void onContentsChanged() {
this.onChange.run();
}
}
}

View file

@ -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)));

View file

@ -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<Handler> implements SyncedMountedStorage {
public static final Codec<DepotMountedStorage> 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();
}
}
}

View file

@ -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<DepotMountedStorage> {
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;
}
}

View file

@ -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)

View file

@ -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;

View file

@ -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<FluidTank> 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<Integer> boundedIntStr(int min) {
return ExtraCodecs.validate(
INT_STR,