diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 45321163b5c..8bab0fc46d3 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -364,7 +364,6 @@ private void startInstance() { } CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); - DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); if (bedrockThreadCount == null) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 27942924295..3bb154f0490 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -244,7 +244,7 @@ protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttrib public void setLastDeathPosition(@Nullable GlobalPos pos) { if (pos != null) { dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_POS, pos.getPosition()); - dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(pos.getDimension())); + dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(session, pos.getDimension())); dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, true); } else { dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index e841dd43c6b..66e3da46169 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1482,7 +1482,7 @@ private void startGame() { startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setSeed(-1L); - startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(chunkCache.getBedrockDimension())); + startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(this, chunkCache.getBedrockDimension())); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setDifficulty(1); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java index 9e8597b0f54..1128df51934 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java @@ -53,6 +53,12 @@ public class PreferencesCache { @Setter private boolean prefersCustomSkulls; + /** + * If the session wants to enable above bedrock Nether building. + */ + @Setter + private boolean prefersAboveBedrockNetherBuilding; + /** * Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}. */ @@ -63,6 +69,7 @@ public PreferencesCache(GeyserSession session) { this.session = session; prefersCustomSkulls = session.getGeyser().getConfig().isAllowCustomSkulls(); + prefersAboveBedrockNetherBuilding = session.getGeyser().getConfig().isAboveBedrockNetherBuilding(); } /** @@ -84,4 +91,11 @@ public void updateShowCoordinates() { public boolean showCustomSkulls() { return prefersCustomSkulls && session.getGeyser().getConfig().isAllowCustomSkulls(); } + + /** + * @return true if the session prefers above bedrock Nether building, and the config allows them. + */ + public boolean allowAboveBedrockNetherBuilding() { + return prefersAboveBedrockNetherBuilding && session.getGeyser().getConfig().isAboveBedrockNetherBuilding(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java index d1246a0b44b..d25894c227b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockPositionTrackingDBClientRequestTranslator.java @@ -58,7 +58,7 @@ public void translate(GeyserSession session, PositionTrackingDBClientRequestPack // Build the NBT data for the update NbtMapBuilder builder = NbtMap.builder(); - builder.putInt("dim", DimensionUtils.javaToBedrock(pos.dimension())); + builder.putInt("dim", DimensionUtils.javaToBedrock(session, pos.dimension())); builder.putString("id", "0x" + String.format("%08X", packet.getTrackingId())); builder.putByte("version", (byte) 1); // Not sure what this is for diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 3d9f08ec7c4..5eb5000ab15 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -85,7 +85,7 @@ public void translate(GeyserSession session, ClientboundLoginPacket packet) { // If the player is already initialized and a join game packet is sent, they // are swapping servers if (session.isSpawned()) { - String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), packet.getDimension()); + String fakeDim = DimensionUtils.getTemporaryDimension(session, session.getDimension(), packet.getDimension()); DimensionUtils.switchDimension(session, fakeDim); session.getWorldCache().removeScoreboard(); @@ -146,7 +146,7 @@ public void translate(GeyserSession session, ClientboundLoginPacket packet) { if (!newDimension.equals(session.getDimension())) { DimensionUtils.switchDimension(session, newDimension); - } else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.equalsIgnoreCase(DimensionUtils.NETHER)) { + } else if (session.getPreferencesCache().allowAboveBedrockNetherBuilding() && newDimension.equalsIgnoreCase(DimensionUtils.NETHER)) { // If the player is spawning into the "fake" nether, send them some fog session.sendFog("minecraft:fog_hell"); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index 15ee2f8de5e..2ba44249993 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -84,8 +84,8 @@ public void translate(GeyserSession session, ClientboundRespawnPacket packet) { String newDimension = packet.getDimension(); if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) { // Switching to a new world (based off the world name change or new dimension); send a fake dimension change - if (DimensionUtils.javaToBedrock(session.getDimension()) == DimensionUtils.javaToBedrock(newDimension)) { - String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), newDimension); + if (DimensionUtils.javaToBedrock(session, session.getDimension()) == DimensionUtils.javaToBedrock(session, newDimension)) { + String fakeDim = DimensionUtils.getTemporaryDimension(session, session.getDimension(), newDimension); DimensionUtils.switchDimension(session, fakeDim); } session.setWorldName(packet.getWorldName()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java index f82c33032a5..f5655868e94 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java @@ -76,7 +76,7 @@ public void translate(GeyserSession session, ClientboundAnimatePacket packet) { // Spawn custom particle SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket(); stringPacket.setIdentifier("geyseropt:enchanted_hit_multiple"); - stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension())); + stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session, session.getDimension())); stringPacket.setPosition(Vector3f.ZERO); stringPacket.setUniqueEntityId(entity.getGeyserId()); stringPacket.setMolangVariablesJson(Optional.empty()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java index 6adb053d757..13e2041dce0 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelParticlesTranslator.java @@ -189,7 +189,7 @@ private Function createParticle(GeyserSession session, return packet; }; } else if (particleMapping.identifier() != null) { - int dimensionId = DimensionUtils.javaToBedrock(session.getDimension()); + int dimensionId = DimensionUtils.javaToBedrock(session, session.getDimension()); return (position) -> { SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket(); stringPacket.setIdentifier(particleMapping.identifier()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java index 34fbf2d9c62..8b11c145ca1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaMapItemDataTranslator.java @@ -47,7 +47,7 @@ public void translate(GeyserSession session, ClientboundMapItemDataPacket packet boolean shouldStore = false; mapItemDataPacket.setUniqueMapId(packet.getMapId()); - mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension())); + mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session, session.getDimension())); mapItemDataPacket.setLocked(packet.isLocked()); mapItemDataPacket.setOrigin(Vector3i.ZERO); // Required since 1.19.20 mapItemDataPacket.setScale(packet.getScale()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetDefaultSpawnPositionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetDefaultSpawnPositionTranslator.java index 9662fdd819b..970fdc203b1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetDefaultSpawnPositionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetDefaultSpawnPositionTranslator.java @@ -39,7 +39,7 @@ public class JavaSetDefaultSpawnPositionTranslator extends PacketTranslator BedrockDimension.THE_END; - case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; + case DimensionUtils.NETHER -> session.getPreferencesCache().allowAboveBedrockNetherBuilding() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER; default -> BedrockDimension.OVERWORLD; }); } - public static int javaToBedrock(BedrockDimension dimension) { - if (dimension == BedrockDimension.THE_NETHER) { - return BEDROCK_NETHER_ID; - } else if (dimension == BedrockDimension.THE_END) { - return 2; - } else { - return 0; - } + /** + * Maps a Minecraft Java edition dimension to a Bedrock dimension identifier. + * + * @param javaDimension Minecraft Java edition dimension identifier + * @return Minecraft Bedrock edition dimension identifier + */ + public static int javaToBedrock(String javaDimension) { + return switch (javaDimension) { + case NETHER -> 1; + case THE_END -> 2; + default -> 0; + }; } /** - * Map the Java edition dimension IDs to Bedrock edition + * Maps a Minecraft Java edition dimension to a Bedrock dimension identifier. * - * @param javaDimension Dimension ID to convert - * @return Converted Bedrock edition dimension ID + * @param session Geyser session (this is used to check whether the "fake" Nether is enabled) + * @param javaDimension Minecraft Java edition dimension identifier + * @return Minecraft Bedrock edition dimension identifier */ - public static int javaToBedrock(String javaDimension) { + public static int javaToBedrock(GeyserSession session, String javaDimension) { return switch (javaDimension) { - case NETHER -> BEDROCK_NETHER_ID; + case NETHER -> session.getPreferencesCache().allowAboveBedrockNetherBuilding() ? 2 : 1; case THE_END -> 2; default -> 0; }; } /** - * The Nether dimension in Bedrock does not permit building above Y128 - the Bedrock above the dimension. - * This workaround sets the Nether as the End dimension to ignore this limit. + * Maps a Minecraft Bedrock edition dimension to a Bedrock dimension identifier. * - * @param isAboveNetherBedrockBuilding true if we should apply The End workaround + * @param session Geyser session (this is used to check whether the "fake" Nether is enabled) + * @param dimension Minecraft Bedrock edition dimension + * @return Minecraft Bedrock edition dimension identifier */ - public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) { - // Change dimension ID to the End to allow for building above Bedrock - BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? 2 : 1; + public static int javaToBedrock(GeyserSession session, BedrockDimension dimension) { + if (dimension == BedrockDimension.THE_NETHER) { + return session.getPreferencesCache().allowAboveBedrockNetherBuilding() ? 2 : 1; + } else if (dimension == BedrockDimension.THE_END) { + return 2; + } else { + return 0; + } } /** * Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional * dimension switch. * + * @param session Geyser session (this is used to check whether the "fake" Nether is enabled) * @param currentDimension the current dimension of the player * @param newDimension the new dimension that the player will be transferred to * @return the fake dimension to transfer to */ - public static String getTemporaryDimension(String currentDimension, String newDimension) { - if (BEDROCK_NETHER_ID == 2) { + public static String getTemporaryDimension(GeyserSession session, String currentDimension, String newDimension) { + if (session.getPreferencesCache().allowAboveBedrockNetherBuilding()) { // Prevents rare instances of Bedrock locking up - return javaToBedrock(newDimension) == 2 ? OVERWORLD : NETHER; + return javaToBedrock(session, newDimension) == 2 ? OVERWORLD : NETHER; } // Check current Bedrock dimension and not just the Java dimension. // Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161 - return javaToBedrock(currentDimension) == 0 ? NETHER : OVERWORLD; - } - - public static boolean isCustomBedrockNetherId() { - return BEDROCK_NETHER_ID == 2; + return javaToBedrock(session, currentDimension) == 0 ? NETHER : OVERWORLD; } } diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index 5957fb9d98f..b38ea03d222 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import org.geysermc.cumulus.component.DropdownComponent; import org.geysermc.cumulus.form.CustomForm; +import org.geysermc.cumulus.form.ModalForm; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.WorldManager; @@ -54,7 +55,8 @@ public static CustomForm buildForm(GeyserSession session) { // Only show the client title if any of the client settings are available boolean showClientSettings = session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED - || session.getGeyser().getConfig().isAllowCustomSkulls(); + || session.getGeyser().getConfig().isAllowCustomSkulls() + || session.getGeyser().getConfig().isAboveBedrockNetherBuilding(); if (showClientSettings) { builder.label("geyser.settings.title.client"); @@ -75,6 +77,10 @@ public static CustomForm buildForm(GeyserSession session) { if (session.getGeyser().getConfig().isAllowCustomSkulls()) { builder.toggle("geyser.settings.option.customSkulls", session.getPreferencesCache().isPrefersCustomSkulls()); } + + if (session.getGeyser().getConfig().isAboveBedrockNetherBuilding()) { + builder.toggle("geyser.settings.option.aboveBedrockNetherBuilding", session.getPreferencesCache().isPrefersAboveBedrockNetherBuilding()); + } } boolean canModifyServer = session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server"); @@ -126,6 +132,15 @@ public static CustomForm buildForm(GeyserSession session) { if (session.getGeyser().getConfig().isAllowCustomSkulls()) { session.getPreferencesCache().setPrefersCustomSkulls(response.next()); } + + if (session.getGeyser().getConfig().isAboveBedrockNetherBuilding()) { + boolean result = response.next(); + // If the setting changed, send a notice prompting the user of any side effects + if (session.getPreferencesCache().isPrefersAboveBedrockNetherBuilding() != result) { + session.getPreferencesCache().setPrefersAboveBedrockNetherBuilding(result); + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.settings.option.aboveBedrockNetherBuilding.notice", session.locale())); + } + } } if (canModifyServer) {