9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 18:09:27 +00:00

Merge pull request #384 from jhqwqmc/dev

feat(block): 添加按钮方块行为
This commit is contained in:
XiaoMoMi
2025-09-20 03:02:58 +08:00
committed by GitHub
19 changed files with 573 additions and 33 deletions

View File

@@ -37,6 +37,8 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key SIMPLE_PARTICLE_BLOCK = Key.from("craftengine:simple_particle_block");
public static final Key WALL_TORCH_PARTICLE_BLOCK = Key.from("craftengine:wall_torch_particle_block");
public static final Key FENCE_BLOCK = Key.from("craftengine:fence_block");
public static final Key BUTTON_BLOCK = Key.from("craftengine:button_block");
public static final Key FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK = Key.from("craftengine:face_attached_horizontal_directional_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -72,5 +74,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(SIMPLE_PARTICLE_BLOCK, SimpleParticleBlockBehavior.FACTORY);
register(WALL_TORCH_PARTICLE_BLOCK, WallTorchParticleBlockBehavior.FACTORY);
register(FENCE_BLOCK, FenceBlockBehavior.FACTORY);
register(BUTTON_BLOCK, ButtonBlockBehavior.FACTORY);
register(FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK, FaceAttachedHorizontalDirectionalBlockBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,217 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntitySelectors;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MGameEvents;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.BooleanProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class ButtonBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final BooleanProperty poweredProperty;
private final int ticksToStayPressed;
private final boolean canButtonBeActivatedByArrows;
private final SoundData buttonClickOnSound;
private final SoundData buttonClickOffSound;
public ButtonBlockBehavior(CustomBlock customBlock,
BooleanProperty powered,
int ticksToStayPressed,
boolean canButtonBeActivatedByArrows,
SoundData buttonClickOnSound,
SoundData buttonClickOffSound) {
super(customBlock);
this.poweredProperty = powered;
this.ticksToStayPressed = ticksToStayPressed;
this.canButtonBeActivatedByArrows = canButtonBeActivatedByArrows;
this.buttonClickOnSound = buttonClickOnSound;
this.buttonClickOffSound = buttonClickOffSound;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
if (!state.get(this.poweredProperty)) {
press(BlockStateUtils.getBlockOwner(state.customBlockState().literalObject()),
state, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()),
context.getPlayer() != null ? context.getPlayer().serverPlayer() : null);
return InteractionResult.SUCCESS_AND_CANCEL;
}
return InteractionResult.PASS;
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (blockState == null) return;
if (FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3]) && !blockState.get(this.poweredProperty)) {
press(thisBlock, blockState, args[1], args[2], null);
}
}
@Override
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) {
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (blockState == null) return;
if (!(boolean) args[3] && blockState.get(this.poweredProperty)) {
updateNeighbours(thisBlock, blockState, args[1], args[2]);
}
}
@Override
public void onRemove(Object thisBlock, Object[] args, Callable<Object> superMethod) {
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (blockState == null) return;
if (!(boolean) args[4] && blockState.get(this.poweredProperty)) {
updateNeighbours(thisBlock, blockState, args[1], args[2]);
}
}
@Override
public int getSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (blockState == null) return 0;
return blockState.get(this.poweredProperty) ? 15 : 0;
}
@Override
public int getDirectSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (blockState == null) return 0;
return blockState.get(this.poweredProperty)
&& FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(blockState)
== DirectionUtils.fromNMSDirection(args[3]) ? 15 : 0;
}
@Override
public boolean isSignalSource(Object thisBlock, Object[] args, Callable<Object> superMethod) {
return true;
}
@Override
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object state = args[0];
Object level = args[1];
Object pos = args[2];
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
if (blockState == null) return;
if (blockState.get(this.poweredProperty)) {
checkPressed(thisBlock, state, level, pos);
}
}
@Override
public void entityInside(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object state = args[0];
Object level = args[1];
Object pos = args[2];
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
if (blockState == null) return;
if (this.canButtonBeActivatedByArrows && !blockState.get(this.poweredProperty)) {
checkPressed(thisBlock, state, level, pos);
}
}
private void checkPressed(Object thisBlock, Object state, Object level, Object pos) {
Object abstractArrow = this.canButtonBeActivatedByArrows ? FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(
level, CoreReflections.clazz$AbstractArrow, FastNMS.INSTANCE.method$AABB$move(
FastNMS.INSTANCE.method$VoxelShape$bounds(FastNMS.INSTANCE.method$BlockState$getShape(
state, level, pos, CoreReflections.instance$CollisionContext$empty
)), pos), MEntitySelectors.NO_SPECTATORS).stream().findFirst().orElse(null) : null;
boolean flag = abstractArrow != null;
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
if (blockState == null) return;
boolean poweredValue = blockState.get(this.poweredProperty);
if (flag != poweredValue) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState.with(this.poweredProperty, flag).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
updateNeighbours(thisBlock, blockState, level, pos);
playSound(null, level, pos, flag);
Object gameEvent = VersionHelper.isOrAbove1_20_5()
? FastNMS.INSTANCE.method$Holder$direct(flag ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE)
: flag ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE;
FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, abstractArrow, gameEvent, pos);
}
if (flag) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed);
}
}
private void updateNeighbours(Object thisBlock, ImmutableBlockState state, Object level, Object pos) {
Direction direction = FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(state);
if (direction == null) return;
Direction opposite = direction.opposite();
Object nmsDirection = DirectionUtils.toNMSDirection(opposite);
Object orientation = null;
if (VersionHelper.isOrAbove1_21_2()) {
@SuppressWarnings("unchecked")
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) state.owner().value().getProperty("facing");
if (facing != null) {
orientation = FastNMS.INSTANCE.method$ExperimentalRedstoneUtils$initialOrientation(
level, nmsDirection, opposite.axis().isHorizontal() ? CoreReflections.instance$Direction$UP : DirectionUtils.toNMSDirection(state.get(facing).toDirection())
);
}
}
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, pos, thisBlock, orientation);
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, FastNMS.INSTANCE.method$BlockPos$relative(pos, nmsDirection), thisBlock, orientation);
}
private void playSound(@Nullable Object player, Object level, Object pos, boolean hitByArrow) {
SoundData soundData = getSound(hitByArrow);
if (soundData == null) return;
Object sound = FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(soundData.id()), Optional.empty());
FastNMS.INSTANCE.method$LevelAccessor$playSound(level, player, pos, sound, CoreReflections.instance$SoundSource$BLOCKS, soundData.volume().get(), soundData.pitch().get());
}
private SoundData getSound(boolean isOn) {
return isOn ? this.buttonClickOnSound : this.buttonClickOffSound;
}
private void press(Object thisBlock, ImmutableBlockState state, Object level, Object pos, @Nullable Object player) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, state.with(this.poweredProperty, true).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed);
playSound(player, level, pos, true);
Object gameEvent = VersionHelper.isOrAbove1_20_5() ? FastNMS.INSTANCE.method$Holder$direct(MGameEvents.BLOCK_ACTIVATE) : MGameEvents.BLOCK_ACTIVATE;
FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, player, gameEvent, pos);
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings({"unchecked", "DuplicatedCode"})
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
BooleanProperty powered = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.button.missing_powered");
int ticksToStayPressed = ResourceConfigUtils.getAsInt(arguments.getOrDefault("ticks-to-stay-pressed", 30), "ticks-to-stay-pressed");
boolean canButtonBeActivatedByArrows = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-button-be-activated-by-arrows", true), "can-button-be-activated-by-arrows");
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData buttonClickOnSound = null;
SoundData buttonClickOffSound = null;
if (sounds != null) {
buttonClickOnSound = Optional.ofNullable(sounds.get("on")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
buttonClickOffSound = Optional.ofNullable(sounds.get("off")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
return new ButtonBlockBehavior(block, powered, ticksToStayPressed, canButtonBeActivatedByArrows, buttonClickOnSound, buttonClickOffSound);
}
}
}

