Add support for extended world height (#5002)

This commit is contained in:
RK_01 2024-10-03 18:12:57 +02:00 committed by GitHub
parent 172cf82725
commit 26c4c5250a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 141 additions and 73 deletions

View file

@ -76,6 +76,7 @@ import org.geysermc.geyser.erosion.UnixSocketClientListener;
import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.event.GeyserEventBus;
import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.extension.GeyserExtensionManager;
import org.geysermc.geyser.impl.MinecraftVersionImpl; import org.geysermc.geyser.impl.MinecraftVersionImpl;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.network.netty.GeyserServer; import org.geysermc.geyser.network.netty.GeyserServer;
@ -95,7 +96,6 @@ import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.AssetUtils;
import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.Metrics; import org.geysermc.geyser.util.Metrics;
import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.geyser.util.MinecraftAuthLogger;
import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.NewsHandler;
@ -425,7 +425,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
} }
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether BedrockDimension.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads");
if (bedrockThreadCount == null) { if (bedrockThreadCount == null) {

View file

@ -25,17 +25,84 @@
package org.geysermc.geyser.level; package org.geysermc.geyser.level;
import lombok.ToString;
/** /**
* A data structure to represent what Bedrock believes are the height requirements for a specific dimension. * A data structure to represent what Bedrock believes are the height requirements for a specific dimension.
* As of 1.18.30, biome count is representative of the height of the world, and out-of-bounds chunks can crash * As of 1.18.30, biome count is representative of the height of the world, and out-of-bounds chunks can crash
* the client. * the client.
*
* @param minY The minimum height Bedrock Edition will accept.
* @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest.
* @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's.
*/ */
public record BedrockDimension(int minY, int height, boolean doUpperHeightWarn) { @ToString
public static final BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true); public class BedrockDimension {
public static final BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false);
public static final BedrockDimension THE_END = new BedrockDimension(0, 256, true); public static final int OVERWORLD_ID = 0;
public static final int DEFAULT_NETHER_ID = 1;
public static final int END_ID = 2;
// Changes if the above-bedrock Nether building workaround is applied
public static int BEDROCK_NETHER_ID = DEFAULT_NETHER_ID;
public static final BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true, OVERWORLD_ID);
public static final BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false, -1) {
@Override
public int bedrockId() {
return BEDROCK_NETHER_ID;
}
};
public static final BedrockDimension THE_END = new BedrockDimension(0, 256, true, END_ID);
public static final String NETHER_IDENTIFIER = "minecraft:the_nether";
private final int minY;
private final int height;
private final boolean doUpperHeightWarn;
private final int bedrockId;
/**
* @param minY The minimum height Bedrock Edition will accept.
* @param height The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest.
* @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's.
* @param bedrockId the Bedrock dimension ID of this dimension.
*/
public BedrockDimension(int minY, int height, boolean doUpperHeightWarn, int bedrockId) {
this.minY = minY;
this.height = height;
this.doUpperHeightWarn = doUpperHeightWarn;
this.bedrockId = bedrockId;
}
/**
* 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.
*
* @param isAboveNetherBedrockBuilding true if we should apply The End workaround
*/
public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) {
// Change dimension ID to the End to allow for building above Bedrock
BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? END_ID : DEFAULT_NETHER_ID;
}
public static boolean isCustomBedrockNetherId() {
return BEDROCK_NETHER_ID == END_ID;
}
public int maxY() {
return minY + height;
}
public int minY() {
return minY;
}
public int height() {
return height;
}
public boolean doUpperHeightWarn() {
return doUpperHeightWarn;
}
public int bedrockId() {
return bedrockId;
}
} }

View file

