Global Storage

- Carriages now track stored items and fluids
- Fixed a number of inconsistencies in the new PSI behaviour
- Fixed several issues with portal cut-off
- PSIs can no longer interface with carriage contraptions that are located in two dimensions
This commit is contained in:
simibubi 2022-05-17 23:23:29 +02:00
parent d63b12554a
commit f291dbc03a
21 changed files with 450 additions and 277 deletions

View file

@ -24,7 +24,7 @@ public class PortableFluidInterfaceTileEntity extends PortableStorageInterfaceTi
@Override
public void startTransferringTo(Contraption contraption, float distance) {
LazyOptional<IFluidHandler> oldcap = capability;
capability = LazyOptional.of(() -> new InterfaceFluidHandler(contraption.fluidInventory));
capability = LazyOptional.of(() -> new InterfaceFluidHandler(contraption.getSharedFluidTanks()));
oldcap.invalidate();
super.startTransferringTo(contraption, distance);
}
@ -110,7 +110,7 @@ public class PortableFluidInterfaceTileEntity extends PortableStorageInterfaceTi
keepAlive();
return drain;
}
public void keepAlive() {
onContentTransferred();
}

View file

@ -16,7 +16,7 @@ import net.minecraftforge.items.ItemStackHandler;
public class PortableItemInterfaceTileEntity extends PortableStorageInterfaceTileEntity {
protected LazyOptional<IItemHandlerModifiable> capability;
public PortableItemInterfaceTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
capability = createEmptyHandler();
@ -25,7 +25,7 @@ public class PortableItemInterfaceTileEntity extends PortableStorageInterfaceTil
@Override
public void startTransferringTo(Contraption contraption, float distance) {
LazyOptional<IItemHandlerModifiable> oldCap = capability;
capability = LazyOptional.of(() -> new InterfaceItemHandler(contraption.inventory));
capability = LazyOptional.of(() -> new InterfaceItemHandler(contraption.getSharedInventory()));
oldCap.invalidate();
super.startTransferringTo(contraption, distance);
}
@ -37,7 +37,7 @@ public class PortableItemInterfaceTileEntity extends PortableStorageInterfaceTil
oldCap.invalidate();
super.stopTransferring();
}
private LazyOptional<IItemHandlerModifiable> createEmptyHandler() {
return LazyOptional.of(() -> new InterfaceItemHandler(new ItemStackHandler(0)));
}

View file

@ -61,24 +61,26 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
boolean onCarriage = context.contraption instanceof CarriageContraption;
if (onCarriage && context.motion.length() > 1 / 4f)
return;
if (!findInterface(context, pos))
context.data.remove(_workingPos_);
// if (findInterface(context, pos))
// context.stall = true;
}
@Override
public void tick(MovementContext context) {
if (context.world.isClientSide) {
if (context.world.isClientSide)
getAnimation(context).tickChaser();
boolean onCarriage = context.contraption instanceof CarriageContraption;
if (onCarriage && context.motion.length() > 1 / 4f)
return;
if (context.world.isClientSide) {
BlockPos pos = new BlockPos(context.position);
findInterface(context, pos);
if (!context.data.contains(_clientPrevPos_)
|| !NbtUtils.readBlockPos(context.data.getCompound(_clientPrevPos_))
.equals(pos)) {
if (!findInterface(context, pos))
reset(context);
}
if (!findInterface(context, pos))
reset(context);
return;
}
@ -88,7 +90,7 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
BlockPos pos = NbtUtils.readBlockPos(context.data.getCompound(_workingPos_));
Vec3 target = VecHelper.getCenterOf(pos);
if (!context.stall
if (!context.stall && !onCarriage
&& context.position.closerThan(target, target.distanceTo(context.position.add(context.motion))))
context.stall = true;
@ -114,6 +116,8 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
}
protected boolean findInterface(MovementContext context, BlockPos pos) {
if (context.contraption instanceof CarriageContraption cc && !cc.notInPortal())
return false;
Optional<Direction> currentFacingIfValid = getCurrentFacingIfValid(context);
if (!currentFacingIfValid.isPresent())
return false;
@ -121,9 +125,10 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
Direction currentFacing = currentFacingIfValid.get();
PortableStorageInterfaceTileEntity psi =
findStationaryInterface(context.world, pos, context.state, currentFacing);
if (psi == null)
return false;
if ((psi.isTransferring() || psi.isPowered()) && !context.world.isClientSide)
if (psi.isPowered())
return false;
context.data.put(_workingPos_, NbtUtils.writeBlockPos(psi.getBlockPos()));
@ -170,14 +175,16 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
private PortableStorageInterfaceTileEntity getStationaryInterfaceAt(Level world, BlockPos pos, BlockState state,
Direction facing) {
BlockEntity te = world.getBlockEntity(pos);
if (!(te instanceof PortableStorageInterfaceTileEntity))
if (!(te instanceof PortableStorageInterfaceTileEntity psi))
return null;
BlockState blockState = world.getBlockState(pos);
if (blockState.getBlock() != state.getBlock())
return null;
if (blockState.getValue(PortableStorageInterfaceBlock.FACING) != facing.getOpposite())
return null;
return (PortableStorageInterfaceTileEntity) te;
if (psi.isPowered())
return null;
return psi;
}
private Optional<Direction> getCurrentFacingIfValid(MovementContext context) {

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions.components.actors;
import java.util.List;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
@ -52,6 +53,14 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
return connectedEntity != null && isConnected();
}
@Override
public void initialize() {
super.initialize();
powered = level.hasNeighborSignal(worldPosition);
if (!powered)
notifyContraptions();
}
protected abstract void invalidateCapability();
@Override
@ -109,7 +118,10 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
super.read(compound, clientPacket);
transferTimer = compound.getInt("Timer");
distance = compound.getFloat("Distance");
boolean poweredPreviously = powered;
powered = compound.getBoolean("Powered");
if (clientPacket && powered != poweredPreviously && !powered)
notifyContraptions();
}
@Override
@ -125,9 +137,18 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
if (isBlockPowered == powered)
return;
powered = isBlockPowered;
if (!powered)
notifyContraptions();
if (powered)
stopTransferring();
sendData();
}
private void notifyContraptions() {
level.getEntitiesOfClass(AbstractContraptionEntity.class, new AABB(worldPosition).inflate(3))
.forEach(AbstractContraptionEntity::refreshPSIs);
}
public boolean isPowered() {
return powered;
}

View file

@ -36,17 +36,21 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override
public Vec3 getActiveAreaOffset(MovementContext context) {
return Vec3.atLowerCornerOf(context.state.getValue(SawBlock.FACING).getNormal()).scale(.65f);
return Vec3.atLowerCornerOf(context.state.getValue(SawBlock.FACING)
.getNormal())
.scale(.65f);
}
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
super.visitNewPosition(context, pos);
Vec3 facingVec = Vec3.atLowerCornerOf(context.state.getValue(SawBlock.FACING).getNormal());
Vec3 facingVec = Vec3.atLowerCornerOf(context.state.getValue(SawBlock.FACING)
.getNormal());
facingVec = context.rotation.apply(facingVec);
Direction closestToFacing = Direction.getNearest(facingVec.x, facingVec.y, facingVec.z);
if(closestToFacing.getAxis().isVertical() && context.data.contains("BreakingPos")) {
if (closestToFacing.getAxis()
.isVertical() && context.data.contains("BreakingPos")) {
context.data.remove("BreakingPos");
context.stall = false;
}
@ -64,15 +68,17 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
Optional<AbstractBlockBreakQueue> dynamicTree = TreeCutter.findDynamicTree(brokenState.getBlock(), pos);
if (dynamicTree.isPresent()) {
dynamicTree.get().destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos));
dynamicTree.get()
.destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos));
return;
}
TreeCutter.findTree(context.world, pos).destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos));
TreeCutter.findTree(context.world, pos)
.destroyBlocks(context.world, null, (stack, dropPos) -> dropItemFromCutTree(context, stack, dropPos));
}
public void dropItemFromCutTree(MovementContext context, BlockPos pos, ItemStack stack) {
ItemStack remainder = ItemHandlerHelper.insertItem(context.contraption.inventory, stack, false);
ItemStack remainder = ItemHandlerHelper.insertItem(context.contraption.getSharedInventory(), stack, false);
if (remainder.isEmpty())
return;
@ -87,7 +93,7 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override
@OnlyIn(value = Dist.CLIENT)
public void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffer) {
ContraptionMatrices matrices, MultiBufferSource buffer) {
SawRenderer.renderInContraption(context, renderWorld, matrices, buffer);
}

