diff --git a/MinecraftClient/Mapping/Dimension.cs b/MinecraftClient/Mapping/Dimension.cs
new file mode 100644
index 00000000..0ca08e6e
--- /dev/null
+++ b/MinecraftClient/Mapping/Dimension.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MinecraftClient.Mapping
+{
+
+ ///
+ /// The dimension type, available after 1.16.2
+ ///
+ public class Dimension
+ {
+ ///
+ /// The name of the dimension type (for example, "minecraft:overworld").
+ ///
+ public readonly string Name;
+
+ ///
+ /// Whether piglins shake and transform to zombified piglins.
+ ///
+ public readonly bool piglin_safe;
+
+ ///
+ /// When false, compasses spin randomly. When true, nether portals can spawn zombified piglins.
+ ///
+ public readonly bool natural;
+
+ ///
+ /// How much light the dimension has.
+ ///
+ public readonly float ambient_light;
+
+
+ ///
+ /// If set, the time of the day is the specified value.
+ /// Value: -1: not set
+ /// Value: [0, 24000]: time of the day
+ ///
+ public readonly long fixed_time = -1;
+
+ ///
+ /// A resource location defining what block tag to use for infiniburn.
+ /// Value: "" or minecraft resource "minecraft:...".
+ ///
+ public readonly string infiniburn;
+
+ ///
+ /// Whether players can charge and use respawn anchors.
+ ///
+ public readonly bool respawn_anchor_works;
+
+ ///
+ /// Whether the dimension has skylight access or not.
+ ///
+ public readonly bool has_skylight;
+
+ ///
+ /// Whether players can use a bed to sleep.
+ ///
+ public readonly bool bed_works;
+
+ ///
+ /// unknown
+ /// Values: "minecraft:overworld", "minecraft:the_nether", "minecraft:the_end" or something else.
+ ///
+ public readonly string effects;
+
+ ///
+ /// Whether players with the Bad Omen effect can cause a raid.
+ ///
+ public readonly bool has_raids;
+
+ ///
+ /// The minimum Y level.
+ ///
+ public readonly int min_y = 0;
+
+ ///
+ /// The minimum Y level.
+ ///
+ public readonly int max_y = 256;
+
+ ///
+ /// The maximum height.
+ ///
+ public readonly int height = 256;
+
+ ///
+ /// The maximum height to which chorus fruits and nether portals can bring players within this dimension.
+ ///
+ public readonly int logical_height;
+
+ ///
+ /// The multiplier applied to coordinates when traveling to the dimension.
+ ///
+ public readonly double coordinate_scale;
+
+ ///
+ /// Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. Also causes lava to spread thinner.
+ ///
+ public readonly bool ultrawarm;
+
+ ///
+ /// Whether the dimension has a bedrock ceiling or not. When true, causes lava to spread faster.
+ ///
+ public readonly bool has_ceiling;
+
+
+ ///
+ /// Create from the "Dimension Codec" NBT Tag Compound
+ ///
+ /// ChunkColumn X
+ /// ChunkColumn Y
+ /// chunk at the given location
+ public Dimension(string name, Dictionary nbt)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+ if (nbt == null)
+ throw new ArgumentNullException("nbt Data");
+
+ this.Name = name;
+
+ if (nbt.ContainsKey("piglin_safe"))
+ this.piglin_safe = 1 == (byte)nbt["piglin_safe"];
+ if (nbt.ContainsKey("natural"))
+ this.natural = 1 == (byte)nbt["natural"];
+ if (nbt.ContainsKey("ambient_light"))
+ this.ambient_light = (float)nbt["ambient_light"];
+ if (nbt.ContainsKey("fixed_time"))
+ this.fixed_time = (long)nbt["fixed_time"];
+ if (nbt.ContainsKey("infiniburn"))
+ this.infiniburn = (string)nbt["infiniburn"];
+ if (nbt.ContainsKey("respawn_anchor_works"))
+ this.respawn_anchor_works = 1 == (byte)nbt["respawn_anchor_works"];
+ if (nbt.ContainsKey("has_skylight"))
+ this.has_skylight = 1 == (byte)nbt["has_skylight"];
+ if (nbt.ContainsKey("bed_works"))
+ this.bed_works = 1 == (byte)nbt["bed_works"];
+ if (nbt.ContainsKey("effects"))
+ this.effects = (string)nbt["effects"];
+ if (nbt.ContainsKey("has_raids"))
+ this.has_raids = 1 == (byte)nbt["has_raids"];
+ if (nbt.ContainsKey("min_y"))
+ this.min_y = (int)nbt["min_y"];
+ if (nbt.ContainsKey("height"))
+ this.height = (int)nbt["height"];
+ if (nbt.ContainsKey("min_y") && nbt.ContainsKey("height"))
+ this.max_y = this.min_y + this.height;
+ if (nbt.ContainsKey("logical_height"))
+ this.logical_height = (int)nbt["logical_height"];
+ if (nbt.ContainsKey("coordinate_scale"))
+ {
+ var coordinate_scale_obj = nbt["coordinate_scale"];
+ if (coordinate_scale_obj.GetType() == typeof(float))
+ this.coordinate_scale = (float)coordinate_scale_obj;
+ else
+ this.coordinate_scale = (double)coordinate_scale_obj;
+ }
+ if (nbt.ContainsKey("ultrawarm"))
+ this.ultrawarm = 1 == (byte)nbt["ultrawarm"];
+ if (nbt.ContainsKey("has_ceiling"))
+ this.has_ceiling = 1 == (byte)nbt["has_ceiling"];
+ }
+
+ }
+}
diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs
index 00f18071..ad02d75a 100644
--- a/MinecraftClient/Mapping/Location.cs
+++ b/MinecraftClient/Mapping/Location.cs
@@ -25,6 +25,11 @@ namespace MinecraftClient.Mapping
///
public double Z;
+ ///
+ /// Current world: to get the lowest Y coordinate
+ ///
+ public static World world;
+
///
/// Get location with zeroed coordinates
///
@@ -79,7 +84,10 @@ namespace MinecraftClient.Mapping
{
get
{
- return (int)Math.Floor(Y / Chunk.SizeY);
+ if (world.GetDimension() == null)
+ return (int)Math.Floor(Y / Chunk.SizeY); // old version, always start at zero
+ else
+ return (int)Math.Floor((Y - world.GetDimension().min_y) / Chunk.SizeY);
}
}
diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs
index 9a57baae..b324007a 100644
--- a/MinecraftClient/Mapping/World.cs
+++ b/MinecraftClient/Mapping/World.cs
@@ -16,6 +16,11 @@ namespace MinecraftClient.Mapping
///
private Dictionary> chunks = new Dictionary>();
+ ///
+ /// The dimension info of the world
+ ///
+ private Dimension dimension;
+
///
/// Lock for thread safety
///
@@ -78,6 +83,30 @@ namespace MinecraftClient.Mapping
}
}
+ public World()
+ {
+ Location.world = this;
+ }
+
+ ///
+ /// Set dimension type
+ ///
+ /// The name of the dimension type
+ /// The dimension type (NBT Tag Compound)
+ public void SetDimension(string name, Dictionary nbt)
+ {
+ this.dimension = new Dimension(name, nbt);
+ }
+
+ ///
+ /// Get dimension type
+ ///
+ /// The chunk column
+ public Dimension GetDimension()
+ {
+ return this.dimension;
+ }
+
///
/// Get chunk column at the specified location
///
@@ -176,6 +205,7 @@ namespace MinecraftClient.Mapping
try
{
chunks = new Dictionary>();
+ dimension = null;
}
finally
{
diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj
index 44aec940..86351c15 100644
--- a/MinecraftClient/MinecraftClient.csproj
+++ b/MinecraftClient/MinecraftClient.csproj
@@ -79,6 +79,7 @@
+
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index a276ee7a..a55a214c 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -302,17 +302,23 @@ namespace MinecraftClient.Protocol.Handlers
for (int i = 0; i < worldCount; i++)
dataTypes.ReadNextString(packetData); // World Names - 1.16 and above
dataTypes.ReadNextNbt(packetData); // Dimension Codec - 1.16 and above
+
}
- //Current dimension - String identifier in 1.16, varInt below 1.16, byte below 1.9.1
+ string currentDimensionName = null;
+ Dictionary currentDimensionType = null;
+
+ // Current dimension
+ // NBT Tag Compound: 1.16.2 and above
+ // String identifier: 1.16 and 1.16.1
+ // varInt: [1.9.1 to 1.15.2]
+ // byte: below 1.9.1
if (protocolversion >= MC116Version)
{
if (protocolversion >= MC1162Version)
- dataTypes.ReadNextNbt(packetData);
+ currentDimensionType = dataTypes.ReadNextNbt(packetData);
else
dataTypes.ReadNextString(packetData);
- // TODO handle dimensions for 1.16+, needed for terrain handling
- // TODO this data give min and max y which will be needed for chunk collumn handling
this.currentDimension = 0;
}
else if (protocolversion >= MC191Version)
@@ -322,8 +328,16 @@ namespace MinecraftClient.Protocol.Handlers
if (protocolversion < MC114Version)
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
+
if (protocolversion >= MC116Version)
- dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
+ currentDimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above
+
+ if (protocolversion >= MC1162Version)
+ new Task(() =>
+ {
+ handler.GetWorld().SetDimension(currentDimensionName, currentDimensionType);
+ }).Start();
+
if (protocolversion >= MC115Version)
dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
@@ -362,13 +376,14 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnTextReceived(message, true);
break;
case PacketTypesIn.Respawn:
+ string dimensionNameInRespawn = null;
+ Dictionary dimensionTypeInRespawn = null;
if (protocolversion >= MC116Version)
{
- // TODO handle dimensions for 1.16+, needed for terrain handling
if (protocolversion >= MC1162Version)
- dataTypes.ReadNextNbt(packetData);
+ dimensionTypeInRespawn = dataTypes.ReadNextNbt(packetData);
else
- dataTypes.ReadNextString(packetData);
+ dataTypes.ReadNextString(packetData);
this.currentDimension = 0;
}
else
@@ -377,7 +392,14 @@ namespace MinecraftClient.Protocol.Handlers
this.currentDimension = dataTypes.ReadNextInt(packetData);
}
if (protocolversion >= MC116Version)
- dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
+ dimensionNameInRespawn = dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
+
+ if (protocolversion >= MC1162Version)
+ new Task(() =>
+ {
+ handler.GetWorld().SetDimension(dimensionNameInRespawn, dimensionTypeInRespawn);
+ }).Start();
+
if (protocolversion < MC114Version)
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
if (protocolversion >= MC115Version)
@@ -435,19 +457,26 @@ namespace MinecraftClient.Protocol.Handlers
int chunkZ = dataTypes.ReadNextInt(packetData);
if (protocolversion >= MC117Version)
{
- ulong[] verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask
+ ulong[] verticalStripBitmask = null;
+
+ if (protocolversion == MC117Version || protocolversion == MC1171Version)
+ verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask
+
dataTypes.ReadNextNbt(packetData); // Heightmaps
- int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length
- for (int i = 0; i < biomesLength; i++)
+ if (protocolversion == MC117Version || protocolversion == MC1171Version)
{
- dataTypes.SkipNextVarInt(packetData); // Biomes
+ int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length
+ for (int i = 0; i < biomesLength; i++)
+ {
+ dataTypes.SkipNextVarInt(packetData); // Biomes
+ }
}
int dataSize = dataTypes.ReadNextVarInt(packetData); // Size
new Task(() =>
{
- pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, currentDimension, packetData);
+ pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData);
}).Start();
}
else
@@ -600,10 +629,10 @@ namespace MinecraftClient.Protocol.Handlers
if (protocolversion >= MC1162Version)
{
long chunkSection = dataTypes.ReadNextLong(packetData);
- int sectionX = (int)(chunkSection >> 42);
- int sectionY = (int)((chunkSection << 44) >> 44);
- int sectionZ = (int)((chunkSection << 22) >> 42);
- dataTypes.ReadNextBool(packetData); // Useless boolean
+ int sectionX = (int)((chunkSection >> 42) & 0x3FFFFF);
+ int sectionZ = (int)((chunkSection >> 20) & 0x3FFFFF);
+ int sectionY = (int)((chunkSection) & 0xFFFFF);
+ dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update)
int blocksSize = dataTypes.ReadNextVarInt(packetData);
for (int i = 0; i < blocksSize; i++)
{
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
index 0a932bca..f2a22b9f 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs
@@ -27,123 +27,150 @@ namespace MinecraftClient.Protocol.Handlers
this.handler = handler;
}
+ ///
+ /// Reading the "Block states" field: consists of 4096 entries, representing all the blocks in the chunk section.
+ ///
+ /// Blocks will store in this chunk
+ /// Cache for reading data
+ private Chunk ReadBlockStatesField(ref Chunk chunk, Queue cache)
+ {
+ // read Block states (Type: Paletted Container)
+ byte bitsPerEntry = dataTypes.ReadNextByte(cache);
+
+ // 1.18(1.18.1) add a pattle named "Single valued" to replace the vertical strip bitmask in the old
+ if (bitsPerEntry == 0 && protocolversion >= Protocol18Handler.MC1181Version)
+ {
+ // Palettes: Single valued - 1.18(1.18.1) and above
+ ushort value = (ushort)dataTypes.ReadNextVarInt(cache);
+
+ dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero
+
+ // Empty chunks will not be stored
+ if (new Block(value).Type == Material.Air)
+ return null;
+
+ for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
+ {
+ for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
+ {
+ for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
+ {
+ chunk[blockX, blockY, blockZ] = new Block(value);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Palettes: Indirect or Direct
+ bool usePalette = (bitsPerEntry <= 8);
+
+ // Indirect Mode: For block states with bits per entry <= 4, 4 bits are used to represent a block.
+ if (bitsPerEntry < 4) bitsPerEntry = 4;
+
+ // Direct Mode: Bit mask covering bitsPerEntry bits
+ // EG, if bitsPerEntry = 5, valueMask = 00011111 in binary
+ uint valueMask = (uint)((1 << bitsPerEntry) - 1);
+
+ int paletteLength = 0; // Assume zero when length is absent
+ if (usePalette) paletteLength = dataTypes.ReadNextVarInt(cache);
+
+ int[] palette = new int[paletteLength];
+ for (int i = 0; i < paletteLength; i++)
+ palette[i] = dataTypes.ReadNextVarInt(cache);
+
+ // Block IDs are packed in the array of 64-bits integers
+ ulong[] dataArray = dataTypes.ReadNextULongArray(cache);
+
+ int longIndex = 0;
+ int startOffset = 0 - bitsPerEntry;
+ for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
+ {
+ for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
+ {
+ for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
+ {
+ // NOTICE: In the future a single ushort may not store the entire block id;
+ // the Block class may need to change if block state IDs go beyond 65535
+ ushort blockId;
+
+ // Calculate location of next block ID inside the array of Longs
+ startOffset += bitsPerEntry;
+
+ if ((startOffset + bitsPerEntry) > 64)
+ {
+ // In MC 1.16+, padding is applied to prevent overlapping between Longs:
+ // [ LONG INTEGER ][ LONG INTEGER ]
+ // [Block][Block][Block]XXXXX[Block][Block][Block]XXXXX
+
+ // When overlapping, move forward to the beginning of the next Long
+ startOffset = 0;
+ longIndex++;
+ }
+
+ // Extract Block ID
+ blockId = (ushort)((dataArray[longIndex] >> startOffset) & valueMask);
+
+ // Map small IDs to actual larger block IDs
+ if (usePalette)
+ {
+ if (paletteLength <= blockId)
+ {
+ int blockNumber = (blockY * Chunk.SizeZ + blockZ) * Chunk.SizeX + blockX;
+ throw new IndexOutOfRangeException(String.Format("Block ID {0} is outside Palette range 0-{1}! (bitsPerBlock: {2}, blockNumber: {3})",
+ blockId,
+ paletteLength - 1,
+ bitsPerEntry,
+ blockNumber));
+ }
+
+ blockId = (ushort)palette[blockId];
+ }
+
+ // We have our block, save the block into the chunk
+ chunk[blockX, blockY, blockZ] = new Block(blockId);
+ }
+ }
+ }
+ }
+
+ return chunk;
+ }
+
///
/// Process chunk column data from the server and (un)load the chunk from the Minecraft world - 1.17 and above
///
/// Chunk X location
/// Chunk Z location
- /// Chunk mask for reading data, store in bitset
- /// Current dimension type (0 = overworld)
+ /// Chunk mask for reading data, store in bitset, used in 1.17 and 1.17.1
/// Cache for reading chunk data
- public void ProcessChunkColumnData(int chunkX, int chunkZ, ulong[] chunkMasks, int currentDimension, Queue cache)
+ public void ProcessChunkColumnData(int chunkX, int chunkZ, ulong[] verticalStripBitmask, Queue cache)
{
- int chunkColumnSize = chunkMasks.Length * 64;
+ var world = handler.GetWorld();
+ while (world.GetDimension() == null)
+ ; // Dimension parsing unfinished
+
+ int chunkColumnSize = (world.GetDimension().height + 15) / 16;
+
if (protocolversion >= Protocol18Handler.MC117Version)
{
// 1.17 and above chunk format
// Unloading chunks is handled by a separate packet
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
{
- if ((chunkMasks[chunkY / 64] & (1UL << (chunkY % 64))) != 0)
+ // 1.18 and above always contains all chunk section in data
+ // 1.17 and 1.17.1 need vertical strip bitmask to know if the chunk section is included
+ if ((protocolversion >= Protocol18Handler.MC1181Version) ||
+ (((protocolversion == Protocol18Handler.MC117Version) ||
+ (protocolversion == Protocol18Handler.MC1171Version)) &&
+ ((verticalStripBitmask[chunkY / 64] & (1UL << (chunkY % 64))) != 0)))
{
// Non-air block count inside chunk section, for lighting purposes
int blockCnt = dataTypes.ReadNextShort(cache);
- // read Block states (Type: Paletted Container)
+ // Read Block states (Type: Paletted Container)
Chunk chunk = new Chunk();
-
- byte bitsPerEntry = dataTypes.ReadNextByte(cache);
- if (bitsPerEntry == 0 && protocolversion >= Protocol18Handler.MC1181Version)
- {
- // Palettes: Single valued - 1.xx and above
- ushort value = (ushort)dataTypes.ReadNextVarInt(cache);
-
- dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero
-
- for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
- {
- for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
- {
- for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
- {
- chunk[blockX, blockY, blockZ] = new Block(value);
- }
- }
- }
- }
- else
- {
- // Palettes: Indirect or Direct
- bool usePalette = (bitsPerEntry <= 8);
-
- // Indirect Mode: For block states with bits per entry <= 4, 4 bits are used to represent a block.
- if (bitsPerEntry < 4) bitsPerEntry = 4;
-
- // Direct Mode: Bit mask covering bitsPerBlock bits
- // EG, if bitsPerBlock = 5, valueMask = 00011111 in binary
- uint valueMask = (uint)((1 << bitsPerEntry) - 1);
-
- int paletteLength = 0; // Assume zero when length is absent
- if (usePalette) paletteLength = dataTypes.ReadNextVarInt(cache);
-
- int[] palette = new int[paletteLength];
- for (int i = 0; i < paletteLength; i++)
- palette[i] = dataTypes.ReadNextVarInt(cache);
-
- // Block IDs are packed in the array of 64-bits integers
- ulong[] dataArray = dataTypes.ReadNextULongArray(cache);
-
- int longIndex = 0;
- int startOffset = 0 - bitsPerEntry;
- for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
- {
- for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
- {
- for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
- {
- // NOTICE: In the future a single ushort may not store the entire block id;
- // the Block class may need to change if block state IDs go beyond 65535
- ushort blockId;
-
- // Calculate location of next block ID inside the array of Longs
- startOffset += bitsPerEntry;
-
- if ((startOffset + bitsPerEntry) > 64)
- {
- // In MC 1.16+, padding is applied to prevent overlapping between Longs:
- // [ LONG INTEGER ][ LONG INTEGER ]
- // [Block][Block][Block]XXXXX[Block][Block][Block]XXXXX
-
- // When overlapping, move forward to the beginning of the next Long
- startOffset = 0;
- longIndex++;
- }
-
- // Extract Block ID
- blockId = (ushort)((dataArray[longIndex] >> startOffset) & valueMask);
-
- // Map small IDs to actual larger block IDs
- if (usePalette)
- {
- if (paletteLength <= blockId)
- {
- int blockNumber = (blockY * Chunk.SizeZ + blockZ) * Chunk.SizeX + blockX;
- throw new IndexOutOfRangeException(String.Format("Block ID {0} is outside Palette range 0-{1}! (bitsPerBlock: {2}, blockNumber: {3})",
- blockId,
- paletteLength - 1,
- bitsPerEntry,
- blockNumber));
- }
-
- blockId = (ushort)palette[blockId];
- }
-
- // We have our block, save the block into the chunk
- chunk[blockX, blockY, blockZ] = new Block(blockId);
- }
- }
- }
- }
+ ReadBlockStatesField(ref chunk, cache);
//We have our chunk, save the chunk into the world
handler.InvokeOnMainThread(() =>
@@ -153,11 +180,11 @@ namespace MinecraftClient.Protocol.Handlers
handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk;
});
+ // Skip Read Biomes (Type: Paletted Container) - 1.18(1.18.1) and above
if (protocolversion >= Protocol18Handler.MC1181Version)
{
- // skip read Biomes (Type: Paletted Container)
byte bitsPerEntryBiome = dataTypes.ReadNextByte(cache); // Bits Per Entry
- if (bitsPerEntryBiome == 0 && protocolversion >= Protocol18Handler.MC1181Version)
+ if (bitsPerEntryBiome == 0)
{
dataTypes.SkipNextVarInt(cache); // Value
dataTypes.SkipNextVarInt(cache); // Data Array Length