From e987bb5996fe70facc17c159c7edb2d5313e5902 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 05:00:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B6=B2=E4=BD=93=E7=A2=B0?= =?UTF-8?q?=E6=92=9E=E5=AE=B6=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/behavior/BukkitItemBehaviors.java | 2 + .../item/behavior/FurnitureItemBehavior.java | 2 +- .../LiquidCollisionFurnitureItemBehavior.java | 157 ++++++++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java 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 23fde9447..f236c8c29 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 @@ -8,6 +8,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key BLOCK_ITEM = Key.from("craftengine:block_item"); public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item"); public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); + public static final Key ON_LIQUID_FURNITURE_ITEM = Key.from("craftengine:liquid_collision_furniture_item"); public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item"); public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item"); public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); @@ -21,6 +22,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(BLOCK_ITEM, BlockItemBehavior.FACTORY); register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); + register(ON_LIQUID_FURNITURE_ITEM, LiquidCollisionFurnitureItemBehavior.FACTORY); register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY); register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 30863c737..12e62569e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -39,7 +39,7 @@ import java.util.function.Predicate; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); - private static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); + protected static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); private final Key id; private final Map rules; private final boolean ignorePlacer; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java new file mode 100644 index 000000000..74b31a612 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java @@ -0,0 +1,157 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.entity.furniture.AlignmentRule; +import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.core.entity.furniture.RotationRule; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +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.pack.PendingConfigSection; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +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.BlockHitResult; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.util.*; + +public class LiquidCollisionFurnitureItemBehavior extends FurnitureItemBehavior { + public static final Factory FACTORY = new Factory(); + private final List liquidTypes; + private final boolean sourceOnly; + + public LiquidCollisionFurnitureItemBehavior(Key id, Map rules, boolean ignorePlacer, boolean ignoreEntities, boolean sourceOnly, List liquidTypes) { + super(id, rules, ignorePlacer, ignoreEntities); + this.liquidTypes = liquidTypes; + this.sourceOnly = sourceOnly; + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return use(context.getLevel(), context.getPlayer(), context.getHand()); + } + + @Override + public InteractionResult use(World world, @Nullable Player player, InteractionHand hand) { + try { + if (player == null) return InteractionResult.FAIL; + Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$ANY); + Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(blockHitResult); + BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos), FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + Direction direction = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult)); + boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult); + Vec3d hitPos = LocationUtils.fromVec(CoreReflections.field$HitResult$location.get(blockHitResult)); + Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world.serverWorld(), blockPos)); + if (fluidType == MFluids.EMPTY) { + return InteractionResult.PASS; + } + String liquid = null; + if (fluidType == MFluids.LAVA) { + liquid = "lava"; + } else if (fluidType == MFluids.WATER) { + liquid = "water"; + } else if (fluidType == MFluids.FLOWING_LAVA) { + if (this.sourceOnly) return InteractionResult.PASS; + liquid = "lava"; + } else if (fluidType == MFluids.FLOWING_WATER) { + if (this.sourceOnly) return InteractionResult.PASS; + liquid = "water"; + } + if (!this.liquidTypes.contains(liquid)) { + return InteractionResult.PASS; + } + if (miss) { + return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above))); + } else { + boolean inside = CoreReflections.field$BlockHitResult$inside.getBoolean(blockHitResult); + return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside))); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error handling use", e); + return InteractionResult.FAIL; + } + } + + public static class Factory implements ItemBehaviorFactory { + + @Override + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { + Object id = arguments.get("furniture"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); + } + Map rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules"); + Key furnitureId; + if (id instanceof Map map) { + Map furnitureSection; + if (map.containsKey(key.toString())) { + // 防呆 + furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } else { + furnitureSection = MiscUtils.castToMap(map, false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } + furnitureId = key; + // 兼容老版本 + if (rulesMap == null) { + Map placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement"); + if (placementSection != null) { + rulesMap = new HashMap<>(); + for (Map.Entry entry : placementSection.entrySet()) { + if (entry.getValue() instanceof Map innerMap) { + if (innerMap.containsKey("rules")) { + Map rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules"); + if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) { + rulesMap.put(entry.getKey(), rules); + } + } + } + } + } + } + } else { + furnitureId = Key.of(id.toString()); + } + Map rules = new EnumMap<>(AnchorType.class); + if (rulesMap != null) { + for (Map.Entry entry : rulesMap.entrySet()) { + try { + AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT)); + Map ruleSection = MiscUtils.castToMap(entry.getValue(), true); + rules.put(type, new Rule( + ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY), + ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY) + )); + } catch (IllegalArgumentException ignored) { + Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey()); + } + } + } + return new LiquidCollisionFurnitureItemBehavior(furnitureId, rules, + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-placer"), "ignore-placer"), + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-entities"), "ignore-entities"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("source-only", true), "source-only"), + MiscUtils.getAsStringList(arguments.get("liquid-type")) + ); + } + } +}