putByte( ($chunk->isPopulated() ? self::FLAG_POPULATED : 0) ); //subchunks $subChunks = $chunk->getSubChunks(); $count = count($subChunks); $stream->putByte($count); foreach($subChunks as $y => $subChunk){ $stream->putByte($y); $stream->putInt($subChunk->getEmptyBlockId()); $layers = $subChunk->getBlockLayers(); $stream->putByte(count($layers)); foreach($layers as $blocks){ $wordArray = $blocks->getWordArray(); $palette = $blocks->getPalette(); $stream->putByte($blocks->getBitsPerBlock()); $stream->put($wordArray); $serialPalette = pack("L*", ...$palette); $stream->putInt(strlen($serialPalette)); $stream->put($serialPalette); } } //biomes $stream->put($chunk->getBiomeIdArray()); return $stream->getBuffer(); } /** * Deserializes a fast-serialized chunk */ public static function deserializeTerrain(string $data) : Chunk{ $stream = new BinaryStream($data); $flags = $stream->getByte(); $terrainPopulated = (bool) ($flags & self::FLAG_POPULATED); $subChunks = []; $count = $stream->getByte(); for($subCount = 0; $subCount < $count; ++$subCount){ $y = $stream->getByte(); $airBlockId = $stream->getInt(); /** @var PalettedBlockArray[] $layers */ $layers = []; for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){ $bitsPerBlock = $stream->getByte(); $words = $stream->get(PalettedBlockArray::getExpectedWordArraySize($bitsPerBlock)); /** @var int[] $unpackedPalette */ $unpackedPalette = unpack("L*", $stream->get($stream->getInt())); //unpack() will never fail here $palette = array_values($unpackedPalette); $layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); } $subChunks[$y] = new SubChunk($airBlockId, $layers); } $biomeIds = new BiomeArray($stream->get(256)); return new Chunk($subChunks, $biomeIds, $terrainPopulated); } }