diff --git a/Jenkinsfile b/Jenkinsfile index 6564bd1f9..09e88e86e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,8 +35,8 @@ pipeline { rtMavenResolver( id: "maven-resolver", serverId: "opencollab-artifactory", - releaseRepo: "release", - snapshotRepo: "snapshot" + releaseRepo: "maven-deploy-release", + snapshotRepo: "maven-deploy-snapshot" ) rtMavenRun( pom: 'pom.xml', diff --git a/README.md b/README.md index 205162847..65a1261bd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here! -### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.201 and Minecraft Java v1.16.4 - v1.16.5. +### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.210 and Minecraft Java v1.16.4 - v1.16.5. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser. diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java index 56fa7581b..51e68a263 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java @@ -53,11 +53,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener { placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())); placeBlockSoundPacket.setBabySound(false); if (worldManager.isLegacy()) { - placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session, + placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(worldManager.getBlockAt(session, event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()))); } else { String javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); - placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID))); + placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID))); } placeBlockSoundPacket.setIdentifier(":"); session.sendUpstreamPacket(placeBlockSoundPacket); diff --git a/connector/pom.xml b/connector/pom.xml index 1ee651a2a..fb253117c 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -31,7 +31,7 @@ com.github.CloudburstMC.Protocol - bedrock-v422 + bedrock-v428 42da92f compile diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java index 6ae65643c..52183c431 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -31,7 +31,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import net.kyori.adventure.text.Component; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.chat.MessageTranslator; public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { @@ -60,8 +59,8 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { * By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange. */ @Override - public void updateDefaultBlockMetadata() { - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID); + public void updateDefaultBlockMetadata(GeyserSession session) { + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockRuntimeCommandBlockId()); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java index 8ab368e70..805105c64 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java @@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; /** * This class is used as a base for minecarts with a default block to display like furnaces and spawners @@ -44,10 +43,15 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); - updateDefaultBlockMetadata(); metadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1); } + @Override + public void spawnEntity(GeyserSession session) { + updateDefaultBlockMetadata(session); + super.spawnEntity(session); + } + @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { @@ -56,7 +60,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { customBlock = (int) entityMetadata.getValue(); if (showCustomBlock) { - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock)); } } @@ -73,16 +77,16 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { if (entityMetadata.getId() == 12) { if ((boolean) entityMetadata.getValue()) { showCustomBlock = true; - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock)); metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); } else { showCustomBlock = false; - updateDefaultBlockMetadata(); + updateDefaultBlockMetadata(session); } } super.updateBedrockMetadata(entityMetadata, session); } - public void updateDefaultBlockMetadata() { } + public void updateDefaultBlockMetadata(GeyserSession session) { } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java index 76ca0567e..bd0fe9b80 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java @@ -31,14 +31,19 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class FallingBlockEntity extends Entity { + private final int javaId; public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) { super(entityId, geyserId, entityType, position, motion, rotation); + this.javaId = javaId; + } - this.metadata.put(EntityData.VARIANT, BlockTranslator.getBedrockBlockId(javaId)); + @Override + public void spawnEntity(GeyserSession session) { + this.metadata.put(EntityData.VARIANT, session.getBlockTranslator().getBedrockBlockId(javaId)); + super.spawnEntity(session); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java index e3af51be6..fdf24f176 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java @@ -44,15 +44,15 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 13 && !showCustomBlock) { hasFuel = (boolean) entityMetadata.getValue(); - updateDefaultBlockMetadata(); + updateDefaultBlockMetadata(session); } super.updateBedrockMetadata(entityMetadata, session); } @Override - public void updateDefaultBlockMetadata() { - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID)); + public void updateDefaultBlockMetadata(GeyserSession session) { + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID)); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index 4f0a224e2..a898ea389 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -40,7 +40,6 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import java.util.concurrent.TimeUnit; @@ -49,15 +48,19 @@ import java.util.concurrent.TimeUnit; */ public class ItemFrameEntity extends Entity { + /** + * Used to construct the block entity tag on spawning. + */ + private final HangingDirection direction; /** * Used for getting the Bedrock block position. * Blocks deal with integers whereas entities deal with floats. */ - private final Vector3i bedrockPosition; + private Vector3i bedrockPosition; /** * Specific block 'state' we are emulating in Bedrock. */ - private final int bedrockRuntimeId; + private int bedrockRuntimeId; /** * Rotation of item in frame. */ @@ -69,19 +72,21 @@ public class ItemFrameEntity extends Entity { public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) { super(entityId, geyserId, entityType, position, motion, rotation); - NbtMapBuilder blockBuilder = NbtMap.builder() - .putString("name", "minecraft:frame") - .putInt("version", BlockTranslator.getBlockStateVersion()); - blockBuilder.put("states", NbtMap.builder() - .putInt("facing_direction", direction.ordinal()) - .putByte("item_frame_map_bit", (byte) 0) - .build()); - bedrockRuntimeId = BlockTranslator.getItemFrame(blockBuilder.build()); - bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); + this.direction = direction; } @Override public void spawnEntity(GeyserSession session) { + NbtMapBuilder blockBuilder = NbtMap.builder() + .putString("name", "minecraft:frame") + .putInt("version", session.getBlockTranslator().getBlockStateVersion()); + blockBuilder.put("states", NbtMap.builder() + .putInt("facing_direction", direction.ordinal()) + .putByte("item_frame_map_bit", (byte) 0) + .build()); + bedrockRuntimeId = session.getBlockTranslator().getItemFrame(blockBuilder.build()); + bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); + session.getItemFrameCache().put(bedrockPosition, entityId); // Delay is required, or else loading in frames on chunk load is sketchy at best session.getConnector().getGeneralThreadPool().schedule(() -> { @@ -136,7 +141,7 @@ public class ItemFrameEntity extends Entity { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); - updateBlockPacket.setRuntimeId(BlockTranslator.BEDROCK_AIR_ID); + updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId()); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java index ed5f28d17..49b12a3e1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class MinecartEntity extends Entity { @@ -58,7 +57,7 @@ public class MinecartEntity extends Entity { if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class // Custom block if (entityMetadata.getId() == 10) { - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue())); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); } // Custom block offset diff --git a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java index 143e36373..2f7af73eb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java @@ -28,6 +28,7 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { @@ -37,8 +38,8 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { } @Override - public void updateDefaultBlockMetadata() { - metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID)); + public void updateDefaultBlockMetadata(GeyserSession session) { + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID)); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java index 3151ae474..0d265b56e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java @@ -33,7 +33,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class EndermanEntity extends MonsterEntity { @@ -45,7 +44,7 @@ public class EndermanEntity extends MonsterEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Held block if (entityMetadata.getId() == 15) { - metadata.put(EntityData.CARRIED_BLOCK, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue())); + metadata.put(EntityData.CARRIED_BLOCK, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); } // "Is screaming" - controls sound if (entityMetadata.getId() == 16) { diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index d24cea328..3b5af7f99 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v419.Bedrock_v419; import com.nukkitx.protocol.bedrock.v422.Bedrock_v422; +import com.nukkitx.protocol.bedrock.v428.Bedrock_v428; import java.util.ArrayList; import java.util.List; @@ -40,9 +41,7 @@ public class BedrockProtocol { * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC.toBuilder() - .minecraftVersion("1.16.201") - .build(); + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v428.V428_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -52,9 +51,10 @@ public class BedrockProtocol { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder() .minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta .build()); - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v422.V422_CODEC.toBuilder() .minecraftVersion("1.16.200/1.16.201") .build()); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 7ebfaeda5..829ae23ef 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -26,15 +26,18 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; -import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; +import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.v428.Bedrock_v428; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.AdvancementsCache; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_100; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210; import org.geysermc.connector.utils.*; import java.io.FileInputStream; @@ -68,6 +71,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.getUpstream().getSession().setPacketCodec(packetCodec); + // Set the block translation based off of version + session.setBlockTranslator(packetCodec.getProtocolVersion() >= Bedrock_v428.V428_CODEC.getProtocolVersion() + ? BlockTranslator1_16_210.INSTANCE : BlockTranslator1_16_100.INSTANCE); + LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket); PlayStatusPacket playStatus = new PlayStatusPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index cf4ad8095..ee3f07f3e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -92,6 +92,7 @@ import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.translators.collision.CollisionManager; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.skin.SkinManager; import org.geysermc.connector.utils.*; import org.geysermc.floodgate.util.BedrockData; @@ -162,6 +163,12 @@ public class GeyserSession implements CommandSender { */ private final CollisionManager collisionManager; + /** + * Stores the block translations for this specific version. + */ + @Setter + private BlockTranslator blockTranslator; + private final Map skullCache = new ConcurrentHashMap<>(); private final Long2ObjectMap storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 0f08e4e84..5258219ba 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -127,7 +127,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE) { // Otherwise insufficient permissions - int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId()); + int blockState = session.getBlockTranslator().getJavaBlockState(packet.getBlockRuntimeId()); String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, ""); // In the future this can be used for structure blocks too, however not all elements // are available in each GUI @@ -271,7 +271,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { @@ -39,9 +41,12 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator { + ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket(); + serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData()); + serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID); + session.sendUpstreamPacket(serverSettingsResponsePacket); + }, 1, TimeUnit.SECONDS); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index 5046f1cf0..f0bbbeada 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -179,7 +179,7 @@ public class BedrockActionTranslator extends PacketTranslator 0) { canModifyBedrock = new String[canModifyJava.size()]; for (int i = 0; i < canModifyBedrock.length; i++) { @@ -189,7 +189,7 @@ public abstract class ItemTranslator { if (!block.startsWith("minecraft:")) block = "minecraft:" + block; // Get the Bedrock identifier of the item and replace it. // This will unfortunately be limited - for example, beds and banners will be translated weirdly - canModifyBedrock[i] = BlockTranslator.getBedrockBlockIdentifier(block).replace("minecraft:", ""); + canModifyBedrock[i] = session.getBlockTranslator().getBedrockBlockIdentifier(block).replace("minecraft:", ""); } } return canModifyBedrock; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java index 9bff2652e..e362e335f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java @@ -84,7 +84,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>(); - private static final Map FLOWER_POT_BLOCKS = new HashMap<>(); private static final Int2BooleanMap LECTERN_BOOK_STATES = new Int2BooleanOpenHashMap(); private static final Int2IntMap NOTEBLOCK_PITCHES = new Int2IntOpenHashMap(); private static final Int2BooleanMap IS_STICKY_PISTON = new Int2BooleanOpenHashMap(); @@ -202,15 +199,6 @@ public class BlockStateValues { return FLOWER_POT_VALUES; } - /** - * Get the map of contained flower pot plants to Bedrock CompoundTag - * - * @return Map of flower pot blocks. - */ - public static Map getFlowerPotBlocks() { - return FLOWER_POT_BLOCKS; - } - public static Int2BooleanMap getLecternBookStates() { return LECTERN_BOOK_STATES; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java index 2dd455958..ec1c79950 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java @@ -34,16 +34,20 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import lombok.Getter; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.world.chunk.ChunkSection; +import org.geysermc.connector.network.translators.world.chunk.EmptyChunkProvider; import org.geysermc.connector.utils.FileUtils; -import org.reflections.Reflections; import java.io.DataInputStream; import java.io.InputStream; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.zip.GZIPInputStream; -public class BlockTranslator { +public abstract class BlockTranslator { /** * The Java block runtime ID of air */ @@ -51,11 +55,11 @@ public class BlockTranslator { /** * The Bedrock block runtime ID of air */ - public static final int BEDROCK_AIR_ID; - public static final int BEDROCK_WATER_ID; + private final int bedrockAirId; + private final int bedrockWaterId; - private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); - private static final Int2IntMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2IntOpenHashMap(); + private final Int2IntMap javaToBedrockBlockMap = new Int2IntOpenHashMap(); + private final Int2IntMap bedrockToJavaBlockMap = new Int2IntOpenHashMap(); /** * Stores a list of differences in block identifiers. * Items will not be added to this list if the key and value is the same. @@ -63,7 +67,8 @@ public class BlockTranslator { private static final Object2ObjectMap JAVA_TO_BEDROCK_IDENTIFIERS = new Object2ObjectOpenHashMap<>(); private static final BiMap JAVA_ID_BLOCK_MAP = HashBiMap.create(); private static final IntSet WATERLOGGED = new IntOpenHashSet(); - private static final Object2IntMap ITEM_FRAMES = new Object2IntOpenHashMap<>(); + private final Object2IntMap itemFrames = new Object2IntOpenHashMap<>(); + private final Map flowerPotBlocks = new HashMap<>(); // Bedrock carpet ID, used in LlamaEntity.java for decoration public static final int CARPET = 171; @@ -85,7 +90,10 @@ public class BlockTranslator { /** * Runtime command block ID, used for fixing command block minecart appearances */ - public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID; + @Getter + private final int bedrockRuntimeCommandBlockId; + + private final EmptyChunkProvider emptyChunkProvider; /** * A list of all Java runtime wool IDs, for use with block breaking math and shears @@ -102,67 +110,34 @@ public class BlockTranslator { * Contains a map of Java blocks to their respective Bedrock block tag, if the Java identifier is different from Bedrock. * Required to fix villager trades with these blocks. */ - private static final Map JAVA_IDENTIFIER_TO_BEDROCK_TAG; + private final Map javaIdentifierToBedrockTag; private static final int BLOCK_STATE_VERSION = 17825808; + /** + * Stores the raw blocks JSON until it is no longer needed. + */ + public static JsonNode BLOCKS_JSON; + static { - /* Load block palette */ - InputStream stream = FileUtils.getResource("bedrock/blockpalette.nbt"); - - NbtList blocksTag; - try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(stream))) { - NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); - blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND); - } catch (Exception e) { - throw new AssertionError("Unable to get blocks from runtime block states", e); - } - - JAVA_IDENTIFIER_TO_BEDROCK_TAG = new Object2ObjectOpenHashMap<>(); - - // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette, - // as we no longer send a block palette - Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size()); - - for (int i = 0; i < blocksTag.size(); i++) { - NbtMap tag = blocksTag.get(i); - NbtMap blockTag = tag.getCompound("block"); - if (blockStateOrderedMap.containsKey(blockTag)) { - throw new AssertionError("Duplicate block states in Bedrock palette"); - } - blockStateOrderedMap.put(blockTag, i); - } - - stream = FileUtils.getResource("mappings/blocks.json"); - JsonNode blocks; + InputStream stream = FileUtils.getResource("mappings/blocks.json"); try { - blocks = GeyserConnector.JSON_MAPPER.readTree(stream); + BLOCKS_JSON = GeyserConnector.JSON_MAPPER.readTree(stream); } catch (Exception e) { throw new AssertionError("Unable to load Java block mappings", e); } - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") - : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); - - int waterRuntimeId = -1; int javaRuntimeId = -1; - int airRuntimeId = -1; int cobwebRuntimeId = -1; - int commandBlockRuntimeId = -1; int furnaceRuntimeId = -1; int furnaceLitRuntimeId = -1; int spawnerRuntimeId = -1; int uniqueJavaId = -1; - Iterator> blocksIterator = blocks.fields(); + Iterator> blocksIterator = BLOCKS_JSON.fields(); while (blocksIterator.hasNext()) { javaRuntimeId++; Map.Entry entry = blocksIterator.next(); String javaId = entry.getKey(); - NbtMap blockTag = buildBedrockState(entry.getValue()); - int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1); - if (bedrockRuntimeId == -1) { - throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID!"); - } // TODO fix this, (no block should have a null hardness) JsonNode hardnessNode = entry.getValue().get("block_hardness"); @@ -198,52 +173,22 @@ public class BlockTranslator { String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); - boolean javaIdentifierSameAsBedrock = cleanJavaIdentifier.equals(bedrockIdentifier); - if (!JAVA_ID_TO_JAVA_IDENTIFIER_MAP.containsValue(cleanJavaIdentifier)) { uniqueJavaId++; JAVA_ID_TO_JAVA_IDENTIFIER_MAP.put(uniqueJavaId, cleanJavaIdentifier); - if (!javaIdentifierSameAsBedrock) { - JAVA_IDENTIFIER_TO_BEDROCK_TAG.put(cleanJavaIdentifier, blockTag); - } } - if (!javaIdentifierSameAsBedrock) { + // Keeping this here since this is currently unchanged between versions + if (!cleanJavaIdentifier.equals(bedrockIdentifier)) { JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier); } - // Get the tag needed for non-empty flower pots - if (entry.getValue().get("pottable") != null) { - BlockStateValues.getFlowerPotBlocks().put(cleanJavaIdentifier, buildBedrockState(entry.getValue())); - } - - if ("minecraft:water[level=0]".equals(javaId)) { - waterRuntimeId = bedrockRuntimeId; - } - boolean waterlogged = entry.getKey().contains("waterlogged=true") - || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); - - if (waterlogged) { - BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId); - WATERLOGGED.add(javaRuntimeId); - } else { - BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, javaRuntimeId); - } - - JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); - - if (bedrockIdentifier.equals("minecraft:air")) { - airRuntimeId = bedrockRuntimeId; - - } else if (javaId.contains("wool")) { + if (javaId.contains("wool")) { JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); } else if (javaId.contains("cobweb")) { cobwebRuntimeId = javaRuntimeId; - } else if (javaId.equals("minecraft:command_block[conditional=false,facing=north]")) { - commandBlockRuntimeId = bedrockRuntimeId; - } else if (javaId.startsWith("minecraft:furnace[facing=north")) { if (javaId.contains("lit=true")) { furnaceLitRuntimeId = javaRuntimeId; @@ -261,11 +206,6 @@ public class BlockTranslator { } JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; - if (commandBlockRuntimeId == -1) { - throw new AssertionError("Unable to find command block in palette"); - } - BEDROCK_RUNTIME_COMMAND_BLOCK_ID = commandBlockRuntimeId; - if (furnaceRuntimeId == -1) { throw new AssertionError("Unable to find furnace in palette"); } @@ -281,35 +221,123 @@ public class BlockTranslator { } JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId; + BlockTranslator1_16_100.init(); + BlockTranslator1_16_210.init(); + BLOCKS_JSON = null; // We no longer require this so let it garbage collect away + } + + public BlockTranslator(String paletteFile) { + /* Load block palette */ + InputStream stream = FileUtils.getResource(paletteFile); + + NbtList blocksTag; + try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)))) { + NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); + blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND); + } catch (Exception e) { + throw new AssertionError("Unable to get blocks from runtime block states", e); + } + + javaIdentifierToBedrockTag = new Object2ObjectOpenHashMap<>(); + + // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette, + // as we no longer send a block palette + Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size()); + + for (int i = 0; i < blocksTag.size(); i++) { + NbtMap tag = blocksTag.get(i); + if (blockStateOrderedMap.containsKey(tag)) { + throw new AssertionError("Duplicate block states in Bedrock palette: " + tag); + } + blockStateOrderedMap.put(tag, i); + } + + int airRuntimeId = -1; + int commandBlockRuntimeId = -1; + int javaRuntimeId = -1; + int waterRuntimeId = -1; + Iterator> blocksIterator = BLOCKS_JSON.fields(); + while (blocksIterator.hasNext()) { + javaRuntimeId++; + Map.Entry entry = blocksIterator.next(); + String javaId = entry.getKey(); + + NbtMap blockTag = buildBedrockState(entry.getValue()); + int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1); + if (bedrockRuntimeId == -1) { + throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID! Built compound tag: \n" + blockTag); + } + + switch (javaId) { + case "minecraft:air": + airRuntimeId = bedrockRuntimeId; + break; + case "minecraft:water[level=0]": + waterRuntimeId = bedrockRuntimeId; + break; + case "minecraft:command_block[conditional=false,facing=north]": + commandBlockRuntimeId = bedrockRuntimeId; + break; + } + + boolean waterlogged = entry.getKey().contains("waterlogged=true") + || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); + + if (waterlogged) { + bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId); + WATERLOGGED.add(javaRuntimeId); + } else { + bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId, javaRuntimeId); + } + + String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; + + // Get the tag needed for non-empty flower pots + if (entry.getValue().get("pottable") != null) { + flowerPotBlocks.put(cleanJavaIdentifier, blockTag); + } + + if (!cleanJavaIdentifier.equals(entry.getValue().get("bedrock_identifier").asText())) { + javaIdentifierToBedrockTag.put(cleanJavaIdentifier, blockTag); + } + + javaToBedrockBlockMap.put(javaRuntimeId, bedrockRuntimeId); + } + + if (commandBlockRuntimeId == -1) { + throw new AssertionError("Unable to find command block in palette"); + } + bedrockRuntimeCommandBlockId = commandBlockRuntimeId; + if (waterRuntimeId == -1) { throw new AssertionError("Unable to find water in palette"); } - BEDROCK_WATER_ID = waterRuntimeId; + bedrockWaterId = waterRuntimeId; if (airRuntimeId == -1) { throw new AssertionError("Unable to find air in palette"); } - BEDROCK_AIR_ID = airRuntimeId; + bedrockAirId = airRuntimeId; // Loop around again to find all item frame runtime IDs for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) { if (entry.getKey().getString("name").equals("minecraft:frame")) { - ITEM_FRAMES.put(entry.getKey(), entry.getIntValue()); + itemFrames.put(entry.getKey(), entry.getIntValue()); } } - } - private BlockTranslator() { + this.emptyChunkProvider = new EmptyChunkProvider(bedrockAirId); } public static void init() { // no-op } - private static NbtMap buildBedrockState(JsonNode node) { + private NbtMap buildBedrockState(JsonNode node) { NbtMapBuilder tagBuilder = NbtMap.builder(); - tagBuilder.putString("name", node.get("bedrock_identifier").textValue()) - .putInt("version", BlockTranslator.BLOCK_STATE_VERSION); + String bedrockIdentifier = node.get("bedrock_identifier").textValue(); + tagBuilder.putString("name", bedrockIdentifier) + .putInt("version", getBlockStateVersion()); NbtMapBuilder statesBuilder = NbtMap.builder(); @@ -332,36 +360,67 @@ public class BlockTranslator { } } } - tagBuilder.put("states", statesBuilder.build()); + tagBuilder.put("states", adjustBlockStateForVersion(bedrockIdentifier, statesBuilder).build()); return tagBuilder.build(); } - public static int getBedrockBlockId(int state) { - return JAVA_TO_BEDROCK_BLOCK_MAP.get(state); + /** + * @return an adjusted state list, if necessary, that converts Geyser's new mapping to Bedrock's older version + * of the mapping. + */ + protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) { + return statesBuilder; } - public static int getJavaBlockState(int bedrockId) { - return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId); + public int getBedrockBlockId(int state) { + return javaToBedrockBlockMap.get(state); + } + + public int getJavaBlockState(int bedrockId) { + return bedrockToJavaBlockMap.get(bedrockId); } /** * @param javaIdentifier the Java identifier of the block to search for * @return the Bedrock identifier if different, or else the Java identifier */ - public static String getBedrockBlockIdentifier(String javaIdentifier) { + public String getBedrockBlockIdentifier(String javaIdentifier) { return JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(javaIdentifier, javaIdentifier); } - public static int getItemFrame(NbtMap tag) { - return ITEM_FRAMES.getOrDefault(tag, -1); + public int getItemFrame(NbtMap tag) { + return itemFrames.getOrDefault(tag, -1); } - public static boolean isItemFrame(int bedrockBlockRuntimeId) { - return ITEM_FRAMES.values().contains(bedrockBlockRuntimeId); + public boolean isItemFrame(int bedrockBlockRuntimeId) { + return itemFrames.values().contains(bedrockBlockRuntimeId); } - public static int getBlockStateVersion() { - return BLOCK_STATE_VERSION; + /** + * Get the map of contained flower pot plants to Bedrock CompoundTag + * + * @return Map of flower pot blocks. + */ + public Map getFlowerPotBlocks() { + return flowerPotBlocks; + } + + public int getBedrockAirId() { + return bedrockAirId; + } + + public int getBedrockWaterId() { + return bedrockWaterId; + } + + public abstract int getBlockStateVersion(); + + public byte[] getEmptyChunkData() { + return emptyChunkProvider.getEmptyLevelChunkData(); + } + + public ChunkSection getEmptyChunkSection() { + return emptyChunkProvider.getEmptySection(); } /** @@ -380,10 +439,6 @@ public class BlockTranslator { return JAVA_ID_BLOCK_MAP; } - public static int getJavaWaterloggedState(int bedrockId) { - return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId); - } - /** * Get the item a Java client would receive when pressing * the Pick Block key on a specific Java block state. @@ -411,7 +466,7 @@ public class BlockTranslator { * * @return the block tag of the block name mapped from Java to Bedrock. */ - public static NbtMap getBedrockBlockNbt(String cleanJavaIdentifier) { - return JAVA_IDENTIFIER_TO_BEDROCK_TAG.get(cleanJavaIdentifier); + public NbtMap getBedrockBlockNbt(String cleanJavaIdentifier) { + return javaIdentifierToBedrockTag.get(cleanJavaIdentifier); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java new file mode 100644 index 000000000..e10a503ea --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_100.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2021 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.world.block; + +import com.google.common.collect.ImmutableSet; +import com.nukkitx.nbt.NbtMapBuilder; + +import java.util.Set; + +public class BlockTranslator1_16_100 extends BlockTranslator { + private static final Set CORRECTED_STATES = ImmutableSet.of("minecraft:stripped_warped_stem", + "minecraft:stripped_warped_hyphae", "minecraft:stripped_crimson_stem", "minecraft:stripped_crimson_hyphae"); + + public static final BlockTranslator1_16_100 INSTANCE = new BlockTranslator1_16_100(); + + public BlockTranslator1_16_100() { + super("bedrock/blockpalette.1_16_100.nbt"); + } + + @Override + public int getBlockStateVersion() { + return 17825808; + } + + @Override + protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) { + if (CORRECTED_STATES.contains(bedrockIdentifier)) { + statesBuilder.putInt("deprecated", 0); + } + return super.adjustBlockStateForVersion(bedrockIdentifier, statesBuilder); + } + + public static void init() { + // no-op + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java new file mode 100644 index 000000000..58861cb9c --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_16_210.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2021 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.world.block; + +public class BlockTranslator1_16_210 extends BlockTranslator { + public static final BlockTranslator1_16_210 INSTANCE = new BlockTranslator1_16_210(); + + public BlockTranslator1_16_210() { + super("bedrock/blockpalette.1_16_210.nbt"); + } + + @Override + public int getBlockStateVersion() { + return 17879555; + } + + public static void init() { + // no-op + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java index e05fcc67b..2417f1e6e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java @@ -47,9 +47,9 @@ public interface BedrockOnlyBlockEntity { * @param blockState Java BlockState of block. * @return Bedrock tag, or null if not a Bedrock-only Block Entity */ - static NbtMap getTag(Vector3i position, int blockState) { + static NbtMap getTag(GeyserSession session, Vector3i position, int blockState) { if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) { - return FlowerPotBlockEntityTranslator.getTag(blockState, position); + return FlowerPotBlockEntityTranslator.getTag(session, blockState, position); } else if (PistonBlockEntityTranslator.isBlock(blockState)) { return PistonBlockEntityTranslator.getTag(blockState, position); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java index 9eebe37d7..062fd4922 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java @@ -31,7 +31,6 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockEntityUtils; public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { @@ -50,7 +49,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R * @param position Bedrock position of flower pot. * @return Bedrock tag of flower pot. */ - public static NbtMap getTag(int blockState, Vector3i position) { + public static NbtMap getTag(GeyserSession session, int blockState, Vector3i position) { NbtMapBuilder tagBuilder = NbtMap.builder() .putInt("x", position.getX()) .putInt("y", position.getY()) @@ -62,7 +61,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R if (name != null) { // Get the Bedrock CompoundTag of the block. // This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states. - NbtMap plant = BlockStateValues.getFlowerPotBlocks().get(name); + NbtMap plant = session.getBlockTranslator().getFlowerPotBlocks().get(name); if (plant != null) { tagBuilder.put("PlantBlock", plant.toBuilder().build()); } @@ -77,15 +76,16 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R @Override public void updateBlock(GeyserSession session, int blockState, Vector3i position) { - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + NbtMap tag = getTag(session, blockState, position); + BlockEntityUtils.updateBlockEntity(session, tag, position); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); - updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState)); + updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(blockState)); updateBlockPacket.setBlockPosition(position); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); session.sendUpstreamPacket(updateBlockPacket); - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + BlockEntityUtils.updateBlockEntity(session, tag, position); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java index 7a5086241..672fa1a35 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java @@ -30,7 +30,6 @@ import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Getter; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; @@ -44,14 +43,14 @@ public class BlockStorage { private final IntList palette; private BitArray bitArray; - public BlockStorage() { - this(BitArrayVersion.V2); + public BlockStorage(int airBlockId) { + this(airBlockId, BitArrayVersion.V2); } - public BlockStorage(BitArrayVersion version) { + public BlockStorage(int airBlockId, BitArrayVersion version) { this.bitArray = version.createArray(SIZE); this.palette = new IntArrayList(16); - this.palette.add(BlockTranslator.BEDROCK_AIR_ID); // Air is at the start of every palette and controls what the default block is in second-layer non-air block spaces. + this.palette.add(airBlockId); // Air is at the start of every palette and controls what the default block is in second-layer non-air block spaces. } public BlockStorage(BitArray bitArray, IntList palette) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java index 2709e3e23..53528d654 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java @@ -34,8 +34,8 @@ public class ChunkSection { private final BlockStorage[] storage; - public ChunkSection() { - this(new BlockStorage[]{new BlockStorage(), new BlockStorage()}); + public ChunkSection(int airBlockId) { + this(new BlockStorage[]{new BlockStorage(airBlockId), new BlockStorage(airBlockId)}); } public ChunkSection(BlockStorage[] storage) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java new file mode 100644 index 000000000..1bc7d684f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/EmptyChunkProvider.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2021 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.world.chunk; + +import com.nukkitx.nbt.NBTOutputStream; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtUtils; +import lombok.Getter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class EmptyChunkProvider { + @Getter + private final byte[] emptyLevelChunkData; + @Getter + private final ChunkSection emptySection; + + public EmptyChunkProvider(int airId) { + BlockStorage emptyStorage = new BlockStorage(airId); + emptySection = new ChunkSection(new BlockStorage[]{emptyStorage}); + + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size + + try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) { + stream.writeTag(NbtMap.EMPTY); + } + + emptyLevelChunkData = outputStream.toByteArray(); + } catch (IOException e) { + throw new AssertionError("Unable to generate empty level chunk data"); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index 24b137f77..a75c20872 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -132,7 +132,7 @@ public class BlockUtils { miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG); boolean isInWater = session.getConnector().getConfig().isCacheChunks() - && BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == BlockTranslator.BEDROCK_WATER_ID; + && session.getBlockTranslator().getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == session.getBlockTranslator().getBedrockWaterId(); boolean insideOfWaterWithoutAquaAffinity = isInWater && ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getNbt(), "minecraft:aqua_affinity") < 1; 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 45785545c..b3a31e1ab 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -36,9 +36,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; @@ -65,13 +63,11 @@ import org.geysermc.connector.network.translators.world.chunk.ChunkSection; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; import java.util.List; -import static org.geysermc.connector.network.translators.world.block.BlockTranslator.*; +import static org.geysermc.connector.network.translators.world.block.BlockTranslator.JAVA_AIR_ID; @UtilityClass public class ChunkUtils { @@ -81,26 +77,6 @@ public class ChunkUtils { */ public static final Object2IntMap CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>(); - private static final NbtMap EMPTY_TAG = NbtMap.builder().build(); - public static final byte[] EMPTY_LEVEL_CHUNK_DATA; - - public static final BlockStorage EMPTY_STORAGE = new BlockStorage(); - public static final ChunkSection EMPTY_SECTION = new ChunkSection(new BlockStorage[]{ EMPTY_STORAGE }); - - static { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size - - try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) { - stream.writeTag(EMPTY_TAG); - } - - EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray(); - } catch (IOException e) { - throw new AssertionError("Unable to generate empty level chunk data"); - } - } - private static int indexYZXtoXZY(int yzx) { return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8); } @@ -162,20 +138,20 @@ public class ChunkUtils { if (javaPalette instanceof GlobalPalette) { // As this is the global palette, simply iterate through the whole chunk section once - ChunkSection section = new ChunkSection(); + ChunkSection section = new ChunkSection(session.getBlockTranslator().getBedrockAirId()); for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { int javaId = javaData.get(yzx); - int bedrockId = BlockTranslator.getBedrockBlockId(javaId); + int bedrockId = session.getBlockTranslator().getBedrockBlockId(javaId); int xzy = indexYZXtoXZY(yzx); section.getBlockStorageArray()[0].setFullBlock(xzy, bedrockId); if (BlockTranslator.isWaterlogged(javaId)) { - section.getBlockStorageArray()[1].setFullBlock(xzy, BEDROCK_WATER_ID); + section.getBlockStorageArray()[1].setFullBlock(xzy, session.getBlockTranslator().getBedrockWaterId()); } // Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) { - bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag( + bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session, Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)), javaId )); @@ -192,7 +168,7 @@ public class ChunkUtils { // Iterate through palette and convert state IDs to Bedrock, doing some additional checks as we go for (int i = 0; i < javaPalette.size(); i++) { int javaId = javaPalette.idToState(i); - bedrockPalette.add(BlockTranslator.getBedrockBlockId(javaId)); + bedrockPalette.add(session.getBlockTranslator().getBedrockBlockId(javaId)); if (BlockTranslator.isWaterlogged(javaId)) { waterloggedPaletteIds.set(i); @@ -211,7 +187,7 @@ public class ChunkUtils { for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { int paletteId = javaData.get(yzx); if (pistonOrFlowerPaletteIds.get(paletteId)) { - bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag( + bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(session, Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)), javaPalette.idToState(paletteId) )); @@ -248,8 +224,8 @@ public class ChunkUtils { // V1 palette IntList layer1Palette = new IntArrayList(2); - layer1Palette.add(BEDROCK_AIR_ID); // Air - see BlockStorage's constructor for more information - layer1Palette.add(BEDROCK_WATER_ID); + layer1Palette.add(session.getBlockTranslator().getBedrockAirId()); // Air - see BlockStorage's constructor for more information + layer1Palette.add(session.getBlockTranslator().getBedrockWaterId()); layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) }; } @@ -376,7 +352,7 @@ public class ChunkUtils { skull.despawnEntity(session, position); } - int blockId = BlockTranslator.getBedrockBlockId(blockState); + int blockId = session.getBlockTranslator().getBedrockBlockId(blockState); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); @@ -390,9 +366,9 @@ public class ChunkUtils { waterPacket.setDataLayer(1); waterPacket.setBlockPosition(position); if (BlockTranslator.isWaterlogged(blockState)) { - waterPacket.setRuntimeId(BEDROCK_WATER_ID); + waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockWaterId()); } else { - waterPacket.setRuntimeId(BEDROCK_AIR_ID); + waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId()); } session.sendUpstreamPacket(waterPacket); @@ -447,7 +423,7 @@ public class ChunkUtils { data.setChunkX(chunkX + x); data.setChunkZ(chunkZ + z); data.setSubChunksLength(0); - data.setData(EMPTY_LEVEL_CHUNK_DATA); + data.setData(session.getBlockTranslator().getEmptyChunkData()); data.setCachingEnabled(false); session.sendUpstreamPacket(data); diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt new file mode 100644 index 000000000..4513be031 Binary files /dev/null and b/connector/src/main/resources/bedrock/blockpalette.1_16_100.nbt differ diff --git a/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt b/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt new file mode 100644 index 000000000..178a370ed Binary files /dev/null and b/connector/src/main/resources/bedrock/blockpalette.1_16_210.nbt differ diff --git a/connector/src/main/resources/bedrock/blockpalette.nbt b/connector/src/main/resources/bedrock/blockpalette.nbt deleted file mode 100644 index b92a06260..000000000 Binary files a/connector/src/main/resources/bedrock/blockpalette.nbt and /dev/null differ diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index bffb5617c..3d3b60de7 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit bffb5617c1ecdacc10031c6ec36988a5f04cb5c6 +Subproject commit 3d3b60de724f3f552f351c5f400269fde7598b67 diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index abadb6412..216e90086 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit abadb6412c22f2f34eded7ea720e04e8bc7d0f22 +Subproject commit 216e9008678a761b3885808f4f3d43000404381b