mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'extended-world-height'
This commit is contained in:
commit
4844302883
20 changed files with 9706 additions and 60 deletions
|
@ -39,8 +39,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set
|
||||||
- Some Entity Flags
|
- Some Entity Flags
|
||||||
- Structure block UI
|
- Structure block UI
|
||||||
|
|
||||||
Extended height features can be "supported", but require additional work.
|
|
||||||
|
|
||||||
## What can't be fixed
|
## What can't be fixed
|
||||||
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page.
|
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page.
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.GeyserMC</groupId>
|
<groupId>com.github.GeyserMC</groupId>
|
||||||
<artifactId>MCProtocolLib</artifactId>
|
<artifactId>MCProtocolLib</artifactId>
|
||||||
<version>6e318f5</version>
|
<version>e427237</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
|
|
|
@ -45,6 +45,8 @@ public interface GeyserConfiguration {
|
||||||
|
|
||||||
Map<String, ? extends IUserAuthenticationInfo> getUserAuths();
|
Map<String, ? extends IUserAuthenticationInfo> getUserAuths();
|
||||||
|
|
||||||
|
boolean isExtendedWorldHeight();
|
||||||
|
|
||||||
boolean isCommandSuggestions();
|
boolean isCommandSuggestions();
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
|
|
@ -55,6 +55,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||||
private BedrockConfiguration bedrock = new BedrockConfiguration();
|
private BedrockConfiguration bedrock = new BedrockConfiguration();
|
||||||
private RemoteConfiguration remote = new RemoteConfiguration();
|
private RemoteConfiguration remote = new RemoteConfiguration();
|
||||||
|
|
||||||
|
@JsonProperty("extended-world-height")
|
||||||
|
private boolean extendedWorldHeight = false;
|
||||||
|
|
||||||
@JsonProperty("floodgate-key-file")
|
@JsonProperty("floodgate-key-file")
|
||||||
private String floodgateKeyFile = "public-key.pem";
|
private String floodgateKeyFile = "public-key.pem";
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||||
import com.nukkitx.protocol.bedrock.v440.Bedrock_v440;
|
import com.nukkitx.protocol.bedrock.v440.Bedrock_v440;
|
||||||
import com.nukkitx.protocol.bedrock.v448.Bedrock_v448;
|
import com.nukkitx.protocol.bedrock.v448.Bedrock_v448;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -40,15 +41,17 @@ public class BedrockProtocol {
|
||||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||||
* release of the game that Geyser supports.
|
* release of the game that Geyser supports.
|
||||||
*/
|
*/
|
||||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v440.V440_CODEC;
|
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v448.V448_CODEC;
|
||||||
/**
|
/**
|
||||||
* A list of all supported Bedrock versions that can join Geyser
|
* A list of all supported Bedrock versions that can join Geyser
|
||||||
*/
|
*/
|
||||||
public static final List<BedrockPacketCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
|
public static final List<BedrockPacketCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
if (!GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v440.V440_CODEC);
|
||||||
|
}
|
||||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v448.V448_CODEC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -137,6 +137,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session.getConnector().getConfig().isExtendedWorldHeight()) {
|
||||||
|
// Allow extended world height in the overworld to work
|
||||||
|
stackPacket.getExperiments().add(new ExperimentData("caves_and_cliffs", true));
|
||||||
|
}
|
||||||
|
|
||||||
session.sendUpstreamPacket(stackPacket);
|
session.sendUpstreamPacket(stackPacket);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1070,6 +1070,10 @@ public class GeyserSession implements CommandSender {
|
||||||
settings.setServerAuthoritativeBlockBreaking(false);
|
settings.setServerAuthoritativeBlockBreaking(false);
|
||||||
startGamePacket.setPlayerMovementSettings(settings);
|
startGamePacket.setPlayerMovementSettings(settings);
|
||||||
|
|
||||||
|
if (connector.getConfig().isExtendedWorldHeight()) {
|
||||||
|
startGamePacket.getExperiments().add(new ExperimentData("caves_and_cliffs", true));
|
||||||
|
}
|
||||||
|
|
||||||
upstream.sendPacket(startGamePacket);
|
upstream.sendPacket(startGamePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1191,11 +1195,7 @@ public class GeyserSession implements CommandSender {
|
||||||
* @param packet the bedrock packet from the NukkitX protocol lib
|
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||||
*/
|
*/
|
||||||
public void sendUpstreamPacket(BedrockPacket packet) {
|
public void sendUpstreamPacket(BedrockPacket packet) {
|
||||||
if (upstream != null) {
|
|
||||||
upstream.sendPacket(packet);
|
upstream.sendPacket(packet);
|
||||||
} else {
|
|
||||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " but the session was null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1204,11 +1204,7 @@ public class GeyserSession implements CommandSender {
|
||||||
* @param packet the bedrock packet from the NukkitX protocol lib
|
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||||
*/
|
*/
|
||||||
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
||||||
if (upstream != null) {
|
|
||||||
upstream.sendPacketImmediately(packet);
|
upstream.sendPacketImmediately(packet);
|
||||||
} else {
|
|
||||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " immediately but the session was null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||||
|
@ -44,6 +45,13 @@ public class ChunkCache {
|
||||||
@Setter
|
@Setter
|
||||||
private int heightY;
|
private int heightY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Bedrock client believes they are in a world with a minimum of -64 and maximum of 320
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean isExtendedHeight = false;
|
||||||
|
|
||||||
public ChunkCache(GeyserSession session) {
|
public ChunkCache(GeyserSession session) {
|
||||||
this.cache = !session.getConnector().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing
|
this.cache = !session.getConnector().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing
|
||||||
chunks = cache ? new Long2ObjectOpenHashMap<>() : null;
|
chunks = cache ? new Long2ObjectOpenHashMap<>() : null;
|
||||||
|
@ -74,19 +82,19 @@ public class ChunkCache {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < minY || (y >> 4) > column.getChunks().length - 1) {
|
if (y < minY || ((y - minY) >> 4) > column.getChunks().length - 1) {
|
||||||
// Y likely goes above or below the height limit of this world
|
// Y likely goes above or below the height limit of this world
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()];
|
Chunk chunk = column.getChunks()[(y - minY) >> 4];
|
||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
if (block != BlockStateValues.JAVA_AIR_ID) {
|
if (block != BlockStateValues.JAVA_AIR_ID) {
|
||||||
// A previously empty chunk, which is no longer empty as a block has been added to it
|
// A previously empty chunk, which is no longer empty as a block has been added to it
|
||||||
chunk = new Chunk();
|
chunk = new Chunk();
|
||||||
// Fixes the chunk assuming that all blocks is the `block` variable we are updating. /shrug
|
// Fixes the chunk assuming that all blocks is the `block` variable we are updating. /shrug
|
||||||
chunk.getPalette().stateToId(BlockStateValues.JAVA_AIR_ID);
|
chunk.getPalette().stateToId(BlockStateValues.JAVA_AIR_ID);
|
||||||
column.getChunks()[(y >> 4) - getChunkMinY()] = chunk;
|
column.getChunks()[(y - minY) >> 4] = chunk;
|
||||||
} else {
|
} else {
|
||||||
// Nothing to update
|
// Nothing to update
|
||||||
return;
|
return;
|
||||||
|
@ -106,12 +114,12 @@ public class ChunkCache {
|
||||||
return BlockStateValues.JAVA_AIR_ID;
|
return BlockStateValues.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < minY || (y >> 4) > column.getChunks().length - 1) {
|
if (y < minY || ((y - minY) >> 4) > column.getChunks().length - 1) {
|
||||||
// Y likely goes above or below the height limit of this world
|
// Y likely goes above or below the height limit of this world
|
||||||
return BlockStateValues.JAVA_AIR_ID;
|
return BlockStateValues.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()];
|
Chunk chunk = column.getChunks()[(y - minY) >> 4];
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.nukkitx.math.vector.Vector3d;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.ChatColor;
|
import org.geysermc.connector.common.ChatColor;
|
||||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
|
@ -42,6 +43,14 @@ import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
||||||
@Translator(packet = MovePlayerPacket.class)
|
@Translator(packet = MovePlayerPacket.class)
|
||||||
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
||||||
|
/* The upper and lower bounds to check for the void floor that only exists in Bedrock */
|
||||||
|
private static final int BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y;
|
||||||
|
private static final int BEDROCK_OVERWORLD_VOID_FLOOR_LOWER_Y;
|
||||||
|
|
||||||
|
static {
|
||||||
|
BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y = GeyserConnector.getInstance().getConfig().isExtendedWorldHeight() ? -104 : -40;
|
||||||
|
BEDROCK_OVERWORLD_VOID_FLOOR_LOWER_Y = BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y + 2;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(MovePlayerPacket packet, GeyserSession session) {
|
public void translate(MovePlayerPacket packet, GeyserSession session) {
|
||||||
|
@ -108,10 +117,13 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
|
|
||||||
if (notMovingUp) {
|
if (notMovingUp) {
|
||||||
int floorY = position.getFloorY();
|
int floorY = position.getFloorY();
|
||||||
if (floorY <= -38 && floorY >= -40) {
|
// If the client believes the world has extended height, then it also believes the void floor
|
||||||
// Work around there being a floor at Y -40 and teleport the player below it
|
// still exists, just at a lower spot
|
||||||
// Moving from below Y -40 to above the void floor works fine
|
boolean extendedWorld = session.getChunkCache().isExtendedHeight();
|
||||||
//TODO: This will need to be changed for 1.17
|
if (floorY <= (extendedWorld ? BEDROCK_OVERWORLD_VOID_FLOOR_LOWER_Y : -38)
|
||||||
|
&& floorY >= (extendedWorld ? BEDROCK_OVERWORLD_VOID_FLOOR_UPPER_Y : -40)) {
|
||||||
|
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||||
|
// Moving from below to above the void floor works fine
|
||||||
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
||||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||||
|
|
|
@ -55,8 +55,6 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
||||||
PlayerEntity entity = session.getPlayerEntity();
|
PlayerEntity entity = session.getPlayerEntity();
|
||||||
entity.setEntityId(packet.getEntityId());
|
entity.setEntityId(packet.getEntityId());
|
||||||
|
|
||||||
ChunkUtils.applyDimensionHeight(session, packet.getDimension());
|
|
||||||
|
|
||||||
// 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
|
||||||
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
||||||
|
@ -113,5 +111,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
||||||
if (!newDimension.equals(session.getDimension())) {
|
if (!newDimension.equals(session.getDimension())) {
|
||||||
DimensionUtils.switchDimension(session, newDimension);
|
DimensionUtils.switchDimension(session, newDimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChunkUtils.applyDimensionHeight(session, packet.getDimension());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,6 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
||||||
session.setThunder(false);
|
session.setThunder(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChunkUtils.applyDimensionHeight(session, packet.getDimension());
|
|
||||||
|
|
||||||
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
||||||
if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) {
|
if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) {
|
||||||
// Switching to a new world (based off the world name change); send a fake dimension change
|
// Switching to a new world (based off the world name change); send a fake dimension change
|
||||||
|
@ -93,5 +91,7 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
||||||
session.setWorldName(packet.getWorldName());
|
session.setWorldName(packet.getWorldName());
|
||||||
DimensionUtils.switchDimension(session, newDimension);
|
DimensionUtils.switchDimension(session, newDimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChunkUtils.applyDimensionHeight(session, packet.getDimension());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,16 @@ import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.buffer.ByteBufOutputStream;
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.utils.BiomeUtils;
|
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
||||||
|
import org.geysermc.connector.utils.BiomeUtils;
|
||||||
import org.geysermc.connector.utils.ChunkUtils;
|
import org.geysermc.connector.utils.ChunkUtils;
|
||||||
|
|
||||||
@Translator(packet = ServerChunkDataPacket.class)
|
@Translator(packet = ServerChunkDataPacket.class)
|
||||||
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
|
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
|
||||||
|
// Caves and cliffs supports 3D biomes by implementing a very similar palette system to blocks
|
||||||
|
private static final boolean NEW_BIOME_WRITE = GeyserConnector.getInstance().getConfig().isExtendedWorldHeight();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
|
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
|
||||||
|
@ -79,7 +81,11 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
||||||
ChunkSection section = sections[i];
|
ChunkSection section = sections[i];
|
||||||
size += (section != null ? section : session.getBlockMappings().getEmptyChunkSection()).estimateNetworkSize();
|
size += (section != null ? section : session.getBlockMappings().getEmptyChunkSection()).estimateNetworkSize();
|
||||||
}
|
}
|
||||||
size += 256; // Biomes
|
if (NEW_BIOME_WRITE) {
|
||||||
|
size += ChunkUtils.EMPTY_CHUNK_DATA.length; // Consists only of biome data
|
||||||
|
} else {
|
||||||
|
size += 256; // Biomes pre-1.18
|
||||||
|
}
|
||||||
size += 1; // Border blocks
|
size += 1; // Border blocks
|
||||||
size += 1; // Extra data length (always 0)
|
size += 1; // Extra data length (always 0)
|
||||||
size += chunkData.getBlockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity
|
size += chunkData.getBlockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity
|
||||||
|
@ -93,7 +99,19 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
||||||
(section != null ? section : session.getBlockMappings().getEmptyChunkSection()).writeToNetwork(byteBuf);
|
(section != null ? section : session.getBlockMappings().getEmptyChunkSection()).writeToNetwork(byteBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NEW_BIOME_WRITE) {
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
BiomeUtils.toNewBedrockBiome(column.getBiomeData(), i).writeToNetwork(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// As of 1.17.10, Bedrock hardcodes to always read 32 biome sections
|
||||||
|
int remainingEmptyBiomes = 32 - sectionCount;
|
||||||
|
for (int i = 0; i < remainingEmptyBiomes; i++) {
|
||||||
|
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
byteBuf.writeBytes(BiomeUtils.toBedrockBiome(column.getBiomeData())); // Biomes - 256 bytes
|
byteBuf.writeBytes(BiomeUtils.toBedrockBiome(column.getBiomeData())); // Biomes - 256 bytes
|
||||||
|
}
|
||||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,15 @@ import java.util.function.BiFunction;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
public class BlockRegistryPopulator {
|
public class BlockRegistryPopulator {
|
||||||
private static final ImmutableMap<String, BiFunction<String, NbtMapBuilder, String>> STATE_MAPPER = ImmutableMap.<String, BiFunction<String, NbtMapBuilder, String>>builder()
|
private static final ImmutableMap<String, BiFunction<String, NbtMapBuilder, String>> STATE_MAPPER;
|
||||||
.put("1_17_0", (bedrockIdentifier, statesBuilder) -> {
|
|
||||||
|
private static final Object2IntMap<String> PALETTE_VERSIONS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ImmutableMap.Builder<String, BiFunction<String, NbtMapBuilder, String>> stateMapperBuilder = ImmutableMap.<String, BiFunction<String, NbtMapBuilder, String>>builder()
|
||||||
|
.put("1_17_10", (bedrockIdentifier, statesBuilder) -> null);
|
||||||
|
if (!GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
stateMapperBuilder.put("1_17_0", (bedrockIdentifier, statesBuilder) -> {
|
||||||
if (bedrockIdentifier.contains("candle")) {
|
if (bedrockIdentifier.contains("candle")) {
|
||||||
// Replace candles with sea pickles or cake
|
// Replace candles with sea pickles or cake
|
||||||
if (bedrockIdentifier.contains("cake")) {
|
if (bedrockIdentifier.contains("cake")) {
|
||||||
|
@ -68,16 +75,16 @@ public class BlockRegistryPopulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
});
|
||||||
.put("1_17_10", (bedrockIdentifier, statesBuilder) -> null)
|
}
|
||||||
.build();
|
STATE_MAPPER = stateMapperBuilder.build();
|
||||||
|
|
||||||
private static final Object2IntMap<String> PALETTE_VERSIONS = new Object2IntOpenHashMap<String>() {
|
PALETTE_VERSIONS = new Object2IntOpenHashMap<>();
|
||||||
{
|
if (!GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
put("1_17_0", Bedrock_v440.V440_CODEC.getProtocolVersion());
|
PALETTE_VERSIONS.put("1_17_0", Bedrock_v440.V440_CODEC.getProtocolVersion());
|
||||||
put("1_17_10", Bedrock_v448.V448_CODEC.getProtocolVersion());
|
}
|
||||||
|
PALETTE_VERSIONS.put("1_17_10", Bedrock_v448.V448_CODEC.getProtocolVersion());
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the raw blocks JSON until it is no longer needed.
|
* Stores the raw blocks JSON until it is no longer needed.
|
||||||
|
|
|
@ -58,9 +58,14 @@ import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ItemRegistryPopulator {
|
public class ItemRegistryPopulator {
|
||||||
private static final Map<String, PaletteVersion> PALETTE_VERSIONS = new Object2ObjectOpenHashMap<String, PaletteVersion>(){
|
private static final Map<String, PaletteVersion> PALETTE_VERSIONS;
|
||||||
{
|
|
||||||
put("1_17_0", new PaletteVersion(Bedrock_v440.V440_CODEC.getProtocolVersion(), new Object2ObjectOpenHashMap<String, String>() {
|
static {
|
||||||
|
PALETTE_VERSIONS = new Object2ObjectOpenHashMap<>();
|
||||||
|
if (GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
PALETTE_VERSIONS.put("1_17_10.caves_and_cliffs", new PaletteVersion(Bedrock_v448.V448_CODEC.getProtocolVersion(), Collections.emptyMap()));
|
||||||
|
} else {
|
||||||
|
PALETTE_VERSIONS.put("1_17_0", new PaletteVersion(Bedrock_v440.V440_CODEC.getProtocolVersion(), new Object2ObjectOpenHashMap<String, String>() {
|
||||||
{
|
{
|
||||||
put("minecraft:candle", "minecraft:sea_pickle");
|
put("minecraft:candle", "minecraft:sea_pickle");
|
||||||
put("minecraft:white_candle", "minecraft:sea_pickle");
|
put("minecraft:white_candle", "minecraft:sea_pickle");
|
||||||
|
@ -81,9 +86,9 @@ public class ItemRegistryPopulator {
|
||||||
put("minecraft:black_candle", "minecraft:sea_pickle");
|
put("minecraft:black_candle", "minecraft:sea_pickle");
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
put("1_17_10", new PaletteVersion(Bedrock_v448.V448_CODEC.getProtocolVersion(), Collections.emptyMap()));
|
PALETTE_VERSIONS.put("1_17_10", new PaletteVersion(Bedrock_v448.V448_CODEC.getProtocolVersion(), Collections.emptyMap()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@ -230,10 +235,13 @@ public class ItemRegistryPopulator {
|
||||||
Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
|
Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
|
||||||
Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
|
Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
|
||||||
"minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg",
|
"minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg",
|
||||||
"minecraft:bundle", "minecraft:sculk_sensor");
|
"minecraft:bundle");
|
||||||
if (!usingFurnaceMinecart) {
|
if (!usingFurnaceMinecart) {
|
||||||
javaOnlyItems.add("minecraft:furnace_minecart");
|
javaOnlyItems.add("minecraft:furnace_minecart");
|
||||||
}
|
}
|
||||||
|
if (!GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
javaOnlyItems.add("minecraft:sculk_sensor");
|
||||||
|
}
|
||||||
// Java-only items for this version
|
// Java-only items for this version
|
||||||
javaOnlyItems.addAll(palette.getValue().getAdditionalTranslatedItems().keySet());
|
javaOnlyItems.addAll(palette.getValue().getAdditionalTranslatedItems().keySet());
|
||||||
|
|
||||||
|
@ -246,6 +254,9 @@ public class ItemRegistryPopulator {
|
||||||
// This items has a mapping specifically for this version of the game
|
// This items has a mapping specifically for this version of the game
|
||||||
mappingItem = entry.getValue();
|
mappingItem = entry.getValue();
|
||||||
}
|
}
|
||||||
|
if (entry.getKey().equals("minecraft:sculk_sensor") && GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
mappingItem.setBedrockIdentifier("minecraft:sculk_sensor");
|
||||||
|
}
|
||||||
|
|
||||||
if (usingFurnaceMinecart && entry.getKey().equals("minecraft:furnace_minecart")) {
|
if (usingFurnaceMinecart && entry.getKey().equals("minecraft:furnace_minecart")) {
|
||||||
javaFurnaceMinecartId = itemIndex;
|
javaFurnaceMinecartId = itemIndex;
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
package org.geysermc.connector.utils;
|
package org.geysermc.connector.utils;
|
||||||
|
|
||||||
|
import org.geysermc.connector.network.translators.world.chunk.BlockStorage;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
// Based off of ProtocolSupport's LegacyBiomeData.java:
|
// Based off of ProtocolSupport's LegacyBiomeData.java:
|
||||||
|
@ -40,7 +42,7 @@ public class BiomeUtils {
|
||||||
for (int y = 0; y < 16; y += 4) {
|
for (int y = 0; y < 16; y += 4) {
|
||||||
for (int z = 0; z < 16; z += 4) {
|
for (int z = 0; z < 16; z += 4) {
|
||||||
for (int x = 0; x < 16; x += 4) {
|
for (int x = 0; x < 16; x += 4) {
|
||||||
byte biomeId = biomeID(biomeData, x, y, z);
|
byte biomeId = (byte) biomeID(biomeData, x, y, z);
|
||||||
int offset = ((z + (y / 4)) << 4) | x;
|
int offset = ((z + (y / 4)) << 4) | x;
|
||||||
Arrays.fill(bedrockData, offset, offset + 4, biomeId);
|
Arrays.fill(bedrockData, offset, offset + 4, biomeId);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +51,25 @@ public class BiomeUtils {
|
||||||
return bedrockData;
|
return bedrockData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte biomeID(int[] biomeData, int x, int y, int z) {
|
public static BlockStorage toNewBedrockBiome(int[] biomeData, int ySection) {
|
||||||
|
BlockStorage storage = new BlockStorage(0);
|
||||||
|
int blockY = ySection << 4;
|
||||||
|
int i = 0;
|
||||||
|
// Iterate over biomes like a chunk, grab the biome from Java, and add it to Bedrock's biome palette
|
||||||
|
// Might be able to be optimized by iterating over Java's biome data section?? Unsure.
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int y = blockY; y < (blockY + 16); y++) {
|
||||||
|
int biomeId = biomeID(biomeData, x, y, z);
|
||||||
|
storage.setFullBlock(i, biomeId);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int biomeID(int[] biomeData, int x, int y, int z) {
|
||||||
int biomeId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)];
|
int biomeId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)];
|
||||||
if (biomeId == 0) {
|
if (biomeId == 0) {
|
||||||
biomeId = 42; // Ocean
|
biomeId = 42; // Ocean
|
||||||
|
@ -58,6 +78,6 @@ public class BiomeUtils {
|
||||||
} else if (biomeId >= 170) { // Nether biomes. Dunno why it's like this :microjang:
|
} else if (biomeId >= 170) { // Nether biomes. Dunno why it's like this :microjang:
|
||||||
biomeId += 8;
|
biomeId += 8;
|
||||||
}
|
}
|
||||||
return (byte) biomeId;
|
return biomeId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
|
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
@ -72,10 +74,49 @@ public class ChunkUtils {
|
||||||
* The minimum height Bedrock Edition will accept.
|
* The minimum height Bedrock Edition will accept.
|
||||||
*/
|
*/
|
||||||
private static final int MINIMUM_ACCEPTED_HEIGHT = 0;
|
private static final int MINIMUM_ACCEPTED_HEIGHT = 0;
|
||||||
|
private static final int CAVES_AND_CLIFFS_MINIMUM_HEIGHT = -64;
|
||||||
|
private static final int MINIMUM_ACCEPTED_HEIGHT_OVERWORLD = GeyserConnector.getInstance().getConfig().isExtendedWorldHeight() ?
|
||||||
|
CAVES_AND_CLIFFS_MINIMUM_HEIGHT : MINIMUM_ACCEPTED_HEIGHT;
|
||||||
/**
|
/**
|
||||||
* The maximum height Bedrock Edition will accept.
|
* The maximum chunk height Bedrock Edition will accept, from the lowest point to the highest.
|
||||||
*/
|
*/
|
||||||
private static final int MAXIMUM_ACCEPTED_HEIGHT = 256;
|
private static final int MAXIMUM_ACCEPTED_HEIGHT = 256;
|
||||||
|
private static final int CAVES_AND_CLIFFS_MAXIMUM_HEIGHT = 384;
|
||||||
|
private static final int MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD = GeyserConnector.getInstance().getConfig().isExtendedWorldHeight() ?
|
||||||
|
CAVES_AND_CLIFFS_MAXIMUM_HEIGHT : MAXIMUM_ACCEPTED_HEIGHT;
|
||||||
|
|
||||||
|
public static final byte[] EMPTY_CHUNK_DATA;
|
||||||
|
public static final byte[] EMPTY_BIOME_DATA;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (GeyserConnector.getInstance().getConfig().isExtendedWorldHeight()) {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
try {
|
||||||
|
BlockStorage blockStorage = new BlockStorage(0);
|
||||||
|
blockStorage.writeToNetwork(byteBuf);
|
||||||
|
|
||||||
|
EMPTY_BIOME_DATA = new byte[byteBuf.readableBytes()];
|
||||||
|
byteBuf.readBytes(EMPTY_BIOME_DATA);
|
||||||
|
} finally {
|
||||||
|
byteBuf.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
byteBuf = Unpooled.buffer();
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
byteBuf.writeBytes(EMPTY_BIOME_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
EMPTY_CHUNK_DATA = new byte[byteBuf.readableBytes()];
|
||||||
|
byteBuf.readBytes(EMPTY_CHUNK_DATA);
|
||||||
|
} finally {
|
||||||
|
byteBuf.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EMPTY_BIOME_DATA = null; // Unused
|
||||||
|
EMPTY_CHUNK_DATA = new byte[257]; // 256 bytes for biomes, one for borders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int indexYZXtoXZY(int yzx) {
|
private static int indexYZXtoXZY(int yzx) {
|
||||||
return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8);
|
return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8);
|
||||||
|
@ -91,8 +132,10 @@ public class ChunkUtils {
|
||||||
BitSet waterloggedPaletteIds = new BitSet();
|
BitSet waterloggedPaletteIds = new BitSet();
|
||||||
BitSet pistonOrFlowerPaletteIds = new BitSet();
|
BitSet pistonOrFlowerPaletteIds = new BitSet();
|
||||||
|
|
||||||
|
boolean overworld = session.getChunkCache().isExtendedHeight();
|
||||||
|
|
||||||
for (int sectionY = 0; sectionY < javaSections.length; sectionY++) {
|
for (int sectionY = 0; sectionY < javaSections.length; sectionY++) {
|
||||||
if (yOffset < 0 && sectionY < -yOffset) {
|
if (yOffset < ((overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4) && sectionY < -yOffset) {
|
||||||
// Ignore this chunk since it goes below the accepted height limit
|
// Ignore this chunk since it goes below the accepted height limit
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +171,7 @@ public class ChunkUtils {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sections[sectionY + yOffset] = section;
|
sections[sectionY + (yOffset - ((overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4))] = section;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +244,7 @@ public class ChunkUtils {
|
||||||
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
|
layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) };
|
||||||
}
|
}
|
||||||
|
|
||||||
sections[sectionY + yOffset] = new ChunkSection(layers);
|
sections[sectionY + (yOffset - ((overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4))] = new ChunkSection(layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompoundTag[] blockEntities = column.getTileEntities();
|
CompoundTag[] blockEntities = column.getTileEntities();
|
||||||
|
@ -383,7 +426,7 @@ public class ChunkUtils {
|
||||||
data.setChunkX(chunkX + x);
|
data.setChunkX(chunkX + x);
|
||||||
data.setChunkZ(chunkZ + z);
|
data.setChunkZ(chunkZ + z);
|
||||||
data.setSubChunksLength(0);
|
data.setSubChunksLength(0);
|
||||||
data.setData(new byte[0]);
|
data.setData(EMPTY_CHUNK_DATA);
|
||||||
data.setCachingEnabled(false);
|
data.setCachingEnabled(false);
|
||||||
session.sendUpstreamPacket(data);
|
session.sendUpstreamPacket(data);
|
||||||
|
|
||||||
|
@ -400,7 +443,8 @@ public class ChunkUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the minimum and maximum heights for this dimension
|
* Process the minimum and maximum heights for this dimension.
|
||||||
|
* This must be done after the player has switched dimensions so we know what their dimension is
|
||||||
*/
|
*/
|
||||||
public static void applyDimensionHeight(GeyserSession session, CompoundTag dimensionTag) {
|
public static void applyDimensionHeight(GeyserSession session, CompoundTag dimensionTag) {
|
||||||
int minY = ((IntTag) dimensionTag.get("min_y")).getValue();
|
int minY = ((IntTag) dimensionTag.get("min_y")).getValue();
|
||||||
|
@ -414,8 +458,25 @@ public class ChunkUtils {
|
||||||
throw new RuntimeException("Maximum Y must be a multiple of 16!");
|
throw new RuntimeException("Maximum Y must be a multiple of 16!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minY < MINIMUM_ACCEPTED_HEIGHT || maxY > MAXIMUM_ACCEPTED_HEIGHT) {
|
int dimension = DimensionUtils.javaToBedrock(session.getDimension());
|
||||||
session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds"));
|
boolean extendedHeight = dimension == 0 && session.getConnector().getConfig().isExtendedWorldHeight();
|
||||||
|
session.getChunkCache().setExtendedHeight(extendedHeight);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
if (minY < (extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT)
|
||||||
|
|| maxY > (extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT)) {
|
||||||
|
if (minY >= CAVES_AND_CLIFFS_MINIMUM_HEIGHT && maxY <= CAVES_AND_CLIFFS_MAXIMUM_HEIGHT && dimension == 0 && !session.getConnector().getConfig().isExtendedWorldHeight()) {
|
||||||
|
// This dimension uses heights that would be fixed by enabling the experimental toggle
|
||||||
|
session.getConnector().getLogger().warning(
|
||||||
|
LanguageUtils.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds.caves_and_cliffs",
|
||||||
|
"extended-world-height"));
|
||||||
|
} else {
|
||||||
|
session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds",
|
||||||
|
extendedHeight ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT,
|
||||||
|
extendedHeight ? MAXIMUM_ACCEPTED_HEIGHT_OVERWORLD : MAXIMUM_ACCEPTED_HEIGHT,
|
||||||
|
session.getDimension()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getChunkCache().setMinY(minY);
|
session.getChunkCache().setMinY(minY);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -57,6 +57,11 @@ remote:
|
||||||
# This is designed to be used for forced hosts on proxies
|
# This is designed to be used for forced hosts on proxies
|
||||||
forward-hostname: false
|
forward-hostname: false
|
||||||
|
|
||||||
|
# Allows the overworld world height to be extended from 0 - 255 to -64 - 319. This option cannot be changed during a reload.
|
||||||
|
# 1.17.0-1.17.2 Bedrock clients cannot connect with this option enabled.
|
||||||
|
# Performance issues and/or additional bugs may occur for Bedrock clients as this is an experimental toggle on their end.
|
||||||
|
extended-world-height: false
|
||||||
|
|
||||||
# Floodgate uses encryption to ensure use from authorised sources.
|
# Floodgate uses encryption to ensure use from authorised sources.
|
||||||
# This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity)
|
# This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity)
|
||||||
# You can ignore this when not using Floodgate.
|
# You can ignore this when not using Floodgate.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit fcc8f01e25e481c9c82f2ee9bff23111ec781dd7
|
Subproject commit c8923b1f71c7f66621729be47dec7f911606c6a1
|
Loading…
Reference in a new issue