sentenced to the API package for your crimes

This commit is contained in:
TropheusJ 2025-02-20 10:49:41 -05:00
parent 1888998553
commit 692c1c589e
11 changed files with 207 additions and 125 deletions

View file

@ -0,0 +1,147 @@
package com.simibubi.create.api.contraption;
import com.simibubi.create.impl.contraption.BlockMovementChecksImpl;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
/**
* Provides several interfaces that can define the behavior of blocks when mounting onto contraptions:
* <ul>
* <li>{@link MovementNecessaryCheck}</li>
* <li>{@link MovementAllowedCheck}</li>
* <li>{@link BrittleCheck}</li>
* <li>{@link AttachedCheck}</li>
* <li>{@link NotSupportiveCheck}</li>
* </ul>
* See each one for details.
* <p>
* For each interface, checks can be registered and queried.
* Registration is thread-safe and can be done in parallel mod init.
* Each query will iterate all registered checks of that type in reverse-registration order. If a check returns
* a non-{@link CheckResult#PASS PASS} result, that is the result of the query. If no check catches a query, then
* a best-effort fallback is used.
*/
public class BlockMovementChecks {
public static void registerMovementNecessaryCheck(MovementNecessaryCheck check) {
BlockMovementChecksImpl.registerMovementNecessaryCheck(check);
}
public static void registerMovementAllowedCheck(MovementAllowedCheck check) {
BlockMovementChecksImpl.registerMovementAllowedCheck(check);
}
public static void registerBrittleCheck(BrittleCheck check) {
BlockMovementChecksImpl.registerBrittleCheck(check);
}
public static void registerAttachedCheck(AttachedCheck check) {
BlockMovementChecksImpl.registerAttachedCheck(check);
}
public static void registerNotSupportiveCheck(NotSupportiveCheck check) {
BlockMovementChecksImpl.registerNotSupportiveCheck(check);
}
// queries
public static boolean isMovementNecessary(BlockState state, Level world, BlockPos pos) {
return BlockMovementChecksImpl.isMovementNecessary(state, world, pos);
}
public static boolean isMovementAllowed(BlockState state, Level world, BlockPos pos) {
return BlockMovementChecksImpl.isMovementAllowed(state, world, pos);
}
public static boolean isBrittle(BlockState state) {
return BlockMovementChecksImpl.isBrittle(state);
}
public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction) {
return BlockMovementChecksImpl.isBlockAttachedTowards(state, world, pos, direction);
}
public static boolean isNotSupportive(BlockState state, Direction facing) {
return BlockMovementChecksImpl.isNotSupportive(state, facing);
}
@FunctionalInterface
public interface MovementNecessaryCheck {
/**
* Determine if it's necessary to move the given block. Contraptions
* will generally ignore blocks that are unnecessary to move.
*/
CheckResult isMovementNecessary(BlockState state, Level world, BlockPos pos);
}
@FunctionalInterface
public interface MovementAllowedCheck {
/**
* Determine if the given block is movable. Immobile blocks will generally prevent a contraption from assembling.
* @see ContraptionMovementSetting
*/
CheckResult isMovementAllowed(BlockState state, Level world, BlockPos pos);
}
@FunctionalInterface
public interface BrittleCheck {
/**
* Brittle blocks are blocks that require another block for support, like torches or ladders.
* They're collected first to avoid them breaking when their support block is removed.
*/
CheckResult isBrittle(BlockState state);
}
@FunctionalInterface
public interface AttachedCheck {
/**
* Determine if the given block is attached to the block in the given direction.
* Attached blocks will be moved together. Examples:
* <ul>
* <li>Ladders are attached to their support block</li>
* <li>Pressure plates are attached to the floor</li>
* <li>Fluid tanks are attached to others in their multiblock</li>
* <li>Bed halves are attached to each other</li>
* </ul>
*/
CheckResult isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction);
}
@FunctionalInterface
public interface NotSupportiveCheck {
/**
* Check if the given block is non-supportive in the given direction.
* Non-supportive blocks stop block collection propagation.
* Examples:
* <ul>
* <li>Drills are not supportive for the block in front of them</li>
* <li>Carpets are not supportive for the block above them</li>
* <li>Non-extended stickers are not supportive of the block in front of them</li>
* </ul>
*/
CheckResult isNotSupportive(BlockState state, Direction direction);
}
public enum CheckResult {
SUCCESS, FAIL, PASS;
public boolean toBoolean() {
if (this == PASS) {
throw new IllegalStateException("PASS does not have a boolean value");
}
return this == SUCCESS;
}
public static CheckResult of(boolean b) {
return b ? SUCCESS : FAIL;
}
public static CheckResult of(Boolean b) {
return b == null ? PASS : (b ? SUCCESS : FAIL);
}
}
}

