diff --git a/changelogs/4.6.md b/changelogs/4.6.md new file mode 100644 index 000000000..473edca1f --- /dev/null +++ b/changelogs/4.6.md @@ -0,0 +1,14 @@ +**For Minecraft: Bedrock Edition 1.19.10** + +### 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.6.0 +Released 13th July 2022. + +## General +- Added support for Minecraft: Bedrock Edition 1.19.10. +- Removed support for older versions. diff --git a/composer.json b/composer.json index 0b1621c13..3e925b4c7 100644 --- a/composer.json +++ b/composer.json @@ -34,8 +34,8 @@ "adhocore/json-comment": "^1.1", "fgrosse/phpasn1": "^2.3", "netresearch/jsonmapper": "^4.0", - "pocketmine/bedrock-data": "~1.8.0+bedrock-1.19.0", - "pocketmine/bedrock-protocol": "~10.0.0+bedrock-1.19.0", + "pocketmine/bedrock-data": "~1.9.0+bedrock-1.19.10", + "pocketmine/bedrock-protocol": "~11.0.0+bedrock-1.19.10", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/classloader": "^0.2.0", diff --git a/composer.lock b/composer.lock index 5c293d067..ad83e7609 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "c9c0412cf5610ee7fa623b679b5471fb", + "content-hash": "e03c7f446cee1fdb97ee79817c78470a", "packages": [ { "name": "adhocore/json-comment", @@ -249,16 +249,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "1.8.0+bedrock-1.19.0", + "version": "1.9.0+bedrock-1.19.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "e654d0a6e5dfd55f8546097ea629c528a70c30ee" + "reference": "ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e654d0a6e5dfd55f8546097ea629c528a70c30ee", - "reference": "e654d0a6e5dfd55f8546097ea629c528a70c30ee", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1", + "reference": "ecd798a3e7ead50b7da73141bbb0c4ba14dd76a1", "shasum": "" }, "type": "library", @@ -269,22 +269,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.0" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.19.10" }, - "time": "2022-06-07T16:20:20+00:00" + "time": "2022-07-12T19:33:21+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "10.0.1+bedrock-1.19.0", + "version": "11.0.0+bedrock-1.19.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b" + "reference": "705f928bd010ba093d8781d20006e4cd5f79f335" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/331fb0eb45c26daadf8cf01a3b6f20e909d7684b", - "reference": "331fb0eb45c26daadf8cf01a3b6f20e909d7684b", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/705f928bd010ba093d8781d20006e4cd5f79f335", + "reference": "705f928bd010ba093d8781d20006e4cd5f79f335", "shasum": "" }, "require": { @@ -298,7 +298,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.7.11", + "phpstan/phpstan": "1.8.0", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5" @@ -316,9 +316,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/10.0.1+bedrock-1.19.0" + "source": "https://github.com/pmmp/BedrockProtocol/tree/11.0.0+bedrock-1.19.10" }, - "time": "2022-06-08T01:11:15+00:00" + "time": "2022-07-12T23:47:47+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index ff5c8e0f7..465db22e1 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,7 +31,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.6.0"; + public const BASE_VERSION = "4.6.1"; public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "beta"; diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 68864b6bf..84901197c 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1461,6 +1461,7 @@ abstract class Entity{ $this->location->pitch, $this->location->yaw, $this->location->yaw, //TODO: head yaw + $this->location->yaw, //TODO: body yaw (wtf mojang?) array_map(function(Attribute $attr) : NetworkAttribute{ return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue()); }, $this->attributeMap->getAll()), diff --git a/src/entity/Human.php b/src/entity/Human.php index 6dd0264ad..683e0c7d6 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -48,7 +48,6 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\convert\SkinAdapterSingleton; use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\AddPlayerPacket; -use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\DeviceOS; @@ -58,6 +57,7 @@ use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty; use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\player\Player; use pocketmine\utils\Limits; use pocketmine\world\sound\TotemUseSound; @@ -471,7 +471,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $player->getNetworkSession()->sendDataPacket(AddPlayerPacket::create( $this->getUniqueId(), $this->getName(), - $this->getId(), //TODO: actor unique ID $this->getId(), "", $this->location->asVector3(), @@ -482,7 +481,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())), GameMode::SURVIVAL, $this->getAllNetworkData(), - AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO + UpdateAbilitiesPacket::create(0, 0, $this->getId() /* TODO: this should be unique ID */, []), [], //TODO: entity links "", //device ID (we intentionally don't send this - secvuln) DeviceOS::UNKNOWN //we intentionally don't send this (secvuln) diff --git a/src/item/Item.php b/src/item/Item.php index 2750b5579..2a4312539 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -100,8 +100,9 @@ class Item implements \JsonSerializable{ * Constructs a new Item type. This constructor should ONLY be used when constructing a new item TYPE to register * into the index. * - * NOTE: This should NOT BE USED for creating items to set into an inventory. Use {@link ItemFactory#get} for that + * NOTE: This should NOT BE USED for creating items to set into an inventory. Use VanillaItems for that * purpose. + * @see VanillaItems */ public function __construct(ItemIdentifier $identifier, string $name = "Unknown"){ $this->identifier = $identifier; diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index a3ba8de8e..b75d38147 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -57,7 +57,6 @@ use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\handler\PreSpawnPacketHandler; use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler; use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler; -use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; @@ -93,6 +92,7 @@ use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\network\mcpe\protocol\types\command\CommandData; use pocketmine\network\mcpe\protocol\types\command\CommandEnum; use pocketmine\network\mcpe\protocol\types\command\CommandParameter; +use pocketmine\network\mcpe\protocol\types\command\CommandPermissions; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute; use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; @@ -100,9 +100,13 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; +use pocketmine\network\mcpe\protocol\types\UpdateAbilitiesPacketLayer; +use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; +use pocketmine\network\mcpe\protocol\UpdateAdventureSettingsPacket; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use pocketmine\network\NetworkSessionManager; use pocketmine\network\PacketHandlingException; +use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissions; use pocketmine\player\GameMode; use pocketmine\player\Player; @@ -250,8 +254,8 @@ class NetworkSession{ $permissionHooks = $this->player->getPermissionRecalculationCallbacks(); $permissionHooks->add($permHook = function() : void{ - $this->logger->debug("Syncing available commands and adventure settings due to permission recalculation"); - $this->syncAdventureSettings($this->player); + $this->logger->debug("Syncing available commands and abilities/permissions due to permission recalculation"); + $this->syncAbilities($this->player); $this->syncAvailableCommands(); }); $this->disposeHooks->add(static function() use ($permissionHooks, $permHook) : void{ @@ -716,7 +720,7 @@ class NetworkSession{ $this->syncAttributes($this->player, $this->player->getAttributeMap()->getAll()); $this->player->sendData(null); - $this->syncAdventureSettings($this->player); + $this->syncAbilities($this->player); $this->invManager->syncAll(); $this->setHandler(new InGamePacketHandler($this->player, $this, $this->invManager)); } @@ -766,37 +770,60 @@ class NetworkSession{ public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{ $this->sendDataPacket(SetPlayerGameTypePacket::create(TypeConverter::getInstance()->coreGameModeToProtocol($mode))); if($this->player !== null){ - $this->syncAdventureSettings($this->player); + $this->syncAbilities($this->player); + $this->syncAdventureSettings(); //TODO: we might be able to do this with the abilities packet alone } if(!$isRollback && $this->invManager !== null){ $this->invManager->syncCreative(); } } - /** - * TODO: make this less specialized - */ - public function syncAdventureSettings(Player $for) : void{ + public function syncAbilities(Player $for) : void{ $isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR); - $pk = AdventureSettingsPacket::create( - 0, - $isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL, - 0, + + //ALL of these need to be set for the base layer, otherwise the client will cry + $boolAbilities = [ + UpdateAbilitiesPacketLayer::ABILITY_ALLOW_FLIGHT => $for->getAllowFlight(), + UpdateAbilitiesPacketLayer::ABILITY_FLYING => $for->isFlying(), + UpdateAbilitiesPacketLayer::ABILITY_NO_CLIP => !$for->hasBlockCollision(), + UpdateAbilitiesPacketLayer::ABILITY_OPERATOR => $isOp, + UpdateAbilitiesPacketLayer::ABILITY_TELEPORT => $for->hasPermission(DefaultPermissionNames::COMMAND_TELEPORT), + UpdateAbilitiesPacketLayer::ABILITY_INVULNERABLE => $for->isCreative(), + UpdateAbilitiesPacketLayer::ABILITY_MUTED => false, + UpdateAbilitiesPacketLayer::ABILITY_WORLD_BUILDER => false, + UpdateAbilitiesPacketLayer::ABILITY_INFINITE_RESOURCES => !$for->hasFiniteResources(), + UpdateAbilitiesPacketLayer::ABILITY_LIGHTNING => false, + UpdateAbilitiesPacketLayer::ABILITY_BUILD => !$for->isSpectator(), + UpdateAbilitiesPacketLayer::ABILITY_MINE => !$for->isSpectator(), + UpdateAbilitiesPacketLayer::ABILITY_DOORS_AND_SWITCHES => !$for->isSpectator(), + UpdateAbilitiesPacketLayer::ABILITY_OPEN_CONTAINERS => !$for->isSpectator(), + UpdateAbilitiesPacketLayer::ABILITY_ATTACK_PLAYERS => !$for->isSpectator(), + UpdateAbilitiesPacketLayer::ABILITY_ATTACK_MOBS => !$for->isSpectator(), + ]; + + $this->sendDataPacket(UpdateAbilitiesPacket::create( + $isOp ? CommandPermissions::OPERATOR : CommandPermissions::NORMAL, $isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER, - 0, - $for->getId() - ); + $for->getId(), + [ + //TODO: dynamic flying speed! FINALLY!!!!!!!!!!!!!!!!! + new UpdateAbilitiesPacketLayer(UpdateAbilitiesPacketLayer::LAYER_BASE, $boolAbilities, 0.05, 0.1), + ] + )); + } - $pk->setFlag(AdventureSettingsPacket::WORLD_IMMUTABLE, $for->isSpectator()); - $pk->setFlag(AdventureSettingsPacket::NO_PVP, $for->isSpectator()); - $pk->setFlag(AdventureSettingsPacket::AUTO_JUMP, $for->hasAutoJump()); - $pk->setFlag(AdventureSettingsPacket::ALLOW_FLIGHT, $for->getAllowFlight()); - $pk->setFlag(AdventureSettingsPacket::NO_CLIP, !$for->hasBlockCollision()); - $pk->setFlag(AdventureSettingsPacket::FLYING, $for->isFlying()); - - //TODO: permission flags - - $this->sendDataPacket($pk); + public function syncAdventureSettings() : void{ + if($this->player === null){ + throw new \LogicException("Cannot sync adventure settings for a player that is not yet created"); + } + //everything except auto jump is handled via UpdateAbilitiesPacket + $this->sendDataPacket(UpdateAdventureSettingsPacket::create( + noAttackingMobs: false, + noAttackingPlayers: false, + worldImmutable: false, + showNameTags: true, + autoJump: $this->player->hasAutoJump() + )); } /** diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index bc21ec23e..83c2d03cf 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -979,7 +979,7 @@ class InGamePacketHandler extends PacketHandler{ } if($isFlying !== $this->player->isFlying()){ if(!$this->player->toggleFlight($isFlying)){ - $this->session->syncAdventureSettings($this->player); + $this->session->syncAbilities($this->player); } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 52a69d5b5..5237cdb4d 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -107,7 +107,8 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs()); $this->session->syncAttributes($this->player, $this->player->getAttributeMap()->getAll()); $this->session->syncAvailableCommands(); - $this->session->syncAdventureSettings($this->player); + $this->session->syncAbilities($this->player); + $this->session->syncAdventureSettings(); foreach($this->player->getEffects()->all() as $effect){ $this->session->onEntityEffectAdded($this->player, $effect, false); } diff --git a/src/player/Player.php b/src/player/Player.php index 85190ad17..33be6c4e8 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -421,7 +421,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function setAllowFlight(bool $value) : void{ if($this->allowFlight !== $value){ $this->allowFlight = $value; - $this->getNetworkSession()->syncAdventureSettings($this); + $this->getNetworkSession()->syncAbilities($this); } } @@ -432,7 +432,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function setHasBlockCollision(bool $value) : void{ if($this->blockCollision !== $value){ $this->blockCollision = $value; - $this->getNetworkSession()->syncAdventureSettings($this); + $this->getNetworkSession()->syncAbilities($this); } } @@ -444,7 +444,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($this->flying !== $value){ $this->flying = $value; $this->resetFallDistance(); - $this->getNetworkSession()->syncAdventureSettings($this); + $this->getNetworkSession()->syncAbilities($this); } } @@ -455,7 +455,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function setAutoJump(bool $value) : void{ if($this->autoJump !== $value){ $this->autoJump = $value; - $this->getNetworkSession()->syncAdventureSettings($this); + $this->getNetworkSession()->syncAdventureSettings(); } } diff --git a/src/world/particle/FloatingTextParticle.php b/src/world/particle/FloatingTextParticle.php index 97baccbf4..be1fdd5dc 100644 --- a/src/world/particle/FloatingTextParticle.php +++ b/src/world/particle/FloatingTextParticle.php @@ -28,7 +28,6 @@ use pocketmine\entity\Skin; use pocketmine\math\Vector3; use pocketmine\network\mcpe\convert\SkinAdapterSingleton; use pocketmine\network\mcpe\protocol\AddPlayerPacket; -use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\types\DeviceOS; @@ -40,6 +39,7 @@ use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use Ramsey\Uuid\Uuid; use function str_repeat; @@ -110,7 +110,6 @@ class FloatingTextParticle implements Particle{ $uuid, $name, $this->entityId, //TODO: actor unique ID - $this->entityId, "", $pos, //TODO: check offset null, @@ -120,7 +119,7 @@ class FloatingTextParticle implements Particle{ ItemStackWrapper::legacy(ItemStack::null()), GameMode::SURVIVAL, $actorMetadata, - AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->entityId), + UpdateAbilitiesPacket::create(0, 0, $this->entityId, []), [], "", DeviceOS::UNKNOWN diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 3ddd1d228..746c9a792 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -686,7 +686,7 @@ parameters: path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAdventureSettings\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAbilities\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" count: 2 path: ../../../src/network/mcpe/NetworkSession.php