diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java index 73f83f873..00aff9787 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java @@ -1,43 +1,15 @@ package net.momirealms.craftengine.bukkit.block.behavior; -import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.util.*; -import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; 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.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.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemKeys; -import net.momirealms.craftengine.core.item.context.UseOnContext; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.sparrow.nbt.CompoundTag; -import org.bukkit.GameEvent; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.block.Block; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; import java.util.Map; -import java.util.Optional; public class StrippableBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); - private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip"); private final Key stripped; public StrippableBlockBehavior(CustomBlock block, Key stripped) { @@ -49,85 +21,6 @@ public class StrippableBlockBehavior extends BukkitBlockBehavior { return this.stripped; } - // TODO 转移到 axe_item里 - @SuppressWarnings("unchecked") - @Override - public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { - Item item = (Item) context.getItem(); - if (item == null) { - return InteractionResult.PASS; - } - Optional> optionalCustomItem = item.getCustomItem(); - if (optionalCustomItem.isEmpty()) { - if (!item.is(ItemTags.AXES)) - return InteractionResult.PASS; - } else { - CustomItem customItem = optionalCustomItem.get(); - if (!customItem.settings().tags().contains(ItemTags.AXES) && !item.is(ItemTags.AXES)) - return InteractionResult.PASS; - } - - Player player = context.getPlayer(); - // no adventure mode - if (player.isAdventureMode()) { - return InteractionResult.PASS; - } - Item offHandItem = player.getItemInHand(InteractionHand.OFF_HAND); - // is using a shield - if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && offHandItem.vanillaId().equals(ItemKeys.SHIELD) && !player.isSecondaryUseActive()) { - return InteractionResult.PASS; - } - - Optional optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped()); - if (optionalNewCustomBlock.isEmpty()) { - CraftEngine.instance().logger().warn("stripped block " + stripped() + " does not exist"); - return InteractionResult.FAIL; - } - CustomBlock newCustomBlock = optionalNewCustomBlock.get(); - CompoundTag compoundTag = state.propertiesNbt(); - ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); - - org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); - - BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); - Block block = clicked.block(); - // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle())); - if (EventUtils.fireAndCheckCancel(event)) { - return InteractionResult.FAIL; - } - - BlockPos pos = context.getClickedPos(); - context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); - CraftEngineBlocks.place(block.getLocation(), newState, UpdateOption.UPDATE_ALL_IMMEDIATE, false); - block.getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); - Material material = MaterialUtils.getMaterial(item.vanillaId()); - bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1); - - // resend swing if it's not interactable on client side - if (!InteractUtils.isInteractable( - bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), - context.getHitResult(), item - ) || player.isSecondaryUseActive()) { - player.swingHand(context.getHand()); - } - // shrink item amount - if (VersionHelper.isOrAbove1_20_5()) { - Object itemStack = item.getLiteralObject(); - Object serverPlayer = player.serverPlayer(); - Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND; - try { - CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); - } - } else { - ItemStack itemStack = item.getItem(); - itemStack.damage(1, bukkitPlayer); - } - return InteractionResult.SUCCESS; - } - public static class Factory implements BlockBehaviorFactory { @Override 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 11d072bf4..108890068 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.item; +import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior; import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory; import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener; @@ -38,9 +39,8 @@ import java.util.Set; public class BukkitItemManager extends AbstractItemManager { static { -// registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS); -// registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET); registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL); + registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES); } private static BukkitItemManager instance; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java new file mode 100644 index 000000000..8a62033f2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -0,0 +1,134 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld; +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.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.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +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.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.GameEvent; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +public class AxeItemBehavior extends ItemBehavior { + public static final Factory FACTORY = new Factory(); + public static final AxeItemBehavior INSTANCE = new AxeItemBehavior(); + private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip"); + + private boolean canBlockAttack(Item item) { + if (VersionHelper.isOrAbove1_21_5()) { + return item.hasComponent("minecraft:blocks_attacks"); + } else { + return item.vanillaId().equals(ItemKeys.SHIELD); + } + } + + @SuppressWarnings({"UnstableApiUsage", "unchecked"}) + @Override + public InteractionResult useOnBlock(UseOnContext context) { + Player player = context.getPlayer(); + // no adventure mode for the moment + if (player.isAdventureMode()) { + return InteractionResult.PASS; + } + + BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + BlockData blockData = block.block().getBlockData(); + int stateId = BlockStateUtils.blockDataToId(blockData); + if (BlockStateUtils.isVanillaBlock(stateId)) return InteractionResult.PASS; + + ImmutableBlockState customBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId); + if (customBlockState == null || customBlockState.isEmpty()) return InteractionResult.PASS; + + Optional behaviorOptional = customBlockState.behavior().getAs(StrippableBlockBehavior.class); + if (behaviorOptional.isEmpty()) return InteractionResult.PASS; + Key stripped = behaviorOptional.get().stripped(); + Item offHandItem = (Item) player.getItemInHand(InteractionHand.OFF_HAND); + // is using a shield + if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && canBlockAttack(offHandItem) && !player.isSecondaryUseActive()) { + return InteractionResult.PASS; + } + + Optional optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped); + if (optionalNewCustomBlock.isEmpty()) { + CraftEngine.instance().logger().warn("stripped block " + stripped + " does not exist"); + return InteractionResult.FAIL; + } + CustomBlock newCustomBlock = optionalNewCustomBlock.get(); + CompoundTag compoundTag = customBlockState.propertiesNbt(); + ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag); + + org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); + + BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos()); + // Call bukkit event + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle())); + if (EventUtils.fireAndCheckCancel(event)) { + return InteractionResult.FAIL; + } + + Item item = (Item) context.getItem(); + BlockPos pos = context.getClickedPos(); + context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); + FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); + clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); + Material material = MaterialUtils.getMaterial(item.vanillaId()); + bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1); + + // resend swing if it's not interactable on client side + if (!InteractUtils.isInteractable( + bukkitPlayer, BlockStateUtils.fromBlockData(customBlockState.vanillaBlockState().handle()), + context.getHitResult(), item + ) || player.isSecondaryUseActive()) { + player.swingHand(context.getHand()); + } + // shrink item amount + if (VersionHelper.isOrAbove1_20_5()) { + Object itemStack = item.getLiteralObject(); + Object serverPlayer = player.serverPlayer(); + Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND; + try { + CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); + } + } else { + ItemStack itemStack = item.getItem(); + itemStack.damage(1, bukkitPlayer); + } + return InteractionResult.SUCCESS; + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + return 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 902b06a80..51a030ce8 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 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"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -18,5 +19,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY); register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY); + register(AXE_ITEM, AxeItemBehavior.FACTORY); } }