4 Commits

15 changed files with 596 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
package net.xevianlight.aphelion.block.custom;
import com.mojang.datafixers.kinds.Const;
import com.mojang.math.Constants;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.xevianlight.aphelion.block.custom.base.BasicEntityBlock;
import net.xevianlight.aphelion.block.entity.custom.PipeTestBlockEntity;
import net.xevianlight.aphelion.core.init.ModBlocks;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
// a lot of this is ai slop so take it with a grainlet of salt
public class PipeTestBlock extends BasicEntityBlock {
// shortcuts for each directional property because they're pretty verbose
// t/f here means is/isn't connected outgoing in that direction
public static final BooleanProperty NORTH = BlockStateProperties.NORTH;
public static final BooleanProperty SOUTH = BlockStateProperties.SOUTH;
public static final BooleanProperty EAST = BlockStateProperties.EAST;
public static final BooleanProperty WEST = BlockStateProperties.WEST;
public static final BooleanProperty UP = BlockStateProperties.UP;
public static final BooleanProperty DOWN = BlockStateProperties.DOWN;
// Voxel shape pieces to make the actual pipe model
private static final VoxelShape CORE = Block.box(6, 6, 6, 10, 10, 10);
private static final VoxelShape NORTH_SHAPE = Block.box(6, 6, 0, 10, 10, 6);
private static final VoxelShape SOUTH_SHAPE = Block.box(6, 6, 10, 10, 10, 16);
private static final VoxelShape EAST_SHAPE = Block.box(10, 6, 6, 16, 10, 10);
private static final VoxelShape WEST_SHAPE = Block.box(0, 6, 6, 6, 10, 10);
private static final VoxelShape UP_SHAPE = Block.box(6, 10, 6, 10, 16, 10);
private static final VoxelShape DOWN_SHAPE = Block.box(6, 0, 6, 10, 6, 10);
// Assembles the "VoxelShape" of the block which i assume is just the collision/place/mine hitbox
// later this should be cached for each different shape that we need
@Override
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
VoxelShape shape = CORE;
if (state.getValue(NORTH)) shape = Shapes.join(shape, NORTH_SHAPE, BooleanOp.OR);
if (state.getValue(SOUTH)) shape = Shapes.join(shape, SOUTH_SHAPE, BooleanOp.OR);
if (state.getValue(EAST)) shape = Shapes.join(shape, EAST_SHAPE, BooleanOp.OR);
if (state.getValue(WEST)) shape = Shapes.join(shape, WEST_SHAPE, BooleanOp.OR);
if (state.getValue(UP)) shape = Shapes.join(shape, UP_SHAPE, BooleanOp.OR);
if (state.getValue(DOWN)) shape = Shapes.join(shape, DOWN_SHAPE, BooleanOp.OR);
return shape;
}
public PipeTestBlock(Properties properties) {
super(properties, true);
this.registerDefaultState(this.stateDefinition.any()
.setValue(NORTH, false).setValue(SOUTH, false)
.setValue(EAST, false).setValue(WEST, false)
.setValue(UP, false).setValue(DOWN, false));
}
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {
return null;
}
// This method determines the state; called when first placed
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return makeConnections(context.getLevel(), context.getClickedPos());
}
// Updates the block; called when a neighbor changes
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos currentPos, BlockPos neighborPos) {
return makeConnections(level, currentPos);
}
public static Properties getProperties() {
return Properties.of().noOcclusion();
}
private boolean hasAttachment(@Nullable PipeTestBlockEntity BE, Direction direction) {
return (BE != null && BE.hasAttachment(direction));
}
private BlockState makeConnections(LevelAccessor level, BlockPos pos) {
BlockEntity BE = level.getBlockEntity(pos);
PipeTestBlockEntity PTBE = null;
if (BE instanceof PipeTestBlockEntity found) {
PTBE = found;
}
return this.defaultBlockState()
.setValue(NORTH, canConnect(level, pos.north(), Direction.SOUTH) || hasAttachment(PTBE, Direction.NORTH))
.setValue(SOUTH, canConnect(level, pos.south(), Direction.NORTH) || hasAttachment(PTBE, Direction.SOUTH))
.setValue(EAST, canConnect(level, pos.east(), Direction.WEST) || hasAttachment(PTBE, Direction.EAST))
.setValue(WEST, canConnect(level, pos.west(), Direction.EAST) || hasAttachment(PTBE, Direction.WEST))
.setValue(UP, canConnect(level, pos.above(), Direction.DOWN) || hasAttachment(PTBE, Direction.UP))
.setValue(DOWN, canConnect(level, pos.below(), Direction.UP) || hasAttachment(PTBE, Direction.DOWN));
}
/// If a PipeTestBlock can connect to this position from the given direction.
/// If you're going to the NORTH of yourself, you should be accessing the SOUTH side.
public static boolean canConnect(LevelAccessor levelA, BlockPos neighborPos, Direction accessSide) {
// Methinks this is not the best way to test this.
boolean isPipe = levelA.getBlockState(neighborPos).is(ModBlocks.PIPE_TEST_BLOCK.get());
/// This code is AI, but I think it works? I think the reason it's a levelAccessor instead of a level
/// is that we're not sure if we're in, for example, an inventory slot or not.
/// Either way, this should only trigger when it makes sense (assuming this is correct in the first place)
boolean isInventory;
if (levelA instanceof Level level) {
isInventory = level.getCapability(Capabilities.ItemHandler.BLOCK, neighborPos, accessSide) != null;
} else {
isInventory = false;
}
return isPipe || isInventory;
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
BlockEntity BE = level.getBlockEntity(pos);
if (BE instanceof PipeTestBlockEntity pipe) {
// force everything connected to this pipe to reevaluate
if (pipe.graph != null) pipe.graph.invalidate();
}
super.onRemove(state, level, pos, newState, isMoving);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(NORTH, SOUTH, EAST, WEST, UP, DOWN);
}
@Override
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) {
BlockEntity BE = level.getBlockEntity(pos);
if (BE instanceof PipeTestBlockEntity PTBE) {
ItemInteractionResult r = PTBE.useItemOn(stack, state, level, pos, player, hand, hitResult, this);
BlockState newState = makeConnections(level, pos);
level.setBlock(pos, newState, 3);
return r;
} else {
return super.useItemOn(stack, state, level, pos, player, hand, hitResult);
}
}
// Doesn't block sunlight
// @Override
// public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) {
// return true;
// }
/// This function doesn't do what the AI says it does. I have no idea what it actually does, though
// 2. Prevents the "black shadows" inside or around the pipe
// @Override
// public int getLightBlock(BlockState state, BlockGetter worldIn, BlockPos pos) {
// return 0;
// }
/// I don't know what this does, because Properties.noOcclusion() is the part that made the leaves keep
/// rendering their faces.
// 3. Tells adjacent blocks (like leaves) to keep rendering their faces
// @Override
// public boolean isOcclusionShapeFullBlock(BlockState state, BlockGetter worldIn, BlockPos pos) {
// return false;
// }
// This bit affects how strong the ambient occlusion effect is
@Override
public float getShadeBrightness(BlockState state, BlockGetter world, BlockPos pos) {
return 1.0F; // Maintains full brightness
}
@Override
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
return new PipeTestBlockEntity(blockPos, blockState);
}
}

