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

feat(block): 添加放置方块行为的黑白名单功能

This commit is contained in:
jhqwqmc
2025-06-24 03:30:15 +08:00
parent b6249e7b95
commit 2d54965730
11 changed files with 126 additions and 70 deletions

View File

@@ -9,17 +9,42 @@ import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
public abstract class FacingTriggerableBlockBehavior extends BukkitBlockBehavior {
protected static final List<Key> DEFAULT_BLACKLIST_BLOCKS = List.of(
Key.of("minecraft:bedrock"),
Key.of("minecraft:end_portal_frame"),
Key.of("minecraft:end_portal"),
Key.of("minecraft:nether_portal"),
Key.of("minecraft:barrier"),
Key.of("minecraft:command_block"),
Key.of("minecraft:chain_command_block"),
Key.of("minecraft:repeating_command_block"),
Key.of("minecraft:structure_block"),
Key.of("minecraft:end_gateway"),
Key.of("minecraft:jigsaw"),
Key.of("minecraft:structure_void"),
Key.of("minecraft:test_instance_block"),
Key.of("minecraft:moving_piston"),
Key.of("minecraft:test_block"),
Key.of("minecraft:light")
);
protected final Property<Direction> facingProperty;
protected final Property<Boolean> triggeredProperty;
protected final List<Key> blocks;
protected final boolean whitelistMode;
public FacingTriggerableBlockBehavior(CustomBlock customBlock, Property<Direction> facing, Property<Boolean> triggered) {
public FacingTriggerableBlockBehavior(CustomBlock customBlock, Property<Direction> facing, Property<Boolean> triggered, List<Key> blocks, boolean whitelistMode) {
super(customBlock);
this.facingProperty = facing;
this.triggeredProperty = triggered;
this.blocks = blocks;
this.whitelistMode = whitelistMode;
}
@Override
@@ -50,6 +75,17 @@ public abstract class FacingTriggerableBlockBehavior extends BukkitBlockBehavior
return state.owner().value().defaultState().with(this.facingProperty, direction);
}
protected boolean blockCheck(Object blockState) {
if (blockState == null || FastNMS.INSTANCE.method$BlockStateBase$isAir(blockState)) {
return false;
}
Key blockId = Optional.ofNullable(BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)))
.filter(state -> !state.isEmpty())
.map(state -> state.owner().value().id())
.orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(blockState));
return this.blocks.contains(blockId) == this.whitelistMode;
}
protected abstract Object getTickPriority();
protected abstract void tick(Object state, Object level, Object pos);

View File

