mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-19 15:35:52 +00:00
Protocol changes for 1.18.0
This commit is contained in:
parent
8079ae341a
commit
9931c1d50a
@ -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
3
composer.lock
generated
@ -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": "*",
|
||||
|
@ -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());
|
||||
|
@ -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.";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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{
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user