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

View file

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

View file

@ -61,24 +61,26 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
@Override @Override
public void visitNewPosition(MovementContext context, BlockPos pos) { 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)) if (!findInterface(context, pos))
context.data.remove(_workingPos_); context.data.remove(_workingPos_);
// if (findInterface(context, pos))
// context.stall = true;
} }
@Override @Override
public void tick(MovementContext context) { public void tick(MovementContext context) {
if (context.world.isClientSide) { if (context.world.isClientSide)
getAnimation(context).tickChaser(); 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); BlockPos pos = new BlockPos(context.position);
findInterface(context, pos); if (!findInterface(context, pos))
if (!context.data.contains(_clientPrevPos_) reset(context);
|| !NbtUtils.readBlockPos(context.data.getCompound(_clientPrevPos_))
.equals(pos)) {
if (!findInterface(context, pos))
reset(context);
}
return; return;
} }
@ -88,7 +90,7 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
BlockPos pos = NbtUtils.readBlockPos(context.data.getCompound(_workingPos_)); BlockPos pos = NbtUtils.readBlockPos(context.data.getCompound(_workingPos_));
Vec3 target = VecHelper.getCenterOf(pos); Vec3 target = VecHelper.getCenterOf(pos);
if (!context.stall if (!context.stall && !onCarriage
&& context.position.closerThan(target, target.distanceTo(context.position.add(context.motion)))) && context.position.closerThan(target, target.distanceTo(context.position.add(context.motion))))
context.stall = true; context.stall = true;
@ -114,6 +116,8 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
} }
protected boolean findInterface(MovementContext context, BlockPos pos) { protected boolean findInterface(MovementContext context, BlockPos pos) {
if (context.contraption instanceof CarriageContraption cc && !cc.notInPortal())
return false;
Optional<Direction> currentFacingIfValid = getCurrentFacingIfValid(context); Optional<Direction> currentFacingIfValid = getCurrentFacingIfValid(context);
if (!currentFacingIfValid.isPresent()) if (!currentFacingIfValid.isPresent())
return false; return false;
@ -121,9 +125,10 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
Direction currentFacing = currentFacingIfValid.get(); Direction currentFacing = currentFacingIfValid.get();
PortableStorageInterfaceTileEntity psi = PortableStorageInterfaceTileEntity psi =
findStationaryInterface(context.world, pos, context.state, currentFacing); findStationaryInterface(context.world, pos, context.state, currentFacing);
if (psi == null) if (psi == null)
return false; return false;
if ((psi.isTransferring() || psi.isPowered()) && !context.world.isClientSide) if (psi.isPowered())
return false; return false;
context.data.put(_workingPos_, NbtUtils.writeBlockPos(psi.getBlockPos())); 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, private PortableStorageInterfaceTileEntity getStationaryInterfaceAt(Level world, BlockPos pos, BlockState state,
Direction facing) { Direction facing) {
BlockEntity te = world.getBlockEntity(pos); BlockEntity te = world.getBlockEntity(pos);
if (!(te instanceof PortableStorageInterfaceTileEntity)) if (!(te instanceof PortableStorageInterfaceTileEntity psi))
return null; return null;
BlockState blockState = world.getBlockState(pos); BlockState blockState = world.getBlockState(pos);
if (blockState.getBlock() != state.getBlock()) if (blockState.getBlock() != state.getBlock())
return null; return null;
if (blockState.getValue(PortableStorageInterfaceBlock.FACING) != facing.getOpposite()) if (blockState.getValue(PortableStorageInterfaceBlock.FACING) != facing.getOpposite())
return null; return null;
return (PortableStorageInterfaceTileEntity) te; if (psi.isPowered())
return null;
return psi;
} }
private Optional<Direction> getCurrentFacingIfValid(MovementContext context) { 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 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.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
@ -52,6 +53,14 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
return connectedEntity != null && isConnected(); return connectedEntity != null && isConnected();
} }
@Override
public void initialize() {
super.initialize();
powered = level.hasNeighborSignal(worldPosition);
if (!powered)
notifyContraptions();
}
protected abstract void invalidateCapability(); protected abstract void invalidateCapability();
@Override @Override
@ -109,7 +118,10 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
super.read(compound, clientPacket); super.read(compound, clientPacket);
transferTimer = compound.getInt("Timer"); transferTimer = compound.getInt("Timer");
distance = compound.getFloat("Distance"); distance = compound.getFloat("Distance");
boolean poweredPreviously = powered;
powered = compound.getBoolean("Powered"); powered = compound.getBoolean("Powered");
if (clientPacket && powered != poweredPreviously && !powered)
notifyContraptions();
} }
@Override @Override
@ -125,9 +137,18 @@ public abstract class PortableStorageInterfaceTileEntity extends SmartTileEntity
if (isBlockPowered == powered) if (isBlockPowered == powered)
return; return;
powered = isBlockPowered; powered = isBlockPowered;
if (!powered)
notifyContraptions();
if (powered)
stopTransferring();
sendData(); sendData();
} }
private void notifyContraptions() {
level.getEntitiesOfClass(AbstractContraptionEntity.class, new AABB(worldPosition).inflate(3))
.forEach(AbstractContraptionEntity::refreshPSIs);
}
public boolean isPowered() { public boolean isPowered() {
return powered; return powered;
} }

