Protocol changes for 1.18.0

This commit is contained in:
Dylan K. Taylor 2021-11-30 18:46:29 +00:00
parent 8079ae341a
commit 9931c1d50a
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
10 changed files with 100 additions and 13 deletions

View File

@ -7,6 +7,7 @@
"require": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",

3
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f13c4ed7311b99ac07dca31d2f77ad1d",
"content-hash": "2d144524b177c9e7f699ba7366c16354",
"packages": [
{
"name": "adhocore/json-comment",
@ -2761,6 +2761,7 @@
"platform": {
"php": "^8.0",
"php-64bit": "*",
"ext-chunkutils2": "^0.3.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",

View File

@ -2279,6 +2279,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
$pk->serverSoftwareVersion = sprintf("%s %s", \pocketmine\NAME, \pocketmine\VERSION);
$pk->blockPaletteChecksum = 0; //we don't bother with this (0 skips verification) - the preimage is some dumb stringified NBT, not even actual NBT
$this->dataPacket($pk);
$this->sendDataPacket(new AvailableActorIdentifiersPacket());

View File

@ -74,6 +74,7 @@ namespace pocketmine {
}
$extensions = [
"chunkutils2" => "PocketMine ChunkUtils v2",
"curl" => "cURL",
"ctype" => "ctype",
"date" => "Date",
@ -115,6 +116,16 @@ namespace pocketmine {
}
}
$chunkutils2_version = phpversion("chunkutils2");
$wantedVersionLock = "0.3";
$wantedVersionMin = "$wantedVersionLock.0";
if($chunkutils2_version !== false && (
version_compare($chunkutils2_version, $wantedVersionMin) < 0 ||
preg_match("/^" . preg_quote($wantedVersionLock, "/") . "\.\d+(?:-dev)?$/", $chunkutils2_version) === 0 //lock in at ^0.2, optionally at a patch release
)){
$messages[] = "chunkutils2 ^$wantedVersionMin is required, while you have $chunkutils2_version.";
}
if(extension_loaded("pocketmine")){
$messages[] = "The native PocketMine extension is no longer supported.";
}

View File

@ -28,6 +28,7 @@ namespace pocketmine\level\format;
use pocketmine\block\BlockFactory;
use pocketmine\entity\Entity;
use pocketmine\level\biome\Biome;
use pocketmine\level\Level;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
@ -35,13 +36,20 @@ use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\tile\Spawnable;
use pocketmine\tile\Tile;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
use pocketmine\world\format\PalettedBlockArray;
use function array_fill;
use function array_filter;
use function array_flip;
use function array_values;
use function assert;
use function chr;
use function count;
use function file_get_contents;
use function is_array;
use function json_decode;
use function ord;
use function pack;
use function str_repeat;
@ -841,10 +849,21 @@ class Chunk{
public function networkSerialize() : string{
$result = "";
$subChunkCount = $this->getSubChunkSendCount();
//TODO: HACK! fill in fake subchunks to make up for the new negative space client-side
for($y = 0; $y < 4; ++$y){
$result .= chr(8); //subchunk version 8
$result .= chr(0); //0 layers - client will treat this as all-air
}
for($y = 0; $y < $subChunkCount; ++$y){
$result .= $this->subChunks[$y]->networkSerialize();
}
$result .= $this->biomeIds . chr(0); //border block array count
//TODO: right now we don't support 3D natively, so we just 3Dify our 2D biomes so they fill the column
$encodedBiomePalette = $this->networkSerializeBiomesAsPalette();
$result .= str_repeat($encodedBiomePalette, 25);
$result .= chr(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.
foreach($this->tiles as $tile){
@ -856,6 +875,49 @@ class Chunk{
return $result;
}
private function networkSerializeBiomesAsPalette() : string{
/** @var string[]|null $biomeIdMap */
static $biomeIdMap = null;
if($biomeIdMap === null){
$biomeIdMapRaw = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/biome_id_map.json');
if($biomeIdMapRaw === false) throw new AssumptionFailedError();
$biomeIdMapDecoded = json_decode($biomeIdMapRaw, true);
if(!is_array($biomeIdMapDecoded)) throw new AssumptionFailedError();
$biomeIdMap = array_flip($biomeIdMapDecoded);
}
$biomePalette = new PalettedBlockArray($this->getBiomeId(0, 0));
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
$biomeId = $this->getBiomeId($x, $z);
if(!isset($biomeIdMap[$biomeId])){
//make sure we aren't sending bogus biomes - the 1.18.0 client crashes if we do this
$biomeId = Biome::OCEAN;
}
for($y = 0; $y < 16; ++$y){
$biomePalette->set($x, $y, $z, $biomeId);
}
}
}
$biomePaletteBitsPerBlock = $biomePalette->getBitsPerBlock();
$encodedBiomePalette =
chr(($biomePaletteBitsPerBlock << 1) | 1) . //the last bit is non-persistence (like for blocks), though it has no effect on biomes since they always use integer IDs
$biomePalette->getWordArray();
//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.
$biomePaletteArray = $biomePalette->getPalette();
if($biomePaletteBitsPerBlock !== 0){
$encodedBiomePalette .= Binary::writeUnsignedVarInt(count($biomePaletteArray) << 1);
}
foreach($biomePaletteArray as $p){
$encodedBiomePalette .= Binary::writeUnsignedVarInt($p << 1);
}
return $encodedBiomePalette;
}
/**
* Fast-serializes the chunk for passing between threads
* TODO: tiles and entities

View File

@ -47,21 +47,18 @@ class ChunkRequestTask extends AsyncTask{
/** @var int */
protected $compressionLevel;
/** @var int */
private $subChunkCount;
public function __construct(Level $level, int $chunkX, int $chunkZ, Chunk $chunk){
$this->levelId = $level->getId();
$this->compressionLevel = $level->getServer()->networkCompressionLevel;
$this->chunk = $chunk->networkSerialize();
$this->chunk = $chunk->fastSerialize();
$this->chunkX = $chunkX;
$this->chunkZ = $chunkZ;
$this->subChunkCount = $chunk->getSubChunkSendCount();
}
public function onRun(){
$pk = LevelChunkPacket::withoutCache($this->chunkX, $this->chunkZ, $this->subChunkCount, $this->chunk);
$chunk = Chunk::fastDeserialize($this->chunk);
$pk = LevelChunkPacket::withoutCache($this->chunkX, $this->chunkZ, $chunk->getSubChunkSendCount() + 4, $chunk->networkSerialize());
$batch = new BatchPacket();
$batch->addPacket($pk);

View File

@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
public const CURRENT_PROTOCOL = 471;
public const CURRENT_PROTOCOL = 475;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v1.17.40';
public const MINECRAFT_VERSION = 'v1.18.0';
/** Version number sent to clients in ping responses. */
public const MINECRAFT_VERSION_NETWORK = '1.17.40';
public const MINECRAFT_VERSION_NETWORK = '1.18.0';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;

View File

@ -182,6 +182,8 @@ class StartGamePacket extends DataPacket{
/** @var string */
public $serverSoftwareVersion;
public int $blockPaletteChecksum;
protected function decodePayload(){
$this->entityUniqueId = $this->getEntityUniqueId();
$this->entityRuntimeId = $this->getEntityRuntimeId();
@ -265,6 +267,7 @@ class StartGamePacket extends DataPacket{
$this->multiplayerCorrelationId = $this->getString();
$this->enableNewInventorySystem = $this->getBool();
$this->serverSoftwareVersion = $this->getString();
$this->blockPaletteChecksum = $this->getLLong();
}
protected function encodePayload(){
@ -346,6 +349,7 @@ class StartGamePacket extends DataPacket{
$this->putString($this->multiplayerCorrelationId);
$this->putBool($this->enableNewInventorySystem);
$this->putString($this->serverSoftwareVersion);
$this->putLLong($this->blockPaletteChecksum);
}
public function handle(NetworkSession $session) : bool{

View File

@ -39,8 +39,9 @@ class SubChunkPacket extends DataPacket{
private string $data;
private int $requestResult;
private ?SubChunkPacketHeightMapInfo $heightMapData = null;
private ?int $usedBlobHash = null;
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData) : self{
public static function create(int $dimension, int $subChunkX, int $subChunkY, int $subChunkZ, string $data, int $requestResult, ?SubChunkPacketHeightMapInfo $heightMapData, ?int $usedBlobHash) : self{
$result = new self;
$result->dimension = $dimension;
$result->subChunkX = $subChunkX;
@ -49,6 +50,7 @@ class SubChunkPacket extends DataPacket{
$result->data = $data;
$result->requestResult = $requestResult;
$result->heightMapData = $heightMapData;
$result->usedBlobHash = $usedBlobHash;
return $result;
}
@ -66,6 +68,8 @@ class SubChunkPacket extends DataPacket{
public function getHeightMapData() : ?SubChunkPacketHeightMapInfo{ return $this->heightMapData; }
public function getUsedBlobHash() : ?int{ return $this->usedBlobHash; }
protected function decodePayload() : void{
$this->dimension = $this->getVarInt();
$this->subChunkX = $this->getVarInt();
@ -81,6 +85,7 @@ class SubChunkPacket extends DataPacket{
SubChunkPacketHeightMapType::ALL_TOO_LOW => SubChunkPacketHeightMapInfo::allTooLow(),
default => throw new \UnexpectedValueException("Unknown heightmap data type $heightMapDataType")
};
$this->usedBlobHash = $this->getBool() ? $this->getLLong() : null;
}
protected function encodePayload() : void{
@ -101,6 +106,11 @@ class SubChunkPacket extends DataPacket{
$this->putByte(SubChunkPacketHeightMapType::DATA);
$heightMapData->write($this);
}
$usedBlobHash = $this->usedBlobHash;
$this->putBool($usedBlobHash !== null);
if($usedBlobHash !== null){
$this->putLLong($usedBlobHash);
}
}
public function handle(NetworkSession $handler) : bool{

@ -1 +1 @@
Subproject commit f29b7be8fa3046d2ee4c6421485b97b3f5b07774
Subproject commit 482c679aa5ed0b81c088c2b1ff0b8110a94c8a6c