-
-
Notifications
You must be signed in to change notification settings - Fork 85
Pl3xMap File Protocol
Pl3xMap files are designed to hold relevant information about a Minecraft region in order to display data on the map which cannot be obtained from a PNG/WebP image.
The 8 bytes at the start of this file will always contain the Pl3xMap signature.
The first 7 bytes, 70 6C 33 78 6D 61 70
, can be decoded from HEX -> DEC -> ASCII to read as pl3xmap
.
HEX: 70 6C 33 78 6D 61 70
DEC: 112 108 51 120 109 97 112
ASCII: p l 3 x m a p
The eighth byte is the version of this file (currently 01
).
The next 12 bytes are reserved for 3 integers; The x and z coordinates of a region followed by the region's minimum build height.
int header = 8; // the number of bytes to skip for the header
int regionX = getInt(bytes, header); // get integer starting at the end of header
int regionZ = getInt(bytes, header + 4); // get next integer (integers are 32 bits, so 4 bytes)
int minY = getInt(bytes, header + 8); // get next integer
// convenience method to get an integer from a byte array at a specific position
public int getInt(byte[] bytes, int position) {
int val = 0;
for (int i = 0; i < 4; i++) {
val |= (bytes[position + i] & 0xFF) << (i * 8);
}
return val;
}
All remaining bytes represent blocks in the region. Each block is a 32 bit packed integer.
This is ordered from left-to-right then top-to-bottom, starting from the northwest corner of a region (described as z * 512 + x
).
int count = 0;
for (int z = 0; z < 512; z++) {
for (int x = 0; x < 512; x++) {
int index = z * 512 + x;
System.out.println(index == count++); // will always output true
}
}
You can get a packed integer for a specific block from the file with this formula.
int header = 20; // the number of bytes to skip for the header and region data
int index = ((z & 511) * 512 + (x & 511)); // get the index from world coordinates
int position = header + index * 4; // get the start position of a packed integer
// (4 because 32 bit integer is 4 bytes)
int packed = getInt(bytes, position); // getInt convenience method from above
System.out.println(packed); // outputs the packed integer
The packed integer contains the block, biome, and y coordinate. These are all integers after being unpacked.
The first 10 bits are the block.
The next 10 bits are the biome.
The remaining 12 bits are the y coordinate.
11111111111111111111111111111111 - 32 bits
1111111111 - 10 bits (block)
1111111111 - 10 bits (biome)
111111111111 - 12 bits (yPos)
To pack
int packed = ((block & 1023) << 22) | ((biome & 1023) << 12) | (yPos & 4095)
To unpack
int block = packed >> 22;
int biome = (packed & 0b0000000000_1111111111_000000000000) >>> 12;
int yPos = packed & 0b0000000000_0000000000_111111111111;
Both blocks and biomes are stored as their palette index. Indexes are built when the BlockInfo plugin loads and stores the data in separate files.
Block indexes are stored at web/tiles/blocks.gz
globally.
Biome indexes are stored at web/tiles/world/biomes.gz
per world.
The palettes are simply JSON files with key->value pairs of a unique index and name.
The y coordinate is offset with the region's minimum build height. You can obtain the minimum build height from the region section of the file (explained above). Once you have both values you can simply add them together to get the actual y coordinate.
int y = yPos + minY;
Join the discord for help! https://granny.dev/discord