View File

@@ -25,7 +25,7 @@ import org.bukkit.Registry;
import java.util.*;
import java.util.concurrent.Callable;
public class DirectionalAttachedBlockBehavior extends AbstractCanSurviveBlockBehavior {
public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<?> facingProperty;
private final boolean isSixDirection;
@@ -37,12 +37,11 @@ public class DirectionalAttachedBlockBehavior extends AbstractCanSurviveBlockBeh
public DirectionalAttachedBlockBehavior(CustomBlock customBlock,
Property<?> facingProperty,
boolean isSixDirection,
int delay,
boolean blacklist,
List<Object> tagsCanSurviveOn,
Set<Object> blockStatesCanSurviveOn,
Set<String> customBlocksCansSurviveOn) {
super(customBlock, delay);
super(customBlock);
this.facingProperty = facingProperty;
this.isSixDirection = isSixDirection;
this.tagsCanSurviveOn = tagsCanSurviveOn;
@@ -69,8 +68,8 @@ public class DirectionalAttachedBlockBehavior extends AbstractCanSurviveBlockBeh
}
@Override
protected boolean canSurvive(Object thisBlock, Object blockState, Object world, Object pos) throws Exception {
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null);
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (state == null) return false;
DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null);
if (behavior == null) return false;
@@ -80,10 +79,10 @@ public class DirectionalAttachedBlockBehavior extends AbstractCanSurviveBlockBeh
} else {
direction = ((HorizontalDirection) state.get(behavior.facingProperty)).opposite().toDirection();
}
BlockPos blockPos = LocationUtils.fromBlockPos(pos).relative(direction);
BlockPos blockPos = LocationUtils.fromBlockPos(args[2]).relative(direction);
Object nmsPos = LocationUtils.toBlockPos(blockPos);
Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, nmsPos);
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, world, nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL)
Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], nmsPos);
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, args[1], nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL)
&& mayPlaceOn(nmsState);
}
@@ -144,14 +143,13 @@ public class DirectionalAttachedBlockBehavior extends AbstractCanSurviveBlockBeh
throw new LocalizedResourceConfigException("warning.config.block.behavior.directional_attached.missing_facing");
}
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay");
boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist");
return new DirectionalAttachedBlockBehavior(block, facing, isDirection, delay, blacklistMode, tuple.left(), tuple.mid(), tuple.right());
return new DirectionalAttachedBlockBehavior(block, facing, isDirection, blacklistMode, tuple.left(), tuple.mid(), tuple.right());
}
}
@SuppressWarnings("DuplicatedCode")
private static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments) {
public static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments) {
List<Object> mcTags = new ArrayList<>();
for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("attached-block-tags", List.of()))) {
mcTags.add(BlockTags.getOrCreate(Key.of(tag)));

View File

@@ -0,0 +1,150 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.AnchorType;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.Tuple;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
public class FaceAttachedHorizontalDirectionalBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<AnchorType> anchorTypeProperty;
private final Property<HorizontalDirection> facingProperty;
private final List<Object> tagsCanSurviveOn;
private final Set<Object> blockStatesCanSurviveOn;
private final Set<String> customBlocksCansSurviveOn;
private final boolean blacklistMode;
public FaceAttachedHorizontalDirectionalBlockBehavior(CustomBlock customBlock,
boolean blacklist,
List<Object> tagsCanSurviveOn,
Set<Object> blockStatesCanSurviveOn,
Set<String> customBlocksCansSurviveOn,
Property<AnchorType> anchorType,
Property<HorizontalDirection> facing) {
super(customBlock);
this.tagsCanSurviveOn = tagsCanSurviveOn;
this.blockStatesCanSurviveOn = blockStatesCanSurviveOn;
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
this.blacklistMode = blacklist;
this.anchorTypeProperty = anchorType;
this.facingProperty = facing;
}
@Override
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null));
if (direction == null) return false;
direction = direction.opposite();
Object nmsDirection = DirectionUtils.toNMSDirection(direction);
Object targetPos = FastNMS.INSTANCE.method$BlockPos$relative(args[2], nmsDirection);
Object targetState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], targetPos);
return canAttach(args[1], targetPos, nmsDirection, targetState) && mayPlaceOn(targetState);
}
@SuppressWarnings("unchecked")
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Property<AnchorType> face = (Property<AnchorType>) state.owner().value().getProperty("face");
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) state.owner().value().getProperty("facing");
if (face == null || facing == null) return null;
for (Direction direction : context.getNearestLookingDirections()) {
if (direction.axis() == Direction.Axis.Y) {
state = state
.with(face, direction == Direction.UP ? AnchorType.CEILING : AnchorType.FLOOR)
.with(facing, context.getHorizontalDirection().toHorizontalDirection());
} else {
state = state.with(face, AnchorType.WALL).with(facing, direction.opposite().toHorizontalDirection());
}
if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()))) {
return state;
}
}
return null;
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null));
if (direction == null) return MBlocks.AIR$defaultState;
if (DirectionUtils.toNMSDirection(direction.opposite()) == args[updateShape$direction] && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos])) {
return MBlocks.AIR$defaultState;
}
return superMethod.call();
}
private boolean mayPlaceOn(Object state) {
for (Object tag : this.tagsCanSurviveOn) {
if (FastNMS.INSTANCE.method$BlockStateBase$is(state, tag)) {
return !this.blacklistMode;
}
}
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
if (optionalCustomState.isEmpty()) {
if (!this.blockStatesCanSurviveOn.isEmpty() && this.blockStatesCanSurviveOn.contains(state)) {
return !this.blacklistMode;
}
} else {
ImmutableBlockState belowCustomState = optionalCustomState.get();
if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) {
return !this.blacklistMode;
}
if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) {
return !this.blacklistMode;
}
}
return this.blacklistMode;
}
public static boolean canAttach(Object level, Object targetPos, Object direction, Object targetState) {
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(
targetState, level, targetPos,
FastNMS.INSTANCE.method$Direction$getOpposite(direction),
CoreReflections.instance$SupportType$FULL
);
}
@Nullable
public static Direction getConnectedDirection(ImmutableBlockState state) {
if (state == null) return null;
FaceAttachedHorizontalDirectionalBlockBehavior behavior = state.behavior().getAs(FaceAttachedHorizontalDirectionalBlockBehavior.class).orElse(null);
if (behavior == null) return null;
return switch (state.get(behavior.anchorTypeProperty)) {
case CEILING -> Direction.DOWN;
case FLOOR -> Direction.UP;
default -> state.get(behavior.facingProperty).toDirection();
};
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<AnchorType> anchorType = (Property<AnchorType>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("face"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_face");
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_facing");
Tuple<List<Object>, Set<Object>, Set<String>> tuple = DirectionalAttachedBlockBehavior.readTagsAndState(arguments);
boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist");
return new FaceAttachedHorizontalDirectionalBlockBehavior(block, blacklistMode, tuple.left(), tuple.mid(), tuple.right(), anchorType, facing);
}
}
}