@@ -17,36 +17,13 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class PickaxeBlockBehavior extends FacingTriggerableBlockBehavior {
public static final Factory FACTORY = new Factory();
private static final List<Key> DEFAULT_BLACK_BLOCKS = List.of(
Key.of("minecraft:bedrock"),
Key.of("minecraft:end_portal_frame"),
Key.of("minecraft:end_portal"),
Key.of("minecraft:nether_portal"),
Key.of("minecraft:barrier"),
Key.of("minecraft:command_block"),
Key.of("minecraft:chain_command_block"),
Key.of("minecraft:repeating_command_block"),
Key.of("minecraft:structure_block"),
Key.of("minecraft:end_gateway"),
Key.of("minecraft:jigsaw"),
Key.of("minecraft:structure_void"),
Key.of("minecraft:test_instance_block"),
Key.of("minecraft:moving_piston"),
Key.of("minecraft:test_block"),
Key.of("minecraft:light")
);
private final List<Key> blocks;
private final boolean whitelistMode;
public PickaxeBlockBehavior(CustomBlock customBlock, Property<Direction> facing, Property<Boolean> triggered, List<Key> blocks, boolean whitelistMode) {
super(customBlock, facing, triggered);
this.blocks = blocks;
this.whitelistMode = whitelistMode;
super(customBlock, facing, triggered, blocks, whitelistMode);
}
@Override
@@ -67,22 +44,11 @@ public class PickaxeBlockBehavior extends FacingTriggerableBlockBehavior {
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (blockState == null || blockState.isEmpty()) return;
Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, DirectionUtils.toNMSDirection(blockState.get(this.facingProperty)));
if (breakCheck(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) {
if (blockCheck(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) {
FastNMS.INSTANCE.method$LevelWriter$destroyBlock(level, blockPos, true, null, 512);
}
}
private boolean breakCheck(Object blockState) {
if (blockState == null || FastNMS.INSTANCE.method$BlockStateBase$isAir(blockState)) {
return false;
}
Key blockId = Optional.ofNullable(BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState)))
.filter(state -> !state.isEmpty())
.map(state -> state.owner().value().id())
.orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(blockState));
return this.blocks.contains(blockId) == this.whitelistMode;
}
public static class Factory implements BlockBehaviorFactory {
@Override
@@ -93,7 +59,7 @@ public class PickaxeBlockBehavior extends FacingTriggerableBlockBehavior {
boolean whitelistMode = (boolean) arguments.getOrDefault("whitelist", false);
List<Key> blocks = MiscUtils.getAsStringList(arguments.get("blocks")).stream().map(Key::of).toList();
if (blocks.isEmpty() && !whitelistMode) {
blocks = DEFAULT_BLACK_BLOCKS;
blocks = FacingTriggerableBlockBehavior.DEFAULT_BLACKLIST_BLOCKS;
}
return new PickaxeBlockBehavior(block, facing, triggered, blocks, whitelistMode);
}

View File

@@ -21,6 +21,8 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.Location;
@@ -38,8 +40,8 @@ import java.util.function.Function;
public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
public static final Factory FACTORY = new Factory();
public PlaceBlockBehavior(CustomBlock customBlock, Property<Direction> facing, Property<Boolean> triggered) {
super(customBlock, facing, triggered);
public PlaceBlockBehavior(CustomBlock customBlock, Property<Direction> facing, Property<Boolean> triggered, List<Key> blocks, boolean whitelistMode) {
super(customBlock, facing, triggered, blocks, whitelistMode);
}
@Override
@@ -69,17 +71,23 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
return false;
} else {
Object itemStack1 = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack);
boolean flag = CoreReflections.clazz$BlockItem.isInstance(itemStack1)
&& FastNMS.INSTANCE.method$InteractionResult$consumesAction(FastNMS.INSTANCE.method$BlockItem$place(
itemStack1, FastNMS.INSTANCE.constructor$PlaceBlockBlockPlaceContext(
level, CoreReflections.instance$InteractionHand$MAIN_HAND, itemStack,
FastNMS.INSTANCE.constructor$BlockHitResult(
FastNMS.INSTANCE.method$BlockPos$getCenter(LocationUtils.toBlockPos(blockPos1)),
DirectionUtils.toNMSDirection(opposite),
LocationUtils.toBlockPos(blockPos1),
false
)
)));
boolean flag = false;
if (CoreReflections.clazz$BlockItem.isInstance(itemStack1)) {
Object block = FastNMS.INSTANCE.method$BlockItem$getBlock(itemStack1);
if (blockCheck(FastNMS.INSTANCE.method$Block$defaultState(block))) {
Object blockHitResult = FastNMS.INSTANCE.constructor$BlockHitResult(
FastNMS.INSTANCE.method$BlockPos$getCenter(LocationUtils.toBlockPos(blockPos1)),
DirectionUtils.toNMSDirection(opposite),
LocationUtils.toBlockPos(blockPos1),
false
);
Object placeBlockBlockPlaceContext = FastNMS.INSTANCE.constructor$PlaceBlockBlockPlaceContext(
level, CoreReflections.instance$InteractionHand$MAIN_HAND, itemStack, blockHitResult
);
Object interactionResult = FastNMS.INSTANCE.method$BlockItem$place(itemStack1, placeBlockBlockPlaceContext);
flag = FastNMS.INSTANCE.method$InteractionResult$consumesAction(interactionResult);
}
}
if (!flag) {
Item<ItemStack> item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
@@ -93,12 +101,19 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
CraftEngine.instance().logger().warn("Failed to place unknown block " + blockItemBehavior.block());
continue;
}
ImmutableBlockState placeBlockState = optionalBlock.get().defaultState();
if (placeBlockState.contains(this.facingProperty)) {
placeBlockState = placeBlockState.with(this.facingProperty, opposite);
}
if (blockCheck(placeBlockState.customBlockState().handle())) {
continue;
}
Location placeLocation = new Location(FastNMS.INSTANCE.method$Level$getCraftWorld(level), blockPos1.x(), blockPos1.y(), blockPos1.z());
if (placeLocation.getBlock().getType() != Material.AIR) {
break;
}
// TODO: 修复放置多方块自定义方块问题
if (CraftEngineBlocks.place(placeLocation, optionalBlock.get().defaultState(), UpdateOption.UPDATE_ALL_IMMEDIATE, true)) {
if (CraftEngineBlocks.place(placeLocation, placeBlockState, UpdateOption.UPDATE_ALL_IMMEDIATE, true)) {
return true;
}
}
@@ -211,7 +226,12 @@ public class PlaceBlockBehavior extends FacingTriggerableBlockBehavior {
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Direction> facing = (Property<Direction>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.place_block.missing_facing");
Property<Boolean> triggered = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("triggered"), "warning.config.block.behavior.place_block.missing_triggered");
return new PlaceBlockBehavior(block, facing, triggered);
boolean whitelistMode = (boolean) arguments.getOrDefault("whitelist", false);
List<Key> blocks = MiscUtils.getAsStringList(arguments.get("blocks")).stream().map(Key::of).toList();
if (blocks.isEmpty() && !whitelistMode) {
blocks = FacingTriggerableBlockBehavior.DEFAULT_BLACKLIST_BLOCKS;
}
return new PlaceBlockBehavior(block, facing, triggered, blocks, whitelistMode);
}
}
}

View File

@@ -92,8 +92,7 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
}
public void setJavaComponent(Object type, Object value) {
setSparrowNBTComponent(type, MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value));
// setComponentInternal(type, MRegistryOps.JAVA, value); // 这里可能出现潜在的Integer和Boolean不区分问题
setComponentInternal(type, MRegistryOps.JAVA, value);
}
public void setJsonComponent(Object type, JsonElement value) {

View File

@@ -162,6 +162,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.ROTATE_HEAD, NetworkReflections.clazz$ClientboundRotateHeadPacket);
registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket);
registerNMSPacketConsumer(PacketConsumers.CLIENT_INFO, NetworkReflections.clazz$ServerboundClientInformationPacket);
registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());

View File

@@ -2414,4 +2414,23 @@ public class PacketConsumers {
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityMotionPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CLIENT_INFO = (user, event, packet) -> {
try {
Map<String, Object> information = FastNMS.INSTANCE.method$ServerboundClientInformationPacket$information(packet);
user.setClientInformation(new ClientInformation(
(String) information.getOrDefault("language", "en_us"),
(int) information.getOrDefault("viewDistance", 2),
information.getOrDefault("chatVisibility", null),
(boolean) information.getOrDefault("chatColors", true),
(int) information.getOrDefault("modelCustomisation", 0),
information.getOrDefault("mainHand", null),
(boolean) information.getOrDefault("textFilteringEnabled", false),
(boolean) information.getOrDefault("allowsListing", false),
information.getOrDefault("particleStatus", null)
));
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityMotionPacket", e);
}
};
}

View File

@@ -1089,20 +1089,12 @@ public final class NetworkReflections {
ReflectionUtils.getDeclaredConstructor(clazz$ClientboundMoveEntityPacket$Pos, int.class, short.class, short.class, short.class, boolean.class)
);
// 1.20.2+
public static final Class<?> clazz$ServerboundClientInformationPacket =
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundClientInformationPacket"));
// 1.20.2+
public static final Constructor<?> constructor$ServerboundClientInformationPacket = Optional.ofNullable(clazz$ServerboundClientInformationPacket)
.map(it -> ReflectionUtils.getConstructor(it, 1))
.orElse(null);
// 1.20.2+
public static final Field field$ServerboundClientInformationPacket$information = Optional.ofNullable(clazz$ServerboundClientInformationPacket)
.map(it -> ReflectionUtils.getDeclaredField(it, 0))
.orElse(null);
public static final Class<?> clazz$ServerboundClientInformationPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
List.of("network.protocol.game.PacketPlayInSettings", "network.protocol.common.ServerboundClientInformationPacket"),
List.of("network.protocol.game.ServerboundClientInformationPacket", "network.protocol.common.ServerboundClientInformationPacket")
)
);
public static final Class<?> clazz$ClientboundSetTitleTextPacket = requireNonNull(
ReflectionUtils.getClazz(

View File

@@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.ProtocolVersion;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.ClientInformation;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
@@ -75,6 +76,7 @@ public class BukkitServerPlayer extends Player {
// client side dimension info
private int sectionCount;
private Key clientSideDimension;
private ClientInformation clientInformation;
// check main hand/offhand interaction
private int lastSuccessfulInteraction;
// re-sync attribute timely to prevent some bugs
@@ -884,6 +886,16 @@ public class BukkitServerPlayer extends Player {
this.sentResourcePack = sentResourcePack;
}
@Override
public void setClientInformation(ClientInformation clientInfo) {
this.clientInformation = clientInfo;
}
@Override
public ClientInformation getClientInformation() {
return this.clientInformation;
}
@Override
public void clearView() {
this.entityTypeView.clear();