View file

@ -15,7 +15,8 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
public class DropperMovementBehaviour implements MovementBehaviour {
protected static final MovedDefaultDispenseItemBehaviour DEFAULT_BEHAVIOUR = new MovedDefaultDispenseItemBehaviour();
protected static final MovedDefaultDispenseItemBehaviour DEFAULT_BEHAVIOUR =
new MovedDefaultDispenseItemBehaviour();
private static final Random RNG = new Random();
protected void activate(MovementContext context, BlockPos pos) {
@ -23,7 +24,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (location.isEmpty()) {
context.world.levelEvent(1001, pos, 0);
} else {
setItemStackAt(location, DEFAULT_BEHAVIOUR.dispense(getItemStackAt(location, context), context, pos), context);
setItemStackAt(location, DEFAULT_BEHAVIOUR.dispense(getItemStackAt(location, context), context, pos),
context);
}
}
@ -36,8 +38,13 @@ public class DropperMovementBehaviour implements MovementBehaviour {
}
private void collectItems(MovementContext context) {
getStacks(context).stream().filter(itemStack -> !itemStack.isEmpty() && itemStack.getItem() != Items.AIR && itemStack.getMaxStackSize() > itemStack.getCount()).forEach(itemStack -> itemStack.grow(
ItemHelper.extract(context.contraption.inventory, itemStack::sameItem, ItemHelper.ExtractionCountMode.UPTO, itemStack.getMaxStackSize() - itemStack.getCount(), false).getCount()));
getStacks(context).stream()
.filter(itemStack -> !itemStack.isEmpty() && itemStack.getItem() != Items.AIR
&& itemStack.getMaxStackSize() > itemStack.getCount())
.forEach(itemStack -> itemStack.grow(ItemHelper
.extract(context.contraption.getSharedInventory(), itemStack::sameItem,
ItemHelper.ExtractionCountMode.UPTO, itemStack.getMaxStackSize() - itemStack.getCount(), false)
.getCount()));
}
private void updateTemporaryData(MovementContext context) {
@ -62,7 +69,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (testStack == null || testStack.isEmpty())
continue;
if (testStack.getMaxStackSize() == 1) {
location = new DispenseItemLocation(false, ItemHelper.findFirstMatchingSlotIndex(context.contraption.inventory, testStack::sameItem));
location = new DispenseItemLocation(false, ItemHelper
.findFirstMatchingSlotIndex(context.contraption.getSharedInventory(), testStack::sameItem));
if (!getItemStackAt(location, context).isEmpty())
useable.add(location);
} else if (testStack.getCount() >= 2)
@ -104,7 +112,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (location.isInternal()) {
return getStacks(context).get(location.getSlot());
} else {
return context.contraption.inventory.getStackInSlot(location.getSlot());
return context.contraption.getSharedInventory()
.getStackInSlot(location.getSlot());
}
}
@ -112,7 +121,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (location.isInternal()) {
getStacks(context).set(location.getSlot(), stack);
} else {
context.contraption.inventory.setStackInSlot(location.getSlot(), stack);
context.contraption.getSharedInventory()
.setStackInSlot(location.getSlot(), stack);
}
}

View file

