9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-30 20:39:10 +00:00

重构交互入口

This commit is contained in:
XiaoMoMi
2025-05-23 20:32:04 +08:00
parent e5b61222dd
commit c34ec30212
16 changed files with 258 additions and 228 deletions

View File

@@ -137,7 +137,9 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
}
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
if (this.any) return true;
if (this.any) {
return belowState != Reflections.instance$Blocks$AIR$defaultState;
}
for (Object tag : this.tagsCanSurviveOn) {
if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) {
return true;

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
@@ -12,6 +13,11 @@ import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
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.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.SimpleContext;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
@@ -125,6 +131,35 @@ public class CropBlockBehavior extends BushBlockBehavior {
this.performBoneMeal(args[0], args[2], args[3]);
}
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Item<?> item = context.getItem();
if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode())
return InteractionResult.PASS;
if (isMaxAge(state))
return InteractionResult.PASS;
boolean sendSwing = false;
try {
Object visualState = state.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
if (!is) {
sendSwing = true;
}
} else {
sendSwing = true;
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
return InteractionResult.FAIL;
}
if (sendSwing) {
context.getPlayer().swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}
private void performBoneMeal(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (immutableBlockState == null || immutableBlockState.isEmpty()) {

View File

@@ -3,14 +3,23 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
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.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
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.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.World;
import org.bukkit.block.Block;
import java.util.Map;
@@ -57,6 +66,39 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
return true;
}
@SuppressWarnings("DuplicatedCode")
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Item<?> item = context.getItem();
if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode())
return InteractionResult.PASS;
BlockPos pos = context.getClickedPos();
BukkitBlockInWorld upper = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z());
Block block = upper.block();
if (!block.isEmpty())
return InteractionResult.PASS;
boolean sendSwing = false;
try {
Object visualState = state.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
if (!is) {
sendSwing = true;
}
} else {
sendSwing = true;
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
return InteractionResult.FAIL;
}
if (sendSwing) {
context.getPlayer().swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}
@Override
public void performBoneMeal(Object thisBlock, Object[] args) {
FastNMS.INSTANCE.method$GrassBlock$performBoneMeal(args[0], args[1], args[2], args[3], thisBlock);

View File

@@ -9,6 +9,10 @@ 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.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
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.RandomUtils;
@@ -135,6 +139,34 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
this.increaseStage(args[0], args[2], args[3], args[1]);
}
@SuppressWarnings("DuplicatedCode")
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Item<?> item = context.getItem();
if (item == null || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode())
return InteractionResult.PASS;
boolean sendSwing = false;
try {
Object visualState = state.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
if (!is) {
sendSwing = true;
}
} else {
sendSwing = true;
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
return InteractionResult.FAIL;
}
if (sendSwing) {
context.getPlayer().swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")

View File

@@ -1,15 +1,42 @@
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.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.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.craftengine.shared.block.BlockBehavior;
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) {
@@ -18,7 +45,85 @@ public class StrippableBlockBehavior extends BukkitBlockBehavior {
}
public Key stripped() {
return stripped;
return this.stripped;
}
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
if (item == null) {
return InteractionResult.PASS;
}
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!item.is(ItemTags.AXES))
return InteractionResult.PASS;
} else {
CustomItem<ItemStack> 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<CustomBlock> 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 ? Reflections.instance$EquipmentSlot$MAINHAND : Reflections.instance$EquipmentSlot$OFFHAND;
try {
Reflections.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 {

View File

@@ -1,8 +1,6 @@
package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BoneMealItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
@@ -42,10 +40,8 @@ import java.util.function.Function;
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static {
registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES);
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(BoneMealItemBehavior.INSTANCE, ItemKeys.BONE_MEAL);
}
private static BukkitItemManager instance;

View File

@@ -1,122 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
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.Block;
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");
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
if (!(state.behavior() instanceof StrippableBlockBehavior blockBehavior)) {
return InteractionResult.PASS;
}
Player player = context.getPlayer();
// no adventure mode
if (player.isAdventureMode()) {
return InteractionResult.PASS;
}
Item<ItemStack> offHandItem = (Item<ItemStack>) 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<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().blockById(blockBehavior.stripped());
if (optionalNewCustomBlock.isEmpty()) {
CraftEngine.instance().logger().warn("stripped block " + blockBehavior.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());
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.PASS;
}
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()));
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
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 ? Reflections.instance$EquipmentSlot$MAINHAND : Reflections.instance$EquipmentSlot$OFFHAND;
try {
Reflections.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 id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -1,83 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior;
import net.momirealms.craftengine.bukkit.block.behavior.GrassBlockBehavior;
import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
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.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 org.bukkit.block.Block;
import java.nio.file.Path;
import java.util.Map;
public class BoneMealItemBehavior extends ItemBehavior {
public static final Factory FACTORY = new Factory();
public static final BoneMealItemBehavior INSTANCE = new BoneMealItemBehavior();
@Override
public InteractionResult useOnBlock(UseOnContext context) {
if (context.getPlayer().isAdventureMode()) {
return InteractionResult.PASS;
}
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
boolean shouldHandle =false;
if (state.behavior() instanceof CropBlockBehavior blockBehavior) {
if (!blockBehavior.isMaxAge(state)) {
shouldHandle = true;
}
} else if (state.behavior() instanceof SaplingBlockBehavior) {
shouldHandle = true;
} else if (state.behavior() instanceof GrassBlockBehavior) {
if (block.getLocation().add(0, 1, 0).getBlock().isEmpty()) {
shouldHandle = true;
}
}
if (!shouldHandle) return InteractionResult.PASS;
boolean sendSwing = false;
try {
Object visualState = state.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
if (!is) {
sendSwing = true;
}
} else {
sendSwing = true;
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
}
if (sendSwing) {
context.getPlayer().swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -8,19 +8,15 @@ 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 AXE_ITEM = Key.from("craftengine:axe_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 BONE_MEAL_ITEM = Key.from("craftengine:bone_meal_item");
public static void init() {
register(EMPTY, EmptyItemBehavior.FACTORY);
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(AXE_ITEM, AxeItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
register(BONE_MEAL_ITEM, BoneMealItemBehavior.FACTORY);
}
}

View File

@@ -8,6 +8,7 @@ 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.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.CustomItem;
@@ -75,6 +76,17 @@ public class ItemEventListener implements Listener {
ImmutableBlockState immutableBlockState = null;
int stateId = BlockStateUtils.blockStateToId(blockState);
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
Location interactionPoint = event.getInteractionPoint();
BlockHitResult hitResult = null;
if (action == Action.RIGHT_CLICK_BLOCK && interactionPoint != null) {
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
hitResult = new BlockHitResult(vec3d, direction, pos, false);
}
// 处理自定义方块
if (!BlockStateUtils.isVanillaBlock(stateId)) {
@@ -83,7 +95,7 @@ public class ItemEventListener implements Listener {
CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent(
player,
block.getLocation(),
event.getInteractionPoint(),
interactionPoint,
immutableBlockState,
block,
event.getBlockFace(),
@@ -113,6 +125,20 @@ public class ItemEventListener implements Listener {
event.setCancelled(true);
return;
}
if (hitResult != null) {
UseOnContext useOnContext = new UseOnContext(serverPlayer, hand, itemInHand, hitResult);
if (immutableBlockState.behavior() instanceof AbstractBlockBehavior behavior) {
InteractionResult result = behavior.useOnBlock(useOnContext, immutableBlockState);
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
if (result != InteractionResult.PASS) {
return;
}
}
}
}
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand == null ? Optional.empty() : itemInHand.getCustomItem();
@@ -121,7 +147,6 @@ public class ItemEventListener implements Listener {
// interact block with items
if (hasItem && action == Action.RIGHT_CLICK_BLOCK) {
Location interactionPoint = event.getInteractionPoint();
// some plugins would trigger this event without interaction point
if (interactionPoint == null) {
if (hasCustomItem) {
@@ -129,10 +154,6 @@ public class ItemEventListener implements Listener {
}
return;
}
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false);
// handle block item
if (itemInHand.isBlockItem()) {
@@ -200,9 +221,10 @@ public class ItemEventListener implements Listener {
if (!serverPlayer.isSecondaryUseActive() && interactable) {
return;
}
UseOnContext useOnContext = new UseOnContext(serverPlayer, hand, itemInHand, hitResult);
// 依次执行物品行为
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(serverPlayer, hand, hitResult));
InteractionResult result = itemBehavior.useOnBlock(useOnContext);
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;

View File

@@ -26,10 +26,10 @@ import net.momirealms.craftengine.bukkit.util.EventUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.item.ItemManager;
import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.classpath.ReflectionClassPathAppender;
import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory;
import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.dependency.Dependencies;
import net.momirealms.craftengine.core.plugin.dependency.Dependency;

View File

@@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.gui.*;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;