sync fluid tank contents

This commit is contained in:
TropheusJ 2025-01-25 00:11:28 -05:00
parent 595263e0df
commit da89fcab72
11 changed files with 108 additions and 32 deletions

View file

@ -106,6 +106,7 @@ import com.simibubi.create.content.fluids.tank.FluidTankBlock;
import com.simibubi.create.content.fluids.tank.FluidTankGenerator;
import com.simibubi.create.content.fluids.tank.FluidTankItem;
import com.simibubi.create.content.fluids.tank.FluidTankModel;
import com.simibubi.create.content.fluids.tank.FluidTankMovementBehavior;
import com.simibubi.create.content.kinetics.BlockStressDefaults;
import com.simibubi.create.content.kinetics.belt.BeltBlock;
import com.simibubi.create.content.kinetics.belt.BeltGenerator;
@ -991,6 +992,7 @@ public class AllBlocks {
.onRegister(CreateRegistrate.blockModel(() -> FluidTankModel::standard))
.onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status"))
.transform(mountedFluidStorage(AllMountedStorageTypes.FLUID_TANK))
.onRegister(movementBehaviour(new FluidTankMovementBehavior()))
.addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new)
.model(AssetLookup.customBlockItemModel("_", "block_single_window"))

View file

@ -51,7 +51,7 @@ public abstract class MountedItemStorage implements IItemHandlerModifiable {
/**
* Internal mounted storages are not exposed to the larger contraption inventory.
* They are only for internal use, such as access from a {@link MovementBehaviour}.
* Internal storages are still accessible through {@link MovementContext#getStorage()}
* Internal storages are still accessible through {@link MovementContext#getItemStorage()}
* as well as {@link MountedStorageManager#getAllItemStorages()}.
* A storage being internal implies that it does not provide fuel either.
* This is only called once on assembly.

View file

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

View file

@ -123,9 +123,6 @@ import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.client.model.data.ModelData;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.registries.GameData;
public abstract class Contraption {
@ -1471,12 +1468,7 @@ public abstract class Contraption {
return simplifiedEntityColliders;
}
public void handleContraptionFluidPacket(BlockPos localPos, FluidStack containedFluid) {
// storage.updateContainedFluid(localPos, containedFluid);
}
public void tickStorage(AbstractContraptionEntity entity) {
// storage.entityTick(entity);
public void tick(AbstractContraptionEntity entity) {
}
public boolean containsBlockBreakers() {

View file

@ -3,15 +3,15 @@ package com.simibubi.create.content.contraptions.behaviour;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import org.jetbrains.annotations.Nullable;
import com.google.common.base.Suppliers;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
import com.simibubi.create.content.contraptions.Contraption;
import com.simibubi.create.content.logistics.filter.FilterItemStack;
import net.createmod.catnip.utility.VecHelper;
import org.jetbrains.annotations.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@ -41,7 +41,8 @@ public class MovementContext {
private FilterItemStack filter;
private final Supplier<MountedItemStorage> storage;
private final Supplier<MountedItemStorage> itemStorage;
private final Supplier<MountedFluidStorage> fluidStorage;
public MovementContext(Level world, StructureBlockInfo info, Contraption contraption) {
this.world = world;
@ -59,9 +60,8 @@ public class MovementContext {
data = new CompoundTag();
stall = false;
filter = null;
this.storage = Suppliers.memoize(
() -> contraption.getStorage().getAllItemStorages().get(this.localPos)
);
this.itemStorage = Suppliers.memoize(() -> contraption.getStorage().getAllItemStorages().get(this.localPos));
this.fluidStorage = Suppliers.memoize(() -> contraption.getStorage().getFluids().storages.get(this.localPos));
}
public float getAnimationSpeed() {
@ -106,7 +106,12 @@ public class MovementContext {
}
@Nullable
public MountedItemStorage getStorage() {
return this.storage.get();
public MountedItemStorage getItemStorage() {
return this.itemStorage.get();
}
@Nullable
public MountedFluidStorage getFluidStorage() {
return this.fluidStorage.get();
}
}

View file

@ -2,6 +2,8 @@ package com.simibubi.create.content.contraptions.behaviour.dispenser;
import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour;
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
@ -9,15 +11,12 @@ import com.simibubi.create.foundation.item.ItemHelper;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraftforge.items.IItemHandler;
import org.jetbrains.annotations.Nullable;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.LevelEvent;
import net.minecraftforge.items.IItemHandler;
public class DropperMovementBehaviour implements MovementBehaviour {
@Override
@ -25,7 +24,7 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (context.world.isClientSide)
return;
MountedItemStorage storage = context.getStorage();
MountedItemStorage storage = context.getItemStorage();
if (storage == null)
return;

View file

@ -102,7 +102,7 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
ItemStack toInsert = output.copy();
// try inserting into own inventory first
ItemStack remainder = ItemHandlerHelper.insertItem(context.getStorage(), toInsert, false);
ItemStack remainder = ItemHandlerHelper.insertItem(context.getItemStorage(), toInsert, false);
if (!remainder.isEmpty()) {
// next, try the whole contraption inventory
// note that this contains the dispenser inventory. That's fine.

View file

@ -55,8 +55,8 @@ public class ElevatorContraption extends PulleyContraption {
}
@Override
public void tickStorage(AbstractContraptionEntity entity) {
super.tickStorage(entity);
public void tick(AbstractContraptionEntity entity) {
super.tick(entity);
if (entity.tickCount % 10 != 0)
return;

View file

@ -1,6 +1,7 @@
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;
@ -39,10 +40,9 @@ public class ContraptionFluidPacket extends SimplePacketBase {
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().handleContraptionFluidPacket(localPos, containedFluid);
if (entityByID instanceof AbstractContraptionEntity entity) {
FluidTankMovementBehavior.handlePacket(entity, this.localPos, this.containedFluid);
}
});
return true;
}

View file

@ -0,0 +1,74 @@
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
public class FluidTankMovementBehavior implements MovementBehaviour {
@Override
public boolean mustTickWhileDisabled() {
return true;
}
@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 {
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

@ -38,6 +38,10 @@ public class FluidTankMountedStorage extends WrapperMountedFluidStorage<FluidTan
}
}
public FluidTank getTank() {
return this.wrapped;
}
public static FluidTankMountedStorage fromTank(FluidTankBlockEntity tank) {
// tank has update callbacks, make an isolated copy
FluidTank inventory = tank.getTankInventory();