@ -17,11 +17,13 @@ import net.minecraftforge.items.ItemHandlerHelper;
public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBehaviour {
private static final MovedDefaultDispenseItemBehaviour DEFAULT_INSTANCE = new MovedDefaultDispenseItemBehaviour();
public static void doDispense(Level p_82486_0_, ItemStack p_82486_1_, int p_82486_2_, Vec3 facing, BlockPos p_82486_4_, MovementContext context) {
public static void doDispense(Level p_82486_0_, ItemStack p_82486_1_, int p_82486_2_, Vec3 facing,
BlockPos p_82486_4_, MovementContext context) {
double d0 = p_82486_4_.getX() + facing.x + .5;
double d1 = p_82486_4_.getY() + facing.y + .5;
double d2 = p_82486_4_.getZ() + facing.z + .5;
if (Direction.getNearest(facing.x, facing.y, facing.z).getAxis() == Direction.Axis.Y) {
if (Direction.getNearest(facing.x, facing.y, facing.z)
.getAxis() == Direction.Axis.Y) {
d1 = d1 - 0.125D;
} else {
d1 = d1 - 0.15625D;
@ -29,13 +31,20 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
ItemEntity itementity = new ItemEntity(p_82486_0_, d0, d1, d2, p_82486_1_);
double d3 = p_82486_0_.random.nextDouble() * 0.1D + 0.2D;
itementity.setDeltaMovement(p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.x() * d3 + context.motion.x, p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.y() * d3 + context.motion.y, p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.z() * d3 + context.motion.z);
itementity.setDeltaMovement(
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.x() * d3
+ context.motion.x,
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.y() * d3
+ context.motion.y,
p_82486_0_.random.nextGaussian() * (double) 0.0075F * (double) p_82486_2_ + facing.z() * d3
+ context.motion.z);
p_82486_0_.addFreshEntity(itementity);
}
@Override
public ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos) {
Vec3 facingVec = Vec3.atLowerCornerOf(context.state.getValue(DispenserBlock.FACING).getNormal());
Vec3 facingVec = Vec3.atLowerCornerOf(context.state.getValue(DispenserBlock.FACING)
.getNormal());
facingVec = context.rotation.apply(facingVec);
facingVec.normalize();
@ -46,7 +55,9 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
this.spawnDispenseParticles(context.world, pos, closestToFacing);
return this.dispenseStack(itemStack, context, pos, facingVec);
} else {
if (HopperBlockEntity.addItem(null, iinventory, itemStack.copy().split(1), closestToFacing.getOpposite()).isEmpty())
if (HopperBlockEntity.addItem(null, iinventory, itemStack.copy()
.split(1), closestToFacing.getOpposite())
.isEmpty())
itemStack.shrink(1);
return itemStack;
}
@ -69,7 +80,8 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
}
/**
* Order clients to display dispense particles from the specified block and facing.
* Order clients to display dispense particles from the specified block and
* facing.
*/
protected void spawnDispenseParticles(LevelAccessor world, BlockPos pos, Vec3 facing) {
spawnDispenseParticles(world, pos, getClosestFacingDirection(facing));
@ -83,9 +95,11 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
return Direction.getNearest(exactFacing.x, exactFacing.y, exactFacing.z);
}
protected ItemStack placeItemInInventory(ItemStack consumedFrom, ItemStack output, MovementContext context, BlockPos pos, Vec3 facing) {
protected ItemStack placeItemInInventory(ItemStack consumedFrom, ItemStack output, MovementContext context,
BlockPos pos, Vec3 facing) {
consumedFrom.shrink(1);
ItemStack remainder = ItemHandlerHelper.insertItem(context.contraption.inventory, output.copy(), false);
ItemStack remainder =
ItemHandlerHelper.insertItem(context.contraption.getSharedInventory(), output.copy(), false);
if (!remainder.isEmpty())
DEFAULT_INSTANCE.dispenseStack(output, context, pos, facing);
return consumedFrom;

View file

@ -123,7 +123,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
ItemStack firstRequired = requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0).item;
if (!context.contraption.hasUniversalCreativeCrate) {
IItemHandler iItemHandler = context.contraption.inventory;
IItemHandler iItemHandler = context.contraption.getSharedInventory();
for (ItemRequirement.StackRequirement required : requiredItems) {
int amountFound = ItemHelper
.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO,
@ -202,7 +202,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
ItemStack filter = getFilter(context);
if (AllItems.SCHEMATIC.isIn(filter))
return;
ItemStack held = ItemHelper.extract(context.contraption.inventory,
ItemStack held = ItemHelper.extract(context.contraption.getSharedInventory(),
stack -> FilterItem.test(context.world, stack, filter), 1, false);
player.setItemInHand(InteractionHand.MAIN_HAND, held);
}

View file

@ -21,12 +21,14 @@ import com.simibubi.create.AllItems;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceMovement;
import com.simibubi.create.content.contraptions.components.actors.SeatBlock;
import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import com.simibubi.create.foundation.collision.Matrix3d;
import com.simibubi.create.foundation.mixin.accessor.ServerLevelAccessor;
import com.simibubi.create.foundation.networking.AllPackets;
@ -294,10 +296,11 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
if (!initialized)
contraptionInitialize();
contraption.onEntityTick(level);
contraption.storage.entityTick(this);
tickContraption();
super.tick();
if (!(level instanceof ServerLevelAccessor sl))
return;
@ -353,6 +356,9 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
StructureBlockInfo blockInfo = pair.left;
MovementBehaviour actor = AllMovementBehaviours.of(blockInfo.state);
if (actor == null)
continue;
Vec3 oldMotion = context.motion;
Vec3 actorPosition = toGlobalVector(VecHelper.getCenterOf(blockInfo.pos)
.add(actor.getActiveAreaOffset(context)), 1);
@ -408,6 +414,16 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
contraption.stalled = isStalled();
}
public void refreshPSIs() {
for (MutablePair<StructureBlockInfo, MovementContext> pair : contraption.getActors()) {
MovementContext context = pair.right;
StructureBlockInfo blockInfo = pair.left;
MovementBehaviour actor = AllMovementBehaviours.of(blockInfo.state);
if (actor instanceof PortableStorageInterfaceMovement && isActorActive(context, actor))
actor.visitNewPosition(context, new BlockPos(context.position));
}
}
protected boolean isActorActive(MovementContext context, MovementBehaviour actor) {
return actor.isActive(context);
}
@ -428,7 +444,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
relativeMotion = reverseRotation(relativeMotion, 1);
context.relativeMotion = relativeMotion;
return !new BlockPos(previousPosition).equals(gridPosition)
|| context.relativeMotion.length() > 0 && context.firstMovement;
|| (context.relativeMotion.length() > 0 || context.contraption instanceof CarriageContraption)
&& context.firstMovement;
}
public void move(double x, double y, double z) {

View file

@ -4,7 +4,6 @@ import static com.simibubi.create.content.contraptions.components.structureMovem
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@ -19,7 +18,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@ -63,7 +61,6 @@ import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock
import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import com.simibubi.create.foundation.tileEntity.IMultiTileContainer;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.BlockFace;
@ -85,7 +82,6 @@ import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
@ -114,10 +110,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.templates.FluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import net.minecraftforge.registries.GameData;
@ -126,22 +119,20 @@ public abstract class Contraption {
public Optional<List<AABB>> simplifiedEntityColliders;
public AbstractContraptionEntity entity;
public ContraptionInvWrapper inventory;
public CombinedTankWrapper fluidInventory;
public AABB bounds;
public BlockPos anchor;
public boolean stalled;
public boolean hasUniversalCreativeCrate;
protected Map<BlockPos, StructureBlockInfo> blocks;
protected Map<BlockPos, MountedStorage> storage;
protected Map<BlockPos, MountedFluidStorage> fluidStorage;
protected List<MutablePair<StructureBlockInfo, MovementContext>> actors;
protected Map<BlockPos, MovingInteractionBehaviour> interactors;
protected List<AABB> superglue;
protected List<BlockPos> seats;
protected Map<UUID, Integer> seatMapping;
protected Map<UUID, BlockFace> stabilizedSubContraptions;
protected MountedStorageManager storage;
private Set<SuperGlueEntity> glueToRemove;
private Map<BlockPos, Entity> initialPassengers;
@ -158,13 +149,11 @@ public abstract class Contraption {
public Contraption() {
blocks = new HashMap<>();
storage = new HashMap<>();
seats = new ArrayList<>();
actors = new ArrayList<>();
interactors = new HashMap<>();
superglue = new ArrayList<>();
seatMapping = new HashMap<>();
fluidStorage = new HashMap<>();
glueToRemove = new HashSet<>();
initialPassengers = new HashMap<>();
presentTileEntities = new HashMap<>();
@ -173,6 +162,7 @@ public abstract class Contraption {
pendingSubContraptions = new ArrayList<>();
stabilizedSubContraptions = new HashMap<>();
simplifiedEntityColliders = Optional.empty();
storage = new MountedStorageManager();
}
public ContraptionWorld getContraptionWorld() {
@ -255,20 +245,7 @@ public abstract class Contraption {
stabilizedSubContraptions.put(movedContraption.getUUID(), new BlockFace(toLocalPos(pos), face));
}
// Gather itemhandlers of mounted storage
List<IItemHandlerModifiable> list = storage.values()
.stream()
.map(MountedStorage::getItemHandler)
.collect(Collectors.toList());
inventory =
new ContraptionInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class));
List<IFluidHandler> fluidHandlers = fluidStorage.values()
.stream()
.map(MountedFluidStorage::getFluidHandler)
.collect(Collectors.toList());
fluidInventory = new CombinedTankWrapper(
Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class));
storage.createHandlers();
gatherBBsOffThread();
}
@ -300,10 +277,6 @@ public abstract class Contraption {
}
}
public void onEntityTick(Level world) {
fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, world.isClientSide));
}
/** move the first block in frontier queue */
protected boolean moveBlock(Level world, @Nullable Direction forcedDirection, Queue<BlockPos> frontier,
Set<BlockPos> visited) throws AssemblyException {
@ -642,10 +615,7 @@ public abstract class Contraption {
bounds = bounds.minmax(new AABB(localPos));
BlockEntity te = pair.getValue();
if (te != null && MountedStorage.canUseAsStorage(te))
storage.put(localPos, new MountedStorage(te));
if (te != null && MountedFluidStorage.canUseAsStorage(te))
fluidStorage.put(localPos, new MountedFluidStorage(te));
storage.addBlock(localPos, te);
if (AllMovementBehaviours.contains(captured.state.getBlock()))
actors.add(MutablePair.of(StructureBlockInfo, null));
if (AllInteractionBehaviours.contains(captured.state.getBlock()))
@ -722,14 +692,6 @@ public abstract class Contraption {
NBTHelper.iterateCompoundList(nbt.getList("SubContraptions", Tag.TAG_COMPOUND),
c -> stabilizedSubContraptions.put(c.getUUID("Id"), BlockFace.fromNBT(c.getCompound("Location"))));
storage.clear();
NBTHelper.iterateCompoundList(nbt.getList("Storage", Tag.TAG_COMPOUND), c -> storage
.put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedStorage.deserialize(c.getCompound("Data"))));
fluidStorage.clear();
NBTHelper.iterateCompoundList(nbt.getList("FluidStorage", Tag.TAG_COMPOUND), c -> fluidStorage
.put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedFluidStorage.deserialize(c.getCompound("Data"))));
interactors.clear();
NBTHelper.iterateCompoundList(nbt.getList("Interactors", Tag.TAG_COMPOUND), c -> {
BlockPos pos = NbtUtils.readBlockPos(c.getCompound("Pos"));
@ -738,32 +700,7 @@ public abstract class Contraption {
interactors.put(pos, behaviour);
});
if (spawnData)
fluidStorage.forEach((pos, mfs) -> {
BlockEntity tileEntity = presentTileEntities.get(pos);
if (!(tileEntity instanceof FluidTankTileEntity))
return;
FluidTankTileEntity tank = (FluidTankTileEntity) tileEntity;
IFluidTank tankInventory = tank.getTankInventory();
if (tankInventory instanceof FluidTank)
((FluidTank) tankInventory).setFluid(mfs.tank.getFluid());
tank.getFluidLevel()
.startWithValue(tank.getFillState());
mfs.assignTileEntity(tank);
});
IItemHandlerModifiable[] handlers = new IItemHandlerModifiable[storage.size()];
int index = 0;
for (MountedStorage mountedStorage : storage.values())
handlers[index++] = mountedStorage.getItemHandler();
IFluidHandler[] fluidHandlers = new IFluidHandler[fluidStorage.size()];
index = 0;
for (MountedFluidStorage mountedStorage : fluidStorage.values())
fluidHandlers[index++] = mountedStorage.getFluidHandler();
inventory = new ContraptionInvWrapper(handlers);
fluidInventory = new CombinedTankWrapper(fluidHandlers);
storage.read(nbt, presentTileEntities, spawnData);
if (nbt.contains("BoundsFront"))
bounds = NBTHelper.readAABB(nbt.getList("BoundsFront", 5));
@ -790,35 +727,15 @@ public abstract class Contraption {
}
ListTag superglueNBT = new ListTag();
ListTag storageNBT = new ListTag();
if (!spawnPacket) {
for (AABB glueEntry : superglue) {
CompoundTag c = new CompoundTag();
SuperGlueEntity.writeBoundingBox(c, glueEntry);
superglueNBT.add(c);
}
for (BlockPos pos : storage.keySet()) {
CompoundTag c = new CompoundTag();
MountedStorage mountedStorage = storage.get(pos);
if (!mountedStorage.isValid())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
storageNBT.add(c);
}
}
ListTag fluidStorageNBT = new ListTag();
for (BlockPos pos : fluidStorage.keySet()) {
CompoundTag c = new CompoundTag();
MountedFluidStorage mountedStorage = fluidStorage.get(pos);
if (!mountedStorage.isValid())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
fluidStorageNBT.add(c);
}
storage.write(nbt, spawnPacket);
ListTag interactorNBT = new ListTag();
for (BlockPos pos : interactors.keySet()) {
@ -847,8 +764,6 @@ public abstract class Contraption {
nbt.put("Actors", actorsNBT);
nbt.put("Interactors", interactorNBT);
nbt.put("Superglue", superglueNBT);
nbt.put("Storage", storageNBT);
nbt.put("FluidStorage", fluidStorageNBT);
nbt.put("Anchor", NbtUtils.writeBlockPos(anchor));
nbt.putBoolean("Stalled", stalled);
nbt.putBoolean("BottomlessSupply", hasUniversalCreativeCrate);
@ -962,10 +877,7 @@ public abstract class Contraption {
}
public void removeBlocksFromWorld(Level world, BlockPos offset) {
storage.values()
.forEach(MountedStorage::removeStorageFromWorld);
fluidStorage.values()
.forEach(MountedFluidStorage::removeStorageFromWorld);
storage.removeStorageFromWorld();
glueToRemove.forEach(glue -> {
superglue.add(glue.getBoundingBox()
@ -1125,23 +1037,13 @@ public abstract class Contraption {
tag.put("LastKnownPos", NbtUtils.writeBlockPos(BlockPos.ZERO.below(Integer.MAX_VALUE - 1)));
tileEntity.load(tag);
if (storage.containsKey(block.pos)) {
MountedStorage mountedStorage = storage.get(block.pos);
if (mountedStorage.isValid())
mountedStorage.addStorageToWorld(tileEntity);
}
if (fluidStorage.containsKey(block.pos)) {
MountedFluidStorage mountedStorage = fluidStorage.get(block.pos);
if (mountedStorage.isValid())
mountedStorage.addStorageToWorld(tileEntity);
}
storage.addStorageToWorld(block, tileEntity);
}
transform.apply(tileEntity);
}
}
for (StructureBlockInfo block : blocks.values()) {
if (!shouldUpdateAfterMovement(block))
continue;
@ -1149,20 +1051,15 @@ public abstract class Contraption {
world.markAndNotifyBlock(targetPos, world.getChunkAt(targetPos), block.state, block.state,
Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_ALL, 512);
}
for (int i = 0; i < inventory.getSlots(); i++) {
if (!inventory.isSlotExternal(i))
inventory.setStackInSlot(i, ItemStack.EMPTY);
}
for (int i = 0; i < fluidInventory.getTanks(); i++)
fluidInventory.drain(fluidInventory.getFluidInTank(i), FluidAction.EXECUTE);
for (AABB box : superglue) {
box = new AABB(transform.apply(new Vec3(box.minX, box.minY, box.minZ)),
transform.apply(new Vec3(box.maxX, box.maxY, box.maxZ)));
if (!world.isClientSide)
world.addFreshEntity(new SuperGlueEntity(world, box));
}
storage.clear();
}
public void addPassengersToWorld(Level world, StructureTransform transform, List<Entity> seatedEntities) {
@ -1240,8 +1137,6 @@ public abstract class Contraption {
bounds = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
}
public void addExtraInventories(Entity entity) {}
public Map<UUID, Integer> getSeatMapping() {
return seatMapping;
}
@ -1282,12 +1177,6 @@ public abstract class Contraption {
return interactors;
}
public void updateContainedFluid(BlockPos localPos, FluidStack containedFluid) {
MountedFluidStorage mountedFluidStorage = fluidStorage.get(localPos);
if (mountedFluidStorage != null)
mountedFluidStorage.updateFluid(containedFluid);
}
@OnlyIn(Dist.CLIENT)
public ContraptionLighter<?> makeLighter() {
// TODO: move lighters to registry
@ -1349,39 +1238,22 @@ public abstract class Contraption {
return maxDistSq;
}
// TODO: unused?
// private static class ContraptionTileWorld extends WrappedWorld implements IFlywheelWorld {
//
// private final BlockEntity te;
// private final StructureBlockInfo info;
//
// public ContraptionTileWorld(Level world, BlockEntity te, StructureBlockInfo info) {
// super(world);
// this.te = te;
// this.info = info;
// }
//
// @Override
// public BlockState getBlockState(BlockPos pos) {
// if (!pos.equals(te.getBlockPos()))
// return Blocks.AIR.defaultBlockState();
// return info.state;
// }
//
// @Override
// public boolean isLoaded(BlockPos pos) {
// return pos.equals(te.getBlockPos());
// }
// }
public IItemHandlerModifiable getSharedInventory() {
return storage.getItems();
}
public IFluidHandler getSharedFluidTanks() {
return storage.getFluids();
}
public Collection<StructureBlockInfo> getRenderedBlocks() {
return blocks.values();
}
public Collection<BlockEntity> getSpecialRenderedTEs() {
return specialRenderedTileEntities;
}
public boolean isHiddenInPortal(BlockPos localPos) {
return false;
}
@ -1389,6 +1261,10 @@ public abstract class Contraption {
public Optional<List<AABB>> getSimplifiedEntityColliders() {
return simplifiedEntityColliders;
}
public void handleContraptionFluidPacket(BlockPos localPos, FluidStack containedFluid) {
storage.updateContainedFluid(localPos, containedFluid);
}
public static class ContraptionInvWrapper extends CombinedInvWrapper {
protected final boolean isExternal;

View file

@ -0,0 +1,180 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption.ContraptionInvWrapper;
import com.simibubi.create.content.contraptions.fluids.tank.FluidTankTileEntity;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.templates.FluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;
public class MountedStorageManager {
private ContraptionInvWrapper inventory;
private CombinedTankWrapper fluidInventory;
private Map<BlockPos, MountedStorage> storage;
private Map<BlockPos, MountedFluidStorage> fluidStorage;
public MountedStorageManager() {
storage = new HashMap<>();
fluidStorage = new HashMap<>();
}
public void entityTick(AbstractContraptionEntity entity) {
fluidStorage.forEach((pos, mfs) -> mfs.tick(entity, pos, entity.level.isClientSide));
}
public void createHandlers() {
List<IItemHandlerModifiable> list = storage.values()
.stream()
.map(MountedStorage::getItemHandler)
.collect(Collectors.toList());
inventory =
new ContraptionInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class));
List<IFluidHandler> fluidHandlers = fluidStorage.values()
.stream()
.map(MountedFluidStorage::getFluidHandler)
.collect(Collectors.toList());
fluidInventory = new CombinedTankWrapper(
Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class));
}
public void addBlock(BlockPos localPos, BlockEntity te) {
if (te != null && MountedStorage.canUseAsStorage(te))
storage.put(localPos, new MountedStorage(te));
if (te != null && MountedFluidStorage.canUseAsStorage(te))
fluidStorage.put(localPos, new MountedFluidStorage(te));
}
public void read(CompoundTag nbt, Map<BlockPos, BlockEntity> presentTileEntities, boolean clientPacket) {
storage.clear();
NBTHelper.iterateCompoundList(nbt.getList("Storage", Tag.TAG_COMPOUND), c -> storage
.put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedStorage.deserialize(c.getCompound("Data"))));
fluidStorage.clear();
NBTHelper.iterateCompoundList(nbt.getList("FluidStorage", Tag.TAG_COMPOUND), c -> fluidStorage
.put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedFluidStorage.deserialize(c.getCompound("Data"))));
if (clientPacket && presentTileEntities != null)
fluidStorage.forEach((pos, mfs) -> {
BlockEntity tileEntity = presentTileEntities.get(pos);
if (!(tileEntity instanceof FluidTankTileEntity))
return;
FluidTankTileEntity tank = (FluidTankTileEntity) tileEntity;
IFluidTank tankInventory = tank.getTankInventory();
if (tankInventory instanceof FluidTank)
((FluidTank) tankInventory).setFluid(mfs.tank.getFluid());
tank.getFluidLevel()
.startWithValue(tank.getFillState());
mfs.assignTileEntity(tank);
});
IItemHandlerModifiable[] handlers = new IItemHandlerModifiable[storage.size()];
int index = 0;
for (MountedStorage mountedStorage : storage.values())
handlers[index++] = mountedStorage.getItemHandler();
IFluidHandler[] fluidHandlers = new IFluidHandler[fluidStorage.size()];
index = 0;
for (MountedFluidStorage mountedStorage : fluidStorage.values())
fluidHandlers[index++] = mountedStorage.getFluidHandler();
inventory = new ContraptionInvWrapper(handlers);
fluidInventory = new CombinedTankWrapper(fluidHandlers);
}
public void write(CompoundTag nbt, boolean clientPacket) {
ListTag storageNBT = new ListTag();
if (!clientPacket)
for (BlockPos pos : storage.keySet()) {
CompoundTag c = new CompoundTag();
MountedStorage mountedStorage = storage.get(pos);
if (!mountedStorage.isValid())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
storageNBT.add(c);
}
ListTag fluidStorageNBT = new ListTag();
for (BlockPos pos : fluidStorage.keySet()) {
CompoundTag c = new CompoundTag();
MountedFluidStorage mountedStorage = fluidStorage.get(pos);
if (!mountedStorage.isValid())
continue;
c.put("Pos", NbtUtils.writeBlockPos(pos));
c.put("Data", mountedStorage.serialize());
fluidStorageNBT.add(c);
}
nbt.put("Storage", storageNBT);
nbt.put("FluidStorage", fluidStorageNBT);
}
public void removeStorageFromWorld() {
storage.values()
.forEach(MountedStorage::removeStorageFromWorld);
fluidStorage.values()
.forEach(MountedFluidStorage::removeStorageFromWorld);
}
public void addStorageToWorld(StructureBlockInfo block, BlockEntity tileEntity) {
if (storage.containsKey(block.pos)) {
MountedStorage mountedStorage = storage.get(block.pos);
if (mountedStorage.isValid())
mountedStorage.addStorageToWorld(tileEntity);
}
if (fluidStorage.containsKey(block.pos)) {
MountedFluidStorage mountedStorage = fluidStorage.get(block.pos);
if (mountedStorage.isValid())
mountedStorage.addStorageToWorld(tileEntity);
}
}
public void clear() {
for (int i = 0; i < inventory.getSlots(); i++)
if (!inventory.isSlotExternal(i))
inventory.setStackInSlot(i, ItemStack.EMPTY);
for (int i = 0; i < fluidInventory.getTanks(); i++)
fluidInventory.drain(fluidInventory.getFluidInTank(i), FluidAction.EXECUTE);
}
public void updateContainedFluid(BlockPos localPos, FluidStack containedFluid) {
MountedFluidStorage mountedFluidStorage = fluidStorage.get(localPos);
if (mountedFluidStorage != null)
mountedFluidStorage.updateFluid(containedFluid);
}
public void attachExternal(IItemHandlerModifiable externalStorage) {
inventory = new ContraptionInvWrapper(externalStorage, inventory);
}
public IItemHandlerModifiable getItems() {
return inventory;
}
public IFluidHandler getFluids() {
return fluidInventory;
}
}

View file

@ -23,14 +23,11 @@ public interface MovementBehaviour {
return true;
}
default void tick(MovementContext context) {
}
default void tick(MovementContext context) {}
default void startMoving(MovementContext context) {
}
default void startMoving(MovementContext context) {}
default void visitNewPosition(MovementContext context, BlockPos pos) {
}
default void visitNewPosition(MovementContext context, BlockPos pos) {}
default Vec3 getActiveAreaOffset(MovementContext context) {
return Vec3.ZERO;
@ -39,7 +36,7 @@ public interface MovementBehaviour {
default void dropItem(MovementContext context, ItemStack stack) {
ItemStack remainder;
if (AllConfigs.SERVER.kinetics.moveItemsToStorage.get())
remainder = ItemHandlerHelper.insertItem(context.contraption.inventory, stack, false);
remainder = ItemHandlerHelper.insertItem(context.contraption.getSharedInventory(), stack, false);
else
remainder = stack;
if (remainder.isEmpty())
@ -52,14 +49,11 @@ public interface MovementBehaviour {
context.world.addFreshEntity(itemEntity);
}
default void onSpeedChanged(MovementContext context, Vec3 oldMotion, Vec3 motion) {
}
default void onSpeedChanged(MovementContext context, Vec3 oldMotion, Vec3 motion) {}
default void stopMoving(MovementContext context) {
}
default void stopMoving(MovementContext context) {}
default void writeExtraData(MovementContext context) {
}
default void writeExtraData(MovementContext context) {}
default boolean renderAsNormalTileEntity() {
return false;
@ -71,12 +65,12 @@ public interface MovementBehaviour {
@OnlyIn(Dist.CLIENT)
default void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffer) {
}
ContraptionMatrices matrices, MultiBufferSource buffer) {}
@OnlyIn(Dist.CLIENT)
@Nullable
default ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld, MovementContext context) {
default ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld,
MovementContext context) {
return null;
}
}

View file

@ -428,7 +428,7 @@ public class OrientedContraptionEntity extends AbstractContraptionEntity {
.normalize()
.scale(1));
if (fuel < 5 && contraption != null) {
ItemStack coal = ItemHelper.extract(contraption.inventory, FUEL_ITEMS, 1, false);
ItemStack coal = ItemHelper.extract(contraption.getSharedInventory(), FUEL_ITEMS, 1, false);
if (!coal.isEmpty())
fuel += 3600;
}
@ -457,15 +457,17 @@ public class OrientedContraptionEntity extends AbstractContraptionEntity {
}
protected void attachInventoriesFromRidingCarts(Entity riding, boolean isOnCoupling, UUID couplingId) {
if (isOnCoupling) {
Couple<MinecartController> coupledCarts = getCoupledCartsIfPresent();
if (coupledCarts == null)
return;
coupledCarts.map(MinecartController::cart)
.forEach(contraption::addExtraInventories);
if (!(contraption instanceof MountedContraption mc))
return;
if (!isOnCoupling) {
mc.addExtraInventories(riding);
return;
}
contraption.addExtraInventories(riding);
Couple<MinecartController> coupledCarts = getCoupledCartsIfPresent();
if (coupledCarts == null)
return;
coupledCarts.map(MinecartController::cart)
.forEach(mc::addExtraInventories);
}
@Override

View file

@ -34,7 +34,6 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.InvWrapper;
public class MountedContraption extends Contraption {
@ -95,8 +94,8 @@ public class MountedContraption extends Contraption {
for (Axis axis : Iterate.axes) {
if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis))
continue;
for (AbstractMinecart abstractMinecartEntity : world
.getEntitiesOfClass(AbstractMinecart.class, new AABB(pos))) {
for (AbstractMinecart abstractMinecartEntity : world.getEntitiesOfClass(AbstractMinecart.class,
new AABB(pos))) {
if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity))
break;
connectedCart = abstractMinecartEntity;
@ -118,8 +117,8 @@ public class MountedContraption extends Contraption {
for (Axis axis : Iterate.axes) {
if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis))
continue;
for (AbstractMinecart abstractMinecartEntity : world
.getEntitiesOfClass(AbstractMinecart.class, new AABB(pos))) {
for (AbstractMinecart abstractMinecartEntity : world.getEntitiesOfClass(AbstractMinecart.class,
new AABB(pos))) {
if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity))
break;
return true;
@ -156,14 +155,11 @@ public class MountedContraption extends Contraption {
return true;
}
@Override
public void addExtraInventories(Entity cart) {
if (!(cart instanceof Container))
return;
IItemHandlerModifiable handlerFromInv = new ContraptionInvWrapper(true, new InvWrapper((Container) cart));
inventory = new ContraptionInvWrapper(handlerFromInv, inventory);
if (cart instanceof Container container)
storage.attachExternal(new ContraptionInvWrapper(true, new InvWrapper(container)));
}
@Override
@OnlyIn(Dist.CLIENT)
public ContraptionLighter<?> makeLighter() {

View file

@ -23,13 +23,13 @@ public class ContraptionFluidPacket extends SimplePacketBase {
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);
@ -45,7 +45,7 @@ public class ContraptionFluidPacket extends SimplePacketBase {
if (!(entityByID instanceof AbstractContraptionEntity))
return;
AbstractContraptionEntity contraptionEntity = (AbstractContraptionEntity) entityByID;
contraptionEntity.getContraption().updateContainedFluid(localPos, containedFluid);
contraptionEntity.getContraption().handleContraptionFluidPacket(localPos, containedFluid);
});
context.get()
.setPacketHandled(true);

View file

@ -54,7 +54,6 @@ public class FunnelMovementBehaviour implements MovementBehaviour {
else
succ(context, pos);
}
private void extract(MovementContext context, BlockPos pos) {
@ -64,23 +63,22 @@ public class FunnelMovementBehaviour implements MovementBehaviour {
if (context.state.getValue(FunnelBlock.FACING) != Direction.DOWN)
entityPos = entityPos.add(0, -.5f, 0);
if (!world.getBlockState(pos).getCollisionShape(world, pos).isEmpty())
return;//only drop items if the target block is a empty space
if (!world.getBlockState(pos)
.getCollisionShape(world, pos)
.isEmpty())
return;
if (!world.getEntitiesOfClass(ItemEntity.class, new AABB(new BlockPos(entityPos))).isEmpty())
return;//don't drop items if there already are any in the target block space
if (!world.getEntitiesOfClass(ItemEntity.class, new AABB(new BlockPos(entityPos)))
.isEmpty())
return;
ItemStack filter = getFilter(context);
int filterAmount = context.tileData.getInt("FilterAmount");
if (filterAmount <= 0)
filterAmount = hasFilter ? AllConfigs.SERVER.logistics.defaultExtractionLimit.get() : 1;
ItemStack extract = ItemHelper.extract(
context.contraption.inventory,
s -> FilterItem.test(world, s, filter),
ItemHelper.ExtractionCountMode.UPTO,
filterAmount,
false);
ItemStack extract = ItemHelper.extract(context.contraption.getSharedInventory(),
s -> FilterItem.test(world, s, filter), ItemHelper.ExtractionCountMode.UPTO, filterAmount, false);
if (extract.isEmpty())
return;
@ -88,12 +86,10 @@ public class FunnelMovementBehaviour implements MovementBehaviour {
if (world.isClientSide)
return;
ItemEntity entity = new ItemEntity(world, entityPos.x, entityPos.y, entityPos.z, extract);
entity.setDeltaMovement(Vec3.ZERO);
entity.setPickUpDelay(5);
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 1/16f, .1f);
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 1 / 16f, .1f);
world.addFreshEntity(entity);
}
@ -108,7 +104,8 @@ public class FunnelMovementBehaviour implements MovementBehaviour {
ItemStack toInsert = item.getItem();
if (!filter.isEmpty() && !FilterItem.test(context.world, toInsert, filter))
continue;
ItemStack remainder = ItemHandlerHelper.insertItemStacked(context.contraption.inventory, toInsert, false);
ItemStack remainder =
ItemHandlerHelper.insertItemStacked(context.contraption.getSharedInventory(), toInsert, false);
if (remainder.getCount() == toInsert.getCount())
continue;
if (remainder.isEmpty()) {
@ -120,7 +117,7 @@ public class FunnelMovementBehaviour implements MovementBehaviour {
item.setItem(remainder);
}
}
@Override
public boolean renderAsNormalTileEntity() {
return true;

View file

@ -55,9 +55,13 @@ public class TrackNodeLocation extends Vec3i {
@Override
public boolean equals(Object pOther) {
return super.equals(pOther) && pOther instanceof TrackNodeLocation tnl
return equalsIgnoreDim(pOther) && pOther instanceof TrackNodeLocation tnl
&& Objects.equals(tnl.dimension, dimension);
}
public boolean equalsIgnoreDim(Object pOther) {
return super.equals(pOther);
}
@Override
public int hashCode() {

View file

@ -20,6 +20,7 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.MountedStorageManager;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -60,6 +61,7 @@ public class Carriage {
public int bogeySpacing;
public Couple<CarriageBogey> bogeys;
public MountedStorageManager storage;
CompoundTag serialisedEntity;
Map<Integer, CompoundTag> serialisedPassengers;
@ -76,6 +78,7 @@ public class Carriage {
this.presentConductors = Couple.create(false, false);
this.serialisedPassengers = new HashMap<>();
this.entities = new HashMap<>();
this.storage = new MountedStorageManager();
bogey1.setLeading();
bogey1.carriage = this;
@ -92,6 +95,7 @@ public class Carriage {
}
public void setContraption(Level level, CarriageContraption contraption) {
this.storage = null;
CarriageContraptionEntity entity = CarriageContraptionEntity.create(level, contraption);
entity.setCarriage(this);
contraption.startMoving(level);
@ -177,7 +181,7 @@ public class Carriage {
double moved = point.travel(graph, toMove, trackSelector, signalListener, point.ignoreTurns(), c -> {
for (DimensionalCarriageEntity dce : entities.values())
if (c.either(tnl -> tnl.equals(dce.pivot)))
if (c.either(tnl -> tnl.equalsIgnoreDim(dce.pivot)))
return false;
if (entities.size() > 1) {
train.status.doublePortal();
@ -220,7 +224,7 @@ public class Carriage {
}
public void updateConductors() {
if (anyAvailableEntity() == null || entities.size() > 1)
if (anyAvailableEntity() == null || entities.size() > 1 || serialisedPassengers.size() > 0)
return;
presentConductors.replace($ -> false);
for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) {
@ -321,15 +325,6 @@ public class Carriage {
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2);
int prevmin = dce.minAllowedLocalCoord();
int prevmax = dce.maxAllowedLocalCoord();
dce.updateCutoff(leading);
if (prevmin != dce.minAllowedLocalCoord() || prevmax != dce.maxAllowedLocalCoord()) {
dce.updateRenderedCutoff();
dce.updatePassengerLoadout();
}
if (isOnTwoBogeys()) {
dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point,
@ -337,18 +332,27 @@ public class Carriage {
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition()
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2));
continue;
}
if (dimension.equals(otherDimension)) {
dce.rotationAnchors = leadingBogey.points.map(TravellingPoint::getPosition);
continue;
} else {
if (dimension.equals(otherDimension)) {
dce.rotationAnchors = leadingBogey.points.map(TravellingPoint::getPosition);
} else {
dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition()
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition()
: pivoted(dce, dimension, point, leadingWheelSpacing));
}
}
int prevmin = dce.minAllowedLocalCoord();
int prevmax = dce.maxAllowedLocalCoord();
dce.updateCutoff(leading);
dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition()
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition()
: pivoted(dce, dimension, point, leadingWheelSpacing));
if (prevmin != dce.minAllowedLocalCoord() || prevmax != dce.maxAllowedLocalCoord()) {
dce.updateRenderedCutoff();
dce.updatePassengerLoadout();
}
}
}
@ -595,6 +599,7 @@ public class Carriage {
CompoundTag tag = new CompoundTag();
tag.putFloat("Cutoff", cutoff);
tag.putInt("DiscardTicks", discardTicks);
storage.write(tag, false);
if (pivot != null)
tag.put("Pivot", pivot.write(null));
if (positionAnchor != null)
@ -607,6 +612,7 @@ public class Carriage {
public void read(CompoundTag tag) {
cutoff = tag.getFloat("Cutoff");
discardTicks = tag.getInt("DiscardTicks");
storage.read(tag, null, false);
if (tag.contains("Pivot"))
pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null);
if (positionAnchor != null)
@ -749,13 +755,13 @@ public class Carriage {
Entity entity = this.entity.get();
if (!(entity instanceof CarriageContraptionEntity cce))
return;
if (!entity.level.isClientSide())
return;
Contraption contraption = cce.getContraption();
if (!(contraption instanceof CarriageContraption cc))
return;
cc.portalCutoffMin = minAllowedLocalCoord();
cc.portalCutoffMax = maxAllowedLocalCoord();
if (!entity.level.isClientSide())
return;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(cce));
}
@ -776,12 +782,12 @@ public class Carriage {
return;
}
this.entity = new WeakReference<>(cce);
cce.setGraph(train.graph == null ? null : train.graph.id);
cce.setCarriage(Carriage.this);
cce.syncCarriage();
this.entity = new WeakReference<>(cce);
if (level instanceof ServerLevel sl)
sl.tryAddFreshEntityWithPassengers(entity);

View file

@ -14,6 +14,7 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionType;
import com.simibubi.create.content.contraptions.components.structureMovement.MountedStorageManager;
import com.simibubi.create.content.contraptions.components.structureMovement.NonStationaryLighter;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionLighter;
@ -41,16 +42,21 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
public class CarriageContraption extends Contraption {
private Direction assemblyDirection;
private boolean forwardControls;
private boolean backwardControls;
public Couple<Boolean> blazeBurnerConductors;
public Map<BlockPos, Couple<Boolean>> conductorSeats;
public ArrivalSoundQueue soundQueue;
protected MountedStorageManager storageProxy;
// during assembly only
private int bogeys;
private boolean sidewaysControls;
@ -116,6 +122,21 @@ public class CarriageContraption extends Contraption {
return info.state.getValue(ControlsBlock.FACING) == direction.getOpposite();
}
public void swapStorageAfterAssembly(CarriageContraptionEntity cce) {
// Ensure that the entity does not hold its inventory data, because the global
// carriage manages it instead
Carriage carriage = cce.getCarriage();
if (carriage.storage == null) {
carriage.storage = storage;
storage = new MountedStorageManager();
}
storageProxy = carriage.storage;
}
public void returnStorageForDisassembly(MountedStorageManager storage) {
this.storage = storage;
}
@Override
protected boolean isAnchoringBlockAt(BlockPos pos) {
return false;
@ -272,7 +293,7 @@ public class CarriageContraption extends Contraption {
return !withinVisible(localPos) || atSeam(localPos);
}
private boolean notInPortal() {
public boolean notInPortal() {
return portalCutoffMin == Integer.MIN_VALUE && portalCutoffMax == Integer.MAX_VALUE;
}
@ -294,4 +315,15 @@ public class CarriageContraption extends Contraption {
return coord > portalCutoffMin && coord < portalCutoffMax;
}
@Override
public IItemHandlerModifiable getSharedInventory() {
return storageProxy.getItems();
}
@Override
public IFluidHandler getSharedFluidTanks() {
return storageProxy.getFluids();
}
}

View file

@ -186,6 +186,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
dimensional.entity = new WeakReference<>(this);
dimensional.pivot = null;
carriage.updateContraptionAnchors();
dimensional.updateRenderedCutoff();
}
updateTrackGraph();
} else
@ -316,7 +317,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
@Override
protected boolean isActorActive(MovementContext context, MovementBehaviour actor) {
return !getContraption().isHiddenInPortal(context.localPos) && super.isActorActive(context, actor);
return (contraption instanceof CarriageContraption cc) && (cc.notInPortal() || level.isClientSide())
&& super.isActorActive(context, actor);
}
@Override
@ -605,6 +607,13 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
this.carriage = carriage;
this.trainId = carriage.train.id;
this.carriageIndex = carriage.train.carriages.indexOf(carriage);
if (contraption instanceof CarriageContraption cc)
cc.swapStorageAfterAssembly(this);
DimensionalCarriageEntity dimensional = carriage.getDimensional(level);
dimensional.pivot = null;
carriage.updateContraptionAnchors();
dimensional.updateRenderedCutoff();
}
public void setGraph(@Nullable UUID graphId) {

View file

@ -541,6 +541,8 @@ public class Train {
if (entity == null)
return false;
if (entity.getContraption() instanceof CarriageContraption cc)
cc.returnStorageForDisassembly(carriage.storage);
entity.setPos(Vec3
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset)));
entity.disassemble();