View File

@@ -0,0 +1,155 @@
package net.xevianlight.aphelion.block.entity.custom;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.custom.PipeTestBlock;
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import net.xevianlight.aphelion.core.init.ModBlocks;
import net.xevianlight.aphelion.systems.conveyor.*;
import net.xevianlight.aphelion.util.FloodFill3D;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class PipeTestBlockEntity extends BlockEntity implements TickableBlockEntity {
public @Nullable ConveyorNetwork graph = null;
public final Map<Direction, @Nullable ConveyorAttachment> attachments = new HashMap<>();
public final Map<Direction, @Nullable ConveyorOutput> outputs = new HashMap<>();
public PipeTestBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.PIPE_TEST_BLOCK_ENTITY.get(), pos, blockState);
}
@Override
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
}
@Override
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
if (this.graph == null) initGraph(level, pos);
// TODO: Call this as little as necessary
makeOutputs(level, state, pos);
for (Direction dir : Direction.values()) {
ConveyorAttachment attachment = attachments.get(dir);
if (attachment != null) {
attachment.tick(level, state, pos, dir, graph);
}
}
}
@Override
public void firstTick(Level level, BlockState state, BlockPos pos) {
}
private void addOutput(Direction dir, ConveyorOutput output) {
outputs.put(dir, output);
if (graph != null) graph.outputs.add(output);
}
private void removeOutput(Direction dir) {
ConveyorOutput old = outputs.get(dir);
if (graph != null) graph.outputs.remove(old);
outputs.remove(dir);
}
private boolean canOutputTo(Level level, BlockPos pos, Direction accessSide) {
return level.getCapability(Capabilities.ItemHandler.BLOCK, pos, accessSide) != null;
}
protected void makeOutputs(ServerLevel level, BlockState state, BlockPos pos) {
BlockPos.MutableBlockPos neighbor = new BlockPos.MutableBlockPos();
for (Direction dir : Direction.values()) {
neighbor.setWithOffset(pos, dir);
if (canOutputTo(level, neighbor, dir.getOpposite()) && attachments.get(dir) == null && outputs.get(dir) == null) {
addOutput(dir, new BasicItemOutput(level, pos, dir));
}
if (!(canOutputTo(level, neighbor, dir.getOpposite()) && attachments.get(dir) == null) && outputs.get(dir) != null) {
removeOutput(dir);
}
}
}
public boolean hasAttachment(Direction direction) {
return attachments.get(direction) != null;
}
// Simplest implementation I can think of
public static void initGraph(Level level, BlockPos pos) {
Aphelion.LOGGER.info("Init graph from {}", pos);
if (!level.getBlockState(pos).is(ModBlocks.PIPE_TEST_BLOCK.get())) return;
Set<BlockPos> pipes = FloodFill3D.run(
level, pos, 1000,
(var v1, var v2, var state, var v4, var v5, var v6) -> state.is(ModBlocks.PIPE_TEST_BLOCK.get()),
false);
Aphelion.LOGGER.info("Got {} pipes", pipes.size());
ConveyorNetwork graph = new ConveyorNetwork();
for (BlockPos pipePos : pipes) {
BlockEntity BE = level.getBlockEntity(pipePos);
if (BE instanceof PipeTestBlockEntity pipe) {
// Invalidate any old graphs
if (pipe.graph != null) {
pipe.graph.invalidate();
}
graph.addPipe(pipe);
}
}
}
public void setAttachment(Direction side, ConveyorAttachment attachment) {
attachments.put(side, attachment);
}
public boolean trySetAttachment(Direction side, ConveyorAttachment attachment) {
if (hasAttachment(side)) return false;
setAttachment(side, attachment);
return true;
}
public static boolean isAttachmentItem(ItemStack stack) {
//TODO: add actual attachment items instead of just stone
return stack.is(Item.byId(1));
}
// Server only
public ConveyorAttachment getAttachmentForItem(ItemStack stack, Direction side) {
if (level.isClientSide()) throw new RuntimeException("Cannot get attachment item on client side!");
return new BasicItemExtractAttachment((ServerLevel) level, getBlockPos(), side);
}
/// Called from pipe test block's useItemOn
public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult, PipeTestBlock block) {
if (level.isClientSide) return ItemInteractionResult.SUCCESS;
Vec3 relativePos = hitResult.getLocation().subtract(pos.getCenter());
Direction pipeSide = Direction.getNearest(relativePos);
if (isAttachmentItem(stack)) {
boolean success = trySetAttachment(pipeSide, getAttachmentForItem(stack, pipeSide));
return success ? ItemInteractionResult.SUCCESS : ItemInteractionResult.FAIL;
} else {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; // goes through with whatever other interaction (placing a block, etc)
}
}
}