@ -63,12 +63,19 @@ public record JavaDimension(int minY, int maxY, boolean piglinSafe, boolean ultr
if ("minecraft".equals(id.namespace())) { if ("minecraft".equals(id.namespace())) {
String identifier = id.asString(); String identifier = id.asString();
bedrockId = DimensionUtils.javaToBedrock(identifier); bedrockId = DimensionUtils.javaToBedrock(identifier);
isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(identifier); isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(identifier);
} else { } else {
// Effects should give is a clue on how this (custom) dimension is supposed to look like // Effects should give is a clue on how this (custom) dimension is supposed to look like
String effects = dimension.getString("effects"); String effects = dimension.getString("effects");
bedrockId = DimensionUtils.javaToBedrock(effects); bedrockId = DimensionUtils.javaToBedrock(effects);
isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(effects); isNetherLike = BedrockDimension.NETHER_IDENTIFIER.equals(effects);
}
if (minY % 16 != 0) {
throw new RuntimeException("Minimum Y must be a multiple of 16!");
}
if (maxY % 16 != 0) {
throw new RuntimeException("Maximum Y must be a multiple of 16!");
} }
return new JavaDimension(minY, maxY, piglinSafe, ultrawarm, coordinateScale, bedrockId, isNetherLike); return new JavaDimension(minY, maxY, piglinSafe, ultrawarm, coordinateScale, bedrockId, isNetherLike);

View file

@ -74,6 +74,7 @@ import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
@ -84,6 +85,7 @@ import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
import org.cloudburstmc.protocol.bedrock.packet.ClientboundCloseFormPacket; import org.cloudburstmc.protocol.bedrock.packet.ClientboundCloseFormPacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.DimensionDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket;
@ -175,7 +177,6 @@ import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.geysermc.geyser.util.MinecraftAuthLogger; import org.geysermc.geyser.util.MinecraftAuthLogger;
@ -388,6 +389,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Setter
private boolean sprinting; private boolean sprinting;
/**
* The overworld dimension which Bedrock Edition uses.
*/
private BedrockDimension bedrockOverworldDimension = BedrockDimension.OVERWORLD;
/** /**
* The dimension of the player. * The dimension of the player.
* As all entities are in the same world, this can be safely applied to all other entities. * As all entities are in the same world, this can be safely applied to all other entities.
@ -401,7 +406,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
* right before the StartGamePacket is sent. * right before the StartGamePacket is sent.
*/ */
@Setter @Setter
private BedrockDimension bedrockDimension = BedrockDimension.OVERWORLD; private BedrockDimension bedrockDimension = this.bedrockOverworldDimension;
@Setter @Setter
private int breakingBlock; private int breakingBlock;
@ -711,6 +716,30 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
* Send all necessary packets to load Bedrock into the server * Send all necessary packets to load Bedrock into the server
*/ */
public void connect() { public void connect() {
int minY = this.dimensionType.minY();
int maxY = this.dimensionType.maxY();
for (JavaDimension javaDimension : this.getRegistryCache().dimensions().values()) {
if (javaDimension.bedrockId() == BedrockDimension.OVERWORLD_ID) {
minY = Math.min(minY, javaDimension.minY());
maxY = Math.max(maxY, javaDimension.maxY());
}
}
minY = Math.max(minY, -512);
maxY = Math.min(maxY, 512);
if (minY < BedrockDimension.OVERWORLD.minY() || maxY > BedrockDimension.OVERWORLD.maxY()) {
final boolean isInOverworld = this.bedrockDimension == this.bedrockOverworldDimension;
this.bedrockOverworldDimension = new BedrockDimension(minY, maxY - minY, true, BedrockDimension.OVERWORLD_ID);
if (isInOverworld) {
this.bedrockDimension = this.bedrockOverworldDimension;
}
geyser.getLogger().debug("Extending overworld dimension to " + minY + " - " + maxY);
DimensionDataPacket dimensionDataPacket = new DimensionDataPacket();
dimensionDataPacket.getDefinitions().add(new DimensionDefinition("minecraft:overworld", maxY, minY, 5 /* Void */));
upstream.sendPacket(dimensionDataPacket);
}
startGame(); startGame();
sentSpawnPacket = true; sentSpawnPacket = true;
syncEntityProperties(); syncEntityProperties();
@ -1594,7 +1623,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setRotation(Vector2f.from(1, 1));
startGamePacket.setSeed(-1L); startGamePacket.setSeed(-1L);
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(bedrockDimension)); startGamePacket.setDimensionId(bedrockDimension.bedrockId());
startGamePacket.setGeneratorId(1); startGamePacket.setGeneratorId(1);
startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setLevelGameType(GameType.SURVIVAL);
startGamePacket.setDifficulty(1); startGamePacket.setDifficulty(1);

View file

@ -33,6 +33,7 @@ import org.geysermc.erosion.Constants;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -62,7 +63,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
// If the player is already initialized and a join game packet is sent, they // If the player is already initialized and a join game packet is sent, they
// are swapping servers // are swapping servers
if (session.isSpawned()) { if (session.isSpawned()) {
int fakeDim = DimensionUtils.getTemporaryDimension(DimensionUtils.javaToBedrock(session.getBedrockDimension()), newDimension.bedrockId()); int fakeDim = DimensionUtils.getTemporaryDimension(session.getBedrockDimension().bedrockId(), newDimension.bedrockId());
if (fakeDim != newDimension.bedrockId()) { if (fakeDim != newDimension.bedrockId()) {
// The player's current dimension and new dimension are the same // The player's current dimension and new dimension are the same
// We want a dimension switch to clear old chunks out, so switch to a dimension that isn't the one we're currently in. // We want a dimension switch to clear old chunks out, so switch to a dimension that isn't the one we're currently in.
@ -121,9 +122,9 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
} }
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(register, Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8))); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(register, Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
if (DimensionUtils.javaToBedrock(session.getBedrockDimension()) != newDimension.bedrockId()) { if (session.getBedrockDimension().bedrockId() != newDimension.bedrockId()) {
DimensionUtils.switchDimension(session, newDimension); DimensionUtils.switchDimension(session, newDimension);
} else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.isNetherLike()) { } else if (BedrockDimension.isCustomBedrockNetherId() && newDimension.isNetherLike()) {
// If the player is spawning into the "fake" nether, send them some fog // If the player is spawning into the "fake" nether, send them some fog
session.camera().sendFog(DimensionUtils.BEDROCK_FOG_HELL); session.camera().sendFog(DimensionUtils.BEDROCK_FOG_HELL);
} }

View file

@ -29,7 +29,12 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntImmutableList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NBTOutputStream; import org.cloudburstmc.nbt.NBTOutputStream;
@ -56,7 +61,6 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.BlockEntityUtils; import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.mcprotocollib.protocol.data.game.chunk.BitStorage; import org.geysermc.mcprotocollib.protocol.data.game.chunk.BitStorage;
import org.geysermc.mcprotocollib.protocol.data.game.chunk.ChunkSection; import org.geysermc.mcprotocollib.protocol.data.game.chunk.ChunkSection;
import org.geysermc.mcprotocollib.protocol.data.game.chunk.DataPalette; import org.geysermc.mcprotocollib.protocol.data.game.chunk.DataPalette;
@ -509,7 +513,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
levelChunkPacket.setChunkX(packet.getX()); levelChunkPacket.setChunkX(packet.getX());
levelChunkPacket.setChunkZ(packet.getZ()); levelChunkPacket.setChunkZ(packet.getZ());
levelChunkPacket.setData(Unpooled.wrappedBuffer(payload)); levelChunkPacket.setData(Unpooled.wrappedBuffer(payload));
levelChunkPacket.setDimension(DimensionUtils.javaToBedrock(session.getBedrockDimension())); levelChunkPacket.setDimension(session.getBedrockDimension().bedrockId());
session.sendUpstreamPacket(levelChunkPacket); session.sendUpstreamPacket(levelChunkPacket);
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) { for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {

View file

@ -167,7 +167,7 @@ public class ChunkUtils {
byteBuf.readBytes(payload); byteBuf.readBytes(payload);
LevelChunkPacket data = new LevelChunkPacket(); LevelChunkPacket data = new LevelChunkPacket();
data.setDimension(DimensionUtils.javaToBedrock(session.getBedrockDimension())); data.setDimension(session.getBedrockDimension().bedrockId());
data.setChunkX(chunkX); data.setChunkX(chunkX);
data.setChunkZ(chunkZ); data.setChunkZ(chunkZ);
data.setSubChunksLength(0); data.setSubChunksLength(0);
@ -207,13 +207,6 @@ public class ChunkUtils {
int minY = dimension.minY(); int minY = dimension.minY();
int maxY = dimension.maxY(); int maxY = dimension.maxY();
if (minY % 16 != 0) {
throw new RuntimeException("Minimum Y must be a multiple of 16!");
}
if (maxY % 16 != 0) {
throw new RuntimeException("Maximum Y must be a multiple of 16!");
}
BedrockDimension bedrockDimension = session.getBedrockDimension(); BedrockDimension bedrockDimension = session.getBedrockDimension();
// Yell in the console if the world height is too height in the current scenario // Yell in the console if the world height is too height in the current scenario
// The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled // The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled

View file

@ -44,17 +44,8 @@ import java.util.Set;
public class DimensionUtils { public class DimensionUtils {
// Changes if the above-bedrock Nether building workaround is applied
private static int BEDROCK_NETHER_ID = 1;
public static final String BEDROCK_FOG_HELL = "minecraft:fog_hell"; public static final String BEDROCK_FOG_HELL = "minecraft:fog_hell";
public static final String NETHER_IDENTIFIER = "minecraft:the_nether";
private static final int BEDROCK_OVERWORLD_ID = 0;
private static final int BEDROCK_DEFAULT_NETHER_ID = 1;
private static final int BEDROCK_END_ID = 2;
public static void switchDimension(GeyserSession session, JavaDimension javaDimension) { public static void switchDimension(GeyserSession session, JavaDimension javaDimension) {
switchDimension(session, javaDimension, javaDimension.bedrockId()); switchDimension(session, javaDimension, javaDimension.bedrockId());
} }
@ -95,7 +86,7 @@ public class DimensionUtils {
// If the bedrock nether height workaround is enabled, meaning the client is told it's in the end dimension, // If the bedrock nether height workaround is enabled, meaning the client is told it's in the end dimension,
// we check if the player is entering the nether and apply the nether fog to fake the fact that the client // we check if the player is entering the nether and apply the nether fog to fake the fact that the client
// thinks they are in the end dimension. // thinks they are in the end dimension.
if (isCustomBedrockNetherId()) { if (BedrockDimension.isCustomBedrockNetherId()) {
if (javaDimension.isNetherLike()) { if (javaDimension.isNetherLike()) {
session.camera().sendFog(BEDROCK_FOG_HELL); session.camera().sendFog(BEDROCK_FOG_HELL);
} else if (previousDimension != null && previousDimension.isNetherLike()) { } else if (previousDimension != null && previousDimension.isNetherLike()) {
@ -168,22 +159,12 @@ public class DimensionUtils {
public static void setBedrockDimension(GeyserSession session, int bedrockDimension) { public static void setBedrockDimension(GeyserSession session, int bedrockDimension) {
session.setBedrockDimension(switch (bedrockDimension) { session.setBedrockDimension(switch (bedrockDimension) {
case BEDROCK_END_ID -> BedrockDimension.THE_END; case BedrockDimension.END_ID -> BedrockDimension.THE_END;
case BEDROCK_DEFAULT_NETHER_ID -> BedrockDimension.THE_NETHER; // JavaDimension *should* be set to BEDROCK_END_ID if the Nether workaround is enabled. case BedrockDimension.DEFAULT_NETHER_ID -> BedrockDimension.THE_NETHER; // JavaDimension *should* be set to BEDROCK_END_ID if the Nether workaround is enabled.
default -> BedrockDimension.OVERWORLD; default -> session.getBedrockOverworldDimension();
}); });
} }
public static int javaToBedrock(BedrockDimension dimension) {
if (dimension == BedrockDimension.THE_NETHER) {
return BEDROCK_NETHER_ID;
} else if (dimension == BedrockDimension.THE_END) {
return BEDROCK_END_ID;
} else {
return BEDROCK_OVERWORLD_ID;
}
}
/** /**
* Map the Java edition dimension IDs to Bedrock edition * Map the Java edition dimension IDs to Bedrock edition
* *
@ -192,9 +173,9 @@ public class DimensionUtils {
*/ */
public static int javaToBedrock(String javaDimension) { public static int javaToBedrock(String javaDimension) {
return switch (javaDimension) { return switch (javaDimension) {
case NETHER_IDENTIFIER -> BEDROCK_NETHER_ID; case BedrockDimension.NETHER_IDENTIFIER -> BedrockDimension.BEDROCK_NETHER_ID;
case "minecraft:the_end" -> 2; case "minecraft:the_end" -> BedrockDimension.END_ID;
default -> 0; default -> BedrockDimension.OVERWORLD_ID;
}; };
} }
@ -204,22 +185,11 @@ public class DimensionUtils {
public static int javaToBedrock(GeyserSession session) { public static int javaToBedrock(GeyserSession session) {
JavaDimension dimension = session.getDimensionType(); JavaDimension dimension = session.getDimensionType();
if (dimension == null) { if (dimension == null) {
return BEDROCK_OVERWORLD_ID; return BedrockDimension.OVERWORLD_ID;
} }
return dimension.bedrockId(); return dimension.bedrockId();
} }
/**
* 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.
*
* @param isAboveNetherBedrockBuilding true if we should apply The End workaround
*/
public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) {
// Change dimension ID to the End to allow for building above Bedrock
BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? BEDROCK_END_ID : BEDROCK_DEFAULT_NETHER_ID;
}
/** /**
* Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional * Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional
* dimension switch. * dimension switch.
@ -229,16 +199,13 @@ public class DimensionUtils {
* @return the Bedrock fake dimension to transfer to * @return the Bedrock fake dimension to transfer to
*/ */
public static int getTemporaryDimension(int currentBedrockDimension, int newBedrockDimension) { public static int getTemporaryDimension(int currentBedrockDimension, int newBedrockDimension) {
if (isCustomBedrockNetherId()) { if (BedrockDimension.isCustomBedrockNetherId()) {
// Prevents rare instances of Bedrock locking up // Prevents rare instances of Bedrock locking up
return newBedrockDimension == BEDROCK_END_ID ? BEDROCK_OVERWORLD_ID : BEDROCK_END_ID; return newBedrockDimension == BedrockDimension.END_ID ? BedrockDimension.OVERWORLD_ID : BedrockDimension.END_ID;
} }
// Check current Bedrock dimension and not just the Java dimension. // Check current Bedrock dimension and not just the Java dimension.
// Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161 // Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161
return currentBedrockDimension == BEDROCK_OVERWORLD_ID ? BEDROCK_DEFAULT_NETHER_ID : BEDROCK_OVERWORLD_ID; return currentBedrockDimension == BedrockDimension.OVERWORLD_ID ? BedrockDimension.DEFAULT_NETHER_ID : BedrockDimension.OVERWORLD_ID;
} }
public static boolean isCustomBedrockNetherId() {
return BEDROCK_NETHER_ID == BEDROCK_END_ID;
}
} }