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 0bcb6ed2f71..e00677a3182 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 @@ -37,9 +37,13 @@ import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility; +import com.github.steveice10.mc.protocol.data.game.setting.SkinPart; import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientSettingsPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; @@ -366,6 +370,14 @@ public class GeyserSession implements CommandSender { */ private boolean daylightCycle = true; + /** + * Stores the client render distance. + * + * At this time, it is not used to change the amount of chunks rendered. + */ + @Setter + private int clientRenderDistance; + private boolean reducedDebugInfo = false; @Setter @@ -1296,6 +1308,18 @@ public void sendAdventureSettings() { sendUpstreamPacket(adventureSettingsPacket); } + // We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc + private static final List SKIN_PARTS = Arrays.asList(SkinPart.values()); + + /** + * Send a ClientSettingsPacket to the server to indicate client render distance, locale, skin parts, and hand preference. + */ + public void sendJavaClientSettings() { + ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(getLocale(), (byte) clientRenderDistance, + ChatVisibility.FULL, true, SKIN_PARTS, HandPreference.RIGHT_HAND); + sendDownstreamPacket(clientSettingsPacket); + } + /** * Used for updating statistic values since we only get changes from the server * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRequestChunkRadiusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRequestChunkRadiusTranslator.java new file mode 100644 index 00000000000..3cacf08e740 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRequestChunkRadiusTranslator.java @@ -0,0 +1,45 @@ +/* + * 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.bedrock; + +import com.nukkitx.protocol.bedrock.packet.RequestChunkRadiusPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = RequestChunkRadiusPacket.class) +public class BedrockRequestChunkRadiusTranslator extends PacketTranslator { + + @Override + public void translate(RequestChunkRadiusPacket packet, GeyserSession session) { + session.setClientRenderDistance(packet.getRadius()); + + if (session.isLoggedIn()) { + // Render distance was changed in-game + session.sendJavaClientSettings(); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java index 2964bd65c25..c5af3a8011a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java @@ -54,7 +54,7 @@ public void translate(RespawnPacket packet, GeyserSession session) { if (session.isSpawned()) { // Client might be stuck; resend spawn information PlayerEntity entity = session.getPlayerEntity(); - if (entity == null) return; + SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); entityDataPacket.setRuntimeEntityId(entity.getGeyserId()); entityDataPacket.getMetadata().putAll(entity.getMetadata()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index 33e46c95681..78b85a2b96b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -25,11 +25,7 @@ package org.geysermc.connector.network.translators.java; -import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; -import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility; -import com.github.steveice10.mc.protocol.data.game.setting.SkinPart; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.ClientSettingsPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket; import com.nukkitx.protocol.bedrock.data.GameRuleData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; @@ -41,9 +37,6 @@ import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.connector.utils.PluginMessageUtils; -import java.util.Arrays; -import java.util.List; - @Translator(packet = ServerJoinGamePacket.class) public class JavaJoinGameTranslator extends PacketTranslator { @@ -90,11 +83,7 @@ public void translate(ServerJoinGamePacket packet, GeyserSession session) { session.setRenderDistance(packet.getViewDistance()); - // We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc - String locale = session.getLocale(); - List skinParts = Arrays.asList(SkinPart.values()); - ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, HandPreference.RIGHT_HAND); - session.sendDownstreamPacket(clientSettingsPacket); + session.sendJavaClientSettings(); session.sendDownstreamPacket(new ClientPluginMessagePacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData())); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java index b379443e3d5..c9c7bd56ae4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.RespawnPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; @@ -79,6 +80,15 @@ public void translate(ServerPlayerPositionRotationPacket packet, GeyserSession s ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId()); session.sendDownstreamPacket(teleportConfirmPacket); + if (session.getRenderDistance() > 47) { + // See DimensionUtils for an explanation + ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); + chunkRadiusUpdatedPacket.setRadius(session.getRenderDistance()); + session.sendUpstreamPacket(chunkRadiusUpdatedPacket); + + session.setLastChunkPosition(null); + } + ChunkUtils.updateChunkPosition(session, pos.toInt()); session.getConnector().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.entity.player.spawn", packet.getX(), packet.getY(), packet.getZ())); 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 b6ad3dfb869..daa8d97e509 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 @@ -27,6 +27,7 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; @@ -58,6 +59,13 @@ public JavaChunkDataTranslator() { public void translate(ServerChunkDataPacket packet, GeyserSession session) { if (session.isSpawned()) { ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt()); + } else if (session.getRenderDistance() > 47 && session.getClientRenderDistance() < 32) { + // Ensure the chunk gets loaded + // If the player is far away enough, it will load during a dimension switch, but + // it will also just discard the chunk + session.getPlayerEntity().moveAbsolute(session, + Vector3f.from(packet.getColumn().getX() << 4, Short.MAX_VALUE, packet.getColumn().getZ() << 4), + session.getPlayerEntity().getRotation(), false, true); } if (packet.getColumn().getBiomeData() == null && !cacheChunks) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index e002162c333..74097733b5f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -28,8 +28,10 @@ import com.github.steveice10.mc.protocol.data.game.entity.Effect; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.ChangeDimensionPacket; +import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import com.nukkitx.protocol.bedrock.packet.MobEffectPacket; import com.nukkitx.protocol.bedrock.packet.StopSoundPacket; import org.geysermc.connector.GeyserConnector; @@ -63,6 +65,20 @@ public static void switchDimension(GeyserSession session, String javaDimension) session.getLecternCache().clear(); session.getSkullCache().clear(); + if (session.getRenderDistance() > 47) { + // The server-sided view distance wasn't a thing until Minecraft Java 1.14 + // So ViaVersion compensates by sending a "view distance" of 64 + // That's fine, except when the actual view distance sent from the server is five chunks + // The client locks up when switching dimensions, expecting more chunks than it's getting + // To solve this, we cap at 32 unless we know that the render distance actually exceeds 32 + // 47 is the Bedrock equivalent of 32 + session.getConnector().getLogger().debug("Applying dimension switching workaround for Bedrock render distance of " + session.getRenderDistance()); + ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); + chunkRadiusUpdatedPacket.setRadius(47); + session.sendUpstreamPacket(chunkRadiusUpdatedPacket); + // Will be re-adjusted on spawn + } + Vector3i pos = Vector3i.from(0, Short.MAX_VALUE, 0); ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket(); @@ -125,8 +141,9 @@ public static String getNewDimension(CompoundTag dimensionTag) { GeyserConnector.getInstance().getLogger().debug("Dimension tag was null or empty."); return OVERWORLD; } - if (dimensionTag.getValue().get("effects") != null) { - return ((StringTag) dimensionTag.getValue().get("effects")).getValue(); + Tag effectsTag = dimensionTag.getValue().get("effects"); + if (effectsTag instanceof StringTag) { + return ((StringTag) effectsTag).getValue(); } GeyserConnector.getInstance().getLogger().debug("Effects portion of the tag was null or empty."); return OVERWORLD;