View file

@ -7,6 +7,8 @@ import javax.annotation.Nullable;
import com.simibubi.create.api.registry.SimpleRegistry;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
@ -15,6 +17,7 @@ import net.minecraftforge.common.extensions.IForgeBlock;
/**
* Defines whether a block is movable by contraptions.
* This is used as a fallback check for {@link BlockMovementChecks#isMovementAllowed(BlockState, Level, BlockPos)}.
* The registry uses suppliers, so the setting of a block can change. This is useful for config options.
*/
public enum ContraptionMovementSetting {

View file

@ -31,6 +31,7 @@ import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.api.behaviour.interaction.MovingInteractionBehaviour;
import com.simibubi.create.api.behaviour.movement.MovementBehaviour;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.content.contraptions.actors.contraptionControls.ContraptionControlsMovement;
import com.simibubi.create.content.contraptions.actors.harvester.HarvesterMovementBehaviour;
import com.simibubi.create.content.contraptions.actors.seat.SeatBlock;
@ -682,7 +683,7 @@ public abstract class Contraption {
toLocalPos(NbtUtils.readBlockPos(nbt.getCompound("Controller"))) :
localPos;
nbt.put("Controller", NbtUtils.writeBlockPos(controllerPos));
if (updateTags.containsKey(localPos))
updateTags.get(localPos).put("Controller", NbtUtils.writeBlockPos(controllerPos));

View file

@ -12,7 +12,7 @@ import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllKeys;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.CenteredSideValueBoxTransform;

View file

@ -8,8 +8,8 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.api.schematic.requirement.SpecialEntityItemRequirement;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.content.contraptions.bearing.BearingBlock;
import com.simibubi.create.content.contraptions.chassis.AbstractChassisBlock;
import com.simibubi.create.content.kinetics.base.DirectionalKineticBlock;

View file

@ -5,7 +5,7 @@ import java.util.Set;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllPackets;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import net.createmod.catnip.data.Iterate;
import net.createmod.catnip.levelWrappers.RayTraceLevel;

View file

@ -5,7 +5,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import net.createmod.catnip.data.Iterate;
import net.minecraft.core.BlockPos;

View file

@ -14,8 +14,8 @@ import java.util.Queue;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.content.contraptions.AssemblyException;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.content.contraptions.ContraptionType;
import com.simibubi.create.content.contraptions.TranslatingContraption;
import com.simibubi.create.content.contraptions.piston.MechanicalPistonBlock.PistonState;

View file

@ -7,9 +7,9 @@ import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.AssemblyException;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.content.contraptions.ContraptionCollider;
import com.simibubi.create.content.contraptions.ControlledContraptionEntity;
import com.simibubi.create.content.contraptions.piston.LinearActuatorBlockEntity;

View file

@ -6,7 +6,7 @@ import java.util.stream.Collectors;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.BlockMovementChecks;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.content.contraptions.StructureTransform;
import com.simibubi.create.content.schematics.cannon.MaterialChecklist;
import com.simibubi.create.content.schematics.requirement.ItemRequirement;

View file

@ -1,4 +1,4 @@
package com.simibubi.create.content.contraptions;
package com.simibubi.create.impl.contraption;
import java.util.ArrayList;
import java.util.List;
@ -6,6 +6,13 @@ import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.api.connectivity.ConnectivityHandler;
import com.simibubi.create.api.contraption.BlockMovementChecks;
import com.simibubi.create.api.contraption.BlockMovementChecks.AttachedCheck;
import com.simibubi.create.api.contraption.BlockMovementChecks.BrittleCheck;
import com.simibubi.create.api.contraption.BlockMovementChecks.CheckResult;
import com.simibubi.create.api.contraption.BlockMovementChecks.MovementAllowedCheck;
import com.simibubi.create.api.contraption.BlockMovementChecks.MovementNecessaryCheck;
import com.simibubi.create.api.contraption.BlockMovementChecks.NotSupportiveCheck;
import com.simibubi.create.api.contraption.ContraptionMovementSetting;
import com.simibubi.create.content.contraptions.actors.AttachedActorBlock;
import com.simibubi.create.content.contraptions.actors.harvester.HarvesterBlock;
@ -67,121 +74,100 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.material.PushReaction;
public class BlockMovementChecks {
public class BlockMovementChecksImpl {
private static final List<MovementNecessaryCheck> movementNecessaryChecks = new ArrayList<>();
private static final List<MovementAllowedCheck> movementAllowedChecks = new ArrayList<>();
private static final List<BrittleCheck> brittleChecks = new ArrayList<>();
private static final List<AttachedCheck> attachedChecks = new ArrayList<>();
private static final List<NotSupportiveCheck> notSupportiveChecks = new ArrayList<>();
private static final List<MovementNecessaryCheck> MOVEMENT_NECESSARY_CHECKS = new ArrayList<>();
private static final List<MovementAllowedCheck> MOVEMENT_ALLOWED_CHECKS = new ArrayList<>();
private static final List<BrittleCheck> BRITTLE_CHECKS = new ArrayList<>();
private static final List<AttachedCheck> ATTACHED_CHECKS = new ArrayList<>();
private static final List<NotSupportiveCheck> NOT_SUPPORTIVE_CHECKS = new ArrayList<>();
// registration adds to the start so newer ones are queried first
// synchronize these so they're safe to call in async mod init
// Registration
// Add new checks to the front instead of the end
public static void registerMovementNecessaryCheck(MovementNecessaryCheck check) {
MOVEMENT_NECESSARY_CHECKS.add(0, check);
public static synchronized void registerMovementNecessaryCheck(MovementNecessaryCheck check) {
BlockMovementChecksImpl.movementNecessaryChecks.add(0, check);
}
public static void registerMovementAllowedCheck(MovementAllowedCheck check) {
MOVEMENT_ALLOWED_CHECKS.add(0, check);
public static synchronized void registerMovementAllowedCheck(MovementAllowedCheck check) {
BlockMovementChecksImpl.movementAllowedChecks.add(0, check);
}
public static void registerBrittleCheck(BrittleCheck check) {
BRITTLE_CHECKS.add(0, check);
public static synchronized void registerBrittleCheck(BrittleCheck check) {
BlockMovementChecksImpl.brittleChecks.add(0, check);
}
public static void registerAttachedCheck(AttachedCheck check) {
ATTACHED_CHECKS.add(0, check);
public static synchronized void registerAttachedCheck(AttachedCheck check) {
BlockMovementChecksImpl.attachedChecks.add(0, check);
}
public static void registerNotSupportiveCheck(NotSupportiveCheck check) {
NOT_SUPPORTIVE_CHECKS.add(0, check);
public static synchronized void registerNotSupportiveCheck(NotSupportiveCheck check) {
BlockMovementChecksImpl.notSupportiveChecks.add(0, check);
}
public static void registerAllChecks(AllChecks checks) {
registerMovementNecessaryCheck(checks);
registerMovementAllowedCheck(checks);
registerBrittleCheck(checks);
registerAttachedCheck(checks);
registerNotSupportiveCheck(checks);
}
// Actual check methods
// queries
public static boolean isMovementNecessary(BlockState state, Level world, BlockPos pos) {
for (MovementNecessaryCheck check : MOVEMENT_NECESSARY_CHECKS) {
for (MovementNecessaryCheck check : movementNecessaryChecks) {
CheckResult result = check.isMovementNecessary(state, world, pos);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isMovementNecessaryFallback(state, world, pos);
return BlockMovementChecksImpl.isMovementNecessaryFallback(state, world, pos);
}
public static boolean isMovementAllowed(BlockState state, Level world, BlockPos pos) {
for (MovementAllowedCheck check : MOVEMENT_ALLOWED_CHECKS) {
for (MovementAllowedCheck check : movementAllowedChecks) {
CheckResult result = check.isMovementAllowed(state, world, pos);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isMovementAllowedFallback(state, world, pos);
return BlockMovementChecksImpl.isMovementAllowedFallback(state, world, pos);
}
/**
* Brittle blocks will be collected first, as they may break when other blocks
* are removed before them
*/
public static boolean isBrittle(BlockState state) {
for (BrittleCheck check : BRITTLE_CHECKS) {
for (BrittleCheck check : brittleChecks) {
CheckResult result = check.isBrittle(state);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isBrittleFallback(state);
return BlockMovementChecksImpl.isBrittleFallback(state);
}
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction) {
for (AttachedCheck check : ATTACHED_CHECKS) {
for (AttachedCheck check : attachedChecks) {
CheckResult result = check.isBlockAttachedTowards(state, world, pos, direction);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isBlockAttachedTowardsFallback(state, world, pos, direction);
return BlockMovementChecksImpl.isBlockAttachedTowardsFallback(state, world, pos, direction);
}
/**
* Non-Supportive blocks will not continue a chain of blocks picked up by e.g. a
* piston
*/
public static boolean isNotSupportive(BlockState state, Direction facing) {
for (NotSupportiveCheck check : NOT_SUPPORTIVE_CHECKS) {
for (NotSupportiveCheck check : notSupportiveChecks) {
CheckResult result = check.isNotSupportive(state, facing);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isNotSupportiveFallback(state, facing);
return BlockMovementChecksImpl.isNotSupportiveFallback(state, facing);
}
// Fallback checks
// fallbacks
private static boolean isMovementNecessaryFallback(BlockState state, Level world, BlockPos pos) {
if (isBrittle(state))
if (BlockMovementChecks.isBrittle(state))
return true;
if (AllBlockTags.MOVABLE_EMPTY_COLLIDER.matches(state))
return true;
if (state.getCollisionShape(world, pos)
.isEmpty())
return false;
if (state.canBeReplaced())
return false;
return true;
return !state.canBeReplaced();
}
private static boolean isMovementAllowedFallback(BlockState state, Level world, BlockPos pos) {
@ -194,7 +180,7 @@ public class BlockMovementChecks {
return false;
if (AllBlockTags.NON_MOVABLE.matches(state))
return false;
if (ContraptionMovementSetting.get(state.getBlock()) == ContraptionMovementSetting.UNMOVABLE)
if (ContraptionMovementSetting.get(state) == ContraptionMovementSetting.UNMOVABLE)
return false;
// Move controllers only when they aren't moving
@ -212,8 +198,8 @@ public class BlockMovementChecks {
}
if (block instanceof PulleyBlock) {
BlockEntity be = world.getBlockEntity(pos);
if (be instanceof PulleyBlockEntity)
return !((PulleyBlockEntity) be).running;
if (be instanceof PulleyBlockEntity pulley)
return !pulley.running;
}
if (AllBlocks.BELT.has(state))
@ -260,8 +246,7 @@ public class BlockMovementChecks {
return AllBlockTags.BRITTLE.matches(state);
}
private static boolean isBlockAttachedTowardsFallback(BlockState state, Level world, BlockPos pos,
Direction direction) {
private static boolean isBlockAttachedTowardsFallback(BlockState state, Level world, BlockPos pos, Direction direction) {
Block block = state.getBlock();
if (block instanceof LadderBlock)
return state.getValue(LadderBlock.FACING) == direction.getOpposite();
@ -337,7 +322,7 @@ public class BlockMovementChecks {
return ConnectivityHandler.isConnected(world, pos, pos.relative(direction));
if (AllBlocks.STICKER.has(state) && state.getValue(StickerBlock.EXTENDED)) {
return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
&& !BlockMovementChecks.isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
}
if (block instanceof AbstractBogeyBlock<?> bogey)
return bogey.getStickySurfaces(world, pos, state)
@ -357,7 +342,7 @@ public class BlockMovementChecks {
return state.getValue(BlockStateProperties.FACING) == facing;
if (AllBlocks.CART_ASSEMBLER.has(state))
return Direction.DOWN == facing;
return facing == Direction.DOWN;
if (AllBlocks.MECHANICAL_SAW.has(state))
return state.getValue(BlockStateProperties.FACING) == facing;
if (AllBlocks.PORTABLE_STORAGE_INTERFACE.has(state))
@ -381,60 +366,6 @@ public class BlockMovementChecks {
return facing == state.getValue(StickerBlock.FACING);
if (state.getBlock() instanceof SlidingDoorBlock)
return false;
return isBrittle(state);
return BlockMovementChecks.isBrittle(state);
}
// Check classes
public static interface MovementNecessaryCheck {
public CheckResult isMovementNecessary(BlockState state, Level world, BlockPos pos);
}
public static interface MovementAllowedCheck {
public CheckResult isMovementAllowed(BlockState state, Level world, BlockPos pos);
}
public static interface BrittleCheck {
/**
* Brittle blocks will be collected first, as they may break when other blocks
* are removed before them
*/
public CheckResult isBrittle(BlockState state);
}
public static interface AttachedCheck {
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public CheckResult isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction);
}
public static interface NotSupportiveCheck {
/**
* Non-Supportive blocks will not continue a chain of blocks picked up by e.g. a
* piston
*/
public CheckResult isNotSupportive(BlockState state, Direction direction);
}
public static interface AllChecks
extends MovementNecessaryCheck, MovementAllowedCheck, BrittleCheck, AttachedCheck, NotSupportiveCheck {
}
public static enum CheckResult {
SUCCESS, FAIL, PASS;
public Boolean toBoolean() {
return this == PASS ? null : (this == SUCCESS ? true : false);
}
public static CheckResult of(boolean b) {
return b ? SUCCESS : FAIL;
}
public static CheckResult of(Boolean b) {
return b == null ? PASS : (b ? SUCCESS : FAIL);
}
}
}