diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java index ef8c3ec0a..db1768737 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/world/GeyserModWorldManager.java @@ -30,6 +30,7 @@ import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -39,10 +40,12 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BannerBlockEntity; import net.minecraft.world.level.block.entity.BannerPatternLayers; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.status.ChunkStatus; import org.checkerframework.checker.nullness.qual.NonNull; +import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.platform.mod.GeyserModBootstrap; @@ -56,6 +59,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen import java.util.HashMap; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; public class GeyserModWorldManager extends GeyserWorldManager { @@ -161,6 +165,27 @@ public class GeyserModWorldManager extends GeyserWorldManager { return future; } + @Override + public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer> apply) { + server.execute(() -> { + ServerPlayer player = getPlayer(session); + if (player == null) { + return; + } + + BlockPos blockPos = new BlockPos(pos.getX(), pos.getY(), pos.getZ()); + // Don't create a new block entity if invalid + //noinspection resource - level() is just a getter + BlockEntity blockEntity = player.level().getChunkAt(blockPos).getBlockEntity(blockPos); + if (blockEntity instanceof DecoratedPotBlockEntity pot) { + List sherds = pot.getDecorations().ordered() + .stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()) + .toList(); + apply.accept(sherds); + } + }); + } + private ServerPlayer getPlayer(GeyserSession session) { return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid()); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java index e247a72c7..73356c4e7 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -29,10 +29,13 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.DecoratedPot; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.math.vector.Vector3i; +import org.geysermc.erosion.bukkit.BukkitUtils; import org.geysermc.erosion.bukkit.PickBlockUtils; import org.geysermc.erosion.bukkit.SchedulerUtils; import org.geysermc.geyser.GeyserImpl; @@ -43,8 +46,10 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; +import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; /** * The base world manager to use when there is no supported NMS revision @@ -146,6 +151,21 @@ public class GeyserSpigotWorldManager extends WorldManager { return future.thenApply(RAW_TRANSFORMER); } + public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer> apply) { + Player bukkitPlayer; + if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) { + return; + } + Block block = bukkitPlayer.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); + SchedulerUtils.runTask(this.plugin, () -> { + var state = BukkitUtils.getBlockState(block); + if (!(state instanceof DecoratedPot pot)) { + return; + } + apply.accept(pot.getShards().stream().map(material -> material.getKey().toString()).toList()); + }, block); + } + /** * This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id * to the current one. diff --git a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java index 3670b6b73..4a20771f2 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -43,9 +43,11 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemCodecHel import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -209,6 +211,13 @@ public abstract class WorldManager { return CompletableFuture.completedFuture(null); } + /** + * Retrieves decorated pot sherds from the server. Used to ensure the data is not erased on animation sent + * through the BlockEntityDataPacket. + */ + public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer> apply) { + } + protected static final Function, DataComponents> RAW_TRANSFORMER = map -> { try { Map, DataComponent> components = new HashMap<>(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java index d6e45adc3..f56750d12 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.protocol.java.level; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket; import org.cloudburstmc.protocol.bedrock.packet.BlockEventPacket; import org.geysermc.geyser.api.util.PlatformType; @@ -126,6 +127,26 @@ public class JavaBlockEventTranslator extends PacketTranslator { + BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket(); + blockEntityPacket.setBlockPosition(position); + + NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("DecoratedPot", position); + builder.putList("sherds", NbtType.STRING, sherds); + builder.putByte("animation", switch (potValue.getWobbleStyle()) { + case POSITIVE -> (byte) 2; + case NEGATIVE -> (byte) 1; + }); + + blockEntityPacket.setData(builder.build()); + session.sendUpstreamPacket(blockEntityPacket); + }); + } else if (session.getGeyser().getLogger().isDebug()) { + session.getGeyser().getLogger().debug("Unhandled block event packet: " + packet); } } }