View File

@@ -4,6 +4,7 @@ import io.papermc.paper.event.entity.EntityInsideBlockEvent;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntitySelectors;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.EventUtils;
@@ -100,8 +101,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
int signalForState = this.getSignalForState(state);
if (signalForState == 0) {
this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock);
} else {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime);
}
}
@@ -111,7 +110,10 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
case MOBS -> CoreReflections.clazz$LivingEntity;
};
Object box = FastNMS.INSTANCE.method$AABB$move(CoreReflections.instance$BasePressurePlateBlock$TOUCH_AABB, pos);
return FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(level, box, clazz) > 0 ? 15 : 0;
return !FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(
level, clazz, box,
MEntitySelectors.NO_SPECTATORS.and(entity -> !FastNMS.INSTANCE.method$Entity$isIgnoringBlockTriggers(entity))
).isEmpty() ? 15 : 0;
}
private Object setSignalForState(Object state, int strength) {

View File

@@ -395,7 +395,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
JsonObject jsonObject = entry.getValue();
Key serializerType = Key.of(jsonObject.get("type").getAsString());
// noinspection unchecked
@SuppressWarnings("unchecked")
RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>> serializer = (RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType);
if (serializer == null) {
continue;

View File

@@ -12,6 +12,7 @@ import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
@@ -19,16 +20,18 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlockStateProperties;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MLootContextParams;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.BlockSettings;
import net.momirealms.craftengine.core.block.DelegatingBlockState;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.ObjectHolder;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.World;
@@ -39,6 +42,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
public final class BlockStateGenerator {
private static MethodHandle constructor$CraftEngineBlockState;
@@ -64,7 +69,21 @@ public final class BlockStateGenerator {
.method(ElementMatchers.is(CoreReflections.method$StateHolder$getValue))
.intercept(MethodDelegation.to(GetPropertyValueInterceptor.INSTANCE))
.method(ElementMatchers.is(CoreReflections.method$StateHolder$setValue))
.intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE));
.intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE))
.method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isBlock))
.intercept(MethodDelegation.to(IsBlockInterceptor.INSTANCE))
.method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isHolderSetBlock))
.intercept(MethodDelegation.to(IsHolderSetBlockInterceptor.INSTANCE));
if (CoreReflections.method$BlockStateBase$isHolderBlock != null) {
stateBuilder = stateBuilder
.method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isHolderBlock))
.intercept(MethodDelegation.to(IsHolderBlockInterceptor.INSTANCE));
}
if (CoreReflections.method$BlockStateBase$isResourceKeyBlock != null) {
stateBuilder = stateBuilder
.method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isResourceKeyBlock))
.intercept(MethodDelegation.to(IsResourceKeyBlockInterceptor.INSTANCE));
}
Class<?> clazz$CraftEngineBlock = stateBuilder.make().load(BlockStateGenerator.class.getClassLoader()).getLoaded();
constructor$CraftEngineBlockState = VersionHelper.isOrAbove1_20_5() ?
MethodHandles.publicLookup().in(clazz$CraftEngineBlock)
@@ -183,6 +202,78 @@ public final class BlockStateGenerator {
}
}
public static class IsBlockInterceptor {
public static final IsBlockInterceptor INSTANCE = new IsBlockInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args) {
DelegatingBlockState customState = (DelegatingBlockState) thisObj;
ImmutableBlockState thisState = customState.blockState();
if (thisState == null) return false;
if (!(args[0] instanceof DelegatingBlock delegatingBlock)) return false;
BlockBehavior behavior = delegatingBlock.behaviorDelegate().value();
if (behavior == null) return false;
return behavior.block().equals(thisState.owner().value());
}
}
public static class IsHolderSetBlockInterceptor {
public static final IsHolderSetBlockInterceptor INSTANCE = new IsHolderSetBlockInterceptor();
@SuppressWarnings("unchecked")
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args) {
DelegatingBlockState customState = (DelegatingBlockState) thisObj;
ImmutableBlockState thisState = customState.blockState();
if (thisState == null) return false;
CustomBlock thisBlock = thisState.owner().value();
for (Object holder : (Iterable<Object>) args[0]) {
Object block = FastNMS.INSTANCE.method$Holder$value(holder);
if (!(block instanceof DelegatingBlock delegatingBlock)) continue;
BlockBehavior behavior = delegatingBlock.behaviorDelegate().value();
if (behavior == null) continue;
if (behavior.block().equals(thisBlock)) return true;
}
return false;
}
}
public static class IsHolderBlockInterceptor {
public static final IsHolderBlockInterceptor INSTANCE = new IsHolderBlockInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args) {
Object block = FastNMS.INSTANCE.method$Holder$value(args[0]);
if (!(block instanceof DelegatingBlock delegatingBlock)) return false;
BlockBehavior behavior = delegatingBlock.behaviorDelegate().value();
if (behavior == null) return false;
DelegatingBlockState customState = (DelegatingBlockState) thisObj;
ImmutableBlockState thisState = customState.blockState();
if (thisState == null) return false;
CustomBlock thisBlock = thisState.owner().value();
return behavior.block().equals(thisBlock);
}
}
public static class IsResourceKeyBlockInterceptor {
public static final IsResourceKeyBlockInterceptor INSTANCE = new IsResourceKeyBlockInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args) {
Object block = FastNMS.INSTANCE.method$HolderGetter$getResourceKey(MBuiltInRegistries.BLOCK, args[0])
.map(FastNMS.INSTANCE::method$Holder$value)
.orElse(null);
if (!(block instanceof DelegatingBlock delegatingBlock)) return false;
BlockBehavior behavior = delegatingBlock.behaviorDelegate().value();
if (behavior == null) return false;
DelegatingBlockState customState = (DelegatingBlockState) thisObj;
ImmutableBlockState thisState = customState.blockState();
if (thisState == null) return false;
CustomBlock thisBlock = thisState.owner().value();
return behavior.block().equals(thisBlock);
}
}
public static class CreateStateInterceptor {
public static final CreateStateInterceptor INSTANCE = new CreateStateInterceptor();

View File

@@ -961,6 +961,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
*/
public static class HelloListener implements NMSPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) {
BukkitServerPlayer player = (BukkitServerPlayer) user;
@@ -984,7 +985,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
} else {
Optional<UUID> uuid;
try {
// noinspection unchecked
uuid = (Optional<UUID>) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet);
} catch (Throwable t) {
CraftEngine.instance().logger().severe("Failed to get uuid from ServerboundHelloPacket", t);
@@ -1478,6 +1478,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
public static class EditBookListener implements NMSPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) {
if (!Config.filterBook()) return;
@@ -1492,7 +1493,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
List<String> pages;
try {
// noinspection unchecked
pages = (List<String>) NetworkReflections.methodHandle$ServerboundEditBookPacket$pagesGetter.invokeExact(packet);
} catch (Throwable t) {
CraftEngine.instance().logger().warn("Failed to get pages from ServerboundEditBookPacket", t);
@@ -1501,7 +1501,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
List<String> newPages = new ArrayList<>(pages.size());
Optional<String> title;
try {
// noinspection unchecked
title = (Optional<String>) NetworkReflections.methodHandle$ServerboundEditBookPacket$titleGetter.invokeExact(packet);
} catch (Throwable t) {
CraftEngine.instance().logger().warn("Failed to get title from ServerboundEditBookPacket", t);
@@ -1733,6 +1732,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
public static class FinishConfigurationListener implements NMSPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) {
if (!VersionHelper.isOrAbove1_20_2() || !Config.sendPackOnJoin()) {
@@ -1793,7 +1793,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
}
Queue<Object> configurationTasks;
try {
// noinspection unchecked
configurationTasks = (Queue<Object>) CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$configurationTasksGetter.invokeExact(packetListener);
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to get configuration tasks for player " + user.name(), e);
@@ -3665,7 +3664,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != BaseEntityData.CustomName.id()) continue;
// noinspection unchecked
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;
Object textComponent = optionalTextComponent.get();

View File

@@ -25,7 +25,7 @@ import java.util.Optional;
public class MinecartPacketHandler implements EntityPacketHandler {
public static final MinecartPacketHandler INSTANCE = new MinecartPacketHandler();
private static final BlockStateHandler BLOCK_STATE_HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE;
private static final BlockStateHandler BLOCK_STATE_HANDLER = VersionHelper.isOrAbove1_21_5() ? BlockStateHandler_1_21_5.INSTANCE : BlockStateHandler_1_20.INSTANCE;
@Override
public void handleSetEntityData(Player user, ByteBufPacketEvent event) {
@@ -69,8 +69,8 @@ public class MinecartPacketHandler implements EntityPacketHandler {
Object handle(NetWorkUser user, Object packedItem, int entityDataId);
}
static class BlockStateHandler_1_21_3 implements BlockStateHandler {
protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_21_3();
static class BlockStateHandler_1_21_5 implements BlockStateHandler {
protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_21_5();
@Override
public Object handle(NetWorkUser user, Object packedItem, int entityDataId) {

View File

@@ -4400,4 +4400,36 @@ public final class CoreReflections {
"world.level.block.FenceGateBlock"
)
);
public static final Class<?> clazz$GameEvent = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.gameevent.GameEvent")
)
);
public static final Class<?> clazz$HolderSet = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("core.HolderSet")
)
);
public static final Method method$BlockStateBase$isBlock = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockStateBase, boolean.class, new String[]{"is", "a"}, clazz$Block)
);
public static final Method method$BlockStateBase$isHolderSetBlock = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockStateBase, boolean.class, new String[]{"is", "a"}, clazz$HolderSet)
);
// 1.20.2+
public static final Method method$BlockStateBase$isHolderBlock = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredMethod(clazz$BlockStateBase, boolean.class, new String[]{"is", "a"}, clazz$Holder),
VersionHelper.isOrAbove1_20_2()
);
// 1.20.3+
public static final Method method$BlockStateBase$isResourceKeyBlock = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredMethod(clazz$BlockStateBase, boolean.class, new String[]{"is", "a"}, clazz$ResourceKey),
VersionHelper.isOrAbove1_20_3()
);
}

