From ee868bcccca69c02a3ea724667c2d9c4cd0b114c Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 15 Mar 2021 21:22:01 +0300 Subject: [PATCH 01/14] update argument type constants (#4082) --- .../mcpe/protocol/AvailableCommandsPacket.php | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 3ca5484ca4..eea350ece0 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -49,25 +49,26 @@ class AvailableCommandsPacket extends DataPacket{ * ARG_FLAG_VALID | (type const) */ public const ARG_TYPE_INT = 0x01; - public const ARG_TYPE_FLOAT = 0x02; - public const ARG_TYPE_VALUE = 0x03; - public const ARG_TYPE_WILDCARD_INT = 0x04; - public const ARG_TYPE_OPERATOR = 0x05; - public const ARG_TYPE_TARGET = 0x06; + public const ARG_TYPE_FLOAT = 0x03; + public const ARG_TYPE_VALUE = 0x04; + public const ARG_TYPE_WILDCARD_INT = 0x05; + public const ARG_TYPE_OPERATOR = 0x06; + public const ARG_TYPE_TARGET = 0x07; + public const ARG_TYPE_WILDCARD_TARGET = 0x08; - public const ARG_TYPE_FILEPATH = 0x0e; + public const ARG_TYPE_FILEPATH = 0x10; - public const ARG_TYPE_STRING = 0x1d; + public const ARG_TYPE_STRING = 0x20; - public const ARG_TYPE_POSITION = 0x25; + public const ARG_TYPE_POSITION = 0x28; - public const ARG_TYPE_MESSAGE = 0x29; + public const ARG_TYPE_MESSAGE = 0x2c; - public const ARG_TYPE_RAWTEXT = 0x2b; + public const ARG_TYPE_RAWTEXT = 0x2e; - public const ARG_TYPE_JSON = 0x2f; + public const ARG_TYPE_JSON = 0x32; - public const ARG_TYPE_COMMAND = 0x36; + public const ARG_TYPE_COMMAND = 0x3f; /** * Enums are a little different: they are composed as follows: From 1bb2d162ab93f1430cb1511b01e4009e5906caea Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 16 Mar 2021 21:42:18 +0000 Subject: [PATCH 02/14] Simplify CommandReader while stream_select() doesn't work on pipes, if it ever starts working properly in the future, we'll need this code. In the meantime, it's harmless (it just immediately returns 1 anyway). --- src/pocketmine/command/CommandReader.php | 41 +++++++++--------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/pocketmine/command/CommandReader.php b/src/pocketmine/command/CommandReader.php index 10970d64b6..2486cb740d 100644 --- a/src/pocketmine/command/CommandReader.php +++ b/src/pocketmine/command/CommandReader.php @@ -116,37 +116,28 @@ class CommandReader extends Thread{ * @return bool if the main execution should continue reading lines */ private function readLine() : bool{ - $line = ""; - if(!is_resource(self::$stdin)){ $this->initStdin(); } - switch($this->type){ - /** @noinspection PhpMissingBreakStatementInspection */ - case self::TYPE_STREAM: - //stream_select doesn't work on piped streams for some reason - $r = [self::$stdin]; - $w = $e = null; - if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds - return true; - }elseif($count === false){ //stream error - $this->initStdin(); - } - - case self::TYPE_PIPED: - if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF - $this->initStdin(); - $this->synchronized(function() : void{ - $this->wait(200000); - }); //prevent CPU waste if it's end of pipe - return true; //loop back round - } - - $line = trim($raw); - break; + $r = [self::$stdin]; + $w = $e = null; + if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds + return true; + }elseif($count === false){ //stream error + $this->initStdin(); } + if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF + $this->initStdin(); + $this->synchronized(function() : void{ + $this->wait(200000); + }); //prevent CPU waste if it's end of pipe + return true; //loop back round + } + + $line = trim($raw); + if($line !== ""){ $this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line); if($this->notifier !== null){ From 9479a1a0ab497c5bc4142f172d911421ecb2293d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Mar 2021 14:08:44 +0000 Subject: [PATCH 03/14] Bump phpunit/phpunit from 9.5.2 to 9.5.3 (#4084) Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.2 to 9.5.3. - [Release notes](https://github.com/sebastianbergmann/phpunit/releases) - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-9.5.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.2...9.5.3) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- composer.lock | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/composer.lock b/composer.lock index 34154cd9f9..4d1daba6de 100644 --- a/composer.lock +++ b/composer.lock @@ -1486,16 +1486,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.2", + "version": "9.5.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4" + "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4", - "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/27241ac75fc37ecf862b6e002bf713b6566cbe41", + "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41", "shasum": "" }, "require": { @@ -1573,7 +1573,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.3" }, "funding": [ { @@ -1585,7 +1585,7 @@ "type": "github" } ], - "time": "2021-02-02T14:45:58+00:00" + "time": "2021-03-17T07:30:34+00:00" }, { "name": "sebastian/cli-parser", @@ -2682,30 +2682,35 @@ }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2729,9 +2734,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.9.1" + "source": "https://github.com/webmozarts/assert/tree/1.10.0" }, - "time": "2020-07-08T17:02:28+00:00" + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], From eaf85b028a8aff66c96ed342781d2b6d9adda077 Mon Sep 17 00:00:00 2001 From: mmm545 <64874108+mmm545@users.noreply.github.com> Date: Fri, 19 Mar 2021 23:42:21 +0300 Subject: [PATCH 04/14] fix prebuilt binaries link (#4090) --- BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index fa2295f20d..e5338066e4 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -8,7 +8,7 @@ ## Custom PHP binaries Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries. -- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.3-Aggregate) +- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.4-Aggregate) - [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php` If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`. From 0e2dc51ec81a48bd81e104123b99d3cb33749fce Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 19 Mar 2021 21:06:30 +0000 Subject: [PATCH 05/14] added some missing things to the protocol --- .../network/mcpe/protocol/CommandOutputPacket.php | 9 +++++++-- src/pocketmine/network/mcpe/protocol/EventPacket.php | 7 +++++++ src/pocketmine/network/mcpe/protocol/SetTitlePacket.php | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/CommandOutputPacket.php b/src/pocketmine/network/mcpe/protocol/CommandOutputPacket.php index b6bc2856e0..ac7549d728 100644 --- a/src/pocketmine/network/mcpe/protocol/CommandOutputPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CommandOutputPacket.php @@ -33,6 +33,11 @@ use function count; class CommandOutputPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::COMMAND_OUTPUT_PACKET; + public const TYPE_LAST = 1; + public const TYPE_SILENT = 2; + public const TYPE_ALL = 3; + public const TYPE_DATA_SET = 4; + /** @var CommandOriginData */ public $originData; /** @var int */ @@ -53,7 +58,7 @@ class CommandOutputPacket extends DataPacket{ $this->messages[] = $this->getCommandMessage(); } - if($this->outputType === 4){ + if($this->outputType === self::TYPE_DATA_SET){ $this->unknownString = $this->getString(); } } @@ -81,7 +86,7 @@ class CommandOutputPacket extends DataPacket{ $this->putCommandMessage($message); } - if($this->outputType === 4){ + if($this->outputType === self::TYPE_DATA_SET){ $this->putString($this->unknownString); } } diff --git a/src/pocketmine/network/mcpe/protocol/EventPacket.php b/src/pocketmine/network/mcpe/protocol/EventPacket.php index bdf4407295..7e4246ddf4 100644 --- a/src/pocketmine/network/mcpe/protocol/EventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EventPacket.php @@ -48,6 +48,13 @@ class EventPacket extends DataPacket{ public const TYPE_CAULDRON_BLOCK_USED = 15; public const TYPE_COMPOSTER_BLOCK_USED = 16; public const TYPE_BELL_BLOCK_USED = 17; + public const TYPE_ACTOR_DEFINITION = 18; + public const TYPE_RAID_UPDATE = 19; + public const TYPE_PLAYER_MOVEMENT_ANOMALY = 20; //anti cheat + public const TYPE_PLAYER_MOVEMENT_CORRECTED = 21; + public const TYPE_HONEY_HARVESTED = 22; + public const TYPE_TARGET_BLOCK_HIT = 23; + public const TYPE_PIGLIN_BARTER = 24; /** @var int */ public $playerRuntimeId; diff --git a/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php b/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php index 346737f451..3c3b89f7e5 100644 --- a/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetTitlePacket.php @@ -36,6 +36,9 @@ class SetTitlePacket extends DataPacket{ public const TYPE_SET_SUBTITLE = 3; public const TYPE_SET_ACTIONBAR_MESSAGE = 4; public const TYPE_SET_ANIMATION_TIMES = 5; + public const TYPE_SET_TITLE_JSON = 6; + public const TYPE_SET_SUBTITLE_JSON = 7; + public const TYPE_SET_ACTIONBAR_MESSAGE_JSON = 8; /** @var int */ public $type; From 3667e95ff66775577be89036f9f14e2df07c420f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 19 Mar 2021 21:41:22 +0000 Subject: [PATCH 06/14] Added PlayerAuthInputFlags --- .../mcpe/protocol/PlayerAuthInputPacket.php | 5 ++ .../protocol/types/PlayerAuthInputFlags.php | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/pocketmine/network/mcpe/protocol/types/PlayerAuthInputFlags.php diff --git a/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php index cee616bd10..16d8e7c6dc 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php @@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\math\Vector3; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\InputMode; +use pocketmine\network\mcpe\protocol\types\PlayerAuthInputFlags; use pocketmine\network\mcpe\protocol\types\PlayMode; use function assert; @@ -60,6 +61,7 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{ private $delta; /** + * @param int $inputFlags @see InputFlags * @param int $inputMode @see InputMode * @param int $playMode @see PlayMode * @param Vector3|null $vrGazeDirection only used when PlayMode::VR @@ -111,6 +113,9 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{ return $this->moveVecZ; } + /** + * @see PlayerAuthInputFlags + */ public function getInputFlags() : int{ return $this->inputFlags; } diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerAuthInputFlags.php b/src/pocketmine/network/mcpe/protocol/types/PlayerAuthInputFlags.php new file mode 100644 index 0000000000..83104c88ed --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerAuthInputFlags.php @@ -0,0 +1,75 @@ + Date: Fri, 19 Mar 2021 22:14:07 +0000 Subject: [PATCH 07/14] Clean up PHPStan baselines --- tests/phpstan/configs/actual-problems.neon | 5 ---- .../check-explicit-mixed-baseline.neon | 30 ------------------- tests/phpstan/configs/l7-baseline.neon | 15 ---------- 3 files changed, 50 deletions(-) diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index e6a9617ff2..9e499015f9 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -10,11 +10,6 @@ parameters: count: 1 path: ../../../src/pocketmine/Player.php - - - message: "#^Cannot instantiate interface pocketmine\\\\level\\\\format\\\\io\\\\LevelProvider\\.$#" - count: 1 - path: ../../../src/pocketmine/Server.php - - message: "#^Instanceof between pocketmine\\\\plugin\\\\PluginManager and pocketmine\\\\plugin\\\\PluginManager will always evaluate to true\\.$#" count: 1 diff --git a/tests/phpstan/configs/check-explicit-mixed-baseline.neon b/tests/phpstan/configs/check-explicit-mixed-baseline.neon index 708b62e9d0..8276232e60 100644 --- a/tests/phpstan/configs/check-explicit-mixed-baseline.neon +++ b/tests/phpstan/configs/check-explicit-mixed-baseline.neon @@ -5,11 +5,6 @@ parameters: count: 1 path: ../../../src/pocketmine/CrashDump.php - - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, mixed given\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - message: "#^Cannot access offset float\\|int on mixed\\.$#" count: 1 @@ -20,11 +15,6 @@ parameters: count: 1 path: ../../../src/pocketmine/CrashDump.php - - - message: "#^Cannot access offset 'name' on mixed\\.$#" - count: 1 - path: ../../../src/pocketmine/CrashDump.php - - message: "#^Cannot cast mixed to int\\.$#" count: 7 @@ -265,21 +255,6 @@ parameters: count: 1 path: ../../../src/pocketmine/level/generator/Flat.php - - - message: "#^Parameter \\#4 \\$q0 of static method pocketmine\\\\level\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, mixed given\\.$#" - count: 1 - path: ../../../src/pocketmine/level/generator/noise/Noise.php - - - - message: "#^Parameter \\#5 \\$q1 of static method pocketmine\\\\level\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, mixed given\\.$#" - count: 1 - path: ../../../src/pocketmine/level/generator/noise/Noise.php - - - - message: "#^Cannot access offset int on mixed\\.$#" - count: 6 - path: ../../../src/pocketmine/level/generator/noise/Noise.php - - message: "#^Parameter \\#1 \\$buffer of class pocketmine\\\\network\\\\mcpe\\\\protocol\\\\BatchPacket constructor expects string, mixed given\\.$#" count: 1 @@ -585,11 +560,6 @@ parameters: count: 1 path: ../../../src/pocketmine/utils/Internet.php - - - message: "#^Parameter \\#2 \\$str of function fwrite expects string, mixed given\\.$#" - count: 1 - path: ../../../src/pocketmine/utils/MainLogger.php - - message: "#^Cannot access offset 'status' on mixed\\.$#" count: 1 diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 6f5268eddd..5b8a923c8b 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -695,21 +695,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php - - - message: "#^Call to an undefined method object\\:\\:Add\\(\\)\\.$#" - count: 1 - path: ../../../src/pocketmine/network/upnp/UPnP.php - - - - message: "#^Call to an undefined method object\\:\\:Remove\\(\\)\\.$#" - count: 1 - path: ../../../src/pocketmine/network/upnp/UPnP.php - - - - message: "#^Parameter \\#1 \\$event of method pocketmine\\\\plugin\\\\PluginManager\\:\\:registerEvent\\(\\) expects class\\-string\\, class\\-string\\ given\\.$#" - count: 1 - path: ../../../src/pocketmine/plugin/PluginManager.php - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 2 From cb06be615aa3780d4c83a947520fa55c0d908618 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 19 Mar 2021 22:16:30 +0000 Subject: [PATCH 08/14] Backport InventoryTransactionPacket impl from PM4 this version is far better, and we're going to need it to deal with the PlayerAuthInputPacket bullshit. --- src/pocketmine/Player.php | 567 +++++++++--------- .../protocol/InventoryTransactionPacket.php | 133 ++-- .../inventory/MismatchTransactionData.php | 50 ++ .../types/inventory/NormalTransactionData.php | 54 ++ .../inventory/ReleaseItemTransactionData.php | 92 +++ .../types/inventory/TransactionData.php | 72 +++ .../UseItemOnEntityTransactionData.php | 109 ++++ .../inventory/UseItemTransactionData.php | 130 ++++ tests/phpstan/configs/l7-baseline.neon | 15 + 9 files changed, 840 insertions(+), 382 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index dcec663c40..a61d435cd2 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -152,7 +152,12 @@ use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\GameMode; +use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset; +use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor; use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece; @@ -411,7 +416,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var float */ protected $lastRightClickTime = 0.0; - /** @var \stdClass|null */ + /** @var UseItemTransactionData|null */ protected $lastRightClickData = null; /** @@ -2426,7 +2431,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var InventoryAction[] $actions */ $actions = []; $isCraftingPart = false; - foreach($packet->actions as $networkInventoryAction){ + foreach($packet->trData->getActions() as $networkInventoryAction){ if( $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and ( $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or @@ -2484,321 +2489,313 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->craftingTransaction = null; } - switch($packet->transactionType){ - case InventoryTransactionPacket::TYPE_NORMAL: - $this->setUsingItem(false); - $transaction = new InventoryTransaction($this, $actions); + if($packet->trData instanceof NormalTransactionData){ + $this->setUsingItem(false); + $transaction = new InventoryTransaction($this, $actions); - try{ - $transaction->execute(); - }catch(TransactionValidationException $e){ - $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); - $this->server->getLogger()->debug("Actions: " . json_encode($packet->actions)); + try{ + $transaction->execute(); + }catch(TransactionValidationException $e){ + $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); + $this->server->getLogger()->debug("Actions: " . json_encode($packet->trData->getActions())); - return false; - } + return false; + } - //TODO: fix achievement for getting iron from furnace + //TODO: fix achievement for getting iron from furnace - return true; - case InventoryTransactionPacket::TYPE_MISMATCH: - if(count($packet->actions) > 0){ - $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions)); - } - $this->setUsingItem(false); - $this->sendAllInventories(); + return true; + }elseif($packet->trData instanceof MismatchTransactionData){ + if(count($packet->trData->getActions()) > 0){ + $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->trData->getActions()) . ", " . json_encode($packet->trData->getActions())); + } + $this->setUsingItem(false); + $this->sendAllInventories(); - return true; - case InventoryTransactionPacket::TYPE_USE_ITEM: - $blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z); - $face = $packet->trData->face; + return true; + }elseif($packet->trData instanceof UseItemTransactionData){ - $type = $packet->trData->actionType; - switch($type){ - case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK: - //TODO: start hack for client spam bug - $spamBug = ($this->lastRightClickData !== null and - microtime(true) - $this->lastRightClickTime < 0.1 and //100ms - $this->lastRightClickData->playerPos->distanceSquared($packet->trData->playerPos) < 0.00001 and - $this->lastRightClickData->x === $packet->trData->x and - $this->lastRightClickData->y === $packet->trData->y and - $this->lastRightClickData->z === $packet->trData->z and - $this->lastRightClickData->clickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error - ); - //get rid of continued spam if the player clicks and holds right-click - $this->lastRightClickData = $packet->trData; - $this->lastRightClickTime = microtime(true); - if($spamBug){ - return true; - } - //TODO: end hack for client spam bug - - $this->setUsingItem(false); - - if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ - }elseif($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ - return true; - } - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ - $this->inventory->sendHeldItem($this); - }else{ - $item = $this->inventory->getItemInHand(); - $oldItem = clone $item; - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - - return true; - } - } - - $this->inventory->sendHeldItem($this); - - if($blockVector->distanceSquared($this) > 10000){ - return true; - } - - $target = $this->level->getBlock($blockVector); - $block = $target->getSide($face); - - /** @var Block[] $blocks */ - $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + $blockVector = $packet->trData->getBlockPos(); + $face = $packet->trData->getFace(); + switch($packet->trData->getActionType()){ + case UseItemTransactionData::ACTION_CLICK_BLOCK: + //TODO: start hack for client spam bug + $spamBug = ($this->lastRightClickData !== null and + microtime(true) - $this->lastRightClickTime < 0.1 and //100ms + $this->lastRightClickData->getPlayerPos()->distanceSquared($packet->trData->getPlayerPos()) < 0.00001 and + $this->lastRightClickData->getBlockPos()->equals($packet->trData->getBlockPos()) and + $this->lastRightClickData->getClickPos()->distanceSquared($packet->trData->getClickPos()) < 0.00001 //signature spam bug has 0 distance, but allow some error + ); + //get rid of continued spam if the player clicks and holds right-click + $this->lastRightClickData = $packet->trData; + $this->lastRightClickTime = microtime(true); + if($spamBug){ return true; - case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK: - $this->doCloseInventory(); + } + //TODO: end hack for client spam bug + $this->setUsingItem(false); + + if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ + }elseif($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ + return true; + } + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ + $this->inventory->sendHeldItem($this); + }else{ $item = $this->inventory->getItemInHand(); $oldItem = clone $item; - - if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ - if($this->isSurvival()){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - - $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); } + return true; } + } - $this->inventory->sendContents($this); - $this->inventory->sendHeldItem($this); - - $target = $this->level->getBlock($blockVector); - /** @var Block[] $blocks */ - $blocks = $target->getAllSides(); - $blocks[] = $target; - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); - - foreach($blocks as $b){ - $tile = $this->level->getTile($b); - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - } + $this->inventory->sendHeldItem($this); + if($blockVector->distanceSquared($this) > 10000){ return true; - case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR: - if($this->isUsingItem()){ - $slot = $this->inventory->getItemInHand(); - if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ - $ev = new PlayerItemConsumeEvent($this, $slot); - if($this->hasItemCooldown($slot)){ - $ev->setCancelled(); - } - $ev->call(); - if($ev->isCancelled() or !$this->consumeObject($slot)){ - $this->inventory->sendContents($this); - return true; - } - $this->resetItemCooldown($slot); - if($this->isSurvival()){ - $slot->pop(); - $this->inventory->setItemInHand($slot); - $this->inventory->addItem($slot->getResidue()); - } - $this->setUsingItem(false); + } + + $target = $this->level->getBlock($blockVector); + $block = $target->getSide($face); + + /** @var Block[] $blocks */ + $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + return true; + case UseItemTransactionData::ACTION_BREAK_BLOCK: + $this->doCloseInventory(); + + $item = $this->inventory->getItemInHand(); + $oldItem = clone $item; + + if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ + if($this->isSurvival()){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); } - } - $directionVector = $this->getDirectionVector(); - if($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ - $this->inventory->sendHeldItem($this); - return true; - }else{ - $item = $this->inventory->getItemInHand(); + $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); } + return true; + } - $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); - if($this->hasItemCooldown($item) or $this->isSpectator()){ - $ev->setCancelled(); + $this->inventory->sendContents($this); + $this->inventory->sendHeldItem($this); + + $target = $this->level->getBlock($blockVector); + /** @var Block[] $blocks */ + $blocks = $target->getAllSides(); + $blocks[] = $target; + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + foreach($blocks as $b){ + $tile = $this->level->getTile($b); + if($tile instanceof Spawnable){ + $tile->spawnTo($this); } + } - $ev->call(); - if($ev->isCancelled()){ - $this->inventory->sendHeldItem($this); - return true; - } - - if($item->onClickAir($this, $directionVector)){ - $this->resetItemCooldown($item); + return true; + case UseItemTransactionData::ACTION_CLICK_AIR: + if($this->isUsingItem()){ + $slot = $this->inventory->getItemInHand(); + if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ + $ev = new PlayerItemConsumeEvent($this, $slot); + if($this->hasItemCooldown($slot)){ + $ev->setCancelled(); + } + $ev->call(); + if($ev->isCancelled() or !$this->consumeObject($slot)){ + $this->inventory->sendContents($this); + return true; + } + $this->resetItemCooldown($slot); if($this->isSurvival()){ + $slot->pop(); + $this->inventory->setItemInHand($slot); + $this->inventory->addItem($slot->getResidue()); + } + $this->setUsingItem(false); + } + } + $directionVector = $this->getDirectionVector(); + + if($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ + $this->inventory->sendHeldItem($this); + return true; + }else{ + $item = $this->inventory->getItemInHand(); + } + + $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); + if($this->hasItemCooldown($item) or $this->isSpectator()){ + $ev->setCancelled(); + } + + $ev->call(); + if($ev->isCancelled()){ + $this->inventory->sendHeldItem($this); + return true; + } + + if($item->onClickAir($this, $directionVector)){ + $this->resetItemCooldown($item); + if($this->isSurvival()){ + $this->inventory->setItemInHand($item); + } + } + + $this->setUsingItem(true); + + return true; + default: + //unknown + break; + } + + $this->inventory->sendContents($this); + return false; + }elseif($packet->trData instanceof UseItemOnEntityTransactionData){ + $target = $this->level->getEntity($packet->trData->getEntityRuntimeId()); + if($target === null){ + return false; + } + + switch($packet->trData->getActionType()){ + case UseItemOnEntityTransactionData::ACTION_INTERACT: + break; //TODO + case UseItemOnEntityTransactionData::ACTION_ATTACK: + if(!$target->isAlive()){ + return true; + } + if($target instanceof ItemEntity or $target instanceof Arrow){ + $this->kick("Attempting to attack an invalid entity"); + $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); + return false; + } + + $cancelled = false; + + $heldItem = $this->inventory->getItemInHand(); + $oldItem = clone $heldItem; + + if(!$this->canInteract($target, 8) or $this->isSpectator()){ + $cancelled = true; + }elseif($target instanceof Player){ + if(!$this->server->getConfigBool("pvp")){ + $cancelled = true; + } + } + + $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); + + $meleeEnchantmentDamage = 0; + /** @var EnchantmentInstance[] $meleeEnchantments */ + $meleeEnchantments = []; + foreach($heldItem->getEnchantments() as $enchantment){ + $type = $enchantment->getType(); + if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ + $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); + $meleeEnchantments[] = $enchantment; + } + } + $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); + + if($cancelled){ + $ev->setCancelled(); + } + + if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ + $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); + } + + $target->attack($ev); + + if($ev->isCancelled()){ + if($heldItem instanceof Durable and $this->isSurvival()){ + $this->inventory->sendContents($this); + } + return true; + } + + if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ + $pk = new AnimatePacket(); + $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; + $pk->entityRuntimeId = $target->getId(); + $this->server->broadcastPacket($target->getViewers(), $pk); + if($target instanceof Player){ + $target->dataPacket($pk); + } + } + + foreach($meleeEnchantments as $enchantment){ + $type = $enchantment->getType(); + assert($type instanceof MeleeWeaponEnchantment); + $type->onPostAttack($this, $target, $enchantment->getLevel()); + } + + if($this->isAlive()){ + //reactive damage like thorns might cause us to be killed by attacking another mob, which + //would mean we'd already have dropped the inventory by the time we reached here + if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival + $this->inventory->setItemInHand($heldItem); + } + + $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); + } + + return true; + default: + break; //unknown + } + + $this->inventory->sendContents($this); + return false; + }elseif($packet->trData instanceof ReleaseItemTransactionData){ + try{ + switch($packet->trData->getActionType()){ + case ReleaseItemTransactionData::ACTION_RELEASE: + if($this->isUsingItem()){ + $item = $this->inventory->getItemInHand(); + if($this->hasItemCooldown($item)){ + $this->inventory->sendContents($this); + return false; + } + if($item->onReleaseUsing($this)){ + $this->resetItemCooldown($item); $this->inventory->setItemInHand($item); } + return true; } - - $this->setUsingItem(true); - - return true; + break; default: - //unknown break; } - break; - case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY: - $target = $this->level->getEntity($packet->trData->entityRuntimeId); - if($target === null){ - return false; - } - - $type = $packet->trData->actionType; - - switch($type){ - case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT: - break; //TODO - case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK: - if(!$target->isAlive()){ - return true; - } - if($target instanceof ItemEntity or $target instanceof Arrow){ - $this->kick("Attempting to attack an invalid entity"); - $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); - return false; - } - - $cancelled = false; - - $heldItem = $this->inventory->getItemInHand(); - $oldItem = clone $heldItem; - - if(!$this->canInteract($target, 8) or $this->isSpectator()){ - $cancelled = true; - }elseif($target instanceof Player){ - if(!$this->server->getConfigBool("pvp")){ - $cancelled = true; - } - } - - $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); - - $meleeEnchantmentDamage = 0; - /** @var EnchantmentInstance[] $meleeEnchantments */ - $meleeEnchantments = []; - foreach($heldItem->getEnchantments() as $enchantment){ - $type = $enchantment->getType(); - if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ - $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); - $meleeEnchantments[] = $enchantment; - } - } - $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); - - if($cancelled){ - $ev->setCancelled(); - } - - if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ - $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); - } - - $target->attack($ev); - - if($ev->isCancelled()){ - if($heldItem instanceof Durable and $this->isSurvival()){ - $this->inventory->sendContents($this); - } - return true; - } - - if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ - $pk = new AnimatePacket(); - $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; - $pk->entityRuntimeId = $target->getId(); - $this->server->broadcastPacket($target->getViewers(), $pk); - if($target instanceof Player){ - $target->dataPacket($pk); - } - } - - foreach($meleeEnchantments as $enchantment){ - $type = $enchantment->getType(); - assert($type instanceof MeleeWeaponEnchantment); - $type->onPostAttack($this, $target, $enchantment->getLevel()); - } - - if($this->isAlive()){ - //reactive damage like thorns might cause us to be killed by attacking another mob, which - //would mean we'd already have dropped the inventory by the time we reached here - if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival - $this->inventory->setItemInHand($heldItem); - } - - $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); - } - - return true; - default: - break; //unknown - } - - break; - case InventoryTransactionPacket::TYPE_RELEASE_ITEM: - try{ - $type = $packet->trData->actionType; - switch($type){ - case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: - if($this->isUsingItem()){ - $item = $this->inventory->getItemInHand(); - if($this->hasItemCooldown($item)){ - $this->inventory->sendContents($this); - return false; - } - if($item->onReleaseUsing($this)){ - $this->resetItemCooldown($item); - $this->inventory->setItemInHand($item); - } - }else{ - break; - } - - return true; - default: - break; - } - }finally{ - $this->setUsingItem(false); - } - - $this->inventory->sendContents($this); - break; - default: - $this->inventory->sendContents($this); - break; + }finally{ + $this->setUsingItem(false); + } + $this->inventory->sendContents($this); + return false; + }else{ + $this->inventory->sendContents($this); + return false; } - - return false; //TODO } public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index 1c8662fd81..0fa447ed69 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -25,9 +25,15 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\NetworkSession as PacketHandlerInterface; use pocketmine\network\mcpe\protocol\types\inventory\InventoryTransactionChangedSlotsHack; -use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; +use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\TransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; +use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; +use UnexpectedValueException as PacketDecodeException; use function count; class InventoryTransactionPacket extends DataPacket{ @@ -39,137 +45,70 @@ class InventoryTransactionPacket extends DataPacket{ public const TYPE_USE_ITEM_ON_ENTITY = 3; public const TYPE_RELEASE_ITEM = 4; - public const USE_ITEM_ACTION_CLICK_BLOCK = 0; - public const USE_ITEM_ACTION_CLICK_AIR = 1; - public const USE_ITEM_ACTION_BREAK_BLOCK = 2; - - public const RELEASE_ITEM_ACTION_RELEASE = 0; //bow shoot - public const RELEASE_ITEM_ACTION_CONSUME = 1; //eat food, drink potion - - public const USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0; - public const USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1; - /** @var int */ public $requestId; /** @var InventoryTransactionChangedSlotsHack[] */ public $requestChangedSlots; - - /** @var int */ - public $transactionType; /** @var bool */ public $hasItemStackIds; - - /** @var NetworkInventoryAction[] */ - public $actions = []; - - /** @var \stdClass */ + /** @var TransactionData */ public $trData; - protected function decodePayload(){ - $this->requestId = $this->readGenericTypeNetworkId(); + protected function decodePayload() : void{ + $in = $this; + $this->requestId = $in->readGenericTypeNetworkId(); $this->requestChangedSlots = []; if($this->requestId !== 0){ - for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ - $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($this); + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($in); } } - $this->transactionType = $this->getUnsignedVarInt(); + $transactionType = $in->getUnsignedVarInt(); - $this->hasItemStackIds = $this->getBool(); + $this->hasItemStackIds = $in->getBool(); - for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $this->actions[] = $action = (new NetworkInventoryAction())->read($this, $this->hasItemStackIds); - } - - $this->trData = new \stdClass(); - - switch($this->transactionType){ + switch($transactionType){ case self::TYPE_NORMAL: + $this->trData = new NormalTransactionData(); + break; case self::TYPE_MISMATCH: - //Regular ComplexInventoryTransaction doesn't read any extra data + $this->trData = new MismatchTransactionData(); break; case self::TYPE_USE_ITEM: - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->getBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); - $this->trData->face = $this->getVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->playerPos = $this->getVector3(); - $this->trData->clickPos = $this->getVector3(); - $this->trData->blockRuntimeId = $this->getUnsignedVarInt(); + $this->trData = new UseItemTransactionData(); break; case self::TYPE_USE_ITEM_ON_ENTITY: - $this->trData->entityRuntimeId = $this->getEntityRuntimeId(); - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->playerPos = $this->getVector3(); - $this->trData->clickPos = $this->getVector3(); + $this->trData = new UseItemOnEntityTransactionData(); break; case self::TYPE_RELEASE_ITEM: - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->headPos = $this->getVector3(); + $this->trData = new ReleaseItemTransactionData(); break; default: - throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); + throw new PacketDecodeException("Unknown transaction type $transactionType"); } + + $this->trData->decode($in, $this->hasItemStackIds); } - protected function encodePayload(){ - $this->writeGenericTypeNetworkId($this->requestId); + protected function encodePayload() : void{ + $out = $this; + $out->writeGenericTypeNetworkId($this->requestId); if($this->requestId !== 0){ - $this->putUnsignedVarInt(count($this->requestChangedSlots)); + $out->putUnsignedVarInt(count($this->requestChangedSlots)); foreach($this->requestChangedSlots as $changedSlots){ - $changedSlots->write($this); + $changedSlots->write($out); } } - $this->putUnsignedVarInt($this->transactionType); + $out->putUnsignedVarInt($this->trData->getTypeId()); - $this->putBool($this->hasItemStackIds); + $out->putBool($this->hasItemStackIds); - $this->putUnsignedVarInt(count($this->actions)); - foreach($this->actions as $action){ - $action->write($this, $this->hasItemStackIds); - } - - switch($this->transactionType){ - case self::TYPE_NORMAL: - case self::TYPE_MISMATCH: - break; - case self::TYPE_USE_ITEM: - $this->putUnsignedVarInt($this->trData->actionType); - $this->putBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); - $this->putVarInt($this->trData->face); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->playerPos); - $this->putVector3($this->trData->clickPos); - $this->putUnsignedVarInt($this->trData->blockRuntimeId); - break; - case self::TYPE_USE_ITEM_ON_ENTITY: - $this->putEntityRuntimeId($this->trData->entityRuntimeId); - $this->putUnsignedVarInt($this->trData->actionType); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->playerPos); - $this->putVector3($this->trData->clickPos); - break; - case self::TYPE_RELEASE_ITEM: - $this->putUnsignedVarInt($this->trData->actionType); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->headPos); - break; - default: - throw new \InvalidArgumentException("Unknown transaction type $this->transactionType"); - } + $this->trData->encode($out, $this->hasItemStackIds); } - public function handle(NetworkSession $session) : bool{ - return $session->handleInventoryTransaction($this); + public function handle(PacketHandlerInterface $handler) : bool{ + return $handler->handleInventoryTransaction($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php new file mode 100644 index 0000000000..2ee19c64d7 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php @@ -0,0 +1,50 @@ +actions) > 0){ + throw new PacketDecodeException("Mismatch transaction type should not have any actions associated with it, but got " . count($this->actions)); + } + } + + protected function encodeData(PacketSerializer $stream) : void{ + + } + + public static function new() : self{ + return new self; //no arguments + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php new file mode 100644 index 0000000000..757bf9d629 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php @@ -0,0 +1,54 @@ +actions = $actions; + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php new file mode 100644 index 0000000000..96b359d17d --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php @@ -0,0 +1,92 @@ +actionType; + } + + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + public function getItemInHand() : ItemStack{ + return $this->itemInHand; + } + + public function getHeadPos() : Vector3{ + return $this->headPos; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_RELEASE_ITEM; + } + + protected function decodeData(PacketSerializer $stream) : void{ + $this->actionType = $stream->getUnsignedVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->headPos = $stream->getVector3(); + } + + protected function encodeData(PacketSerializer $stream) : void{ + $stream->putUnsignedVarInt($this->actionType); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->headPos); + } + + /** + * @param NetworkInventoryAction[] $actions + */ + public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $headPos) : self{ + $result = new self; + $result->actions = $actions; + $result->actionType = $actionType; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->headPos = $headPos; + + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php new file mode 100644 index 0000000000..23089c71b4 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php @@ -0,0 +1,72 @@ +actions; + } + + abstract public function getTypeId() : int; + + /** + * @throws BinaryDataException + * @throws PacketDecodeException + */ + final public function decode(PacketSerializer $stream, bool $hasItemStackIds) : void{ + $actionCount = $stream->getUnsignedVarInt(); + for($i = 0; $i < $actionCount; ++$i){ + $this->actions[] = (new NetworkInventoryAction())->read($stream, $hasItemStackIds); + } + $this->decodeData($stream); + } + + /** + * @throws BinaryDataException + * @throws PacketDecodeException + */ + abstract protected function decodeData(PacketSerializer $stream) : void; + + final public function encode(PacketSerializer $stream, bool $hasItemStackIds) : void{ + $stream->putUnsignedVarInt(count($this->actions)); + foreach($this->actions as $action){ + $action->write($stream, $hasItemStackIds); + } + $this->encodeData($stream); + } + + abstract protected function encodeData(PacketSerializer $stream) : void; +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php new file mode 100644 index 0000000000..f88bbca73c --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php @@ -0,0 +1,109 @@ +entityRuntimeId; + } + + public function getActionType() : int{ + return $this->actionType; + } + + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + public function getItemInHand() : ItemStack{ + return $this->itemInHand; + } + + public function getPlayerPos() : Vector3{ + return $this->playerPos; + } + + public function getClickPos() : Vector3{ + return $this->clickPos; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY; + } + + protected function decodeData(PacketSerializer $stream) : void{ + $this->entityRuntimeId = $stream->getEntityRuntimeId(); + $this->actionType = $stream->getUnsignedVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->playerPos = $stream->getVector3(); + $this->clickPos = $stream->getVector3(); + } + + protected function encodeData(PacketSerializer $stream) : void{ + $stream->putEntityRuntimeId($this->entityRuntimeId); + $stream->putUnsignedVarInt($this->actionType); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->playerPos); + $stream->putVector3($this->clickPos); + } + + /** + * @param NetworkInventoryAction[] $actions + */ + public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ + $result = new self; + $result->actions = $actions; + $result->entityRuntimeId = $entityRuntimeId; + $result->actionType = $actionType; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->playerPos = $playerPos; + $result->clickPos = $clickPos; + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php new file mode 100644 index 0000000000..5c1872bea5 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php @@ -0,0 +1,130 @@ +actionType; + } + + public function getBlockPos() : Vector3{ + return $this->blockPos; + } + + public function getFace() : int{ + return $this->face; + } + + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + public function getItemInHand() : ItemStack{ + return $this->itemInHand; + } + + public function getPlayerPos() : Vector3{ + return $this->playerPos; + } + + public function getClickPos() : Vector3{ + return $this->clickPos; + } + + public function getBlockRuntimeId() : int{ + return $this->blockRuntimeId; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_USE_ITEM; + } + + protected function decodeData(PacketSerializer $stream) : void{ + $this->actionType = $stream->getUnsignedVarInt(); + $x = $y = $z = 0; + $stream->getBlockPosition($x, $y, $z); + $this->blockPos = new Vector3($x, $y, $z); + $this->face = $stream->getVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->playerPos = $stream->getVector3(); + $this->clickPos = $stream->getVector3(); + $this->blockRuntimeId = $stream->getUnsignedVarInt(); + } + + protected function encodeData(PacketSerializer $stream) : void{ + $stream->putUnsignedVarInt($this->actionType); + $stream->putBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z); + $stream->putVarInt($this->face); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->playerPos); + $stream->putVector3($this->clickPos); + $stream->putUnsignedVarInt($this->blockRuntimeId); + } + + /** + * @param NetworkInventoryAction[] $actions + */ + public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos, int $blockRuntimeId) : self{ + $result = new self; + $result->actions = $actions; + $result->actionType = $actionType; + $result->blockPos = $blockPos; + $result->face = $face; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->playerPos = $playerPos; + $result->clickPos = $clickPos; + $result->blockRuntimeId = $blockRuntimeId; + return $result; + } +} diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 5b8a923c8b..3fb827c3de 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -695,6 +695,21 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php + - + message: "#^Parameter \\#1 \\$x of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php + + - + message: "#^Parameter \\#2 \\$y of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php + + - + message: "#^Parameter \\#3 \\$z of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php + - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 2 From c8f396ecbc67996c13ba1d186908708989d88d31 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Sun, 21 Mar 2021 16:56:23 +0000 Subject: [PATCH 09/14] [ci skip] doxygen: link to devdoc.pmmp.io instead of the github repo --- doxygen/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/index.md b/doxygen/index.md index 357c82504e..182bd9ecca 100644 --- a/doxygen/index.md +++ b/doxygen/index.md @@ -7,4 +7,4 @@ This site can be accessed via https://apidoc.pmmp.io. ### Additional developer resources - [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins - [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features - - [DeveloperDocs](https://github.com/pmmp/DeveloperDocs/) - Reference, guides and specifications for the PocketMine-MP API + - [DeveloperDocs](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers From cb930958573ec4672791def4da80757d0909651d Mon Sep 17 00:00:00 2001 From: Dylan T Date: Sun, 21 Mar 2021 16:59:58 +0000 Subject: [PATCH 10/14] [ci skip] readme: added link to devdoc.pmmp.io --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bff7e31be2..3d1787e384 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ ## For developers * [Building and running from source](BUILDING.md) + * [Developer documentation](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers * [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development * [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins * [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features From 4416cd5a2854afdc369224897701a498994a2b56 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Mar 2021 13:26:07 +0000 Subject: [PATCH 11/14] Bump phpstan/phpstan from 0.12.81 to 0.12.82 (#4088) Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 0.12.81 to 0.12.82. - [Release notes](https://github.com/phpstan/phpstan/releases) - [Commits](https://github.com/phpstan/phpstan/compare/0.12.81...0.12.82) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index dc38aa48e4..f6eecb7a02 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "pocketmine/spl": "^0.4.0" }, "require-dev": { - "phpstan/phpstan": "0.12.81", + "phpstan/phpstan": "0.12.82", "phpstan/phpstan-phpunit": "^0.12.6", "phpstan/phpstan-strict-rules": "^0.12.2", "phpunit/phpunit": "^9.2" diff --git a/composer.lock b/composer.lock index 4d1daba6de..7c23581437 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": "c731270da66df8e18ea9487d91bc2e3f", + "content-hash": "df0cb7c9eab4da5e693533deb010f9d1", "packages": [ { "name": "adhocore/json-comment", @@ -1002,16 +1002,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.81", + "version": "0.12.82", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8" + "reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", - "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3920f0fb0aff39263d3a4cb0bca120a67a1a6a11", + "reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11", "shasum": "" }, "require": { @@ -1042,7 +1042,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.81" + "source": "https://github.com/phpstan/phpstan/tree/0.12.82" }, "funding": [ { @@ -1058,7 +1058,7 @@ "type": "tidelift" } ], - "time": "2021-03-08T22:03:02+00:00" + "time": "2021-03-19T06:08:17+00:00" }, { "name": "phpstan/phpstan-phpunit", From c7cdaeae8569c766df7db9f889c67f4e65a38d15 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 22 Mar 2021 21:21:11 +0000 Subject: [PATCH 12/14] Revert "Backport InventoryTransactionPacket impl from PM4" This reverts commit cb06be615aa3780d4c83a947520fa55c0d908618. we can't push this to stable because it would break plugins without any way to know (no protocol or API change). At most, this should have been wrapped into a protocol change. --- src/pocketmine/Player.php | 587 +++++++++--------- .../protocol/InventoryTransactionPacket.php | 133 ++-- .../inventory/MismatchTransactionData.php | 50 -- .../types/inventory/NormalTransactionData.php | 54 -- .../inventory/ReleaseItemTransactionData.php | 92 --- .../types/inventory/TransactionData.php | 72 --- .../UseItemOnEntityTransactionData.php | 109 ---- .../inventory/UseItemTransactionData.php | 130 ---- tests/phpstan/configs/l7-baseline.neon | 15 - 9 files changed, 392 insertions(+), 850 deletions(-) delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index a61d435cd2..dcec663c40 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -152,12 +152,7 @@ use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\GameMode; -use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor; use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece; @@ -416,7 +411,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var float */ protected $lastRightClickTime = 0.0; - /** @var UseItemTransactionData|null */ + /** @var \stdClass|null */ protected $lastRightClickData = null; /** @@ -2431,7 +2426,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var InventoryAction[] $actions */ $actions = []; $isCraftingPart = false; - foreach($packet->trData->getActions() as $networkInventoryAction){ + foreach($packet->actions as $networkInventoryAction){ if( $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and ( $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or @@ -2489,313 +2484,321 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->craftingTransaction = null; } - if($packet->trData instanceof NormalTransactionData){ - $this->setUsingItem(false); - $transaction = new InventoryTransaction($this, $actions); + switch($packet->transactionType){ + case InventoryTransactionPacket::TYPE_NORMAL: + $this->setUsingItem(false); + $transaction = new InventoryTransaction($this, $actions); - try{ - $transaction->execute(); - }catch(TransactionValidationException $e){ - $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); - $this->server->getLogger()->debug("Actions: " . json_encode($packet->trData->getActions())); + try{ + $transaction->execute(); + }catch(TransactionValidationException $e){ + $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); + $this->server->getLogger()->debug("Actions: " . json_encode($packet->actions)); - return false; - } + return false; + } - //TODO: fix achievement for getting iron from furnace + //TODO: fix achievement for getting iron from furnace - return true; - }elseif($packet->trData instanceof MismatchTransactionData){ - if(count($packet->trData->getActions()) > 0){ - $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->trData->getActions()) . ", " . json_encode($packet->trData->getActions())); - } - $this->setUsingItem(false); - $this->sendAllInventories(); + return true; + case InventoryTransactionPacket::TYPE_MISMATCH: + if(count($packet->actions) > 0){ + $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions)); + } + $this->setUsingItem(false); + $this->sendAllInventories(); - return true; - }elseif($packet->trData instanceof UseItemTransactionData){ + return true; + case InventoryTransactionPacket::TYPE_USE_ITEM: + $blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z); + $face = $packet->trData->face; - $blockVector = $packet->trData->getBlockPos(); - $face = $packet->trData->getFace(); - - switch($packet->trData->getActionType()){ - case UseItemTransactionData::ACTION_CLICK_BLOCK: - //TODO: start hack for client spam bug - $spamBug = ($this->lastRightClickData !== null and - microtime(true) - $this->lastRightClickTime < 0.1 and //100ms - $this->lastRightClickData->getPlayerPos()->distanceSquared($packet->trData->getPlayerPos()) < 0.00001 and - $this->lastRightClickData->getBlockPos()->equals($packet->trData->getBlockPos()) and - $this->lastRightClickData->getClickPos()->distanceSquared($packet->trData->getClickPos()) < 0.00001 //signature spam bug has 0 distance, but allow some error - ); - //get rid of continued spam if the player clicks and holds right-click - $this->lastRightClickData = $packet->trData; - $this->lastRightClickTime = microtime(true); - if($spamBug){ - return true; - } - //TODO: end hack for client spam bug - - $this->setUsingItem(false); - - if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ - }elseif($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ + $type = $packet->trData->actionType; + switch($type){ + case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK: + //TODO: start hack for client spam bug + $spamBug = ($this->lastRightClickData !== null and + microtime(true) - $this->lastRightClickTime < 0.1 and //100ms + $this->lastRightClickData->playerPos->distanceSquared($packet->trData->playerPos) < 0.00001 and + $this->lastRightClickData->x === $packet->trData->x and + $this->lastRightClickData->y === $packet->trData->y and + $this->lastRightClickData->z === $packet->trData->z and + $this->lastRightClickData->clickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error + ); + //get rid of continued spam if the player clicks and holds right-click + $this->lastRightClickData = $packet->trData; + $this->lastRightClickTime = microtime(true); + if($spamBug){ return true; } - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ - $this->inventory->sendHeldItem($this); - }else{ - $item = $this->inventory->getItemInHand(); - $oldItem = clone $item; - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } + //TODO: end hack for client spam bug - return true; - } - } + $this->setUsingItem(false); - $this->inventory->sendHeldItem($this); - - if($blockVector->distanceSquared($this) > 10000){ - return true; - } - - $target = $this->level->getBlock($blockVector); - $block = $target->getSide($face); - - /** @var Block[] $blocks */ - $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); - - return true; - case UseItemTransactionData::ACTION_BREAK_BLOCK: - $this->doCloseInventory(); - - $item = $this->inventory->getItemInHand(); - $oldItem = clone $item; - - if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ - if($this->isSurvival()){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - - $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); - } - return true; - } - - $this->inventory->sendContents($this); - $this->inventory->sendHeldItem($this); - - $target = $this->level->getBlock($blockVector); - /** @var Block[] $blocks */ - $blocks = $target->getAllSides(); - $blocks[] = $target; - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); - - foreach($blocks as $b){ - $tile = $this->level->getTile($b); - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - } - - return true; - case UseItemTransactionData::ACTION_CLICK_AIR: - if($this->isUsingItem()){ - $slot = $this->inventory->getItemInHand(); - if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ - $ev = new PlayerItemConsumeEvent($this, $slot); - if($this->hasItemCooldown($slot)){ - $ev->setCancelled(); - } - $ev->call(); - if($ev->isCancelled() or !$this->consumeObject($slot)){ - $this->inventory->sendContents($this); + if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ + }elseif($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ return true; } - $this->resetItemCooldown($slot); - if($this->isSurvival()){ - $slot->pop(); - $this->inventory->setItemInHand($slot); - $this->inventory->addItem($slot->getResidue()); - } - $this->setUsingItem(false); - } - } - $directionVector = $this->getDirectionVector(); - - if($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ - $this->inventory->sendHeldItem($this); - return true; - }else{ - $item = $this->inventory->getItemInHand(); - } - - $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); - if($this->hasItemCooldown($item) or $this->isSpectator()){ - $ev->setCancelled(); - } - - $ev->call(); - if($ev->isCancelled()){ - $this->inventory->sendHeldItem($this); - return true; - } - - if($item->onClickAir($this, $directionVector)){ - $this->resetItemCooldown($item); - if($this->isSurvival()){ - $this->inventory->setItemInHand($item); - } - } - - $this->setUsingItem(true); - - return true; - default: - //unknown - break; - } - - $this->inventory->sendContents($this); - return false; - }elseif($packet->trData instanceof UseItemOnEntityTransactionData){ - $target = $this->level->getEntity($packet->trData->getEntityRuntimeId()); - if($target === null){ - return false; - } - - switch($packet->trData->getActionType()){ - case UseItemOnEntityTransactionData::ACTION_INTERACT: - break; //TODO - case UseItemOnEntityTransactionData::ACTION_ATTACK: - if(!$target->isAlive()){ - return true; - } - if($target instanceof ItemEntity or $target instanceof Arrow){ - $this->kick("Attempting to attack an invalid entity"); - $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); - return false; - } - - $cancelled = false; - - $heldItem = $this->inventory->getItemInHand(); - $oldItem = clone $heldItem; - - if(!$this->canInteract($target, 8) or $this->isSpectator()){ - $cancelled = true; - }elseif($target instanceof Player){ - if(!$this->server->getConfigBool("pvp")){ - $cancelled = true; - } - } - - $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); - - $meleeEnchantmentDamage = 0; - /** @var EnchantmentInstance[] $meleeEnchantments */ - $meleeEnchantments = []; - foreach($heldItem->getEnchantments() as $enchantment){ - $type = $enchantment->getType(); - if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ - $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); - $meleeEnchantments[] = $enchantment; - } - } - $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); - - if($cancelled){ - $ev->setCancelled(); - } - - if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ - $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); - } - - $target->attack($ev); - - if($ev->isCancelled()){ - if($heldItem instanceof Durable and $this->isSurvival()){ - $this->inventory->sendContents($this); - } - return true; - } - - if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ - $pk = new AnimatePacket(); - $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; - $pk->entityRuntimeId = $target->getId(); - $this->server->broadcastPacket($target->getViewers(), $pk); - if($target instanceof Player){ - $target->dataPacket($pk); - } - } - - foreach($meleeEnchantments as $enchantment){ - $type = $enchantment->getType(); - assert($type instanceof MeleeWeaponEnchantment); - $type->onPostAttack($this, $target, $enchantment->getLevel()); - } - - if($this->isAlive()){ - //reactive damage like thorns might cause us to be killed by attacking another mob, which - //would mean we'd already have dropped the inventory by the time we reached here - if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival - $this->inventory->setItemInHand($heldItem); - } - - $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); - } - - return true; - default: - break; //unknown - } - - $this->inventory->sendContents($this); - return false; - }elseif($packet->trData instanceof ReleaseItemTransactionData){ - try{ - switch($packet->trData->getActionType()){ - case ReleaseItemTransactionData::ACTION_RELEASE: - if($this->isUsingItem()){ + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ + $this->inventory->sendHeldItem($this); + }else{ $item = $this->inventory->getItemInHand(); - if($this->hasItemCooldown($item)){ - $this->inventory->sendContents($this); - return false; + $oldItem = clone $item; + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + + return true; } - if($item->onReleaseUsing($this)){ - $this->resetItemCooldown($item); - $this->inventory->setItemInHand($item); + } + + $this->inventory->sendHeldItem($this); + + if($blockVector->distanceSquared($this) > 10000){ + return true; + } + + $target = $this->level->getBlock($blockVector); + $block = $target->getSide($face); + + /** @var Block[] $blocks */ + $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + return true; + case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK: + $this->doCloseInventory(); + + $item = $this->inventory->getItemInHand(); + $oldItem = clone $item; + + if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ + if($this->isSurvival()){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + + $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); } return true; } - break; + + $this->inventory->sendContents($this); + $this->inventory->sendHeldItem($this); + + $target = $this->level->getBlock($blockVector); + /** @var Block[] $blocks */ + $blocks = $target->getAllSides(); + $blocks[] = $target; + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + foreach($blocks as $b){ + $tile = $this->level->getTile($b); + if($tile instanceof Spawnable){ + $tile->spawnTo($this); + } + } + + return true; + case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR: + if($this->isUsingItem()){ + $slot = $this->inventory->getItemInHand(); + if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ + $ev = new PlayerItemConsumeEvent($this, $slot); + if($this->hasItemCooldown($slot)){ + $ev->setCancelled(); + } + $ev->call(); + if($ev->isCancelled() or !$this->consumeObject($slot)){ + $this->inventory->sendContents($this); + return true; + } + $this->resetItemCooldown($slot); + if($this->isSurvival()){ + $slot->pop(); + $this->inventory->setItemInHand($slot); + $this->inventory->addItem($slot->getResidue()); + } + $this->setUsingItem(false); + } + } + $directionVector = $this->getDirectionVector(); + + if($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ + $this->inventory->sendHeldItem($this); + return true; + }else{ + $item = $this->inventory->getItemInHand(); + } + + $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); + if($this->hasItemCooldown($item) or $this->isSpectator()){ + $ev->setCancelled(); + } + + $ev->call(); + if($ev->isCancelled()){ + $this->inventory->sendHeldItem($this); + return true; + } + + if($item->onClickAir($this, $directionVector)){ + $this->resetItemCooldown($item); + if($this->isSurvival()){ + $this->inventory->setItemInHand($item); + } + } + + $this->setUsingItem(true); + + return true; default: + //unknown break; } - }finally{ - $this->setUsingItem(false); - } + break; + case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY: + $target = $this->level->getEntity($packet->trData->entityRuntimeId); + if($target === null){ + return false; + } + + $type = $packet->trData->actionType; + + switch($type){ + case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT: + break; //TODO + case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK: + if(!$target->isAlive()){ + return true; + } + if($target instanceof ItemEntity or $target instanceof Arrow){ + $this->kick("Attempting to attack an invalid entity"); + $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); + return false; + } + + $cancelled = false; + + $heldItem = $this->inventory->getItemInHand(); + $oldItem = clone $heldItem; + + if(!$this->canInteract($target, 8) or $this->isSpectator()){ + $cancelled = true; + }elseif($target instanceof Player){ + if(!$this->server->getConfigBool("pvp")){ + $cancelled = true; + } + } + + $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); + + $meleeEnchantmentDamage = 0; + /** @var EnchantmentInstance[] $meleeEnchantments */ + $meleeEnchantments = []; + foreach($heldItem->getEnchantments() as $enchantment){ + $type = $enchantment->getType(); + if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ + $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); + $meleeEnchantments[] = $enchantment; + } + } + $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); + + if($cancelled){ + $ev->setCancelled(); + } + + if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ + $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); + } + + $target->attack($ev); + + if($ev->isCancelled()){ + if($heldItem instanceof Durable and $this->isSurvival()){ + $this->inventory->sendContents($this); + } + return true; + } + + if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ + $pk = new AnimatePacket(); + $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; + $pk->entityRuntimeId = $target->getId(); + $this->server->broadcastPacket($target->getViewers(), $pk); + if($target instanceof Player){ + $target->dataPacket($pk); + } + } + + foreach($meleeEnchantments as $enchantment){ + $type = $enchantment->getType(); + assert($type instanceof MeleeWeaponEnchantment); + $type->onPostAttack($this, $target, $enchantment->getLevel()); + } + + if($this->isAlive()){ + //reactive damage like thorns might cause us to be killed by attacking another mob, which + //would mean we'd already have dropped the inventory by the time we reached here + if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival + $this->inventory->setItemInHand($heldItem); + } + + $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); + } + + return true; + default: + break; //unknown + } + + break; + case InventoryTransactionPacket::TYPE_RELEASE_ITEM: + try{ + $type = $packet->trData->actionType; + switch($type){ + case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: + if($this->isUsingItem()){ + $item = $this->inventory->getItemInHand(); + if($this->hasItemCooldown($item)){ + $this->inventory->sendContents($this); + return false; + } + if($item->onReleaseUsing($this)){ + $this->resetItemCooldown($item); + $this->inventory->setItemInHand($item); + } + }else{ + break; + } + + return true; + default: + break; + } + }finally{ + $this->setUsingItem(false); + } + + $this->inventory->sendContents($this); + break; + default: + $this->inventory->sendContents($this); + break; - $this->inventory->sendContents($this); - return false; - }else{ - $this->inventory->sendContents($this); - return false; } + + return false; //TODO } public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index 0fa447ed69..1c8662fd81 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -25,15 +25,9 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\network\mcpe\NetworkSession as PacketHandlerInterface; +use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\inventory\InventoryTransactionChangedSlotsHack; -use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\TransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; -use UnexpectedValueException as PacketDecodeException; +use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use function count; class InventoryTransactionPacket extends DataPacket{ @@ -45,70 +39,137 @@ class InventoryTransactionPacket extends DataPacket{ public const TYPE_USE_ITEM_ON_ENTITY = 3; public const TYPE_RELEASE_ITEM = 4; + public const USE_ITEM_ACTION_CLICK_BLOCK = 0; + public const USE_ITEM_ACTION_CLICK_AIR = 1; + public const USE_ITEM_ACTION_BREAK_BLOCK = 2; + + public const RELEASE_ITEM_ACTION_RELEASE = 0; //bow shoot + public const RELEASE_ITEM_ACTION_CONSUME = 1; //eat food, drink potion + + public const USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0; + public const USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1; + /** @var int */ public $requestId; /** @var InventoryTransactionChangedSlotsHack[] */ public $requestChangedSlots; + + /** @var int */ + public $transactionType; /** @var bool */ public $hasItemStackIds; - /** @var TransactionData */ + + /** @var NetworkInventoryAction[] */ + public $actions = []; + + /** @var \stdClass */ public $trData; - protected function decodePayload() : void{ - $in = $this; - $this->requestId = $in->readGenericTypeNetworkId(); + protected function decodePayload(){ + $this->requestId = $this->readGenericTypeNetworkId(); $this->requestChangedSlots = []; if($this->requestId !== 0){ - for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ - $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($in); + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($this); } } - $transactionType = $in->getUnsignedVarInt(); + $this->transactionType = $this->getUnsignedVarInt(); - $this->hasItemStackIds = $in->getBool(); + $this->hasItemStackIds = $this->getBool(); - switch($transactionType){ + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $this->actions[] = $action = (new NetworkInventoryAction())->read($this, $this->hasItemStackIds); + } + + $this->trData = new \stdClass(); + + switch($this->transactionType){ case self::TYPE_NORMAL: - $this->trData = new NormalTransactionData(); - break; case self::TYPE_MISMATCH: - $this->trData = new MismatchTransactionData(); + //Regular ComplexInventoryTransaction doesn't read any extra data break; case self::TYPE_USE_ITEM: - $this->trData = new UseItemTransactionData(); + $this->trData->actionType = $this->getUnsignedVarInt(); + $this->getBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); + $this->trData->face = $this->getVarInt(); + $this->trData->hotbarSlot = $this->getVarInt(); + $this->trData->itemInHand = $this->getSlot(); + $this->trData->playerPos = $this->getVector3(); + $this->trData->clickPos = $this->getVector3(); + $this->trData->blockRuntimeId = $this->getUnsignedVarInt(); break; case self::TYPE_USE_ITEM_ON_ENTITY: - $this->trData = new UseItemOnEntityTransactionData(); + $this->trData->entityRuntimeId = $this->getEntityRuntimeId(); + $this->trData->actionType = $this->getUnsignedVarInt(); + $this->trData->hotbarSlot = $this->getVarInt(); + $this->trData->itemInHand = $this->getSlot(); + $this->trData->playerPos = $this->getVector3(); + $this->trData->clickPos = $this->getVector3(); break; case self::TYPE_RELEASE_ITEM: - $this->trData = new ReleaseItemTransactionData(); + $this->trData->actionType = $this->getUnsignedVarInt(); + $this->trData->hotbarSlot = $this->getVarInt(); + $this->trData->itemInHand = $this->getSlot(); + $this->trData->headPos = $this->getVector3(); break; default: - throw new PacketDecodeException("Unknown transaction type $transactionType"); + throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); } - - $this->trData->decode($in, $this->hasItemStackIds); } - protected function encodePayload() : void{ - $out = $this; - $out->writeGenericTypeNetworkId($this->requestId); + protected function encodePayload(){ + $this->writeGenericTypeNetworkId($this->requestId); if($this->requestId !== 0){ - $out->putUnsignedVarInt(count($this->requestChangedSlots)); + $this->putUnsignedVarInt(count($this->requestChangedSlots)); foreach($this->requestChangedSlots as $changedSlots){ - $changedSlots->write($out); + $changedSlots->write($this); } } - $out->putUnsignedVarInt($this->trData->getTypeId()); + $this->putUnsignedVarInt($this->transactionType); - $out->putBool($this->hasItemStackIds); + $this->putBool($this->hasItemStackIds); - $this->trData->encode($out, $this->hasItemStackIds); + $this->putUnsignedVarInt(count($this->actions)); + foreach($this->actions as $action){ + $action->write($this, $this->hasItemStackIds); + } + + switch($this->transactionType){ + case self::TYPE_NORMAL: + case self::TYPE_MISMATCH: + break; + case self::TYPE_USE_ITEM: + $this->putUnsignedVarInt($this->trData->actionType); + $this->putBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); + $this->putVarInt($this->trData->face); + $this->putVarInt($this->trData->hotbarSlot); + $this->putSlot($this->trData->itemInHand); + $this->putVector3($this->trData->playerPos); + $this->putVector3($this->trData->clickPos); + $this->putUnsignedVarInt($this->trData->blockRuntimeId); + break; + case self::TYPE_USE_ITEM_ON_ENTITY: + $this->putEntityRuntimeId($this->trData->entityRuntimeId); + $this->putUnsignedVarInt($this->trData->actionType); + $this->putVarInt($this->trData->hotbarSlot); + $this->putSlot($this->trData->itemInHand); + $this->putVector3($this->trData->playerPos); + $this->putVector3($this->trData->clickPos); + break; + case self::TYPE_RELEASE_ITEM: + $this->putUnsignedVarInt($this->trData->actionType); + $this->putVarInt($this->trData->hotbarSlot); + $this->putSlot($this->trData->itemInHand); + $this->putVector3($this->trData->headPos); + break; + default: + throw new \InvalidArgumentException("Unknown transaction type $this->transactionType"); + } } - public function handle(PacketHandlerInterface $handler) : bool{ - return $handler->handleInventoryTransaction($this); + public function handle(NetworkSession $session) : bool{ + return $session->handleInventoryTransaction($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php deleted file mode 100644 index 2ee19c64d7..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php +++ /dev/null @@ -1,50 +0,0 @@ -actions) > 0){ - throw new PacketDecodeException("Mismatch transaction type should not have any actions associated with it, but got " . count($this->actions)); - } - } - - protected function encodeData(PacketSerializer $stream) : void{ - - } - - public static function new() : self{ - return new self; //no arguments - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php deleted file mode 100644 index 757bf9d629..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php +++ /dev/null @@ -1,54 +0,0 @@ -actions = $actions; - return $result; - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php deleted file mode 100644 index 96b359d17d..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php +++ /dev/null @@ -1,92 +0,0 @@ -actionType; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getHeadPos() : Vector3{ - return $this->headPos; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_RELEASE_ITEM; - } - - protected function decodeData(PacketSerializer $stream) : void{ - $this->actionType = $stream->getUnsignedVarInt(); - $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); - $this->headPos = $stream->getVector3(); - } - - protected function encodeData(PacketSerializer $stream) : void{ - $stream->putUnsignedVarInt($this->actionType); - $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); - $stream->putVector3($this->headPos); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $headPos) : self{ - $result = new self; - $result->actions = $actions; - $result->actionType = $actionType; - $result->hotbarSlot = $hotbarSlot; - $result->itemInHand = $itemInHand; - $result->headPos = $headPos; - - return $result; - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php deleted file mode 100644 index 23089c71b4..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php +++ /dev/null @@ -1,72 +0,0 @@ -actions; - } - - abstract public function getTypeId() : int; - - /** - * @throws BinaryDataException - * @throws PacketDecodeException - */ - final public function decode(PacketSerializer $stream, bool $hasItemStackIds) : void{ - $actionCount = $stream->getUnsignedVarInt(); - for($i = 0; $i < $actionCount; ++$i){ - $this->actions[] = (new NetworkInventoryAction())->read($stream, $hasItemStackIds); - } - $this->decodeData($stream); - } - - /** - * @throws BinaryDataException - * @throws PacketDecodeException - */ - abstract protected function decodeData(PacketSerializer $stream) : void; - - final public function encode(PacketSerializer $stream, bool $hasItemStackIds) : void{ - $stream->putUnsignedVarInt(count($this->actions)); - foreach($this->actions as $action){ - $action->write($stream, $hasItemStackIds); - } - $this->encodeData($stream); - } - - abstract protected function encodeData(PacketSerializer $stream) : void; -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php deleted file mode 100644 index f88bbca73c..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php +++ /dev/null @@ -1,109 +0,0 @@ -entityRuntimeId; - } - - public function getActionType() : int{ - return $this->actionType; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getPlayerPos() : Vector3{ - return $this->playerPos; - } - - public function getClickPos() : Vector3{ - return $this->clickPos; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY; - } - - protected function decodeData(PacketSerializer $stream) : void{ - $this->entityRuntimeId = $stream->getEntityRuntimeId(); - $this->actionType = $stream->getUnsignedVarInt(); - $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); - $this->playerPos = $stream->getVector3(); - $this->clickPos = $stream->getVector3(); - } - - protected function encodeData(PacketSerializer $stream) : void{ - $stream->putEntityRuntimeId($this->entityRuntimeId); - $stream->putUnsignedVarInt($this->actionType); - $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); - $stream->putVector3($this->playerPos); - $stream->putVector3($this->clickPos); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ - $result = new self; - $result->actions = $actions; - $result->entityRuntimeId = $entityRuntimeId; - $result->actionType = $actionType; - $result->hotbarSlot = $hotbarSlot; - $result->itemInHand = $itemInHand; - $result->playerPos = $playerPos; - $result->clickPos = $clickPos; - return $result; - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php deleted file mode 100644 index 5c1872bea5..0000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php +++ /dev/null @@ -1,130 +0,0 @@ -actionType; - } - - public function getBlockPos() : Vector3{ - return $this->blockPos; - } - - public function getFace() : int{ - return $this->face; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getPlayerPos() : Vector3{ - return $this->playerPos; - } - - public function getClickPos() : Vector3{ - return $this->clickPos; - } - - public function getBlockRuntimeId() : int{ - return $this->blockRuntimeId; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_USE_ITEM; - } - - protected function decodeData(PacketSerializer $stream) : void{ - $this->actionType = $stream->getUnsignedVarInt(); - $x = $y = $z = 0; - $stream->getBlockPosition($x, $y, $z); - $this->blockPos = new Vector3($x, $y, $z); - $this->face = $stream->getVarInt(); - $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); - $this->playerPos = $stream->getVector3(); - $this->clickPos = $stream->getVector3(); - $this->blockRuntimeId = $stream->getUnsignedVarInt(); - } - - protected function encodeData(PacketSerializer $stream) : void{ - $stream->putUnsignedVarInt($this->actionType); - $stream->putBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z); - $stream->putVarInt($this->face); - $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); - $stream->putVector3($this->playerPos); - $stream->putVector3($this->clickPos); - $stream->putUnsignedVarInt($this->blockRuntimeId); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos, int $blockRuntimeId) : self{ - $result = new self; - $result->actions = $actions; - $result->actionType = $actionType; - $result->blockPos = $blockPos; - $result->face = $face; - $result->hotbarSlot = $hotbarSlot; - $result->itemInHand = $itemInHand; - $result->playerPos = $playerPos; - $result->clickPos = $clickPos; - $result->blockRuntimeId = $blockRuntimeId; - return $result; - } -} diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 3fb827c3de..5b8a923c8b 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -695,21 +695,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php - - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 2 From 3333df31df685bccc6516d7292039f653d1dd7fd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Mar 2021 13:53:45 +0000 Subject: [PATCH 13/14] Bump phpunit/phpunit from 9.5.3 to 9.5.4 (#4104) Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.3 to 9.5.4. - [Release notes](https://github.com/sebastianbergmann/phpunit/releases) - [Changelog](https://github.com/sebastianbergmann/phpunit/blob/master/ChangeLog-9.5.md) - [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.3...9.5.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 7c23581437..7273d5b4d7 100644 --- a/composer.lock +++ b/composer.lock @@ -935,16 +935,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.12.2", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "245710e971a030f42e08f4912863805570f23d39" + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39", - "reference": "245710e971a030f42e08f4912863805570f23d39", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", "shasum": "" }, "require": { @@ -996,9 +996,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.12.2" + "source": "https://github.com/phpspec/prophecy/tree/1.13.0" }, - "time": "2020-12-19T10:15:11+00:00" + "time": "2021-03-17T13:42:18+00:00" }, { "name": "phpstan/phpstan", @@ -1486,16 +1486,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.3", + "version": "9.5.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41" + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/27241ac75fc37ecf862b6e002bf713b6566cbe41", - "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741", + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741", "shasum": "" }, "require": { @@ -1573,7 +1573,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4" }, "funding": [ { @@ -1585,7 +1585,7 @@ "type": "github" } ], - "time": "2021-03-17T07:30:34+00:00" + "time": "2021-03-23T07:16:29+00:00" }, { "name": "sebastian/cli-parser", From fb20bb38327b4c08ee3976640cd0dd547388a638 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 23 Mar 2021 14:53:53 +0000 Subject: [PATCH 14/14] Don't handle NAN/INF in movements --- src/pocketmine/Player.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index dcec663c40..b59da0c12f 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -196,7 +196,9 @@ use function get_class; use function gettype; use function implode; use function in_array; +use function is_infinite; use function is_int; +use function is_nan; use function is_object; use function is_string; use function json_encode; @@ -2357,8 +2359,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } public function handleMovePlayer(MovePlayerPacket $packet) : bool{ - $newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0); + $rawPos = $packet->position; + foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->yaw, $packet->headYaw, $packet->pitch] as $float){ + if(is_infinite($float) || is_nan($float)){ + $this->server->getLogger()->debug("Invalid movement from " . $this->getName() . ", contains NAN/INF components"); + return false; + } + } + $newPos = $rawPos->round(4)->subtract(0, $this->baseOffset, 0); if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks $this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3()); //Still getting movements from before teleport, ignore them