View file

@ -36,17 +36,21 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override @Override
public Vec3 getActiveAreaOffset(MovementContext context) { 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 @Override
public void visitNewPosition(MovementContext context, BlockPos pos) { public void visitNewPosition(MovementContext context, BlockPos pos) {
super.visitNewPosition(context, 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); facingVec = context.rotation.apply(facingVec);
Direction closestToFacing = Direction.getNearest(facingVec.x, facingVec.y, facingVec.z); 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.data.remove("BreakingPos");
context.stall = false; context.stall = false;
} }
@ -64,15 +68,17 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
Optional<AbstractBlockBreakQueue> dynamicTree = TreeCutter.findDynamicTree(brokenState.getBlock(), pos); Optional<AbstractBlockBreakQueue> dynamicTree = TreeCutter.findDynamicTree(brokenState.getBlock(), pos);
if (dynamicTree.isPresent()) { 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; 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) { 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()) if (remainder.isEmpty())
return; return;
@ -87,7 +93,7 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override @Override
@OnlyIn(value = Dist.CLIENT) @OnlyIn(value = Dist.CLIENT)
public void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld, public void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffer) { ContraptionMatrices matrices, MultiBufferSource buffer) {
SawRenderer.renderInContraption(context, renderWorld, matrices, 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; import net.minecraft.world.item.Items;
public class DropperMovementBehaviour implements MovementBehaviour { 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(); private static final Random RNG = new Random();
protected void activate(MovementContext context, BlockPos pos) { protected void activate(MovementContext context, BlockPos pos) {
@ -23,7 +24,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (location.isEmpty()) { if (location.isEmpty()) {
context.world.levelEvent(1001, pos, 0); context.world.levelEvent(1001, pos, 0);
} else { } 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) { private void collectItems(MovementContext context) {
getStacks(context).stream().filter(itemStack -> !itemStack.isEmpty() && itemStack.getItem() != Items.AIR && itemStack.getMaxStackSize() > itemStack.getCount()).forEach(itemStack -> itemStack.grow( getStacks(context).stream()
ItemHelper.extract(context.contraption.inventory, itemStack::sameItem, ItemHelper.ExtractionCountMode.UPTO, itemStack.getMaxStackSize() - itemStack.getCount(), false).getCount())); .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) { private void updateTemporaryData(MovementContext context) {
@ -62,7 +69,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (testStack == null || testStack.isEmpty()) if (testStack == null || testStack.isEmpty())
continue; continue;
if (testStack.getMaxStackSize() == 1) { 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()) if (!getItemStackAt(location, context).isEmpty())
useable.add(location); useable.add(location);
} else if (testStack.getCount() >= 2) } else if (testStack.getCount() >= 2)
@ -104,7 +112,8 @@ public class DropperMovementBehaviour implements MovementBehaviour {
if (location.isInternal()) { if (location.isInternal()) {
return getStacks(context).get(location.getSlot()); return getStacks(context).get(location.getSlot());
} else { } 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()) { if (location.isInternal()) {
getStacks(context).set(location.getSlot(), stack); getStacks(context).set(location.getSlot(), stack);
} else { } 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 { public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBehaviour {
private static final MovedDefaultDispenseItemBehaviour DEFAULT_INSTANCE = new MovedDefaultDispenseItemBehaviour(); 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 d0 = p_82486_4_.getX() + facing.x + .5;
double d1 = p_82486_4_.getY() + facing.y + .5; double d1 = p_82486_4_.getY() + facing.y + .5;
double d2 = p_82486_4_.getZ() + facing.z + .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; d1 = d1 - 0.125D;
} else { } else {
d1 = d1 - 0.15625D; 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_); ItemEntity itementity = new ItemEntity(p_82486_0_, d0, d1, d2, p_82486_1_);
double d3 = p_82486_0_.random.nextDouble() * 0.1D + 0.2D; 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); p_82486_0_.addFreshEntity(itementity);
} }
@Override @Override
public ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos) { 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 = context.rotation.apply(facingVec);
facingVec.normalize(); facingVec.normalize();
@ -46,7 +55,9 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
this.spawnDispenseParticles(context.world, pos, closestToFacing); this.spawnDispenseParticles(context.world, pos, closestToFacing);
return this.dispenseStack(itemStack, context, pos, facingVec); return this.dispenseStack(itemStack, context, pos, facingVec);
} else { } 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); itemStack.shrink(1);
return itemStack; 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) { protected void spawnDispenseParticles(LevelAccessor world, BlockPos pos, Vec3 facing) {
spawnDispenseParticles(world, pos, getClosestFacingDirection(facing)); spawnDispenseParticles(world, pos, getClosestFacingDirection(facing));
@ -83,9 +95,11 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
return Direction.getNearest(exactFacing.x, exactFacing.y, exactFacing.z); 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); 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()) if (!remainder.isEmpty())
DEFAULT_INSTANCE.dispenseStack(output, context, pos, facing); DEFAULT_INSTANCE.dispenseStack(output, context, pos, facing);
return consumedFrom; return consumedFrom;

View file

@ -123,7 +123,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
ItemStack firstRequired = requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0).item; ItemStack firstRequired = requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0).item;
if (!context.contraption.hasUniversalCreativeCrate) { if (!context.contraption.hasUniversalCreativeCrate) {
IItemHandler iItemHandler = context.contraption.inventory; IItemHandler iItemHandler = context.contraption.getSharedInventory();
for (ItemRequirement.StackRequirement required : requiredItems) { for (ItemRequirement.StackRequirement required : requiredItems) {
int amountFound = ItemHelper int amountFound = ItemHelper
.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO, .extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO,
@ -202,7 +202,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
ItemStack filter = getFilter(context); ItemStack filter = getFilter(context);
if (AllItems.SCHEMATIC.isIn(filter)) if (AllItems.SCHEMATIC.isIn(filter))
return; return;
ItemStack held = ItemHelper.extract(context.contraption.inventory, ItemStack held = ItemHelper.extract(context.contraption.getSharedInventory(),
stack -> FilterItem.test(context.world, stack, filter), 1, false); stack -> FilterItem.test(context.world, stack, filter), 1, false);
player.setItemInHand(InteractionHand.MAIN_HAND, held); 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.AllMovementBehaviours;
import com.simibubi.create.AllSoundEvents; import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.Create; 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.SeatBlock;
import com.simibubi.create.content.contraptions.components.actors.SeatEntity; 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.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket; 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.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket; 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.collision.Matrix3d;
import com.simibubi.create.foundation.mixin.accessor.ServerLevelAccessor; import com.simibubi.create.foundation.mixin.accessor.ServerLevelAccessor;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -294,10 +296,11 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
if (!initialized) if (!initialized)
contraptionInitialize(); contraptionInitialize();
contraption.onEntityTick(level);
contraption.storage.entityTick(this);
tickContraption(); tickContraption();
super.tick(); super.tick();
if (!(level instanceof ServerLevelAccessor sl)) if (!(level instanceof ServerLevelAccessor sl))
return; return;
@ -353,6 +356,9 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
StructureBlockInfo blockInfo = pair.left; StructureBlockInfo blockInfo = pair.left;
MovementBehaviour actor = AllMovementBehaviours.of(blockInfo.state); MovementBehaviour actor = AllMovementBehaviours.of(blockInfo.state);
if (actor == null)
continue;
Vec3 oldMotion = context.motion; Vec3 oldMotion = context.motion;
Vec3 actorPosition = toGlobalVector(VecHelper.getCenterOf(blockInfo.pos) Vec3 actorPosition = toGlobalVector(VecHelper.getCenterOf(blockInfo.pos)
.add(actor.getActiveAreaOffset(context)), 1); .add(actor.getActiveAreaOffset(context)), 1);
@ -408,6 +414,16 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
contraption.stalled = isStalled(); 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) { protected boolean isActorActive(MovementContext context, MovementBehaviour actor) {
return actor.isActive(context); return actor.isActive(context);
} }
@ -428,7 +444,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
relativeMotion = reverseRotation(relativeMotion, 1); relativeMotion = reverseRotation(relativeMotion, 1);
context.relativeMotion = relativeMotion; context.relativeMotion = relativeMotion;
return !new BlockPos(previousPosition).equals(gridPosition) 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) { 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 static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -19,7 +18,6 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import javax.annotation.Nullable; 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.block.vault.ItemVaultTileEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.config.AllConfigs; 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.IMultiTileContainer;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.BlockFace; 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.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.village.poi.PoiType; 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.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block; 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.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler; 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.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import net.minecraftforge.registries.GameData; import net.minecraftforge.registries.GameData;
@ -126,22 +119,20 @@ public abstract class Contraption {
public Optional<List<AABB>> simplifiedEntityColliders; public Optional<List<AABB>> simplifiedEntityColliders;
public AbstractContraptionEntity entity; public AbstractContraptionEntity entity;
public ContraptionInvWrapper inventory;
public CombinedTankWrapper fluidInventory;
public AABB bounds; public AABB bounds;
public BlockPos anchor; public BlockPos anchor;
public boolean stalled; public boolean stalled;
public boolean hasUniversalCreativeCrate; public boolean hasUniversalCreativeCrate;
protected Map<BlockPos, StructureBlockInfo> blocks; protected Map<BlockPos, StructureBlockInfo> blocks;
protected Map<BlockPos, MountedStorage> storage;
protected Map<BlockPos, MountedFluidStorage> fluidStorage;
protected List<MutablePair<StructureBlockInfo, MovementContext>> actors; protected List<MutablePair<StructureBlockInfo, MovementContext>> actors;
protected Map<BlockPos, MovingInteractionBehaviour> interactors; protected Map<BlockPos, MovingInteractionBehaviour> interactors;
protected List<AABB> superglue; protected List<AABB> superglue;
protected List<BlockPos> seats; protected List<BlockPos> seats;
protected Map<UUID, Integer> seatMapping; protected Map<UUID, Integer> seatMapping;
protected Map<UUID, BlockFace> stabilizedSubContraptions; protected Map<UUID, BlockFace> stabilizedSubContraptions;
protected MountedStorageManager storage;
private Set<SuperGlueEntity> glueToRemove; private Set<SuperGlueEntity> glueToRemove;
private Map<BlockPos, Entity> initialPassengers; private Map<BlockPos, Entity> initialPassengers;
@ -158,13 +149,11 @@ public abstract class Contraption {
public Contraption() { public Contraption() {
blocks = new HashMap<>(); blocks = new HashMap<>();
storage = new HashMap<>();
seats = new ArrayList<>(); seats = new ArrayList<>();
actors = new ArrayList<>(); actors = new ArrayList<>();
interactors = new HashMap<>(); interactors = new HashMap<>();
superglue = new ArrayList<>(); superglue = new ArrayList<>();
seatMapping = new HashMap<>(); seatMapping = new HashMap<>();
fluidStorage = new HashMap<>();
glueToRemove = new HashSet<>(); glueToRemove = new HashSet<>();
initialPassengers = new HashMap<>(); initialPassengers = new HashMap<>();
presentTileEntities = new HashMap<>(); presentTileEntities = new HashMap<>();
@ -173,6 +162,7 @@ public abstract class Contraption {
pendingSubContraptions = new ArrayList<>(); pendingSubContraptions = new ArrayList<>();
stabilizedSubContraptions = new HashMap<>(); stabilizedSubContraptions = new HashMap<>();
simplifiedEntityColliders = Optional.empty(); simplifiedEntityColliders = Optional.empty();
storage = new MountedStorageManager();
} }
public ContraptionWorld getContraptionWorld() { public ContraptionWorld getContraptionWorld() {
@ -255,20 +245,7 @@ public abstract class Contraption {
stabilizedSubContraptions.put(movedContraption.getUUID(), new BlockFace(toLocalPos(pos), face)); stabilizedSubContraptions.put(movedContraption.getUUID(), new BlockFace(toLocalPos(pos), face));
} }
// Gather itemhandlers of mounted storage storage.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));
gatherBBsOffThread(); 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 */ /** move the first block in frontier queue */
protected boolean moveBlock(Level world, @Nullable Direction forcedDirection, Queue<BlockPos> frontier, protected boolean moveBlock(Level world, @Nullable Direction forcedDirection, Queue<BlockPos> frontier,
Set<BlockPos> visited) throws AssemblyException { Set<BlockPos> visited) throws AssemblyException {
@ -642,10 +615,7 @@ public abstract class Contraption {
bounds = bounds.minmax(new AABB(localPos)); bounds = bounds.minmax(new AABB(localPos));
BlockEntity te = pair.getValue(); BlockEntity te = pair.getValue();
if (te != null && MountedStorage.canUseAsStorage(te)) storage.addBlock(localPos, te);
storage.put(localPos, new MountedStorage(te));
if (te != null && MountedFluidStorage.canUseAsStorage(te))
fluidStorage.put(localPos, new MountedFluidStorage(te));
if (AllMovementBehaviours.contains(captured.state.getBlock())) if (AllMovementBehaviours.contains(captured.state.getBlock()))
actors.add(MutablePair.of(StructureBlockInfo, null)); actors.add(MutablePair.of(StructureBlockInfo, null));
if (AllInteractionBehaviours.contains(captured.state.getBlock())) if (AllInteractionBehaviours.contains(captured.state.getBlock()))
@ -722,14 +692,6 @@ public abstract class Contraption {
NBTHelper.iterateCompoundList(nbt.getList("SubContraptions", Tag.TAG_COMPOUND), NBTHelper.iterateCompoundList(nbt.getList("SubContraptions", Tag.TAG_COMPOUND),
c -> stabilizedSubContraptions.put(c.getUUID("Id"), BlockFace.fromNBT(c.getCompound("Location")))); 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(); interactors.clear();
NBTHelper.iterateCompoundList(nbt.getList("Interactors", Tag.TAG_COMPOUND), c -> { NBTHelper.iterateCompoundList(nbt.getList("Interactors", Tag.TAG_COMPOUND), c -> {
BlockPos pos = NbtUtils.readBlockPos(c.getCompound("Pos")); BlockPos pos = NbtUtils.readBlockPos(c.getCompound("Pos"));
@ -738,32 +700,7 @@ public abstract class Contraption {
interactors.put(pos, behaviour); interactors.put(pos, behaviour);
}); });
if (spawnData) storage.read(nbt, presentTileEntities, 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);
if (nbt.contains("BoundsFront")) if (nbt.contains("BoundsFront"))
bounds = NBTHelper.readAABB(nbt.getList("BoundsFront", 5)); bounds = NBTHelper.readAABB(nbt.getList("BoundsFront", 5));
@ -790,35 +727,15 @@ public abstract class Contraption {
} }
ListTag superglueNBT = new ListTag(); ListTag superglueNBT = new ListTag();
ListTag storageNBT = new ListTag();
if (!spawnPacket) { if (!spawnPacket) {
for (AABB glueEntry : superglue) { for (AABB glueEntry : superglue) {
CompoundTag c = new CompoundTag(); CompoundTag c = new CompoundTag();
SuperGlueEntity.writeBoundingBox(c, glueEntry); SuperGlueEntity.writeBoundingBox(c, glueEntry);
superglueNBT.add(c); 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(); storage.write(nbt, spawnPacket);
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);
}
ListTag interactorNBT = new ListTag(); ListTag interactorNBT = new ListTag();
for (BlockPos pos : interactors.keySet()) { for (BlockPos pos : interactors.keySet()) {
@ -847,8 +764,6 @@ public abstract class Contraption {
nbt.put("Actors", actorsNBT); nbt.put("Actors", actorsNBT);
nbt.put("Interactors", interactorNBT); nbt.put("Interactors", interactorNBT);
nbt.put("Superglue", superglueNBT); nbt.put("Superglue", superglueNBT);
nbt.put("Storage", storageNBT);
nbt.put("FluidStorage", fluidStorageNBT);
nbt.put("Anchor", NbtUtils.writeBlockPos(anchor)); nbt.put("Anchor", NbtUtils.writeBlockPos(anchor));
nbt.putBoolean("Stalled", stalled); nbt.putBoolean("Stalled", stalled);
nbt.putBoolean("BottomlessSupply", hasUniversalCreativeCrate); nbt.putBoolean("BottomlessSupply", hasUniversalCreativeCrate);
@ -962,10 +877,7 @@ public abstract class Contraption {
} }
public void removeBlocksFromWorld(Level world, BlockPos offset) { public void removeBlocksFromWorld(Level world, BlockPos offset) {
storage.values() storage.removeStorageFromWorld();
.forEach(MountedStorage::removeStorageFromWorld);
fluidStorage.values()
.forEach(MountedFluidStorage::removeStorageFromWorld);
glueToRemove.forEach(glue -> { glueToRemove.forEach(glue -> {
superglue.add(glue.getBoundingBox() superglue.add(glue.getBoundingBox()
@ -1125,23 +1037,13 @@ public abstract class Contraption {
tag.put("LastKnownPos", NbtUtils.writeBlockPos(BlockPos.ZERO.below(Integer.MAX_VALUE - 1))); tag.put("LastKnownPos", NbtUtils.writeBlockPos(BlockPos.ZERO.below(Integer.MAX_VALUE - 1)));
tileEntity.load(tag); tileEntity.load(tag);
storage.addStorageToWorld(block, 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);
}
} }
transform.apply(tileEntity); transform.apply(tileEntity);
} }
} }
for (StructureBlockInfo block : blocks.values()) { for (StructureBlockInfo block : blocks.values()) {
if (!shouldUpdateAfterMovement(block)) if (!shouldUpdateAfterMovement(block))
continue; continue;
@ -1149,20 +1051,15 @@ public abstract class Contraption {
world.markAndNotifyBlock(targetPos, world.getChunkAt(targetPos), block.state, block.state, world.markAndNotifyBlock(targetPos, world.getChunkAt(targetPos), block.state, block.state,
Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_ALL, 512); 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) { for (AABB box : superglue) {
box = new AABB(transform.apply(new Vec3(box.minX, box.minY, box.minZ)), box = new AABB(transform.apply(new Vec3(box.minX, box.minY, box.minZ)),
transform.apply(new Vec3(box.maxX, box.maxY, box.maxZ))); transform.apply(new Vec3(box.maxX, box.maxY, box.maxZ)));
if (!world.isClientSide) if (!world.isClientSide)
world.addFreshEntity(new SuperGlueEntity(world, box)); world.addFreshEntity(new SuperGlueEntity(world, box));
} }
storage.clear();
} }
public void addPassengersToWorld(Level world, StructureTransform transform, List<Entity> seatedEntities) { 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); bounds = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
} }
public void addExtraInventories(Entity entity) {}
public Map<UUID, Integer> getSeatMapping() { public Map<UUID, Integer> getSeatMapping() {
return seatMapping; return seatMapping;
} }
@ -1282,12 +1177,6 @@ public abstract class Contraption {
return interactors; return interactors;
} }
public void updateContainedFluid(BlockPos localPos, FluidStack containedFluid) {
MountedFluidStorage mountedFluidStorage = fluidStorage.get(localPos);
if (mountedFluidStorage != null)
mountedFluidStorage.updateFluid(containedFluid);
}
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public ContraptionLighter<?> makeLighter() { public ContraptionLighter<?> makeLighter() {
// TODO: move lighters to registry // TODO: move lighters to registry
@ -1349,39 +1238,22 @@ public abstract class Contraption {
return maxDistSq; return maxDistSq;
} }
// TODO: unused? public IItemHandlerModifiable getSharedInventory() {
// private static class ContraptionTileWorld extends WrappedWorld implements IFlywheelWorld { return storage.getItems();
// }
// private final BlockEntity te;
// private final StructureBlockInfo info; public IFluidHandler getSharedFluidTanks() {
// return storage.getFluids();
// 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 Collection<StructureBlockInfo> getRenderedBlocks() { public Collection<StructureBlockInfo> getRenderedBlocks() {
return blocks.values(); return blocks.values();
} }
public Collection<BlockEntity> getSpecialRenderedTEs() { public Collection<BlockEntity> getSpecialRenderedTEs() {
return specialRenderedTileEntities; return specialRenderedTileEntities;
} }
public boolean isHiddenInPortal(BlockPos localPos) { public boolean isHiddenInPortal(BlockPos localPos) {
return false; return false;
} }
@ -1389,6 +1261,10 @@ public abstract class Contraption {
public Optional<List<AABB>> getSimplifiedEntityColliders() { public Optional<List<AABB>> getSimplifiedEntityColliders() {
return simplifiedEntityColliders; return simplifiedEntityColliders;
} }
public void handleContraptionFluidPacket(BlockPos localPos, FluidStack containedFluid) {
storage.updateContainedFluid(localPos, containedFluid);
}
public static class ContraptionInvWrapper extends CombinedInvWrapper { public static class ContraptionInvWrapper extends CombinedInvWrapper {
protected final boolean isExternal; 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; 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) { default Vec3 getActiveAreaOffset(MovementContext context) {
return Vec3.ZERO; return Vec3.ZERO;
@ -39,7 +36,7 @@ public interface MovementBehaviour {
default void dropItem(MovementContext context, ItemStack stack) { default void dropItem(MovementContext context, ItemStack stack) {
ItemStack remainder; ItemStack remainder;
if (AllConfigs.SERVER.kinetics.moveItemsToStorage.get()) if (AllConfigs.SERVER.kinetics.moveItemsToStorage.get())
remainder = ItemHandlerHelper.insertItem(context.contraption.inventory, stack, false); remainder = ItemHandlerHelper.insertItem(context.contraption.getSharedInventory(), stack, false);
else else
remainder = stack; remainder = stack;
if (remainder.isEmpty()) if (remainder.isEmpty())
@ -52,14 +49,11 @@ public interface MovementBehaviour {
context.world.addFreshEntity(itemEntity); 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() { default boolean renderAsNormalTileEntity() {
return false; return false;
@ -71,12 +65,12 @@ public interface MovementBehaviour {
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
default void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld, default void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffer) { ContraptionMatrices matrices, MultiBufferSource buffer) {}
}
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
@Nullable @Nullable
default ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld, MovementContext context) { default ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld,
MovementContext context) {
return null; return null;
} }
} }

View file

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

View file

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

View file

@ -23,13 +23,13 @@ public class ContraptionFluidPacket extends SimplePacketBase {
this.localPos = localPos; this.localPos = localPos;
this.containedFluid = containedFluid; this.containedFluid = containedFluid;
} }
public ContraptionFluidPacket(FriendlyByteBuf buffer) { public ContraptionFluidPacket(FriendlyByteBuf buffer) {
entityId = buffer.readInt(); entityId = buffer.readInt();
localPos = buffer.readBlockPos(); localPos = buffer.readBlockPos();
containedFluid = FluidStack.readFromPacket(buffer); containedFluid = FluidStack.readFromPacket(buffer);
} }
@Override @Override
public void write(FriendlyByteBuf buffer) { public void write(FriendlyByteBuf buffer) {
buffer.writeInt(entityId); buffer.writeInt(entityId);
@ -45,7 +45,7 @@ public class ContraptionFluidPacket extends SimplePacketBase {
if (!(entityByID instanceof AbstractContraptionEntity)) if (!(entityByID instanceof AbstractContraptionEntity))
return; return;
AbstractContraptionEntity contraptionEntity = (AbstractContraptionEntity) entityByID; AbstractContraptionEntity contraptionEntity = (AbstractContraptionEntity) entityByID;
contraptionEntity.getContraption().updateContainedFluid(localPos, containedFluid); contraptionEntity.getContraption().handleContraptionFluidPacket(localPos, containedFluid);
}); });
context.get() context.get()
.setPacketHandled(true); .setPacketHandled(true);

View file

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

View file

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

View file

@ -20,6 +20,7 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableDouble;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; 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.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -60,6 +61,7 @@ public class Carriage {
public int bogeySpacing; public int bogeySpacing;
public Couple<CarriageBogey> bogeys; public Couple<CarriageBogey> bogeys;
public MountedStorageManager storage;
CompoundTag serialisedEntity; CompoundTag serialisedEntity;
Map<Integer, CompoundTag> serialisedPassengers; Map<Integer, CompoundTag> serialisedPassengers;
@ -76,6 +78,7 @@ public class Carriage {
this.presentConductors = Couple.create(false, false); this.presentConductors = Couple.create(false, false);
this.serialisedPassengers = new HashMap<>(); this.serialisedPassengers = new HashMap<>();
this.entities = new HashMap<>(); this.entities = new HashMap<>();
this.storage = new MountedStorageManager();
bogey1.setLeading(); bogey1.setLeading();
bogey1.carriage = this; bogey1.carriage = this;
@ -92,6 +95,7 @@ public class Carriage {
} }
public void setContraption(Level level, CarriageContraption contraption) { public void setContraption(Level level, CarriageContraption contraption) {
this.storage = null;
CarriageContraptionEntity entity = CarriageContraptionEntity.create(level, contraption); CarriageContraptionEntity entity = CarriageContraptionEntity.create(level, contraption);
entity.setCarriage(this); entity.setCarriage(this);
contraption.startMoving(level); contraption.startMoving(level);
@ -177,7 +181,7 @@ public class Carriage {
double moved = point.travel(graph, toMove, trackSelector, signalListener, point.ignoreTurns(), c -> { double moved = point.travel(graph, toMove, trackSelector, signalListener, point.ignoreTurns(), c -> {
for (DimensionalCarriageEntity dce : entities.values()) for (DimensionalCarriageEntity dce : entities.values())
if (c.either(tnl -> tnl.equals(dce.pivot))) if (c.either(tnl -> tnl.equalsIgnoreDim(dce.pivot)))
return false; return false;
if (entities.size() > 1) { if (entities.size() > 1) {
train.status.doublePortal(); train.status.doublePortal();
@ -220,7 +224,7 @@ public class Carriage {
} }
public void updateConductors() { public void updateConductors() {
if (anyAvailableEntity() == null || entities.size() > 1) if (anyAvailableEntity() == null || entities.size() > 1 || serialisedPassengers.size() > 0)
return; return;
presentConductors.replace($ -> false); presentConductors.replace($ -> false);
for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) { for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) {
@ -321,15 +325,6 @@ public class Carriage {
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2); 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()) { if (isOnTwoBogeys()) {
dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition() dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
@ -337,18 +332,27 @@ public class Carriage {
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition() dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition()
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2)); leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2));
continue;
}
if (dimension.equals(otherDimension)) { } else {
dce.rotationAnchors = leadingBogey.points.map(TravellingPoint::getPosition); if (dimension.equals(otherDimension)) {
continue; 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() if (prevmin != dce.minAllowedLocalCoord() || prevmax != dce.maxAllowedLocalCoord()) {
: pivoted(dce, dimension, point, leadingWheelSpacing)); dce.updateRenderedCutoff();
dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition() dce.updatePassengerLoadout();
: pivoted(dce, dimension, point, leadingWheelSpacing)); }
} }
} }
@ -595,6 +599,7 @@ public class Carriage {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putFloat("Cutoff", cutoff); tag.putFloat("Cutoff", cutoff);
tag.putInt("DiscardTicks", discardTicks); tag.putInt("DiscardTicks", discardTicks);
storage.write(tag, false);
if (pivot != null) if (pivot != null)
tag.put("Pivot", pivot.write(null)); tag.put("Pivot", pivot.write(null));
if (positionAnchor != null) if (positionAnchor != null)
@ -607,6 +612,7 @@ public class Carriage {
public void read(CompoundTag tag) { public void read(CompoundTag tag) {
cutoff = tag.getFloat("Cutoff"); cutoff = tag.getFloat("Cutoff");
discardTicks = tag.getInt("DiscardTicks"); discardTicks = tag.getInt("DiscardTicks");
storage.read(tag, null, false);
if (tag.contains("Pivot")) if (tag.contains("Pivot"))
pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null); pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null);
if (positionAnchor != null) if (positionAnchor != null)
@ -749,13 +755,13 @@ public class Carriage {
Entity entity = this.entity.get(); Entity entity = this.entity.get();
if (!(entity instanceof CarriageContraptionEntity cce)) if (!(entity instanceof CarriageContraptionEntity cce))
return; return;
if (!entity.level.isClientSide())
return;
Contraption contraption = cce.getContraption(); Contraption contraption = cce.getContraption();
if (!(contraption instanceof CarriageContraption cc)) if (!(contraption instanceof CarriageContraption cc))
return; return;
cc.portalCutoffMin = minAllowedLocalCoord(); cc.portalCutoffMin = minAllowedLocalCoord();
cc.portalCutoffMax = maxAllowedLocalCoord(); cc.portalCutoffMax = maxAllowedLocalCoord();
if (!entity.level.isClientSide())
return;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(cce)); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(cce));
} }
@ -776,12 +782,12 @@ public class Carriage {
return; return;
} }
this.entity = new WeakReference<>(cce);
cce.setGraph(train.graph == null ? null : train.graph.id); cce.setGraph(train.graph == null ? null : train.graph.id);
cce.setCarriage(Carriage.this); cce.setCarriage(Carriage.this);
cce.syncCarriage(); cce.syncCarriage();
this.entity = new WeakReference<>(cce);
if (level instanceof ServerLevel sl) if (level instanceof ServerLevel sl)
sl.tryAddFreshEntityWithPassengers(entity); 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.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; 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.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.NonStationaryLighter;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionLighter; 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.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
public class CarriageContraption extends Contraption { public class CarriageContraption extends Contraption {
private Direction assemblyDirection; private Direction assemblyDirection;
private boolean forwardControls; private boolean forwardControls;
private boolean backwardControls; private boolean backwardControls;
public Couple<Boolean> blazeBurnerConductors; public Couple<Boolean> blazeBurnerConductors;
public Map<BlockPos, Couple<Boolean>> conductorSeats; public Map<BlockPos, Couple<Boolean>> conductorSeats;
public ArrivalSoundQueue soundQueue; public ArrivalSoundQueue soundQueue;
protected MountedStorageManager storageProxy;
// during assembly only // during assembly only
private int bogeys; private int bogeys;
private boolean sidewaysControls; private boolean sidewaysControls;
@ -116,6 +122,21 @@ public class CarriageContraption extends Contraption {
return info.state.getValue(ControlsBlock.FACING) == direction.getOpposite(); 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 @Override
protected boolean isAnchoringBlockAt(BlockPos pos) { protected boolean isAnchoringBlockAt(BlockPos pos) {
return false; return false;
@ -272,7 +293,7 @@ public class CarriageContraption extends Contraption {
return !withinVisible(localPos) || atSeam(localPos); return !withinVisible(localPos) || atSeam(localPos);
} }
private boolean notInPortal() { public boolean notInPortal() {
return portalCutoffMin == Integer.MIN_VALUE && portalCutoffMax == Integer.MAX_VALUE; return portalCutoffMin == Integer.MIN_VALUE && portalCutoffMax == Integer.MAX_VALUE;
} }
@ -294,4 +315,15 @@ public class CarriageContraption extends Contraption {
return coord > portalCutoffMin && coord < portalCutoffMax; 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.entity = new WeakReference<>(this);
dimensional.pivot = null; dimensional.pivot = null;
carriage.updateContraptionAnchors(); carriage.updateContraptionAnchors();
dimensional.updateRenderedCutoff();
} }
updateTrackGraph(); updateTrackGraph();
} else } else
@ -316,7 +317,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
@Override @Override
protected boolean isActorActive(MovementContext context, MovementBehaviour actor) { 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 @Override
@ -605,6 +607,13 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
this.carriage = carriage; this.carriage = carriage;
this.trainId = carriage.train.id; this.trainId = carriage.train.id;
this.carriageIndex = carriage.train.carriages.indexOf(carriage); 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) { public void setGraph(@Nullable UUID graphId) {

View file

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