mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
1.16+ Terrain and Movement support (#1353)
* First implementation * Improve chunk reading performance * Fix indentation * Remove debug information * Update MultiBlockChange packet * Move skip varint to a method * Fix crash when not using block palette * Fix DataTypes.cs not compiling on .NET 4.0 Binary (0b) values not handled so converted to Hexadecimal (0x) * Use the 1.16 chunk parsing code for 1.15 too Document the differences in padding and factor the code Co-authored-by: ORelio <ORelio@users.noreply.github.com>
This commit is contained in:
parent
9b5fde0689
commit
53bd56100f
6 changed files with 1505 additions and 44 deletions
1250
MinecraftClient/Mapping/BlockPalettes/Palette116.cs
Normal file
1250
MinecraftClient/Mapping/BlockPalettes/Palette116.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -48,6 +48,7 @@
|
||||||
GoldOre,
|
GoldOre,
|
||||||
IronOre,
|
IronOre,
|
||||||
CoalOre,
|
CoalOre,
|
||||||
|
NetherGoldOre,
|
||||||
OakLog,
|
OakLog,
|
||||||
SpruceLog,
|
SpruceLog,
|
||||||
BirchLog,
|
BirchLog,
|
||||||
|
|
@ -157,6 +158,7 @@
|
||||||
Torch,
|
Torch,
|
||||||
WallTorch,
|
WallTorch,
|
||||||
Fire,
|
Fire,
|
||||||
|
SoulFire,
|
||||||
Spawner,
|
Spawner,
|
||||||
OakStairs,
|
OakStairs,
|
||||||
Chest,
|
Chest,
|
||||||
|
|
@ -207,6 +209,11 @@
|
||||||
Pumpkin,
|
Pumpkin,
|
||||||
Netherrack,
|
Netherrack,
|
||||||
SoulSand,
|
SoulSand,
|
||||||
|
SoulSoil,
|
||||||
|
Basalt,
|
||||||
|
PolishedBasalt,
|
||||||
|
SoulTorch,
|
||||||
|
SoulWallTorch,
|
||||||
Glowstone,
|
Glowstone,
|
||||||
NetherPortal,
|
NetherPortal,
|
||||||
CarvedPumpkin,
|
CarvedPumpkin,
|
||||||
|
|
@ -249,6 +256,7 @@
|
||||||
RedMushroomBlock,
|
RedMushroomBlock,
|
||||||
MushroomStem,
|
MushroomStem,
|
||||||
IronBars,
|
IronBars,
|
||||||
|
Chain,
|
||||||
GlassPane,
|
GlassPane,
|
||||||
Melon,
|
Melon,
|
||||||
AttachedPumpkinStem,
|
AttachedPumpkinStem,
|
||||||
|
|
@ -685,14 +693,89 @@
|
||||||
Stonecutter,
|
Stonecutter,
|
||||||
Bell,
|
Bell,
|
||||||
Lantern,
|
Lantern,
|
||||||
|
SoulLantern,
|
||||||
Campfire,
|
Campfire,
|
||||||
|
SoulCampfire,
|
||||||
SweetBerryBush,
|
SweetBerryBush,
|
||||||
|
WarpedStem,
|
||||||
|
StrippedWarpedStem,
|
||||||
|
WarpedHyphae,
|
||||||
|
StrippedWarpedHyphae,
|
||||||
|
WarpedNylium,
|
||||||
|
WarpedFungus,
|
||||||
|
WarpedWartBlock,
|
||||||
|
WarpedRoots,
|
||||||
|
NetherSprouts,
|
||||||
|
CrimsonStem,
|
||||||
|
StrippedCrimsonStem,
|
||||||
|
CrimsonHyphae,
|
||||||
|
StrippedCrimsonHyphae,
|
||||||
|
CrimsonNylium,
|
||||||
|
CrimsonFungus,
|
||||||
|
Shroomlight,
|
||||||
|
WeepingVines,
|
||||||
|
WeepingVinesPlant,
|
||||||
|
TwistingVines,
|
||||||
|
TwistingVinesPlant,
|
||||||
|
CrimsonRoots,
|
||||||
|
CrimsonPlanks,
|
||||||
|
WarpedPlanks,
|
||||||
|
CrimsonSlab,
|
||||||
|
WarpedSlab,
|
||||||
|
CrimsonPressurePlate,
|
||||||
|
WarpedPressurePlate,
|
||||||
|
CrimsonFence,
|
||||||
|
WarpedFence,
|
||||||
|
CrimsonTrapdoor,
|
||||||
|
WarpedTrapdoor,
|
||||||
|
CrimsonFenceGate,
|
||||||
|
WarpedFenceGate,
|
||||||
|
CrimsonStairs,
|
||||||
|
WarpedStairs,
|
||||||
|
CrimsonButton,
|
||||||
|
WarpedButton,
|
||||||
|
CrimsonDoor,
|
||||||
|
WarpedDoor,
|
||||||
|
CrimsonSign,
|
||||||
|
WarpedSign,
|
||||||
|
CrimsonWallSign,
|
||||||
|
WarpedWallSign,
|
||||||
StructureBlock,
|
StructureBlock,
|
||||||
Jigsaw,
|
Jigsaw,
|
||||||
Composter,
|
Composter,
|
||||||
|
Target,
|
||||||
BeeNest,
|
BeeNest,
|
||||||
Beehive,
|
Beehive,
|
||||||
HoneyBlock,
|
HoneyBlock,
|
||||||
HoneycombBlock,
|
HoneycombBlock,
|
||||||
|
NetheriteBlock,
|
||||||
|
AncientDebris,
|
||||||
|
CryingObsidian,
|
||||||
|
RespawnAnchor,
|
||||||
|
PottedCrimsonFungus,
|
||||||
|
PottedWarpedFungus,
|
||||||
|
PottedCrimsonRoots,
|
||||||
|
PottedWarpedRoots,
|
||||||
|
Lodestone,
|
||||||
|
Blackstone,
|
||||||
|
BlackstoneStairs,
|
||||||
|
BlackstoneWall,
|
||||||
|
BlackstoneSlab,
|
||||||
|
PolishedBlackstone,
|
||||||
|
PolishedBlackstoneBricks,
|
||||||
|
CrackedPolishedBlackstoneBricks,
|
||||||
|
ChiseledPolishedBlackstone,
|
||||||
|
PolishedBlackstoneBrickSlab,
|
||||||
|
PolishedBlackstoneBrickStairs,
|
||||||
|
PolishedBlackstoneBrickWall,
|
||||||
|
GildedBlackstone,
|
||||||
|
PolishedBlackstoneStairs,
|
||||||
|
PolishedBlackstoneSlab,
|
||||||
|
PolishedBlackstonePressurePlate,
|
||||||
|
PolishedBlackstoneButton,
|
||||||
|
PolishedBlackstoneWall,
|
||||||
|
ChiseledNetherBricks,
|
||||||
|
CrackedNetherBricks,
|
||||||
|
QuartzBricks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@
|
||||||
<Compile Include="Mapping\BlockPalettes\Palette115.cs" />
|
<Compile Include="Mapping\BlockPalettes\Palette115.cs" />
|
||||||
<Compile Include="Mapping\BlockPalettes\BlockPaletteGenerator.cs" />
|
<Compile Include="Mapping\BlockPalettes\BlockPaletteGenerator.cs" />
|
||||||
<Compile Include="Mapping\BlockPalettes\BlockPalette.cs" />
|
<Compile Include="Mapping\BlockPalettes\BlockPalette.cs" />
|
||||||
|
<Compile Include="Mapping\BlockPalettes\Palette116.cs" />
|
||||||
<Compile Include="Mapping\CommandBlockFlags.cs" />
|
<Compile Include="Mapping\CommandBlockFlags.cs" />
|
||||||
<Compile Include="Mapping\CommandBlockMode.cs" />
|
<Compile Include="Mapping\CommandBlockMode.cs" />
|
||||||
<Compile Include="Mapping\Entity.cs" />
|
<Compile Include="Mapping\Entity.cs" />
|
||||||
|
|
@ -401,4 +402,4 @@
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -267,6 +267,19 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Skip a VarInt from a cache of bytes with better performance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
|
public void SkipNextVarInt(Queue<byte> cache)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if ((ReadNextByte(cache) & 0x80) != 128)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an "extended short", which is actually an int of some kind, from the cache of bytes.
|
/// Read an "extended short", which is actually an int of some kind, from the cache of bytes.
|
||||||
/// This is only done with forge. It looks like it's a normal short, except that if the high
|
/// This is only done with forge. It looks like it's a normal short, except that if the high
|
||||||
|
|
@ -286,6 +299,31 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ((high & 0xFF) << 15) | low;
|
return ((high & 0xFF) << 15) | low;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a long from a cache of bytes and remove it from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
|
/// <returns>The long value</returns>
|
||||||
|
public long ReadNextVarLong(Queue<byte> cache)
|
||||||
|
{
|
||||||
|
int numRead = 0;
|
||||||
|
long result = 0;
|
||||||
|
byte read;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
read = ReadNextByte(cache);
|
||||||
|
long value = (read & 0x7F);
|
||||||
|
result |= (value << (7 * numRead));
|
||||||
|
|
||||||
|
numRead++;
|
||||||
|
if (numRead > 10)
|
||||||
|
{
|
||||||
|
throw new OverflowException("VarLong is too big");
|
||||||
|
}
|
||||||
|
} while ((read & 0x80) != 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a single byte from a cache of bytes and remove it from the cache
|
/// Read a single byte from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler);
|
this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler);
|
||||||
this.packetPalette = new PacketTypeHandler(protocolVersion).GetTypeHandler();
|
this.packetPalette = new PacketTypeHandler(protocolVersion).GetTypeHandler();
|
||||||
|
|
||||||
if (handler.GetTerrainEnabled() && protocolversion > MC1152Version)
|
if (handler.GetTerrainEnabled() && protocolversion > MC1164Version)
|
||||||
{
|
{
|
||||||
Translations.WriteLineFormatted("extra.terrainandmovement_disabled");
|
Translations.WriteLineFormatted("extra.terrainandmovement_disabled");
|
||||||
handler.SetTerrainEnabled(false);
|
handler.SetTerrainEnabled(false);
|
||||||
|
|
@ -101,9 +101,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
// Block palette
|
// Block palette
|
||||||
if (protocolversion >= MC113Version)
|
if (protocolversion >= MC113Version)
|
||||||
{
|
{
|
||||||
if (protocolVersion > MC1152Version && handler.GetTerrainEnabled())
|
if (protocolVersion > MC1164Version && handler.GetTerrainEnabled())
|
||||||
throw new NotImplementedException(Translations.Get("exception.palette.block"));
|
throw new NotImplementedException(Translations.Get("exception.palette.block"));
|
||||||
if (protocolVersion >= MC115Version)
|
if (protocolVersion >= MC116Version)
|
||||||
|
Block.Palette = new Palette116();
|
||||||
|
else if (protocolVersion >= MC115Version)
|
||||||
Block.Palette = new Palette115();
|
Block.Palette = new Palette115();
|
||||||
else if (protocolVersion >= MC114Version)
|
else if (protocolVersion >= MC114Version)
|
||||||
Block.Palette = new Palette114();
|
Block.Palette = new Palette114();
|
||||||
|
|
@ -398,6 +400,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
int chunkX = dataTypes.ReadNextInt(packetData);
|
int chunkX = dataTypes.ReadNextInt(packetData);
|
||||||
int chunkZ = dataTypes.ReadNextInt(packetData);
|
int chunkZ = dataTypes.ReadNextInt(packetData);
|
||||||
bool chunksContinuous = dataTypes.ReadNextBool(packetData);
|
bool chunksContinuous = dataTypes.ReadNextBool(packetData);
|
||||||
|
if (protocolversion >= MC116Version && protocolversion <= MC1161Version)
|
||||||
|
dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only
|
||||||
ushort chunkMask = protocolversion >= MC19Version
|
ushort chunkMask = protocolversion >= MC19Version
|
||||||
? (ushort)dataTypes.ReadNextVarInt(packetData)
|
? (ushort)dataTypes.ReadNextVarInt(packetData)
|
||||||
: dataTypes.ReadNextUShort(packetData);
|
: dataTypes.ReadNextUShort(packetData);
|
||||||
|
|
@ -413,8 +417,23 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
if (protocolversion >= MC114Version)
|
if (protocolversion >= MC114Version)
|
||||||
dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above
|
dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above
|
||||||
|
int biomesLength = 0;
|
||||||
|
if (protocolversion >= MC1162Version)
|
||||||
|
if (chunksContinuous)
|
||||||
|
biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - 1.16.2 and above
|
||||||
if (protocolversion >= MC115Version && chunksContinuous)
|
if (protocolversion >= MC115Version && chunksContinuous)
|
||||||
dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
|
{
|
||||||
|
if (protocolversion >= MC1162Version)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < biomesLength; i++)
|
||||||
|
{
|
||||||
|
// Biomes - 1.16.2 and above
|
||||||
|
// Don't use ReadNextVarInt because it cost too much time
|
||||||
|
dataTypes.SkipNextVarInt(packetData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
|
||||||
|
}
|
||||||
int dataSize = dataTypes.ReadNextVarInt(packetData);
|
int dataSize = dataTypes.ReadNextVarInt(packetData);
|
||||||
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
|
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
|
||||||
}
|
}
|
||||||
|
|
@ -514,35 +533,62 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketTypesIn.MultiBlockChange:
|
case PacketTypesIn.MultiBlockChange:
|
||||||
if (handler.GetTerrainEnabled())
|
if (handler.GetTerrainEnabled())
|
||||||
{
|
{
|
||||||
int chunkX = dataTypes.ReadNextInt(packetData);
|
if (protocolversion >= MC1162Version)
|
||||||
int chunkZ = dataTypes.ReadNextInt(packetData);
|
|
||||||
int recordCount = protocolversion < MC18Version
|
|
||||||
? (int)dataTypes.ReadNextShort(packetData)
|
|
||||||
: dataTypes.ReadNextVarInt(packetData);
|
|
||||||
|
|
||||||
for (int i = 0; i < recordCount; i++)
|
|
||||||
{
|
{
|
||||||
byte locationXZ;
|
long chunkSection = dataTypes.ReadNextLong(packetData);
|
||||||
ushort blockIdMeta;
|
int sectionX = (int)(chunkSection >> 42);
|
||||||
int blockY;
|
int sectionY = (int)((chunkSection << 44) >> 44);
|
||||||
|
int sectionZ = (int)((chunkSection << 22) >> 42);
|
||||||
if (protocolversion < MC18Version)
|
dataTypes.ReadNextBool(packetData); // Useless boolean
|
||||||
|
int blocksSize = dataTypes.ReadNextVarInt(packetData);
|
||||||
|
for (int i = 0; i < blocksSize; i++)
|
||||||
{
|
{
|
||||||
blockIdMeta = dataTypes.ReadNextUShort(packetData);
|
ulong block = (ulong)dataTypes.ReadNextVarLong(packetData);
|
||||||
blockY = (ushort)dataTypes.ReadNextByte(packetData);
|
int blockId = (int)(block >> 12);
|
||||||
locationXZ = dataTypes.ReadNextByte(packetData);
|
int localX = (int)((block >> 8) & 0x0F);
|
||||||
}
|
int localZ = (int)((block >> 4) & 0x0F);
|
||||||
else
|
int localY = (int)(block & 0x0F);
|
||||||
{
|
|
||||||
locationXZ = dataTypes.ReadNextByte(packetData);
|
|
||||||
blockY = (ushort)dataTypes.ReadNextByte(packetData);
|
|
||||||
blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData);
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockX = locationXZ >> 4;
|
Block b = new Block((ushort)blockId);
|
||||||
int blockZ = locationXZ & 0x0F;
|
int blockX = (sectionX * 16) + localX;
|
||||||
Block block = new Block(blockIdMeta);
|
int blockY = (sectionY * 16) + localY;
|
||||||
handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block);
|
int blockZ = (sectionZ * 16) + localZ;
|
||||||
|
var l = new Location(blockX, blockY, blockZ);
|
||||||
|
handler.GetWorld().SetBlock(l, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int chunkX = dataTypes.ReadNextInt(packetData);
|
||||||
|
int chunkZ = dataTypes.ReadNextInt(packetData);
|
||||||
|
int recordCount = protocolversion < MC18Version
|
||||||
|
? (int)dataTypes.ReadNextShort(packetData)
|
||||||
|
: dataTypes.ReadNextVarInt(packetData);
|
||||||
|
|
||||||
|
for (int i = 0; i < recordCount; i++)
|
||||||
|
{
|
||||||
|
byte locationXZ;
|
||||||
|
ushort blockIdMeta;
|
||||||
|
int blockY;
|
||||||
|
|
||||||
|
if (protocolversion < MC18Version)
|
||||||
|
{
|
||||||
|
blockIdMeta = dataTypes.ReadNextUShort(packetData);
|
||||||
|
blockY = (ushort)dataTypes.ReadNextByte(packetData);
|
||||||
|
locationXZ = dataTypes.ReadNextByte(packetData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
locationXZ = dataTypes.ReadNextByte(packetData);
|
||||||
|
blockY = (ushort)dataTypes.ReadNextByte(packetData);
|
||||||
|
blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockX = locationXZ >> 4;
|
||||||
|
int blockZ = locationXZ & 0x0F;
|
||||||
|
Block block = new Block(blockIdMeta);
|
||||||
|
handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -75,43 +75,86 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
// EG, if bitsPerBlock = 5, valueMask = 00011111 in binary
|
// EG, if bitsPerBlock = 5, valueMask = 00011111 in binary
|
||||||
uint valueMask = (uint)((1 << bitsPerBlock) - 1);
|
uint valueMask = (uint)((1 << bitsPerBlock) - 1);
|
||||||
|
|
||||||
|
// Block IDs are packed in the array of 64-bits integers
|
||||||
ulong[] dataArray = dataTypes.ReadNextULongArray(cache);
|
ulong[] dataArray = dataTypes.ReadNextULongArray(cache);
|
||||||
|
|
||||||
Chunk chunk = new Chunk();
|
Chunk chunk = new Chunk();
|
||||||
|
|
||||||
if (dataArray.Length > 0)
|
if (dataArray.Length > 0)
|
||||||
{
|
{
|
||||||
|
int longIndex = 0;
|
||||||
|
int startOffset = 0 - bitsPerBlock;
|
||||||
|
|
||||||
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
||||||
{
|
{
|
||||||
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
||||||
{
|
{
|
||||||
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
||||||
{
|
{
|
||||||
int blockNumber = (blockY * Chunk.SizeZ + blockZ) * 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
|
||||||
int startLong = (blockNumber * bitsPerBlock) / 64;
|
|
||||||
int startOffset = (blockNumber * bitsPerBlock) % 64;
|
|
||||||
int endLong = ((blockNumber + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
|
|
||||||
// TODO: In the future a single ushort may not store the entire block id;
|
|
||||||
// the Block code may need to change if block state IDs go beyond 65535
|
|
||||||
ushort blockId;
|
ushort blockId;
|
||||||
if (startLong == endLong)
|
|
||||||
|
// Calculate location of next block ID inside the array of Longs
|
||||||
|
startOffset += bitsPerBlock;
|
||||||
|
bool overlap = false;
|
||||||
|
|
||||||
|
if ((startOffset + bitsPerBlock) > 64)
|
||||||
{
|
{
|
||||||
blockId = (ushort)((dataArray[startLong] >> startOffset) & valueMask);
|
if (protocolversion >= Protocol18Handler.MC116Version)
|
||||||
|
{
|
||||||
|
// 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++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In MC 1.15 and lower, block IDs can overlap between Longs:
|
||||||
|
// [ LONG INTEGER ][ LONG INTEGER ]
|
||||||
|
// [Block][Block][Block][Blo ck][Block][Block][Block][
|
||||||
|
|
||||||
|
// Detect when we reached the next Long or switch to overlap mode
|
||||||
|
if (startOffset >= 64)
|
||||||
|
{
|
||||||
|
startOffset -= 64;
|
||||||
|
longIndex++;
|
||||||
|
}
|
||||||
|
else overlap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Block ID
|
||||||
|
if (overlap)
|
||||||
|
{
|
||||||
|
int endOffset = 64 - startOffset;
|
||||||
|
blockId = (ushort)((dataArray[longIndex] >> startOffset | dataArray[longIndex + 1] << endOffset) & valueMask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int endOffset = 64 - startOffset;
|
blockId = (ushort)((dataArray[longIndex] >> startOffset) & valueMask);
|
||||||
blockId = (ushort)((dataArray[startLong] >> startOffset | dataArray[endLong] << endOffset) & valueMask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map small IDs to actual larger block IDs
|
||||||
if (usePalette)
|
if (usePalette)
|
||||||
{
|
{
|
||||||
// Get the real block ID out of the palette
|
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,
|
||||||
|
bitsPerBlock,
|
||||||
|
blockNumber));
|
||||||
|
}
|
||||||
|
|
||||||
blockId = (ushort)palette[blockId];
|
blockId = (ushort)palette[blockId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have our block, save the block into the chunk
|
||||||
chunk[blockX, blockY, blockZ] = new Block(blockId);
|
chunk[blockX, blockY, blockZ] = new Block(blockId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue