From a56d94a80342493d3834baf26f7fcff146f9e702 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 12 Jun 2025 02:09:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=89=93=E7=81=AB=E7=9F=B3?= =?UTF-8?q?=E9=9F=B3=E6=95=88=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/BukkitItemManager.java | 2 + .../item/behavior/BukkitItemBehaviors.java | 2 + .../behavior/FlintAndSteelItemBehavior.java | 120 ++++++++++++++++++ .../item/listener/ItemEventListener.java | 1 + .../reflection/minecraft/CoreReflections.java | 16 +++ .../plugin/user/BukkitServerPlayer.java | 5 +- .../bukkit/util/BlockStateUtils.java | 15 +++ .../default/configuration/blocks.yml | 2 +- .../core/entity/player/Player.java | 7 +- .../craftengine/core/item/ItemKeys.java | 1 + 10 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index d909239f3..627e1071a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item; import com.saicone.rtag.item.ItemTagStream; import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; +import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener; @@ -43,6 +44,7 @@ public class BukkitItemManager extends AbstractItemManager { static { registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); + registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL); } private static BukkitItemManager instance; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index f8081572b..a30c6bb03 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -10,6 +10,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item"); public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item"); + public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -18,5 +19,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY); register(BUCKET_ITEM, BucketItemBehavior.FACTORY); + register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java new file mode 100644 index 000000000..66b918a98 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FlintAndSteelItemBehavior.java @@ -0,0 +1,120 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.InteractUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.sound.SoundSource; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.nio.file.Path; +import java.util.Map; + +public class FlintAndSteelItemBehavior extends ItemBehavior { + public static final FlintAndSteelItemBehavior INSTANCE = new FlintAndSteelItemBehavior(); + public static final Factory FACTORY = new Factory(); + private static final Key FLINT_SOUND = Key.of("item.flintandsteel.use"); + + @SuppressWarnings("unchecked") + @Override + public InteractionResult useOnBlock(UseOnContext context) { + BlockPos clickedPos = context.getClickedPos(); + BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos); + Block block = clicked.block(); + BlockPos firePos = clickedPos.relative(context.getClickedFace()); + Direction direction = context.getHorizontalDirection(); + + // 最基础的判断能不能着火,不能着火都是扯蛋 + try { + if (!(boolean) CoreReflections.method$BaseFireBlock$canBePlacedAt.invoke(null, context.getLevel().serverWorld(), LocationUtils.toBlockPos(firePos), DirectionUtils.toNMSDirection(direction))) { + return InteractionResult.PASS; + } + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to run BaseFireBlock$canBePlacedAt", e); + return InteractionResult.PASS; + } + + net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer(); + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData())); + if (state == null || state.isEmpty()) { + return InteractionResult.PASS; + } else { + // 玩家交互目标是自定义方块 + if (context.getClickedFace() == Direction.UP) { + // 客户端层面必须可交互 + if (!InteractUtils.isInteractable((Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item) context.getItem())) { + return InteractionResult.PASS; + } + // 且没有shift + if (!player.isSecondaryUseActive()) { + player.playSound(FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + } + } else { + BlockData vanillaBlockState = BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()); + // 原版状态可燃烧,则跳过 + if (BlockStateUtils.isBurnable(BlockStateUtils.blockDataToBlockState(vanillaBlockState))) { + return InteractionResult.PASS; + } + + // 客户端一定觉得这个东西不可燃烧 + BlockPos belowFirePos = firePos.relative(Direction.DOWN); + BukkitBlockInWorld belowFireBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(belowFirePos); + boolean belowCanBurn; + try { + Block belowBlock = belowFireBlock.block(); + belowCanBurn = BlockStateUtils.isBurnable(BlockStateUtils.blockDataToBlockState(belowBlock.getBlockData())) || + (boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke( + BlockStateUtils.blockDataToBlockState(belowFireBlock.block().getBlockData()), context.getLevel().serverWorld(), LocationUtils.toBlockPos(belowFirePos), CoreReflections.instance$Direction$UP, CoreReflections.instance$SupportType$FULL); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to call method$BlockStateBase$isFaceSturdy", e); + return InteractionResult.PASS; + } + + // 客户端觉得这玩意可交互,就会忽略声音 + if (InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item) context.getItem())) { + // 如果按住了shift,则不会挥手,补发 + if (player.isSecondaryUseActive()) { + // 如果底部不能燃烧,则燃烧点位为侧面,需要补发 + if (!belowCanBurn) { + player.playSound(FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.swingHand(context.getHand()); + } + } else { + player.playSound(FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + } + } else { + if (!belowCanBurn) { + player.playSound(FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); + player.swingHand(context.getHand()); + } + } + } + } + return InteractionResult.PASS; + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, Key id, Map arguments) { + return INSTANCE; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index 0bec09f6b..e246395ee 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -33,6 +33,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index e8cf86bb1..b5dbea9c1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -5,6 +5,7 @@ import com.google.gson.JsonElement; import com.mojang.serialization.DynamicOps; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils; @@ -3296,4 +3297,19 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to initialize reflection", e); } } + + public static final Class clazz$BaseFireBlock = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.block.BlockFireAbstract", + "world.level.block.BaseFireBlock" + ) + ); + + public static final Method method$BaseFireBlock$canBePlacedAt = requireNonNull( + ReflectionUtils.getStaticMethod(clazz$BaseFireBlock, boolean.class, clazz$Level, clazz$BlockPos, clazz$Direction) + ); + + public static final Field field$FireBlock$igniteOdds = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$FireBlock, Object2IntMap.class, 0) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 436b5b6a9..16c8c2bfb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -30,6 +30,7 @@ import net.momirealms.craftengine.core.plugin.context.CooldownData; 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.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -283,8 +284,8 @@ public class BukkitServerPlayer extends Player { } @Override - public void playSound(Key sound, float volume, float pitch) { - platformPlayer().playSound(platformPlayer(), sound.toString(), SoundCategory.MASTER, volume, pitch); + public void playSound(Key sound, SoundSource source, float volume, float pitch) { + platformPlayer().playSound(platformPlayer(), sound.toString(), SoundUtils.toBukkit(source), volume, pitch); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index c2eb45f8c..c0079d0e9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -2,8 +2,10 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; 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.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.core.block.*; @@ -22,18 +24,26 @@ import org.jetbrains.annotations.Nullable; import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; public class BlockStateUtils { public static final IdentityHashMap CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>(); private static int vanillaStateSize; private static boolean hasInit; + public static Map IGNITE_ODDS; + @SuppressWarnings("unchecked") public static void init(int size) { if (hasInit) { throw new IllegalStateException("BlockStateUtils has already been initialized"); } vanillaStateSize = size; + try { + IGNITE_ODDS = (Map) CoreReflections.field$FireBlock$igniteOdds.get(MBlocks.FIRE); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize instance$FireBlock$igniteOdds", e); + } hasInit = true; } @@ -248,4 +258,9 @@ public class BlockStateUtils { public static int vanillaStateSize() { return vanillaStateSize; } + + public static boolean isBurnable(Object state) { + Object blockOwner = getBlockOwner(state); + return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0; + } } diff --git a/common-files/src/main/resources/resources/default/configuration/blocks.yml b/common-files/src/main/resources/resources/default/configuration/blocks.yml index 4c1fbbc7d..3fa3526c9 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks.yml @@ -167,7 +167,7 @@ items#misc: template: "default:loot_table/self" settings: template: - - default:sound/sand + - default:sound/stone - default:pickaxe_power/level_1 - default:settings/solid_1x1x1 overrides: diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 5e0309c97..302d40d78 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.BlockPos; import org.jetbrains.annotations.Nullable; @@ -81,7 +82,11 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { playSound(sound, 1f, 1f); } - public abstract void playSound(Key sound, float volume, float pitch); + public void playSound(Key sound, float volume, float pitch) { + playSound(sound, SoundSource.MASTER, volume, pitch); + } + + public abstract void playSound(Key sound, SoundSource source, float volume, float pitch); public abstract void giveItem(Item item); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index d8419d2ea..a90a44e69 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.util.Key; public class ItemKeys { public static final Key AIR = Key.of("minecraft:air"); + public static final Key FLINT_AND_STEEL = Key.of("minecraft:flint_and_steel"); public static final Key STONE = Key.of("minecraft:stone"); public static final Key TRIDENT = Key.of("minecraft:trident"); public static final Key SHIELD = Key.of("minecraft:shield");