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.
This commit is contained in:
RednedEpic 2019-11-27 18:14:38 -06:00
parent 2e90759f40
commit ce68e1442a
8 changed files with 343 additions and 1 deletions

View file

@ -48,6 +48,9 @@ import com.nukkitx.protocol.bedrock.packet.*;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.network.translators.bedrock.*; import org.geysermc.connector.network.translators.bedrock.*;
import org.geysermc.connector.network.translators.block.BlockTranslator; 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.GenericInventoryTranslator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator; 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.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class TranslatorsInit { public class TranslatorsInit {
@ -79,6 +84,9 @@ public class TranslatorsInit {
@Getter @Getter
private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator(); private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
@Getter
private static Map<String, BlockEntityTranslator> blockEntityTranslators = new HashMap<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag(); private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA; public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@ -143,6 +151,7 @@ public class TranslatorsInit {
Registry.registerJava(ServerBlockChangePacket.class, new JavaBlockChangeTranslator()); Registry.registerJava(ServerBlockChangePacket.class, new JavaBlockChangeTranslator());
Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator()); Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator());
Registry.registerJava(ServerUnloadChunkPacket.class, new JavaUnloadChunkTranslator()); Registry.registerJava(ServerUnloadChunkPacket.class, new JavaUnloadChunkTranslator());
Registry.registerJava(ServerUpdateTileEntityPacket.class, new JavaUpdateTileEntityTranslator());
Registry.registerJava(ServerOpenWindowPacket.class, new OpenWindowPacketTranslator()); Registry.registerJava(ServerOpenWindowPacket.class, new OpenWindowPacketTranslator());
@ -159,9 +168,15 @@ public class TranslatorsInit {
itemTranslator = new ItemTranslator(); itemTranslator = new ItemTranslator();
blockTranslator = new BlockTranslator(); blockTranslator = new BlockTranslator();
registerBlockEntityTranslators();
registerInventoryTranslators(); registerInventoryTranslators();
} }
private static void registerBlockEntityTranslators() {
blockEntityTranslators.put("Empty", new EmptyBlockEntityTranslator());
blockEntityTranslators.put("Sign", new SignBlockEntityTranslator());
}
private static void registerInventoryTranslators() { private static void registerInventoryTranslators() {
/*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator()); /*inventoryTranslators.put(WindowType.GENERIC_9X1, new GenericInventoryTranslator());
inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator()); inventoryTranslators.put(WindowType.GENERIC_9X2, new GenericInventoryTranslator());

View file

@ -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<Tag<?>> 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();
}
}

View file

@ -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<Tag<?>> 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);
}
}

View file

@ -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<Tag<?>> translateTag(CompoundTag tag) {
List<Tag<?>> 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();
}
}

View file

@ -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.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3f; 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.network.VarInts;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
@ -53,6 +57,14 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
byteBuf.writeByte(0); // Border blocks - Edu edition only byteBuf.writeByte(0); // Border blocks - Edu edition only
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
ByteBufOutputStream stream = new ByteBufOutputStream(Unpooled.buffer());
NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(stream);
for (CompoundTag blockEntity : chunkData.blockEntities) {
nbtStream.write(blockEntity);
}
byteBuf.writeBytes(stream.buffer());
byte[] payload = new byte[byteBuf.writerIndex()]; byte[] payload = new byte[byteBuf.writerIndex()];
byteBuf.readBytes(payload); byteBuf.readBytes(payload);

View file

@ -0,0 +1,51 @@
/*
* 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.java.world;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTileEntityPacket;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator;
import org.geysermc.connector.utils.BlockEntityUtils;
public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> {
@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);
}
}

View file

@ -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;
}
}

View file

@ -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.Chunk;
import com.github.steveice10.mc.protocol.data.game.chunk.Column; 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.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.TranslatorsInit;
import org.geysermc.connector.network.translators.block.BlockEntry; 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 org.geysermc.connector.world.chunk.ChunkSection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChunkUtils { public class ChunkUtils {
public static ChunkData translateToBedrock(Column column) { public static ChunkData translateToBedrock(Column column) {
@ -16,6 +25,7 @@ public class ChunkUtils {
int chunkSectionCount = chunks.length; int chunkSectionCount = chunks.length;
chunkData.sections = new ChunkSection[chunkSectionCount]; chunkData.sections = new ChunkSection[chunkSectionCount];
List<CompoundTag> blockEntities = new ArrayList<>(Arrays.asList(column.getTileEntities()));
for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) { for (int chunkY = 0; chunkY < chunkSectionCount; chunkY++) {
chunkData.sections[chunkY] = new ChunkSection(); chunkData.sections[chunkY] = new ChunkSection();
Chunk chunk = chunks[chunkY]; Chunk chunk = chunks[chunkY];
@ -38,10 +48,32 @@ public class ChunkUtils {
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z),
9 << 4); // water id 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<com.nukkitx.nbt.tag.CompoundTag> 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; return chunkData;
} }
@ -49,6 +81,6 @@ public class ChunkUtils {
public ChunkSection[] sections; public ChunkSection[] sections;
public byte[] biomes = new byte[256]; public byte[] biomes = new byte[256];
public byte[] blockEntities = new byte[0]; public List<com.nukkitx.nbt.tag.CompoundTag> blockEntities = new ArrayList<>();
} }
} }