From ce68e1442a4f233dd0287084d2b1bb2051af97ac Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Wed, 27 Nov 2019 18:14:38 -0600 Subject: [PATCH] Start work on block entities (partially-functioning) Most block entities should now *show up*. Some like signs will not as of yet, however when another player places one, they will display. Block entities are not yet fully functional and may only just be visible for the time being. --- .../network/translators/TranslatorsInit.java | 15 ++++ .../block/entity/BlockEntityTranslator.java | 73 ++++++++++++++++++ .../entity/EmptyBlockEntityTranslator.java | 50 ++++++++++++ .../entity/SignBlockEntityTranslator.java | 77 +++++++++++++++++++ .../java/world/JavaChunkDataTranslator.java | 12 +++ .../world/JavaUpdateTileEntityTranslator.java | 51 ++++++++++++ .../connector/utils/BlockEntityUtils.java | 32 ++++++++ .../geysermc/connector/utils/ChunkUtils.java | 34 +++++++- 8 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index 6539fb05d..8e3728e6c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -48,6 +48,9 @@ import com.nukkitx.protocol.bedrock.packet.*; import lombok.Getter; import org.geysermc.connector.network.translators.bedrock.*; import org.geysermc.connector.network.translators.block.BlockTranslator; +import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator; +import org.geysermc.connector.network.translators.block.entity.EmptyBlockEntityTranslator; +import org.geysermc.connector.network.translators.block.entity.SignBlockEntityTranslator; import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; @@ -67,6 +70,8 @@ import org.geysermc.connector.network.translators.java.world.*; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class TranslatorsInit { @@ -79,6 +84,9 @@ public class TranslatorsInit { @Getter private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator(); + @Getter + private static Map blockEntityTranslators = new HashMap<>(); + private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag(); public static final byte[] EMPTY_LEVEL_CHUNK_DATA; @@ -143,6 +151,7 @@ public class TranslatorsInit { Registry.registerJava(ServerBlockChangePacket.class, new JavaBlockChangeTranslator()); Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator()); Registry.registerJava(ServerUnloadChunkPacket.class, new JavaUnloadChunkTranslator()); + Registry.registerJava(ServerUpdateTileEntityPacket.class, new JavaUpdateTileEntityTranslator()); Registry.registerJava(ServerOpenWindowPacket.class, new OpenWindowPacketTranslator()); @@ -159,9 +168,15 @@ public class TranslatorsInit { itemTranslator = new ItemTranslator(); blockTranslator = new BlockTranslator(); + registerBlockEntityTranslators(); registerInventoryTranslators(); } + private static void registerBlockEntityTranslators() { + blockEntityTranslators.put("Empty", new EmptyBlockEntityTranslator()); + blockEntityTranslators.put("Sign", new SignBlockEntityTranslator()); + } + private static void registerInventoryTranslators() { /*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator()); inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java new file mode 100644 index 000000000..4913a300d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.block.entity; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.IntTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.Tag; +import org.geysermc.connector.utils.BlockEntityUtils; + +import java.util.List; + +public abstract class BlockEntityTranslator { + + public abstract List> translateTag(CompoundTag tag); + + public abstract CompoundTag getDefaultJavaTag(int x, int y, int z); + + public abstract com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z); + + public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(CompoundTag tag) { + String id = BlockEntityUtils.getBedrockBlockEntityId((String) tag.get("id").getValue()); + int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); + int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); + int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue())); + + CompoundTagBuilder tagBuilder = getConstantBedrockTag(id, x, y, z).toBuilder(); + translateTag(tag).forEach(tagBuilder::tag); + return tagBuilder.buildRootTag(); + } + + protected CompoundTag getConstantJavaTag(String id, int x, int y, int z) { + CompoundTag tag = new CompoundTag(""); + tag.put(new IntTag("x", x)); + tag.put(new IntTag("y", y)); + tag.put(new IntTag("z", z)); + tag.put(new StringTag("id", id)); + return tag; + } + + protected com.nukkitx.nbt.tag.CompoundTag getConstantBedrockTag(String id, int x, int y, int z) { + CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder() + .intTag("x", x) + .intTag("y", y) + .intTag("z", z) + .stringTag("id", id); + return tagBuilder.buildRootTag(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java new file mode 100644 index 000000000..927337bc1 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.block.entity; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.tag.Tag; + +import java.util.ArrayList; +import java.util.List; + +public class EmptyBlockEntityTranslator extends BlockEntityTranslator { + + @Override + public List> translateTag(CompoundTag tag) { + return new ArrayList<>(); + } + + @Override + public CompoundTag getDefaultJavaTag(int x, int y, int z) { + return getConstantJavaTag("empty", x, y, z); + } + + @Override + public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z) { + return getConstantBedrockTag("Empty", x, y, z); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java new file mode 100644 index 000000000..414455c77 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.block.entity; + +import com.github.steveice10.mc.protocol.data.message.Message; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.ByteTag; +import com.nukkitx.nbt.tag.StringTag; +import com.nukkitx.nbt.tag.Tag; +import org.geysermc.connector.utils.MessageUtils; + +import java.util.ArrayList; +import java.util.List; + +public class SignBlockEntityTranslator extends BlockEntityTranslator { + + @Override + public List> translateTag(CompoundTag tag) { + List> tags = new ArrayList<>(); + + String line1 = (String) tag.getValue().get("Text1").getValue(); + String line2 = (String) tag.getValue().get("Text2").getValue(); + String line3 = (String) tag.getValue().get("Text3").getValue(); + String line4 = (String) tag.getValue().get("Text4").getValue(); + + tags.add(new StringTag("Text", MessageUtils.getBedrockMessage(Message.fromString(line1)) + + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line2)) + + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line3)) + + "\n" + MessageUtils.getBedrockMessage(Message.fromString(line4)) + )); + + tags.add(new ByteTag("isMovable", (byte) 0)); + return tags; + } + + @Override + public CompoundTag getDefaultJavaTag(int x, int y, int z) { + CompoundTag tag = getConstantJavaTag("minecraft:sign", x, y, z); + tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "\"text\":\"\"")); + tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "\"text\":\"\"")); + tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "\"text\":\"\"")); + tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text4", "\"text\":\"\"")); + return tag; + } + + @Override + public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(int x, int y, int z) { + CompoundTagBuilder tagBuilder = getConstantBedrockTag("Sign", x, y, z).toBuilder(); + tagBuilder.stringTag("Text", ""); + tagBuilder.byteTag("isMovable", (byte) 0); + return tagBuilder.buildRootTag(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index a620197e0..1c83579a5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -3,10 +3,14 @@ package org.geysermc.connector.network.translators.java.world; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.nbt.NbtUtils; +import com.nukkitx.nbt.stream.NBTOutputStream; +import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.network.VarInts; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; import org.geysermc.api.Geyser; import org.geysermc.connector.network.session.GeyserSession; @@ -53,6 +57,14 @@ public class JavaChunkDataTranslator extends PacketTranslator { + + @Override + public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { + BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket(); + blockEntityPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), + packet.getPosition().getY(), + packet.getPosition().getZ()) + ); + + String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name()); + BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id); + blockEntityPacket.setData(translator.getBlockEntityTag(packet.getNbt())); + session.getUpstream().sendPacket(blockEntityPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java new file mode 100644 index 000000000..6fb62b552 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java @@ -0,0 +1,32 @@ +package org.geysermc.connector.utils; + +import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator; + +public class BlockEntityUtils { + + public static String getBedrockBlockEntityId(String id) { + // This is the only exception when it comes to block entity ids + if (id.contains("piston_head")) + return "PistonArm"; + + id = id.replace("minecraft:", ""); + id = id.replace("_", " "); + String[] words = id.split(" "); + for (int i = 0; i < words.length; i++) { + words[i] = words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase(); + } + + id = String.join(" ", words); + return id.replace(" ", ""); + } + + public static BlockEntityTranslator getBlockEntityTranslator(String name) { + BlockEntityTranslator blockEntityTranslator = TranslatorsInit.getBlockEntityTranslators().get(name); + if (blockEntityTranslator == null) { + return TranslatorsInit.getBlockEntityTranslators().get("Empty"); + } + + return blockEntityTranslator; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 6f2b6d3cc..59a5fe848 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -3,10 +3,19 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.network.translators.block.BlockEntry; +import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator; +import org.geysermc.connector.network.translators.block.entity.SignBlockEntityTranslator; import org.geysermc.connector.world.chunk.ChunkSection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class ChunkUtils { public static ChunkData translateToBedrock(Column column) { @@ -16,6 +25,7 @@ public class ChunkUtils { int chunkSectionCount = chunks.length; chunkData.sections = new ChunkSection[chunkSectionCount]; + List blockEntities = new ArrayList<>(Arrays.asList(column.getTileEntities())); for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) { chunkData.sections[chunkY] = new ChunkSection(); Chunk chunk = chunks[chunkY]; @@ -38,10 +48,32 @@ public class ChunkUtils { section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), 9 << 4); // water id } + + // Block entity data for signs is not sent in this packet, which is needed + // for bedrock, so we need to check the block itself + if (block.getJavaIdentifier().contains("sign")) { + SignBlockEntityTranslator sign = (SignBlockEntityTranslator) BlockEntityUtils.getBlockEntityTranslator("Sign"); + blockEntities.add(sign.getDefaultJavaTag(x, y, z)); + } } } } } + + List bedrockBlockEntities = new ArrayList<>(); + for (CompoundTag tag : blockEntities) { + Tag idTag = tag.get("id"); + if (idTag == null) { + GeyserLogger.DEFAULT.debug("Got tag with no id: " + tag.getValue()); + continue; + } + + String id = BlockEntityUtils.getBedrockBlockEntityId((String) tag.get("id").getValue()); + BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id); + bedrockBlockEntities.add(blockEntityTranslator.getBlockEntityTag(tag)); + } + + chunkData.blockEntities = bedrockBlockEntities; return chunkData; } @@ -49,6 +81,6 @@ public class ChunkUtils { public ChunkSection[] sections; public byte[] biomes = new byte[256]; - public byte[] blockEntities = new byte[0]; + public List blockEntities = new ArrayList<>(); } }