View File

@@ -22,6 +22,7 @@ public final class MBuiltInRegistries {
public static final Object PARTICLE_TYPE;
public static final Object DATA_COMPONENT_TYPE;
public static final Object LOOT_POOL_ENTRY_TYPE;
public static final Object GAME_EVENT;
static {
Field[] fields = CoreReflections.clazz$BuiltInRegistries.getDeclaredFields();
@@ -37,6 +38,7 @@ public final class MBuiltInRegistries {
Object registries$RecipeType = null;
Object registries$DataComponentType = null;
Object registries$LootPoolEntryType = null;
Object registries$GameEvent = null;
for (Field field : fields) {
Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType paramType) {
@@ -67,6 +69,8 @@ public final class MBuiltInRegistries {
registries$Fluid = field.get(null);
} else if (type == CoreReflections.clazz$LootPoolEntryType) {
registries$LootPoolEntryType = field.get(null);
} else if (type == CoreReflections.clazz$GameEvent) {
registries$GameEvent = field.get(null);
}
}
}
@@ -82,6 +86,7 @@ public final class MBuiltInRegistries {
RECIPE_TYPE = requireNonNull(registries$RecipeType);
LOOT_POOL_ENTRY_TYPE = requireNonNull(registries$LootPoolEntryType);
DATA_COMPONENT_TYPE = registries$DataComponentType;
GAME_EVENT = requireNonNull(registries$GameEvent);
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init BuiltInRegistries", e);
}

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import java.util.function.Predicate;
public final class MEntitySelectors {
private MEntitySelectors() {}
public static final Predicate<Object> NO_SPECTATORS = entity -> !FastNMS.INSTANCE.method$Entity$isSpectator(entity);
}

View File

@@ -0,0 +1,15 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
public final class MGameEvents {
private MGameEvents() {}
public static final Object BLOCK_ACTIVATE = getById("block_activate");
public static final Object BLOCK_DEACTIVATE = getById("block_deactivate");
private static Object getById(String id) {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.GAME_EVENT, rl);
}
}

View File

@@ -325,6 +325,9 @@ warning.config.block.behavior.fence.missing_north: "<yellow>Issue found in file
warning.config.block.behavior.fence.missing_east: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'east' property for 'fence_block' behavior.</yellow>"
warning.config.block.behavior.fence.missing_south: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'south' property for 'fence_block' behavior.</yellow>"
warning.config.block.behavior.fence.missing_west: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'west' property for 'fence_block' behavior.</yellow>"
warning.config.block.behavior.face_attached_horizontal_directional.missing_face: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'face' property for 'face_attached_horizontal_directional_block' behavior.</yellow>"
warning.config.block.behavior.face_attached_horizontal_directional.missing_facing: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'facing' property for 'face_attached_horizontal_directional_block' behavior.</yellow>"
warning.config.block.behavior.button.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' property for 'button_block' behavior.</yellow>"
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'.</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid display position '<arg:2>' in 'generation.display' section. Allowed display positions: [<arg:3>]</yellow>"

View File

@@ -319,6 +319,9 @@ warning.config.block.behavior.fence.missing_north: "<yellow>在文件 <arg:0>
warning.config.block.behavior.fence.missing_east: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'fence_block' 行为缺少必需的 'east' 属性</yellow>"
warning.config.block.behavior.fence.missing_south: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'fence_block' 行为缺少必需的 'south' 属性</yellow>"
warning.config.block.behavior.fence.missing_west: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'fence_block' 行为缺少必需的 'west' 属性</yellow>"
warning.config.block.behavior.face_attached_horizontal_directional.missing_face: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'face_attached_horizontal_directional_block' 行为缺少必需的 'face' 属性</yellow>"
warning.config.block.behavior.face_attached_horizontal_directional.missing_facing: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'face_attached_horizontal_directional_block' 行为缺少必需的 'facing' 属性</yellow>"
warning.config.block.behavior.button.missing_powered: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'button_block' 行为缺少必需的 'powered' 属性</yellow>"
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation.display' 区域使用了无效的 display 位置类型 '<arg:2>'. 可用展示类型: [<arg:3>]</yellow>"

View File

@@ -55,18 +55,18 @@ public abstract class BlockBehavior {
superMethod.call();
}
// ServerLevel level, BlockPos pos, RandomSource random
// BlockState state, ServerLevel level, BlockPos pos, RandomSource random
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
superMethod.call();
}
// ServerLevel level, BlockPos pos, RandomSource random
// BlockState state, ServerLevel level, BlockPos pos, RandomSource random
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
superMethod.call();
}
// 1.20-1.20.4 BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify, UseOnContext context
// 1.20.5+ Level level, BlockPos pos, BlockState oldState, boolean movedByPiston
// 1.20.5+ BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
superMethod.call();
}
@@ -95,12 +95,12 @@ public abstract class BlockBehavior {
return false;
}
//BlockState state
// BlockState state
public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception {
return false;
}
//BlockState state, Level level, BlockPos pos
// BlockState state, Level level, BlockPos pos
public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception {
return 0;
}

View File

@@ -22,6 +22,7 @@ public final class Properties {
public static final Key STAIRS_SHAPE = Key.of("craftengine:stairs_shape");
public static final Key SLAB_TYPE = Key.of("craftengine:slab_type");
public static final Key SOFA_SHAPE = Key.of("craftengine:sofa_shape");
public static final Key ANCHOR_TYPE = Key.of("craftengine:anchor_type");
static {
register(BOOLEAN, BooleanProperty.FACTORY);
@@ -38,6 +39,7 @@ public final class Properties {
register(STAIRS_SHAPE, new EnumProperty.Factory<>(StairsShape.class));
register(SLAB_TYPE, new EnumProperty.Factory<>(SlabType.class));
register(SOFA_SHAPE, new EnumProperty.Factory<>(SofaShape.class));
register(ANCHOR_TYPE, new EnumProperty.Factory<>(AnchorType.class));
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum AnchorType {
FLOOR,
WALL,
CEILING
}

View File

@@ -50,7 +50,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3
snake_yaml_version=2.5
anti_grief_version=0.20
nms_helper_version=1.0.91
nms_helper_version=1.0.92
evalex_version=3.5.0
reactive_streams_version=1.0.4
amazon_awssdk_version=2.33.1