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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package net.momirealms.craftengine.core.block.state.properties;
|
||||
|
||||
public enum AttachFace {
|
||||
FLOOR,
|
||||
WALL,
|
||||
CEILING
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user