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

feat(block): 添加按钮方块行为

This commit is contained in:
jhqwqmc
2025-09-19 09:16:26 +08:00
parent 13bcf6333c
commit 6ed54af7ab
15 changed files with 444 additions and 24 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.MEntitySelector;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MGameEvent;
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), MEntitySelector.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 ? MGameEvent.BLOCK_ACTIVATE : MGameEvent.BLOCK_DEACTIVATE)
: flag ? MGameEvent.BLOCK_ACTIVATE : MGameEvent.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(MGameEvent.BLOCK_ACTIVATE) : MGameEvent.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.AttachFace;
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<AttachFace> attachFaceProperty;
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<AttachFace> attachFace,
Property<HorizontalDirection> facing) {
super(customBlock);
this.tagsCanSurviveOn = tagsCanSurviveOn;
this.blockStatesCanSurviveOn = blockStatesCanSurviveOn;
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
this.blacklistMode = blacklist;
this.attachFaceProperty = attachFace;
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<AttachFace> face = (Property<AttachFace>) 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 ? AttachFace.CEILING : AttachFace.FLOOR)
.with(facing, context.getHorizontalDirection().toHorizontalDirection());
} else {
state = state.with(face, AttachFace.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.attachFaceProperty)) {
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<AttachFace> attachFace = (Property<AttachFace>) 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(), attachFace, 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.MEntitySelector;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.EventUtils;
@@ -28,6 +29,7 @@ import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
@@ -111,7 +113,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,
MEntitySelector.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

@@ -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

@@ -4400,4 +4400,10 @@ public final class CoreReflections {
"world.level.block.FenceGateBlock"
)
);
public static final Class<?> clazz$GameEvent = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.gameevent.GameEvent")
)
);
}

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 MEntitySelector {
private MEntitySelector() {}
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 MGameEvent {
private MGameEvent() {}
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

@@ -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 ATTACH_FACE = Key.of("craftengine:attach_face");
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(ATTACH_FACE, new EnumProperty.Factory<>(AttachFace.class));
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum AttachFace {
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