9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 18:09:27 +00:00

Merge pull request #287 from jhqwqmc/dev

refactor(item): 重构使用物品上下文以支持玩家为null的情况
This commit is contained in:
XiaoMoMi
2025-07-14 22:34:30 +08:00
committed by GitHub
11 changed files with 162 additions and 92 deletions

View File

@@ -2,6 +2,7 @@ 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.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*;
@@ -54,7 +55,7 @@ public class AxeItemBehavior extends ItemBehavior {
public InteractionResult useOnBlock(UseOnContext context) {
Player player = context.getPlayer();
// no adventure mode for the moment
if (player.isAdventureMode()) {
if (player != null && player.isAdventureMode()) {
return InteractionResult.PASS;
}
@@ -66,9 +67,9 @@ public class AxeItemBehavior extends ItemBehavior {
Optional<StrippableBlockBehavior> behaviorOptional = customState.behavior().getAs(StrippableBlockBehavior.class);
if (behaviorOptional.isEmpty()) return InteractionResult.PASS;
Key stripped = behaviorOptional.get().stripped();
Item<ItemStack> offHandItem = (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND);
Item<ItemStack> offHandItem = player != null ? (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND) : BukkitItemManager.instance().wrap(new ItemStack(Material.AIR));
// is using a shield
if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && !player.isSecondaryUseActive()) {
if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && player != null && !player.isSecondaryUseActive()) {
return InteractionResult.PASS;
}
@@ -81,13 +82,15 @@ public class AxeItemBehavior extends ItemBehavior {
CompoundTag compoundTag = customState.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;
org.bukkit.entity.Player bukkitPlayer = null;
if (player != null) {
bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
}
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
@@ -98,28 +101,30 @@ public class AxeItemBehavior extends ItemBehavior {
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);
if (bukkitPlayer != null) {
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(customState.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);
// resend swing if it's not interactable on client side
if (!InteractUtils.isInteractable(
bukkitPlayer, BlockStateUtils.fromBlockData(customState.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);
}
} else {
ItemStack itemStack = item.getItem();
itemStack.damage(1, bukkitPlayer);
}
return InteractionResult.SUCCESS;
}

View File

@@ -30,10 +30,7 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Cancellable;
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.*;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Bukkit;
@@ -76,11 +73,12 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
return InteractionResult.FAIL;
}
Player player = context.getPlayer();
CustomBlock block = optionalBlock.get();
BlockPos pos = context.getClickedPos();
int maxY = context.getLevel().worldHeight().getMaxBuildHeight() - 1;
if (context.getClickedFace() == Direction.UP && pos.y() >= maxY) {
context.getPlayer().sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
if (player != null && context.getClickedFace() == Direction.UP && pos.y() >= maxY) {
player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
return InteractionResult.FAIL;
}
@@ -89,15 +87,14 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
return InteractionResult.FAIL;
}
Player player = context.getPlayer();
BlockPos againstPos = context.getAgainstPos();
World world = (World) context.getLevel().platformWorld();
Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z());
Block bukkitBlock = world.getBlockAt(placeLocation);
Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z());
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
if (player.isAdventureMode()) {
if (player != null && player.isAdventureMode()) {
Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData());
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(againstBlockState);
if (optionalCustomState.isEmpty()) {
@@ -113,31 +110,35 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
}
}
// trigger event
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
return InteractionResult.FAIL;
if (player != null) {
// trigger event
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
return InteractionResult.FAIL;
}
}
// it's just world + pos
BlockState previousState = bukkitBlock.getState();
// place custom block
CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false);
// call bukkit event
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) {
// revert changes
previousState.update(true, false);
return InteractionResult.FAIL;
}
if (player != null) {
// call bukkit event
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) {
// revert changes
previousState.update(true, false);
return InteractionResult.FAIL;
}
// call custom event
CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand());
if (EventUtils.fireAndCheckCancel(customPlaceEvent)) {
// revert changes
previousState.update(true, false);
return InteractionResult.FAIL;
// call custom event
CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand());
if (EventUtils.fireAndCheckCancel(customPlaceEvent)) {
// revert changes
previousState.update(true, false);
return InteractionResult.FAIL;
}
}
WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5);
@@ -154,14 +155,16 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
return InteractionResult.SUCCESS_AND_CANCEL;
}
if (!player.isCreativeMode()) {
if (player != null && !player.isCreativeMode()) {
Item<?> item = context.getItem();
item.count(item.count() - 1);
}
block.setPlacedBy(context, blockStateToPlace);
player.swingHand(context.getHand());
if (player != null) {
player.swingHand(context.getHand());
}
context.getLevel().playBlockSound(position, blockStateToPlace.settings().sounds().placeSound());
world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z()));
return InteractionResult.SUCCESS;
@@ -178,16 +181,27 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) {
try {
Object player = context.getPlayer().serverPlayer();
Player cePlayer = context.getPlayer();
Object player = cePlayer != null ? cePlayer.serverPlayer() : null;
Object blockState = state.customBlockState().handle();
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
Object voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player);
Object voxelShape;
if (VersionHelper.isOrAbove1_21_6()) {
voxelShape = CoreReflections.method$CollisionContext$placementContext.invoke(null, player);
} else if (player != null) {
voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player);
} else {
voxelShape = CoreReflections.instance$CollisionContext$empty;
}
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel((World) context.getLevel().platformWorld());
boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) CoreReflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos))
&& (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true));
Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos);
BlockData blockData = FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState);
BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent(block, (org.bukkit.entity.Player) context.getPlayer().platformPlayer(), blockData, defaultReturn, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent(
block, cePlayer != null ? (org.bukkit.entity.Player) cePlayer.platformPlayer() : null, blockData, defaultReturn,
context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND
);
Bukkit.getPluginManager().callEvent(canBuildEvent);
return canBuildEvent.isBuildable();
} catch (ReflectiveOperationException e) {

View File

@@ -51,15 +51,17 @@ public class CompostableItemBehavior extends ItemBehavior {
if (willRaise) {
levelled.setLevel(currentLevel + 1);
EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled);
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
if (context.getPlayer() != null) {
EntityChangeBlockEvent event = new EntityChangeBlockEvent((Entity) context.getPlayer().platformPlayer(), block.block(), levelled);
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
}
block.block().setBlockData(levelled);
}
context.getLevel().levelEvent(WorldEvents.COMPOSTER_COMPOSTS, context.getClickedPos(), willRaise ? 1 : 0);
((World) context.getLevel().platformWorld()).sendGameEvent((Entity) context.getPlayer().platformPlayer(), GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5));
((World) context.getLevel().platformWorld()).sendGameEvent(context.getPlayer() != null ? (Entity) context.getPlayer().platformPlayer() : null, GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5));
if (currentLevel + 1 == 7) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20);
}

