Merge branch 'next-minor' into next-major

This commit is contained in:
Dylan K. Taylor 2022-09-21 15:07:11 +01:00
commit b39eaaf91f
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
16 changed files with 208 additions and 69 deletions

14
changelogs/4.9.md Normal file
View File

@ -0,0 +1,14 @@
**For Minecraft: Bedrock Edition 1.19.30**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 4.9.0
Released 20th September 2022.
## General
- Added support for Minecraft: Bedrock Edition 1.19.30.
- Removed support for older versions.

View File

@ -37,7 +37,7 @@
"pocketmine/bedrock-block-upgrade-schema": "dev-master@dev",
"pocketmine/bedrock-data": "dev-modern-world-support@dev",
"pocketmine/bedrock-item-upgrade-schema": "dev-master",
"pocketmine/bedrock-protocol": "~12.2.0+bedrock-1.19.20",
"pocketmine/bedrock-protocol": "~13.0.0+bedrock-1.19.30",
"pocketmine/binaryutils": "^0.2.1",
"pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0",

34
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": "ab34faed0ca4120106748663b00e9604",
"content-hash": "284e89a86c5b7fdb4d97ec5516666b36",
"packages": [
{
"name": "adhocore/json-comment",
@ -276,12 +276,12 @@
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockData.git",
"reference": "6f480c67ed03abb1b2641802879f5c1aeda11cc2"
"reference": "1ca9241ce66358cdfb2dbde65d609eb9c185c515"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6f480c67ed03abb1b2641802879f5c1aeda11cc2",
"reference": "6f480c67ed03abb1b2641802879f5c1aeda11cc2",
"url": "https://api.github.com/repos/pmmp/BedrockData/zipball/1ca9241ce66358cdfb2dbde65d609eb9c185c515",
"reference": "1ca9241ce66358cdfb2dbde65d609eb9c185c515",
"shasum": ""
},
"type": "library",
@ -294,7 +294,7 @@
"issues": "https://github.com/pmmp/BedrockData/issues",
"source": "https://github.com/pmmp/BedrockData/tree/modern-world-support"
},
"time": "2022-08-09T17:47:56+00:00"
"time": "2022-09-21T13:59:15+00:00"
},
{
"name": "pocketmine/bedrock-item-upgrade-schema",
@ -325,16 +325,16 @@
},
{
"name": "pocketmine/bedrock-protocol",
"version": "12.2.0+bedrock-1.19.21",
"version": "13.0.0+bedrock-1.19.30",
"source": {
"type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "d1b3e83f77e2c6628b64793485260cddc55d92e3"
"reference": "94de2221676ca717587e1ff4e45445c24ada1749"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/d1b3e83f77e2c6628b64793485260cddc55d92e3",
"reference": "d1b3e83f77e2c6628b64793485260cddc55d92e3",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/94de2221676ca717587e1ff4e45445c24ada1749",
"reference": "94de2221676ca717587e1ff4e45445c24ada1749",
"shasum": ""
},
"require": {
@ -366,9 +366,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/12.2.0+bedrock-1.19.21"
"source": "https://github.com/pmmp/BedrockProtocol/tree/bedrock-1.19.30"
},
"time": "2022-08-24T18:47:37+00:00"
"time": "2022-09-20T18:35:00+00:00"
},
{
"name": "pocketmine/binaryutils",
@ -980,16 +980,16 @@
},
{
"name": "ramsey/uuid",
"version": "4.5.0",
"version": "4.5.1",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "ef842484ba57f163c6d465ab744bfecb872a11d4"
"reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/ef842484ba57f163c6d465ab744bfecb872a11d4",
"reference": "ef842484ba57f163c6d465ab744bfecb872a11d4",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/a161a26d917604dc6d3aa25100fddf2556e9f35d",
"reference": "a161a26d917604dc6d3aa25100fddf2556e9f35d",
"shasum": ""
},
"require": {
@ -1058,7 +1058,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.5.0"
"source": "https://github.com/ramsey/uuid/tree/4.5.1"
},
"funding": [
{
@ -1070,7 +1070,7 @@
"type": "tidelift"
}
],
"time": "2022-09-15T01:44:53+00:00"
"time": "2022-09-16T03:22:46+00:00"
},
{
"name": "symfony/polyfill-php81",

View File

@ -177,10 +177,10 @@ namespace pocketmine {
--------------------------------------- ! WARNING ! ---------------------------------------
You're using PHP 8.0 with JIT enabled. This provides significant performance improvements.
You're using PHP with JIT enabled. This provides significant performance improvements.
HOWEVER, it is EXPERIMENTAL, and has already been seen to cause weird and unexpected bugs.
Proceed with caution.
If you want to report any bugs, make sure to mention that you are using PHP 8.0 with JIT.
If you want to report any bugs, make sure to mention that you have enabled PHP JIT.
To turn off JIT, change `opcache.jit` to `0` in your php.ini file.
-------------------------------------------------------------------------------------------

View File

@ -26,13 +26,14 @@ namespace pocketmine\crafting\json;
final class RecipeIngredientData{
public const WILDCARD_META_VALUE = 32767;
/** @required */
public string $name;
public int $meta;
public string $block_states;
public int $count;
public function __construct(string $name){
$this->name = $name;
}
public string $tag;
public string $molang_expression;
public int $molang_version;
public int $count;
}

View File

@ -227,7 +227,7 @@ final class EntityFactory{
*/
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
try{
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$saveId = $nbt->getTag("identifier") ?? $nbt->getTag("id");
$func = null;
if($saveId instanceof StringTag){
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
@ -248,7 +248,7 @@ final class EntityFactory{
public function injectSaveId(string $class, CompoundTag $saveData) : void{
if(isset($this->saveNames[$class])){
$saveData->setTag("id", new StringTag($this->saveNames[$class]));
$saveData->setTag("identifier", new StringTag($this->saveNames[$class]));
}else{
throw new \InvalidArgumentException("Entity $class is not registered");
}

View File

@ -33,7 +33,7 @@ class InstantDamageEffect extends InstantEffect{
public function applyEffect(Living $entity, EffectInstance $instance, float $potency = 1.0, ?Entity $source = null) : void{
//TODO: add particles (witch spell)
$damage = (4 << $instance->getAmplifier()) * $potency;
$damage = (6 << $instance->getAmplifier()) * $potency;
if($source !== null){
$sourceOwner = $source->getOwningEntity();
if($sourceOwner !== null){

View File

@ -30,7 +30,7 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
class RegenerationEffect extends Effect{
public function canTick(EffectInstance $instance) : bool{
if(($interval = (40 >> $instance->getAmplifier())) > 0){
if(($interval = (50 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;

View File

@ -179,6 +179,10 @@ class ItemEntity extends Entity{
return true;
}
public function canSaveWithChunk() : bool{
return !$this->item->isNull() && parent::canSaveWithChunk();
}
public function saveNBT() : CompoundTag{
$nbt = parent::saveNBT();
$nbt->setTag("Item", $this->item->nbtSerialize());

View File

@ -56,6 +56,7 @@ use pocketmine\network\mcpe\handler\LoginPacketHandler;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\handler\PreSpawnPacketHandler;
use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler;
use pocketmine\network\mcpe\handler\SessionStartPacketHandler;
use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
@ -164,6 +165,7 @@ class NetworkSession{
*/
private \SplQueue $compressedQueue;
private bool $forceAsyncCompression = true;
private bool $enableCompression = false; //disabled until handshake completed
private PacketSerializerContext $packetSerializerContext;
@ -196,17 +198,10 @@ class NetworkSession{
$this->connectTime = time();
$this->setHandler(new LoginPacketHandler(
$this->setHandler(new SessionStartPacketHandler(
$this->server,
$this,
function(PlayerInfo $info) : void{
$this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix());
},
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
}
fn() => $this->onSessionStartSuccess()
));
$this->manager->add($this);
@ -221,6 +216,24 @@ class NetworkSession{
return $this->logger;
}
private function onSessionStartSuccess() : void{
$this->logger->debug("Session start handshake completed, awaiting login packet");
$this->flushSendBuffer(true);
$this->enableCompression = true;
$this->setHandler(new LoginPacketHandler(
$this->server,
$this,
function(PlayerInfo $info) : void{
$this->info = $info;
$this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET);
$this->logger->setPrefix($this->getLogPrefix());
},
function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{
$this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey);
}
));
}
protected function createPlayer() : void{
$this->server->createPlayer($this, $this->info, $this->authenticated, $this->cachedOfflinePlayerData)->onCompletion(
\Closure::fromCallable([$this, 'onPlayerCreated']),
@ -335,18 +348,22 @@ class NetworkSession{
}
}
Timings::$playerNetworkReceiveDecompress->startTiming();
try{
$stream = new PacketBatch($this->compressor->decompress($payload));
}catch(DecompressionException $e){
$this->logger->debug("Failed to decompress packet: " . base64_encode($payload));
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
}finally{
Timings::$playerNetworkReceiveDecompress->stopTiming();
if($this->enableCompression){
Timings::$playerNetworkReceiveDecompress->startTiming();
try{
$decompressed = $this->compressor->decompress($payload);
}catch(DecompressionException $e){
$this->logger->debug("Failed to decompress packet: " . base64_encode($payload));
throw PacketHandlingException::wrap($e, "Compressed packet batch decode error");
}finally{
Timings::$playerNetworkReceiveDecompress->stopTiming();
}
}else{
$decompressed = $payload;
}
try{
foreach($stream->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
foreach((new PacketBatch($decompressed))->getPackets($this->packetPool, $this->packetSerializerContext, 500) as [$packet, $buffer]){
if($packet === null){
$this->logger->debug("Unknown packet: " . base64_encode($buffer));
throw new PacketHandlingException("Unknown packet received");
@ -451,7 +468,14 @@ class NetworkSession{
}elseif($this->forceAsyncCompression){
$syncMode = false;
}
$promise = $this->server->prepareBatch(PacketBatch::fromPackets($this->packetSerializerContext, ...$this->sendBuffer), $this->compressor, $syncMode);
$batch = PacketBatch::fromPackets($this->packetSerializerContext, ...$this->sendBuffer);
if($this->enableCompression){
$promise = $this->server->prepareBatch($batch, $this->compressor, $syncMode);
}else{
$promise = new CompressBatchPromise();
$promise->resolve($batch->getBuffer());
}
$this->sendBuffer = [];
$this->queueCompressedNoBufferFlush($promise, $immediate);
}

View File

@ -35,6 +35,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\recipe\CraftingRecipeBlockName;
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe as ProtocolFurnaceRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipeBlockName;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\PotionContainerChangeRecipe as ProtocolPotionContainerChangeRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\PotionTypeRecipe as ProtocolPotionTypeRecipe;
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient;
@ -138,7 +139,10 @@ final class CraftingDataCache{
default => throw new AssumptionFailedError("Unreachable"),
};
foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){
$input = $converter->coreRecipeIngredientToNet($recipe->getInput());
$input = $converter->coreRecipeIngredientToNet($recipe->getInput())->getDescriptor();
if(!$input instanceof IntIdMetaItemDescriptor){
throw new AssumptionFailedError();
}
$recipesWithTypeIds[] = new ProtocolFurnaceRecipe(
CraftingDataPacket::ENTRY_FURNACE_DATA,
$input->getId(),
@ -151,8 +155,11 @@ final class CraftingDataCache{
$potionTypeRecipes = [];
foreach($manager->getPotionTypeRecipes() as $recipe){
$input = $converter->coreRecipeIngredientToNet($recipe->getInput());
$ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient());
$input = $converter->coreRecipeIngredientToNet($recipe->getInput())->getDescriptor();
$ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient())->getDescriptor();
if(!$input instanceof IntIdMetaItemDescriptor || !$ingredient instanceof IntIdMetaItemDescriptor){
throw new AssumptionFailedError();
}
$output = $converter->coreItemStackToNet($recipe->getOutput());
$potionTypeRecipes[] = new ProtocolPotionTypeRecipe(
$input->getId(),
@ -168,7 +175,10 @@ final class CraftingDataCache{
$itemTypeDictionary = GlobalItemTypeDictionary::getInstance()->getDictionary();
foreach($manager->getPotionContainerChangeRecipes() as $recipe){
$input = $itemTypeDictionary->fromStringId($recipe->getInputItemId());
$ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient());
$ingredient = $converter->coreRecipeIngredientToNet($recipe->getIngredient())->getDescriptor();
if(!$ingredient instanceof IntIdMetaItemDescriptor){
throw new AssumptionFailedError();
}
$output = $itemTypeDictionary->fromStringId($recipe->getOutputItemId());
$potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe(
$input,

View File

@ -43,7 +43,9 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor;
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient;
use pocketmine\network\mcpe\protocol\types\recipe\StringIdMetaItemDescriptor;
use pocketmine\player\GameMode;
use pocketmine\player\Player;
use pocketmine\utils\AssumptionFailedError;
@ -111,7 +113,7 @@ class TypeConverter{
public function coreRecipeIngredientToNet(?RecipeIngredient $ingredient) : ProtocolRecipeIngredient{
if($ingredient === null){
return new ProtocolRecipeIngredient(0, 0, 0);
return new ProtocolRecipeIngredient(null, 0);
}
if($ingredient instanceof MetaWildcardRecipeIngredient){
$id = GlobalItemTypeDictionary::getInstance()->getDictionary()->fromStringId($ingredient->getItemId());
@ -128,29 +130,42 @@ class TypeConverter{
}else{
throw new \LogicException("Unsupported recipe ingredient type " . get_class($ingredient) . ", only " . ExactRecipeIngredient::class . " and " . MetaWildcardRecipeIngredient::class . " are supported");
}
return new ProtocolRecipeIngredient($id, $meta, 1);
return new ProtocolRecipeIngredient(new IntIdMetaItemDescriptor($id, $meta), 1);
}
public function netRecipeIngredientToCore(ProtocolRecipeIngredient $ingredient) : ?RecipeIngredient{
if($ingredient->getId() === 0){
$descriptor = $ingredient->getDescriptor();
if($descriptor === null){
return null;
}
$itemId = GlobalItemTypeDictionary::getInstance()->getDictionary()->fromIntId($ingredient->getId());
if($ingredient->getMeta() === self::RECIPE_INPUT_WILDCARD_META){
return new MetaWildcardRecipeIngredient($itemId);
if($descriptor instanceof IntIdMetaItemDescriptor){
$stringId = GlobalItemTypeDictionary::getInstance()->getDictionary()->fromIntId($descriptor->getId());
$meta = $descriptor->getMeta();
}elseif($descriptor instanceof StringIdMetaItemDescriptor){
$stringId = $descriptor->getId();
$meta = $descriptor->getMeta();
}else{
throw new \LogicException("Unsupported conversion of recipe ingredient to core item stack");
}
if($meta === self::RECIPE_INPUT_WILDCARD_META){
return new MetaWildcardRecipeIngredient($stringId);
}
$meta = $ingredient->getMeta();
$blockRuntimeId = null;
if(($blockId = BlockItemIdMap::getInstance()->lookupBlockId($itemId)) !== null){
if(($blockId = BlockItemIdMap::getInstance()->lookupBlockId($stringId)) !== null){
$blockRuntimeId = RuntimeBlockMapping::getInstance()->getBlockStateDictionary()->lookupStateIdFromIdMeta($blockId, $meta);
if($blockRuntimeId !== null){
$meta = 0;
}
}
$result = ItemTranslator::getInstance()->fromNetworkId($ingredient->getId(), $meta, $blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID);
$result = ItemTranslator::getInstance()->fromNetworkId(
GlobalItemTypeDictionary::getInstance()->getDictionary()->fromStringId($stringId),
$meta,
$blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID
);
return new ExactRecipeIngredient($result);
}

View File

@ -51,7 +51,6 @@ use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
@ -660,10 +659,6 @@ class InGamePacketHandler extends PacketHandler{
return true; //this is a broken useless packet, so we don't use it
}
public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{
return true; //no longer used, but the client still sends it for flight changes
}
public function handleBlockActorData(BlockActorDataPacket $packet) : bool{
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
if($pos->distanceSquared($this->player->getLocation()) > 10000){

View File

@ -0,0 +1,76 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\handler;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\RequestNetworkSettingsPacket;
use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm;
use pocketmine\Server;
final class SessionStartPacketHandler extends PacketHandler{
/**
* @phpstan-param \Closure() : void $onSuccess
*/
public function __construct(
private Server $server,
private NetworkSession $session,
private \Closure $onSuccess
){}
public function handleRequestNetworkSettings(RequestNetworkSettingsPacket $packet) : bool{
$protocolVersion = $packet->getProtocolVersion();
if(!$this->isCompatibleProtocol($protocolVersion)){
$this->session->sendDataPacket(PlayStatusPacket::create($protocolVersion < ProtocolInfo::CURRENT_PROTOCOL ? PlayStatusPacket::LOGIN_FAILED_CLIENT : PlayStatusPacket::LOGIN_FAILED_SERVER), true);
//This pocketmine disconnect message will only be seen by the console (PlayStatusPacket causes the messages to be shown for the client)
$this->session->disconnect(
$this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_disconnect_incompatibleProtocol((string) $protocolVersion)),
false
);
return true;
}
//TODO: we're filling in the defaults to get pre-1.19.30 behaviour back for now, but we should explore the new options in the future
$this->session->sendDataPacket(NetworkSettingsPacket::create(
NetworkSettingsPacket::COMPRESS_EVERYTHING,
CompressionAlgorithm::ZLIB,
false,
0,
0
));
($this->onSuccess)();
return true;
}
protected function isCompatibleProtocol(int $protocolVersion) : bool{
return $protocolVersion === ProtocolInfo::CURRENT_PROTOCOL;
}
}

View File

@ -58,7 +58,7 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
* Sometimes this gets changed when the MCPE-layer protocol gets broken to the point where old and new can't
* communicate. It's important that we check this to avoid catastrophes.
*/
private const MCPE_RAKNET_PROTOCOL_VERSION = 10;
private const MCPE_RAKNET_PROTOCOL_VERSION = 11;
private const MCPE_RAKNET_PACKET_ID = "\xfe";

View File

@ -2502,7 +2502,7 @@ class World implements ChunkManager{
continue;
}
if($entity === null){
$saveIdTag = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$saveIdTag = $nbt->getTag("identifier") ?? $nbt->getTag("id");
$saveId = "<unknown>";
if($saveIdTag instanceof StringTag){
$saveId = $saveIdTag->getValue();