diff --git a/changelogs/4.14.md b/changelogs/4.14.md index 206e990486..f266e8b019 100644 --- a/changelogs/4.14.md +++ b/changelogs/4.14.md @@ -12,3 +12,10 @@ Released 8th February 2023. ## General - Added support for Minecraft: Bedrock Edition 1.19.60. - Removed support for older versions. + +# 4.14.1 +Released 15th February 2023. + +## Fixes +- Fixed all players getting kicked with `Receiving packets too fast` if a server tick takes longer than 5 seconds (e.g. because of autosave or GC). +- Fixed players getting kicked when linking with entities. diff --git a/changelogs/4.15.md b/changelogs/4.15.md new file mode 100644 index 0000000000..b80115e88e --- /dev/null +++ b/changelogs/4.15.md @@ -0,0 +1,14 @@ +**For Minecraft: Bedrock Edition 1.19.62** + +### 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.15.0 +Released 17th February 2023. + +## General +- Added support for Minecraft: Bedrock Edition 1.19.62. +- Removed support for older versions. diff --git a/composer.json b/composer.json index 2b6cae2e9f..0b5a1b69f8 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "pocketmine/bedrock-block-upgrade-schema": "^1.0.0", "pocketmine/bedrock-data": "~1.14.0+bedrock-1.19.60", "pocketmine/bedrock-item-upgrade-schema": "^1.0.0", - "pocketmine/bedrock-protocol": "~19.0.0+bedrock-1.19.60", + "pocketmine/bedrock-protocol": "~19.2.0+bedrock-1.19.62", "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 b330a858ee..99ef44d628 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": "89f31843f10576277f645b81a4bef28d", + "content-hash": "4c3c6f98f5d9eb34c3c5dab3a2f59282", "packages": [ { "name": "adhocore/json-comment", @@ -328,16 +328,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "19.0.0+bedrock-1.19.60", + "version": "19.2.0+bedrock-1.19.62", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "3c8cf08d09b8b3fafc209d184e66e50d2e34c06c" + "reference": "a156db582d0b1a6c20c9d9cc9b1df7ef907efd0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/3c8cf08d09b8b3fafc209d184e66e50d2e34c06c", - "reference": "3c8cf08d09b8b3fafc209d184e66e50d2e34c06c", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a156db582d0b1a6c20c9d9cc9b1df7ef907efd0b", + "reference": "a156db582d0b1a6c20c9d9cc9b1df7ef907efd0b", "shasum": "" }, "require": { @@ -369,9 +369,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/19.0.0+bedrock-1.19.60" + "source": "https://github.com/pmmp/BedrockProtocol/tree/19.2.0+bedrock-1.19.62" }, - "time": "2023-02-08T18:38:02+00:00" + "time": "2023-02-17T16:32:49+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 74aeaa19c5..a763bf0700 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.14.1"; + public const BASE_VERSION = "4.15.1"; public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 0caccb53cb..ebc9439607 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -129,7 +129,6 @@ use function array_values; use function base64_encode; use function bin2hex; use function count; -use function function_exists; use function get_class; use function hrtime; use function in_array; @@ -143,7 +142,6 @@ use function strtolower; use function substr; use function time; use function ucfirst; -use function xdebug_is_debugger_active; use const JSON_THROW_ON_ERROR; use const SORT_NUMERIC; @@ -361,12 +359,9 @@ class NetworkSession{ } if($this->incomingPacketBatchBudget <= 0){ - if(!function_exists('xdebug_is_debugger_active') || !xdebug_is_debugger_active()){ + $this->updatePacketBudget(); + if($this->incomingPacketBatchBudget <= 0){ throw new PacketHandlingException("Receiving packets too fast"); - }else{ - //when a debugging session is active, the server may halt at any point for an indefinite length of time, - //in which time the client will continue to send packets - $this->incomingPacketBatchBudget = self::INCOMING_PACKET_BATCH_MAX_BUDGET; } } $this->incomingPacketBatchBudget--; @@ -1143,6 +1138,23 @@ class NetworkSession{ $this->sendDataPacket(ToastRequestPacket::create($title, $body)); } + private function updatePacketBudget() : void{ + $nowNs = hrtime(true); + $timeSinceLastUpdateNs = $nowNs - $this->lastPacketBudgetUpdateTimeNs; + if($timeSinceLastUpdateNs > 50_000_000){ + $ticksSinceLastUpdate = intdiv($timeSinceLastUpdateNs, 50_000_000); + /* + * If the server takes an abnormally long time to process a tick, add the budget for time difference to + * compensate. This extra budget may be very large, but it will disappear the next time a normal update + * occurs. This ensures that backlogs during a large lag spike don't cause everyone to get kicked. + * As long as all the backlogged packets are processed before the next tick, everything should be OK for + * clients behaving normally. + */ + $this->incomingPacketBatchBudget = min($this->incomingPacketBatchBudget, self::INCOMING_PACKET_BATCH_MAX_BUDGET) + (self::INCOMING_PACKET_BATCH_PER_TICK * 2 * $ticksSinceLastUpdate); + $this->lastPacketBudgetUpdateTimeNs = $nowNs; + } + } + public function tick() : void{ if(!$this->isConnected()){ $this->dispose(); @@ -1170,16 +1182,5 @@ class NetworkSession{ } $this->flushSendBuffer(); - - $nowNs = hrtime(true); - $timeSinceLastUpdateNs = $nowNs - $this->lastPacketBudgetUpdateTimeNs; - if($timeSinceLastUpdateNs > 50_000_000){ - $ticksSinceLastUpdate = intdiv($timeSinceLastUpdateNs, 50_000_000); - $this->incomingPacketBatchBudget = min( - $this->incomingPacketBatchBudget + (self::INCOMING_PACKET_BATCH_PER_TICK * 2 * $ticksSinceLastUpdate), - self::INCOMING_PACKET_BATCH_MAX_BUDGET - ); - $this->lastPacketBudgetUpdateTimeNs = $nowNs; - } } } diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index 80f162697a..c2367454b0 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -46,6 +46,7 @@ use pocketmine\player\XboxLivePlayerInfo; use pocketmine\Server; use Ramsey\Uuid\Uuid; use function is_array; +use function preg_match; /** * Handles the initial login phase of the session. This handler is used as the initial state. @@ -84,6 +85,27 @@ class LoginPacketHandler extends PacketHandler{ } $clientData = $this->parseClientData($packet->clientDataJwt); + + //TODO: REMOVE THIS + //Mojang forgot to bump the protocol version when they changed protocol in 1.19.62. Check the game version instead. + if(preg_match('/^(\d+)\.(\d+)\.(\d+)/', $clientData->GameVersion, $matches) !== 1){ + throw new PacketHandlingException("Invalid game version format, expected at least 3 digits"); + } + $major = (int) $matches[1]; + $minor = (int) $matches[2]; + $patch = (int) $matches[3]; + if($major === 1 && $minor === 19 && $patch < 62){ + $this->session->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_FAILED_CLIENT), 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("$packet->protocol (< v1.19.62)")), + false + ); + + return true; + } + try{ $skin = SkinAdapterSingleton::get()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData)); }catch(\InvalidArgumentException | InvalidSkinException $e){ diff --git a/start.sh b/start.sh index 63a9a273b8..0121f3887b 100755 --- a/start.sh +++ b/start.sh @@ -23,7 +23,7 @@ if [ "$PHP_BINARY" == "" ]; then if [ -f ./bin/php7/bin/php ]; then export PHPRC="" PHP_BINARY="./bin/php7/bin/php" - elif [[ ! -z $(type php 2> /dev/null) ]]; then + elif [[ -n $(type php 2> /dev/null) ]]; then PHP_BINARY=$(type -p php) else echo "Couldn't find a PHP binary in system PATH or $PWD/bin/php7/bin" @@ -51,12 +51,12 @@ if [ "$DO_LOOP" == "yes" ]; then if [ ${LOOPS} -gt 0 ]; then echo "Restarted $LOOPS times" fi - "$PHP_BINARY" "$POCKETMINE_FILE" $@ + "$PHP_BINARY" "$POCKETMINE_FILE" "$@" echo "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart." echo "" sleep 5 ((LOOPS++)) done else - exec "$PHP_BINARY" "$POCKETMINE_FILE" $@ + exec "$PHP_BINARY" "$POCKETMINE_FILE" "$@" fi