View File

@@ -51,4 +51,9 @@ public class ModBlockEntities {
BLOCK_ENTITIES.register("rocket_assembler_block_entity", () -> BlockEntityType.Builder.of(
RocketAssemblerBlockEntity::new, ModBlocks.ROCKET_ASSEMBLER_BLOCK.get()).build(null)
);
public static final Supplier<BlockEntityType<PipeTestBlockEntity>> PIPE_TEST_BLOCK_ENTITY =
BLOCK_ENTITIES.register("pipe_test_block_entity", () -> BlockEntityType.Builder.of(
PipeTestBlockEntity::new, ModBlocks.PIPE_TEST_BLOCK.get()).build(null)
);
}

View File

@@ -20,4 +20,5 @@ public class ModBlocks {
public static final DeferredBlock<Block> VAF_MULTIBLOCK_DUMMY_BLOCK = BLOCKS.register("vaf_dummy_block", () -> new VAFMultiblockDummyBlock(VAFMultiblockDummyBlock.getProperties()));
public static final DeferredBlock<Block> OXYGEN_TEST_BLOCK = BLOCKS.register("oxygen_test_block", () -> new OxygenTestBlock(OxygenTestBlock.getProperties()));
public static final DeferredBlock<Block> ROCKET_ASSEMBLER_BLOCK = BLOCKS.register("rocket_assembler_block", () -> new RocketAssemblerBlock(RocketAssemblerBlock.getProperties()));
public static final DeferredBlock<Block> PIPE_TEST_BLOCK = BLOCKS.register("pipe", () -> new PipeTestBlock(PipeTestBlock.getProperties()));
}

