diff --git a/changelogs/5.29.md b/changelogs/5.29.md new file mode 100644 index 0000000000..cb6e50da32 --- /dev/null +++ b/changelogs/5.29.md @@ -0,0 +1,25 @@ +# 5.29.0 +Released 18th June 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.90. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.90. +- Removed support for earlier versions. + +## Fixes +- Fixed thread crashes sometimes not reporting proper cause information in crashdumps. +- Fixed crash when a plugin replaced a player's held tool with a different tool with a damage exceeding the old tool's max damage during an action. +- Fixed performance issue of `PlayerAuthInputPacket` input flags handling (broken change detection). +- Fixed `BaseInventory->addItem()` triggering updates on empty slots when no items were added. +- Fixed slow check in `SubChunk` block layer garbage collection. + +## Internals +- `LoginPacketHandler->processLogin()` signature has changed. This will break any plugins overriding `LoginPacketHandler`. As noted above, this is _not_ covered by the API version guarantee. +- Automated branch sync for `minor-next` and `major-next` is now triggered by `repository_dispatch` from a cron job in this repository instead of `RestrictedActions`. The `RestrictedActions` cron job was getting automatically disabled by GitHub due to repo inactivity. diff --git a/composer.json b/composer.json index 1935bc290a..1decaac394 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", - "pocketmine/bedrock-data": "~5.0.0+bedrock-1.21.80", + "pocketmine/bedrock-data": "~5.1.0+bedrock-1.21.90", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~38.1.0+bedrock-1.21.80", + "pocketmine/bedrock-protocol": "~39.0.0+bedrock-1.21.90", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index cb60c7acea..b6db8fe7d9 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": "69921783f476a0704fa1f8924b901a89", + "content-hash": "bde74cbb65c043a2acf6f62b5b328e67", "packages": [ { "name": "adhocore/json-comment", @@ -204,16 +204,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "5.0.0+bedrock-1.21.80", + "version": "5.1.0+bedrock-1.21.90", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080" + "reference": "89ed34957aeccc63e517aa849af593adae958e98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e38d5ea19f794ec5216e5f96742237e8c4e7f080", - "reference": "e38d5ea19f794ec5216e5f96742237e8c4e7f080", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/89ed34957aeccc63e517aa849af593adae958e98", + "reference": "89ed34957aeccc63e517aa849af593adae958e98", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "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.21.80" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.90" }, - "time": "2025-05-09T14:15:18+00:00" + "time": "2025-06-17T23:44:21+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "38.1.0+bedrock-1.21.80", + "version": "39.0.0+bedrock-1.21.90", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "a1fa215563517050045309bb779a67f75843b867" + "reference": "2b088183d12fc003523400867ee398e3893899ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a1fa215563517050045309bb779a67f75843b867", - "reference": "a1fa215563517050045309bb779a67f75843b867", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2b088183d12fc003523400867ee398e3893899ed", + "reference": "2b088183d12fc003523400867ee398e3893899ed", "shasum": "" }, "require": { @@ -296,9 +296,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/38.1.0+bedrock-1.21.80" + "source": "https://github.com/pmmp/BedrockProtocol/tree/39.0.0+bedrock-1.21.90" }, - "time": "2025-05-28T22:19:59+00:00" + "time": "2025-06-17T23:46:38+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 6150246563..7033c27077 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.28.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.29.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 5f86cde966..d2ab0996bb 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -380,6 +380,7 @@ final class ItemTypeNames{ public const MUSIC_DISC_RELIC = "minecraft:music_disc_relic"; public const MUSIC_DISC_STAL = "minecraft:music_disc_stal"; public const MUSIC_DISC_STRAD = "minecraft:music_disc_strad"; + public const MUSIC_DISC_TEARS = "minecraft:music_disc_tears"; public const MUSIC_DISC_WAIT = "minecraft:music_disc_wait"; public const MUSIC_DISC_WARD = "minecraft:music_disc_ward"; public const MUTTON = "minecraft:mutton"; diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index c15753dad3..5c467f2d47 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -33,6 +33,8 @@ use pocketmine\network\mcpe\JwtUtils; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\LoginPacket; use pocketmine\network\mcpe\protocol\types\login\AuthenticationData; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationInfo; +use pocketmine\network\mcpe\protocol\types\login\AuthenticationType; use pocketmine\network\mcpe\protocol\types\login\ClientData; use pocketmine\network\mcpe\protocol\types\login\ClientDataToSkinDataHelper; use pocketmine\network\mcpe\protocol\types\login\JwtChain; @@ -42,7 +44,11 @@ use pocketmine\player\PlayerInfo; use pocketmine\player\XboxLivePlayerInfo; use pocketmine\Server; use Ramsey\Uuid\Uuid; +use function gettype; use function is_array; +use function is_object; +use function json_decode; +use const JSON_THROW_ON_ERROR; /** * Handles the initial login phase of the session. This handler is used as the initial state. @@ -60,7 +66,9 @@ class LoginPacketHandler extends PacketHandler{ ){} public function handleLogin(LoginPacket $packet) : bool{ - $extraData = $this->fetchAuthData($packet->chainDataJwt); + $authInfo = $this->parseAuthInfo($packet->authInfoJson); + $jwtChain = $this->parseJwtChain($authInfo->Certificate); + $extraData = $this->fetchAuthData($jwtChain); if(!Player::isValidUserName($extraData->displayName)){ $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidName()); @@ -139,11 +147,61 @@ class LoginPacketHandler extends PacketHandler{ return true; } - $this->processLogin($packet, $ev->isAuthRequired()); + $this->processLogin($authInfo->Token, AuthenticationType::from($authInfo->AuthenticationType), $jwtChain->chain, $packet->clientDataJwt, $ev->isAuthRequired()); return true; } + /** + * @throws PacketHandlingException + */ + protected function parseAuthInfo(string $authInfo) : AuthenticationInfo{ + try{ + $authInfoJson = json_decode($authInfo, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($authInfoJson)){ + throw new \RuntimeException("Unexpected type for auth info data: " . gettype($authInfoJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($authInfoJson, new AuthenticationInfo()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + + /** + * @throws PacketHandlingException + */ + protected function parseJwtChain(string $chainDataJwt) : JwtChain{ + try{ + $jwtChainJson = json_decode($chainDataJwt, associative: false, flags: JSON_THROW_ON_ERROR); + }catch(\JsonException $e){ + throw PacketHandlingException::wrap($e); + } + if(!is_object($jwtChainJson)){ + throw new \RuntimeException("Unexpected type for JWT chain data: " . gettype($jwtChainJson) . ", expected object"); + } + + $mapper = new \JsonMapper(); + $mapper->bExceptionOnMissingData = true; + $mapper->bExceptionOnUndefinedProperty = true; + $mapper->bStrictObjectTypeChecking = true; + try{ + $clientData = $mapper->map($jwtChainJson, new JwtChain()); + }catch(\JsonMapper_Exception $e){ + throw PacketHandlingException::wrap($e); + } + return $clientData; + } + /** * @throws PacketHandlingException */ @@ -211,10 +269,15 @@ class LoginPacketHandler extends PacketHandler{ * TODO: This is separated for the purposes of allowing plugins (like Specter) to hack it and bypass authentication. * In the future this won't be necessary. * + * @param null|string[] $legacyCertificate + * * @throws \InvalidArgumentException */ - protected function processLogin(LoginPacket $packet, bool $authRequired) : void{ - $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($packet->chainDataJwt->chain, $packet->clientDataJwt, $authRequired, $this->authCallback)); + protected function processLogin(string $token, AuthenticationType $authType, ?array $legacyCertificate, string $clientData, bool $authRequired) : void{ + if($legacyCertificate === null){ + throw new PacketHandlingException("Legacy certificate cannot be null"); + } + $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($legacyCertificate, $clientData, $authRequired, $this->authCallback)); $this->session->setHandler(null); //drop packets received during login verification } } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index 9aa302c0c0..e528a52012 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -40,7 +40,6 @@ use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\LevelSettings; use pocketmine\network\mcpe\protocol\types\NetworkPermissions; use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings; -use pocketmine\network\mcpe\protocol\types\ServerAuthMovementMode; use pocketmine\network\mcpe\protocol\types\SpawnSettings; use pocketmine\player\Player; use pocketmine\Server; @@ -99,7 +98,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), + new PlayerMovementSettings(0, false), 0, 0, "", diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index a1df394dae..a9ffae6f74 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -120,7 +120,8 @@ class ResourcePacksPacketHandler extends PacketHandler{ hasAddons: false, hasScripts: false, worldTemplateId: Uuid::fromString(Uuid::NIL), - worldTemplateVersion: "" + worldTemplateVersion: "", + forceDisableVibrantVisuals: true, )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index d39c17a471..a971920ec9 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,11 +51,11 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 800; + public const CURRENT_STORAGE_NETWORK_VERSION = 818; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 80, //patch + 90, //patch 3, //revision 0 //is beta ];