diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb539e54..436831ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ Unless otherwise specified, any version comparison below is the comparison of se - (API) Implemented brewing stand, and several related interfaces & objects including `BlockEntityBrewingStand`, `BrewingStandContainer`, `Registries#POTION_MIX_RECIPES`, `PotionMixRecipe` are added to api module. See commit history for more details. +- (API) Implemented picking block with block entity data. The following methods are added: `ItemBaseComponent#getBlockEntityNBT`, + `ItemBaseComponent#setBlockEntityNBT`, `ItemBaseComponent#clearBlockEntityNBT` and `ItemBaseComponent#hasBlockEntityNBT`. - (API) Introduced a number of overloads of `Dimension#addSound`. - Implemented trapdoor except redstone feature (Redstone feature requires the implementation of redstone system). - Implemented sponge and wet sponge. diff --git a/api/src/main/java/org/allaymc/api/eventbus/event/player/PlayerBlockPickEvent.java b/api/src/main/java/org/allaymc/api/eventbus/event/player/PlayerBlockPickEvent.java index ae0a1c597..3453de516 100644 --- a/api/src/main/java/org/allaymc/api/eventbus/event/player/PlayerBlockPickEvent.java +++ b/api/src/main/java/org/allaymc/api/eventbus/event/player/PlayerBlockPickEvent.java @@ -14,14 +14,14 @@ public class PlayerBlockPickEvent extends PlayerEvent implements CancellableEvent { protected BlockStateWithPos clickedBlock; @Setter - protected boolean addUserData; + protected boolean includeBlockEntity; @Setter protected ItemStack itemBlock; - public PlayerBlockPickEvent(EntityPlayer player, BlockStateWithPos clickedBlock, boolean addUserData, ItemStack itemBlock) { + public PlayerBlockPickEvent(EntityPlayer player, BlockStateWithPos clickedBlock, boolean includeBlockEntity, ItemStack itemBlock) { super(player); this.clickedBlock = clickedBlock; - this.addUserData = addUserData; + this.includeBlockEntity = includeBlockEntity; this.itemBlock = itemBlock; } } diff --git a/api/src/main/java/org/allaymc/api/item/component/ItemBaseComponent.java b/api/src/main/java/org/allaymc/api/item/component/ItemBaseComponent.java index 263db3b87..6ca5f44b9 100644 --- a/api/src/main/java/org/allaymc/api/item/component/ItemBaseComponent.java +++ b/api/src/main/java/org/allaymc/api/item/component/ItemBaseComponent.java @@ -581,4 +581,41 @@ default Set getIncompatibleEnchantmentTypes(EnchantmentType typ .filter(enchantmentType -> enchantmentType.isIncompatibleWith(type)) .collect(Collectors.toSet()); } + + /** + * Get the block entity nbt in the item. + *

+ * The block entity nbt will be stored in the item if player pick a block with ctrl pressed, + * and the nbt will be used when the player place the block. Note that not every block has + * block entity, so setting block entity nbt in an item whose block doesn't have block entity + * will have no effect. + * + * @return the block entity nbt in the item, or {@code null} if the item doesn't have block entity nbt. + */ + NbtMap getBlockEntityNBT(); + + /** + * Set the block entity nbt in the item. + *

+ * The position information in the nbt will be simply removed when placing block. + * + * @param blockEntityNBT the block entity nbt to set, can be {@code null} to clean the block entity nbt. + */ + void setBlockEntityNBT(NbtMap blockEntityNBT); + + /** + * Clear the block entity nbt in the item. + */ + default void clearBlockEntityNBT() { + setBlockEntityNBT(null); + } + + /** + * Check if the item has block entity nbt. + * + * @return {@code true} if the item has block entity nbt, {@code false} otherwise. + */ + default boolean hasBlockEntityNBT() { + return getBlockEntityNBT() != null; + } } diff --git a/api/src/main/java/org/allaymc/api/world/Dimension.java b/api/src/main/java/org/allaymc/api/world/Dimension.java index 63f1ff4a8..fb82a782e 100644 --- a/api/src/main/java/org/allaymc/api/world/Dimension.java +++ b/api/src/main/java/org/allaymc/api/world/Dimension.java @@ -816,6 +816,9 @@ default BlockEntity getBlockEntity(int x, int y, int z) { return chunk.getBlockEntity(x & 15, y, z & 15); } + /** + * @see #getBlockEntity(int, int, int) + */ default BlockEntity getBlockEntity(Vector3ic pos) { return getBlockEntity(pos.x(), pos.y(), pos.z()); } diff --git a/server/src/main/java/org/allaymc/server/item/component/ItemBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/item/component/ItemBaseComponentImpl.java index ffe0e79c8..2f77931dc 100644 --- a/server/src/main/java/org/allaymc/server/item/component/ItemBaseComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/item/component/ItemBaseComponentImpl.java @@ -61,6 +61,7 @@ public class ItemBaseComponentImpl implements ItemBaseComponent { protected static final String TAG_NAME = "Name"; protected static final String TAG_LORE = "Lore"; protected static final String TAG_ENCHANTMENT = "ench"; + protected static final String TAG_BLOCK_ENTITY = "BlockEntityTag"; protected static final String TAG_CUSTOM_NBT = "CustomNBT"; private static int STACK_NETWORK_ID_COUNTER = 1; @@ -96,6 +97,9 @@ public class ItemBaseComponentImpl implements ItemBaseComponent { protected NbtMap customNBTContent = NbtMap.EMPTY; @Getter @Setter + protected NbtMap blockEntityNBT; + @Getter + @Setter protected int stackNetworkId; public ItemBaseComponentImpl(ItemStackInitInfo initInfo) { @@ -133,6 +137,8 @@ public void loadExtraTag(NbtMap extraTag) { this.enchantments.put(enchantment.getType(), enchantment); })); + extraTag.listenForCompound(TAG_BLOCK_ENTITY, nbt -> this.blockEntityNBT = nbt); + extraTag.listenForCompound(TAG_CUSTOM_NBT, customNbt -> this.customNBTContent = customNbt); var event = new CItemLoadExtraTagEvent(extraTag); @@ -164,6 +170,10 @@ public NbtMap saveExtraTag() { nbtBuilder.putList(TAG_ENCHANTMENT, NbtType.COMPOUND, enchantmentNBT); } + if (blockEntityNBT != null) { + nbtBuilder.putCompound(TAG_BLOCK_ENTITY, blockEntityNBT); + } + // TODO: item lock type // Custom NBT content @@ -316,15 +326,38 @@ protected boolean tryPlaceBlockState(Dimension dimension, BlockState blockState, } var result = blockType.getBlockBehavior().place(dimension, blockState, placeBlockPos, placementInfo); - if (result && player != null) { + if (result) { dimension.addLevelSoundEvent(placeBlockPos.x() + 0.5f, placeBlockPos.y() + 0.5f, placeBlockPos.z() + 0.5f, SoundEvent.PLACE, blockState.blockStateHash()); - tryConsumeItem(player); - var e = new CItemPlacedAsBlockEvent(dimension, placeBlockPos, thisItemStack); - manager.callEvent(e); + tryApplyBlockEntityNBT(dimension, placeBlockPos); + if (player != null) { + tryConsumeItem(player); + } + manager.callEvent(new CItemPlacedAsBlockEvent(dimension, placeBlockPos, thisItemStack)); } + return result; } + protected void tryApplyBlockEntityNBT(Dimension dimension, Vector3ic placeBlockPos) { + if (this.blockEntityNBT == null) { + return; + } + + var blockEntity = dimension.getBlockEntity(placeBlockPos); + // Block entity should also being spawned when placing block + // if the block entity is implemented + if (blockEntity == null) { + return; + } + + // The position data should be removed + var builder = blockEntityNBT.toBuilder(); + builder.remove("x"); + builder.remove("y"); + builder.remove("z"); + blockEntity.loadNBT(this.blockEntityNBT); + } + protected void tryConsumeItem(EntityPlayer player) { if (player == null || player.getGameType() != GameType.CREATIVE) { thisItemStack.reduceCount(1); diff --git a/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/BlockPickRequestPacketProcessor.java b/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/BlockPickRequestPacketProcessor.java index 7a674481b..d67b566c4 100644 --- a/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/BlockPickRequestPacketProcessor.java +++ b/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/BlockPickRequestPacketProcessor.java @@ -13,6 +13,8 @@ import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketType; import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket; +import java.util.List; + /** * @author Cool_Loong */ @@ -21,9 +23,9 @@ public class BlockPickRequestPacketProcessor extends PacketProcessor