View File

@@ -36,6 +36,7 @@ public static final DeferredItem<Item> MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu
public static final DeferredItem<BlockItem> ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties()));
public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties()));
public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties()));
public static final DeferredItem<BlockItem> PIPE_TEST_BLOCK = ITEMS.register("pipe", () -> new BlockItem(ModBlocks.PIPE_TEST_BLOCK.get(), new Item.Properties()));
public static final DeferredItem<BlockItem> LAUNCH_PAD = ITEMS.register("launch_pad", () -> new BlockItem(ModBlocks.LAUNCH_PAD.get(), LaunchPad.getItemProperties()));
public static final DeferredItem<BlockItem> ROCKET_ASSEMBLER_BLOCK = ITEMS.register("rocket_assembler_block", () -> new BlockItem(ModBlocks.ROCKET_ASSEMBLER_BLOCK.get(), RocketAssemblerBlock.getItemProperties()));
// public static final DeferredItem<BlockItem> VAF_MULTIBLOCK_DUMMY_BLOCK = ITEMS.register("vaf_multiblock_dummy_block", () -> new BlockItem(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), VAFMultiblockDummyBlock.getItemProperties()));

View File

@@ -0,0 +1,61 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.items.IItemHandler;
import net.xevianlight.aphelion.block.entity.custom.PipeTestBlockEntity;
import org.jetbrains.annotations.Nullable;
public class BasicItemExtractAttachment extends ConveyorAttachment implements ConveyorInput {
BlockCapabilityCache<IItemHandler, @Nullable Direction> capabilityCache;
public BasicItemExtractAttachment(ServerLevel level, BlockPos pos, Direction facingDirection) {
super(level, pos, facingDirection);
capabilityCache = BlockCapabilityCache.create(
Capabilities.ItemHandler.BLOCK,
level,
pos.relative(facingDirection),
facingDirection.getOpposite()
);
}
@Override
public void render() {
}
@Override
public void tick(ServerLevel level, BlockState state, BlockPos pos, Direction facingDirection, ConveyorNetwork network) {
// do an extract and distribute
IItemHandler container = capabilityCache.getCapability();
if (container == null) return;
final int EXTRACT_PER_TICK = 4;
int to_extract = EXTRACT_PER_TICK;
int extract_slot_id = container.getSlots() - 1;
while (extract_slot_id >= 0 && container.getStackInSlot(extract_slot_id).isEmpty()) extract_slot_id--;
if (extract_slot_id == -1) return;
// Do a simulated extract, then run through every output side on the graph and do real inserts.
// By the end, remove as many items as we successfully inserted with a real extract
ItemStack to_distribute = container.extractItem(extract_slot_id, to_extract, true);
int extracted_amount = to_distribute.getCount();
to_distribute = network.insertItem(to_distribute, false);
int distributed_amount = extracted_amount;
if (!to_distribute.isEmpty()) {
distributed_amount = extracted_amount - to_distribute.getCount();
}
container.extractItem(extract_slot_id, distributed_amount, false);
}
}

View File

@@ -0,0 +1,39 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.items.IItemHandler;
import org.jetbrains.annotations.Nullable;
public class BasicItemOutput implements ConveyorOutput {
BlockCapabilityCache<IItemHandler, @Nullable Direction> capabilityCache;
public BasicItemOutput(ServerLevel level, BlockPos pos, Direction facingDirection) {
capabilityCache = BlockCapabilityCache.create(
Capabilities.ItemHandler.BLOCK,
level,
pos.relative(facingDirection),
facingDirection.getOpposite()
);
}
@Override
public ItemStack insertItem(ItemStack stack, boolean simulate) {
ItemStack toInsert = stack.copy();
IItemHandler container = capabilityCache.getCapability();
if (container == null) return toInsert;
for (int insertIndex = 0; insertIndex < container.getSlots(); insertIndex++) {
if (toInsert.isEmpty()) break;
toInsert = container.insertItem(insertIndex, toInsert, simulate);
}
return toInsert;
}
}

