getSubChunks()->count(); $count > 0; --$count){ if($chunk->getSubChunk($count - 1)->isEmptyFast()){ continue; } return $count; } return 0; } public static function serialize(Chunk $chunk, ?string $tiles = null) : string{ $stream = new NetworkBinaryStream(); $subChunkCount = self::getSubChunkCount($chunk); $blockMapper = RuntimeBlockMapping::getInstance(); for($y = 0; $y < $subChunkCount; ++$y){ $layers = $chunk->getSubChunk($y)->getBlockLayers(); $stream->putByte(8); //version $stream->putByte(count($layers)); foreach($layers as $blocks){ $stream->putByte(($blocks->getBitsPerBlock() << 1) | 1); //last 1-bit means "network format", but seems pointless $stream->put($blocks->getWordArray()); $palette = $blocks->getPalette(); //these LSHIFT by 1 uvarints are optimizations: the client expects zigzag varints here //but since we know they are always unsigned, we can avoid the extra fcall overhead of //zigzag and just shift directly. $stream->putUnsignedVarInt(count($palette) << 1); //yes, this is intentionally zigzag foreach($palette as $p){ $stream->putUnsignedVarInt($blockMapper->toStaticRuntimeId($p >> 4, $p & 0xf) << 1); } } } $stream->put($chunk->getBiomeIdArray()); $stream->putByte(0); //border block array count //Border block entry format: 1 byte (4 bits X, 4 bits Z). These are however useless since they crash the regular client. if($tiles !== null){ $stream->put($tiles); }else{ $stream->put(self::serializeTiles($chunk)); } return $stream->getBuffer(); } public static function serializeTiles(Chunk $chunk) : string{ $stream = new BinaryStream(); foreach($chunk->getTiles() as $tile){ if($tile instanceof Spawnable){ $stream->put($tile->getSerializedSpawnCompound()); } } return $stream->getBuffer(); } }