View File

@@ -80,7 +80,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
}
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle());
// 点击的是方块上面则只需要判断shift和可交互
if (direction == Direction.UP) {
if (player != null && direction == Direction.UP) {
// 客户端层面必须可交互
if (!InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState,
context.getHitResult(), (Item<ItemStack>) context.getItem())) {
@@ -106,7 +106,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
}
// 客户端觉得这玩意可交互,就会忽略声音
if (InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item<ItemStack>) context.getItem())) {
if (player != null && InteractUtils.isInteractable((Player) player.platformPlayer(), vanillaBlockState, context.getHitResult(), (Item<ItemStack>) context.getItem())) {
// 如果按住了shift则代表尝试对侧面方块点火
if (player.isSecondaryUseActive()) {
// 如果底部不能燃烧,则燃烧点位为侧面,需要补发
@@ -119,7 +119,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
}
} else {
// 如果底部方块不可燃烧才补发
if (!belowCanBurn) {
if (player != null && !belowCanBurn) {
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
player.swingHand(context.getHand());
}
@@ -151,8 +151,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
}
}
}
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
player.swingHand(context.getHand());
if (player != null) {
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
player.swingHand(context.getHand());
}
}
return InteractionResult.PASS;
}

View File

@@ -77,14 +77,14 @@ public class FurnitureItemBehavior extends ItemBehavior {
Player player = context.getPlayer();
// todo adventure check
if (player.isAdventureMode()) {
if (player != null && player.isAdventureMode()) {
return InteractionResult.FAIL;
}
Vec3d clickedPosition = context.getClickLocation();
// trigger event
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
World world = (World) context.getLevel().platformWorld();
// get position and rotation for placement
@@ -100,7 +100,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z());
}
} else {
furnitureYaw = placement.rotationRule().apply(180 + player.xRot());
furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.xRot() : 0));
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z()));
finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right());
}
@@ -121,10 +121,12 @@ public class FurnitureItemBehavior extends ItemBehavior {
return InteractionResult.FAIL;
}
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(),
DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
return InteractionResult.FAIL;
if (player != null) {
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(),
DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
return InteractionResult.FAIL;
}
}
Item<?> item = context.getItem();
@@ -140,10 +142,12 @@ public class FurnitureItemBehavior extends ItemBehavior {
.fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null))
.build(), false);
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
if (EventUtils.fireAndCheckCancel(placeEvent)) {
bukkitFurniture.destroy();
return InteractionResult.FAIL;
if (player != null) {
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
if (EventUtils.fireAndCheckCancel(placeEvent)) {
bukkitFurniture.destroy();
return InteractionResult.FAIL;
}
}
Cancellable dummy = Cancellable.dummy();
@@ -159,12 +163,14 @@ public class FurnitureItemBehavior extends ItemBehavior {
return InteractionResult.SUCCESS_AND_CANCEL;
}
if (!player.isCreativeMode()) {
if (player != null && !player.isCreativeMode()) {
item.count(item.count() - 1);
}
context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound());
player.swingHand(context.getHand());
if (player != null) {
player.swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
@@ -21,6 +22,7 @@ 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.Map;
@@ -36,13 +38,26 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
@Override
public InteractionResult useOnBlock(UseOnContext context) {
return use(context.getLevel(), context.getPlayer(), context.getHand());
return use(context.getLevel(), context.getPlayer(), context.getHand(), context);
}
@Override
public InteractionResult use(World world, Player player, InteractionHand hand) {
public InteractionResult use(World world, @Nullable Player player, InteractionHand hand, @Nullable UseOnContext context) {
try {
Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY);
Object blockHitResult;
if (player != null) {
blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY);
} else if (context != null) {
BlockPos clickedPos = context.getClickedPos();
blockHitResult = CoreReflections.constructor$BlockHitResult.newInstance(
CoreReflections.constructor$Vec3.newInstance(clickedPos.x() + 0.5, clickedPos.y() + 0.5, clickedPos.z() + 0.5),
DirectionUtils.toNMSDirection(context.getHorizontalDirection()),
LocationUtils.toBlockPos(clickedPos),
false
);
} else {
return InteractionResult.FAIL;
}
Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult);
BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
Direction direction = Direction.values()[(int) CoreReflections.method$Direction$ordinal.invoke(CoreReflections.field$BlockHitResul$direction.get(blockHitResult))];

View File

@@ -365,7 +365,7 @@ public class ItemEventListener implements Listener {
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
if (optionalItemBehaviors.isPresent()) {
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand);
InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand, null);
if (result.success()) {
serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks());
}

View File

@@ -3830,4 +3830,24 @@ public final class CoreReflections {
),
!VersionHelper.isOrAbove1_21_5()
);
public static final Object instance$CollisionContext$empty;
static {
try {
instance$CollisionContext$empty = requireNonNull(method$CollisionContext$empty.invoke(null));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 1.21.6+
public static final Method method$CollisionContext$placementContext = ReflectionUtils.getStaticMethod(
clazz$CollisionContext, clazz$CollisionContext, clazz$Player
);
public static final Constructor<?> constructor$BlockHitResult = requireNonNull(
ReflectionUtils.getConstructor(
CoreReflections.clazz$BlockHitResult, CoreReflections.clazz$Vec3, CoreReflections.clazz$Direction, CoreReflections.clazz$BlockPos, boolean.class
)
);
}

View File

@@ -4,7 +4,9 @@ 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.context.UseOnContext;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.jetbrains.annotations.Nullable;
public abstract class ItemBehavior {
@@ -12,7 +14,7 @@ public abstract class ItemBehavior {
return InteractionResult.PASS;
}
public InteractionResult use(World world, Player player, InteractionHand hand) {
public InteractionResult use(World world, @Nullable Player player, InteractionHand hand, @Nullable UseOnContext context) {
return InteractionResult.PASS;
}
}

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.jetbrains.annotations.Nullable;
public class BlockPlaceContext extends UseOnContext {
private final BlockPos relativePos;
@@ -16,7 +17,7 @@ public class BlockPlaceContext extends UseOnContext {
this(context.getLevel(), context.getPlayer(), context.getHand(), context.getItem(), context.getHitResult());
}
public BlockPlaceContext(World world, Player player, InteractionHand hand, Item<?> stack, BlockHitResult hit) {
public BlockPlaceContext(World world, @Nullable Player player, InteractionHand hand, Item<?> stack, BlockHitResult hit) {
super(world, player, hand, stack, hit);
this.relativePos = hit.getBlockPos().relative(hit.getDirection());
this.replaceClicked = true;

View File

@@ -9,8 +9,10 @@ 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.NotNull;
import org.jetbrains.annotations.Nullable;
public class UseOnContext {
@Nullable
private final Player player;
private final InteractionHand hand;
private final BlockHitResult hitResult;
@@ -25,7 +27,7 @@ public class UseOnContext {
this(player.world(), player, hand, stack, hit);
}
public UseOnContext(@NotNull World world, Player player, InteractionHand hand, @NotNull Item<?> stack, BlockHitResult hit) {
public UseOnContext(@NotNull World world, @Nullable Player player, InteractionHand hand, @NotNull Item<?> stack, BlockHitResult hit) {
this.player = player;
this.hand = hand;
this.hitResult = hit;
@@ -58,6 +60,7 @@ public class UseOnContext {
return this.itemStack;
}
@Nullable
public Player getPlayer() {
return this.player;
}
@@ -71,11 +74,11 @@ public class UseOnContext {
}
public Direction getHorizontalDirection() {
return this.player.getDirection();
return this.player == null ? Direction.NORTH : this.player.getDirection();
}
public boolean isSecondaryUseActive() {
return this.player.isSecondaryUseActive();
return this.player != null && this.player.isSecondaryUseActive();
}
public float getRotation() {