View File

@@ -0,0 +1,16 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.xevianlight.aphelion.block.entity.custom.PipeTestBlockEntity;
public class ConveyorAttachment {
ConveyorAttachment(ServerLevel level, BlockPos pos, Direction facingDirection) {}
//TODO: put in the right interface for a render function
void render() {};
public void tick(ServerLevel level, BlockState state, BlockPos pos, Direction facingDirection, ConveyorNetwork network) {}
}

View File

@@ -0,0 +1,10 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
public interface ConveyorInput {
void tick(ServerLevel level, BlockState state, BlockPos pos, Direction facingDirection, ConveyorNetwork graph);
}

View File

@@ -0,0 +1,46 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.world.item.ItemStack;
import net.xevianlight.aphelion.block.entity.custom.PipeTestBlockEntity;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
// I'm naming and structuring the conveyor stuff like this bc i kinda wanna try making a space-engineers
// style request system later... though it might not be necessary, given that the reason it needs to be like that
// in space engineers is mostly space constraints if i had to guess
public class ConveyorNetwork {
boolean isInvalid = false;
public List<PipeTestBlockEntity> pipes = new ArrayList<>();
public Set<ConveyorOutput> outputs = new HashSet<>();
public ItemStack insertItem(ItemStack stack, boolean simulate) {
if (isInvalid) return stack;
for (ConveyorOutput output : outputs) {
stack = output.insertItem(stack, simulate);
if (stack.isEmpty()) break;
}
return stack;
}
public void addPipe(PipeTestBlockEntity pipe) {
pipes.add(pipe);
pipe.graph = this;
for (ConveyorOutput output : pipe.outputs.values()) {
if (output != null) this.outputs.add(output);
}
}
/// Called whenever a pipe is removed from a graph, or when a new graph comes across an old one.
public void invalidate() {
for (PipeTestBlockEntity pipe : pipes) {
pipe.graph = null;
for (ConveyorOutput output : pipe.outputs.values()) {
if (output != null) this.outputs.remove(output);
}
}
this.isInvalid = true;
}
}

View File

@@ -0,0 +1,8 @@
package net.xevianlight.aphelion.systems.conveyor;
import net.minecraft.world.item.ItemStack;
public interface ConveyorOutput {
/// @return Rejected items
ItemStack insertItem(ItemStack stack, boolean simulate);
}

View File

@@ -0,0 +1,11 @@
{
"multipart": [
{ "apply": { "model": "aphelion:block/pipe_core" }},
{ "when": { "north": "true" }, "apply": { "model": "aphelion:block/pipe_side" }},
{ "when": { "east": "true" }, "apply": { "model": "aphelion:block/pipe_side", "y": 90 }},
{ "when": { "south": "true" }, "apply": { "model": "aphelion:block/pipe_side", "y": 180 }},
{ "when": { "west": "true" }, "apply": { "model": "aphelion:block/pipe_side", "y": 270 }},
{ "when": { "up": "true" }, "apply": { "model": "aphelion:block/pipe_side", "x": 270 }},
{ "when": { "down": "true" }, "apply": { "model": "aphelion:block/pipe_side", "x": 90 }}
]
}

View File

@@ -0,0 +1,20 @@
{
"ambientocclusion": false,
"textures": {
"texture": "aphelion:block/pipe_texture"
},
"elements": [
{
"from": [ 6, 6, 6 ],
"to": [ 10, 10, 10 ],
"faces": {
"down": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"up": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"north": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"south": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"west": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"east": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] }
}
}
]
}

View File

@@ -0,0 +1,18 @@
{
"textures": {
"texture": "aphelion:block/pipe_texture"
},
"elements": [
{
"from": [ 6, 6, 0 ],
"to": [ 10, 10, 6 ],
"faces": {
"down": { "texture": "#texture", "uv": [ 6, 0, 10, 6 ] },
"up": { "texture": "#texture", "uv": [ 6, 0, 10, 6 ] },
"north": { "texture": "#texture", "uv": [ 6, 6, 10, 10 ] },
"west": { "texture": "#texture", "uv": [ 0, 6, 6, 10 ] },
"east": { "texture": "#texture", "uv": [ 0, 6, 6, 10 ] }
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B