From eb62dc32948870333ce18c5f642273bf70f1b198 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 30 Nov 2022 21:04:17 +0000 Subject: [PATCH 1/7] Added documentation for RegistryTrait, EnumTrait and CloningRegistryTrait --- src/utils/CloningRegistryTrait.php | 4 ++++ src/utils/EnumTrait.php | 7 +++++++ src/utils/RegistryTrait.php | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/src/utils/CloningRegistryTrait.php b/src/utils/CloningRegistryTrait.php index c69b38d7d..d160b1f45 100644 --- a/src/utils/CloningRegistryTrait.php +++ b/src/utils/CloningRegistryTrait.php @@ -23,6 +23,10 @@ declare(strict_types=1); namespace pocketmine\utils; +/** + * This trait offers the same functionality as RegistryTrait, but also clones any returned objects to prevent outside + * modification. + */ trait CloningRegistryTrait{ use RegistryTrait; diff --git a/src/utils/EnumTrait.php b/src/utils/EnumTrait.php index c9d24e0fc..ba0a4fcf2 100644 --- a/src/utils/EnumTrait.php +++ b/src/utils/EnumTrait.php @@ -23,6 +23,13 @@ declare(strict_types=1); namespace pocketmine\utils; +/** + * This trait allows a class to simulate a Java-style enum. Members are exposed as static methods and handled via + * __callStatic(). + * + * Classes using this trait need to include \@method tags in their class docblock for every enum member. + * Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php + */ trait EnumTrait{ use RegistryTrait; use NotCloneable; diff --git a/src/utils/RegistryTrait.php b/src/utils/RegistryTrait.php index 776980599..f8ffc1143 100644 --- a/src/utils/RegistryTrait.php +++ b/src/utils/RegistryTrait.php @@ -28,6 +28,13 @@ use function count; use function mb_strtoupper; use function preg_match; +/** + * This trait allows a class to simulate object class constants, since PHP doesn't currently support this. + * These faux constants are exposed in static class methods, which are handled using __callStatic(). + * + * Classes using this trait need to include \@method tags in their class docblock for every faux constant. + * Alternatively, just put \@generate-registry-docblock in the docblock and run tools/generate-registry-annotations.php + */ trait RegistryTrait{ /** * @var object[] From 43bc3c7b25b91e67f517862493acb3bc89bba32d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 1 Dec 2022 19:35:59 +0000 Subject: [PATCH 2/7] Added tool to dump JSON contents of encoded crashdump data --- tools/decode-crashdump.php | 87 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tools/decode-crashdump.php diff --git a/tools/decode-crashdump.php b/tools/decode-crashdump.php new file mode 100644 index 000000000..b73cc201f --- /dev/null +++ b/tools/decode-crashdump.php @@ -0,0 +1,87 @@ + $line){ + if(trim($line) === "===BEGIN CRASH DUMP==="){ + $start = $num + 1; + break; + } +} + +if($start === -1){ + fwrite(STDERR, "Crashdump encoded data not found in target file" . PHP_EOL); + exit(1); +} + +$data = array_slice($lines, $start); +array_pop($data); + +$zlibData = base64_decode(implode("", $data), true); +if($zlibData === false){ + fwrite(STDERR, "Invalid encoded data in crashdump" . PHP_EOL); + exit(1); +} +$decoded = zlib_decode($zlibData); +if($decoded === false){ + fwrite(STDERR, "Invalid compressed data in crashdump" . PHP_EOL); + exit(1); +} + +file_put_contents($output, json_encode(json_decode($decoded), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); +echo "Wrote decoded crashdump to " . realpath($output) . PHP_EOL; From 3984d220bb5a3c72214eab116cf1dce26dc8f055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Le=C3=B3n?= <58715544+JavierLeon9966@users.noreply.github.com> Date: Thu, 1 Dec 2022 17:38:41 -0300 Subject: [PATCH 3/7] Implemented the swift sneak enchantment (#5404) Co-authored-by: Dylan T closes #5301 --- src/data/bedrock/EnchantmentIdMap.php | 2 ++ src/data/bedrock/EnchantmentIds.php | 1 + src/item/enchantment/StringToEnchantmentParser.php | 1 + src/item/enchantment/VanillaEnchantments.php | 3 +++ 4 files changed, 7 insertions(+) diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index 3206e6462..d7f436fa3 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -73,6 +73,8 @@ final class EnchantmentIdMap{ $this->register(EnchantmentIds::MENDING, VanillaEnchantments::MENDING()); $this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING()); + + $this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK()); } public function register(int $mcpeId, Enchantment $enchantment) : void{ diff --git a/src/data/bedrock/EnchantmentIds.php b/src/data/bedrock/EnchantmentIds.php index 19184f24b..0c36f54ed 100644 --- a/src/data/bedrock/EnchantmentIds.php +++ b/src/data/bedrock/EnchantmentIds.php @@ -66,4 +66,5 @@ final class EnchantmentIds{ public const PIERCING = 34; public const QUICK_CHARGE = 35; public const SOUL_SPEED = 36; + public const SWIFT_SNEAK = 37; } diff --git a/src/item/enchantment/StringToEnchantmentParser.php b/src/item/enchantment/StringToEnchantmentParser.php index ec4b1d1c6..e76e71642 100644 --- a/src/item/enchantment/StringToEnchantmentParser.php +++ b/src/item/enchantment/StringToEnchantmentParser.php @@ -53,6 +53,7 @@ final class StringToEnchantmentParser extends StringToTParser{ $result->register("respiration", fn() => VanillaEnchantments::RESPIRATION()); $result->register("sharpness", fn() => VanillaEnchantments::SHARPNESS()); $result->register("silk_touch", fn() => VanillaEnchantments::SILK_TOUCH()); + $result->register("swift_sneak", fn() => VanillaEnchantments::SWIFT_SNEAK()); $result->register("thorns", fn() => VanillaEnchantments::THORNS()); $result->register("unbreaking", fn() => VanillaEnchantments::UNBREAKING()); $result->register("vanishing", fn() => VanillaEnchantments::VANISHING()); diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index 09bafb8d9..2be5eed71 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -49,6 +49,7 @@ use pocketmine\utils\RegistryTrait; * @method static Enchantment RESPIRATION() * @method static SharpnessEnchantment SHARPNESS() * @method static Enchantment SILK_TOUCH() + * @method static Enchantment SWIFT_SNEAK() * @method static Enchantment THORNS() * @method static Enchantment UNBREAKING() * @method static Enchantment VANISHING() @@ -95,6 +96,8 @@ final class VanillaEnchantments{ self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1)); self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1)); + + self::register("SWIFT_SNEAK", new Enchantment(KnownTranslationFactory::enchantment_swift_sneak(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::LEGS, 3)); } protected static function register(string $name, Enchantment $member) : void{ From 774e23137ee687baf0515544b237bbc3724ecda1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 1 Dec 2022 20:48:38 +0000 Subject: [PATCH 4/7] changelog: added detail about InventoryManager internals changes this isn't considered part of the API, but certain specialized popular plugins (e.g. InvMenu) require it. --- changelogs/4.11.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelogs/4.11.md b/changelogs/4.11.md index fff7845ea..96ab00a47 100644 --- a/changelogs/4.11.md +++ b/changelogs/4.11.md @@ -95,6 +95,10 @@ Released 25th November 2022. - Improved performance of `ContainerTrait` dropping items on block destroy. ([link](https://github.com/pmmp/PocketMine-MP/commits/24e72ec109c1442b09558df89b6833cf2f2e0ec7)) - Avoid repeated calls to `Position->getWorld()` (use local variables). ([link](https://github.com/pmmp/PocketMine-MP/commit/2940547026db40ce76deb46e992870de3ead79ad)) - Revamped the way `InventoryManager` handles fake inventory slot mappings for stuff like crafting tables. ([link](https://github.com/pmmp/PocketMine-MP/commit/e90abecf38d9c57635fa0497514bba7e546a2469)) + - Inventories are now mapped on a per-slot basis. This means that more than one inventory can be mapped to the same window ID, which is necessary for correctly handling "UI" inventories like crafting tables. + - `InventoryManager->getWindow(int $windowId) : ?Inventory` is replaced by `locateWindowAndSlot` (see below). + - Added `InventoryManager->locateWindowAndSlot(int $windowId, int $netSlotId) : array{Inventory, int}` - accepts a window ID and absolute slot ID, and returns the associated inventory and the slot relative to the inventory's own start (for use with `getItem()` etc.). + - Slot offset mapping for "UI" inventories is now handled in `InventoryManager->createComplexSlotMapping()` instead of in `TypeConverter`. - Console polling is now done on the main thread (no longer a performance concern). ([link](https://github.com/pmmp/PocketMine-MP/commit/b3f03d7ae645de67a54b7300c09b94eeca16298e)) - Console reader subprocess should now automatically die if the server main process is killed, instead of persisting as a zombie. ([link](https://github.com/pmmp/PocketMine-MP/commit/2585160ca2c4df5758b8b980331307402ff9f0fb)) - `ConsoleCommandSender` is no longer responsible for relaying broadcast messages to `MainLogger`. A new `BroadcastLoggerForwarder` has been added, which is subscribed to the appropriate server broadcast channels in order to relay messages. This ensures that chat messages and command audit messages are logged. ([link](https://github.com/pmmp/PocketMine-MP/commit/83e5b0adb6fa0dddec377182bb1c7945ac8f7820)) From b5cfab497dc75ff884b5d91831d5a2f7a5016d99 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 Dec 2022 23:05:30 +0000 Subject: [PATCH 5/7] Clean up inventory content syncing, fixes #5441 these remnants should have been cleaned up in 4.11, but I somehow managed to skip over them. --- src/network/mcpe/InventoryManager.php | 6 ++-- .../mcpe/handler/InGamePacketHandler.php | 31 +++---------------- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 2aea0e0da..fff1e10c7 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -191,8 +191,10 @@ class InventoryManager{ */ public function addPredictedSlotChanges(array $networkInventoryActions) : void{ foreach($networkInventoryActions as $action){ - if($action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && isset($this->windowMap[$action->windowId])){ - //this won't cover stuff like crafting grid due to too much magic + if($action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && ( + isset($this->windowMap[$action->windowId]) || + ($action->windowId === ContainerIds::UI && isset($this->complexSlotToWindowMap[$action->inventorySlot])) + )){ try{ $item = TypeConverter::getInstance()->netItemStackToCore($action->newItem->getItemStack()); }catch(TypeConversionException $e){ diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 53bcd0345..970cebdc5 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -324,9 +324,7 @@ class InGamePacketHandler extends PacketHandler{ $result = $this->handleReleaseItemTransaction($packet->trData); } - if(!$result){ - $this->inventoryManager->syncAll(); - }else{ + if($this->craftingTransaction === null){ //don't sync if we're waiting to complete a crafting transaction $this->inventoryManager->syncMismatchedPredictedSlotChanges(); } return $result; @@ -361,6 +359,7 @@ class InGamePacketHandler extends PacketHandler{ return false; } } + $this->inventoryManager->addPredictedSlotChanges($data->getActions()); if($isCraftingPart){ if($this->craftingTransaction === null){ @@ -381,15 +380,9 @@ class InGamePacketHandler extends PacketHandler{ } $this->player->setUsingItem(false); try{ - $this->inventoryManager->onTransactionStart($this->craftingTransaction); $this->craftingTransaction->execute(); }catch(TransactionException $e){ $this->session->getLogger()->debug("Failed to execute crafting transaction: " . $e->getMessage()); - - //TODO: only sync slots that the client tried to change - foreach($this->craftingTransaction->getInventories() as $inventory){ - $this->inventoryManager->syncContents($inventory); - } return false; }finally{ $this->craftingTransaction = null; @@ -409,18 +402,12 @@ class InGamePacketHandler extends PacketHandler{ $this->player->setUsingItem(false); $transaction = new InventoryTransaction($this->player, $actions); - $this->inventoryManager->onTransactionStart($transaction); try{ $transaction->execute(); }catch(TransactionException $e){ $logger = $this->session->getLogger(); $logger->debug("Failed to execute inventory transaction: " . $e->getMessage()); $logger->debug("Actions: " . json_encode($data->getActions())); - - foreach($transaction->getInventories() as $inventory){ - $this->inventoryManager->syncContents($inventory); - } - return false; } } @@ -430,7 +417,6 @@ class InGamePacketHandler extends PacketHandler{ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{ $this->player->selectHotbarSlot($data->getHotbarSlot()); - $this->inventoryManager->addPredictedSlotChanges($data->getActions()); switch($data->getActionType()){ case UseItemTransactionData::ACTION_CLICK_BLOCK: @@ -516,9 +502,7 @@ class InGamePacketHandler extends PacketHandler{ } $this->player->selectHotbarSlot($data->getHotbarSlot()); - $this->inventoryManager->addPredictedSlotChanges($data->getActions()); - //TODO: use transactiondata for rollbacks here switch($data->getActionType()){ case UseItemOnEntityTransactionData::ACTION_INTERACT: $this->player->interactEntity($target, $data->getClickPosition()); @@ -533,15 +517,10 @@ class InGamePacketHandler extends PacketHandler{ private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{ $this->player->selectHotbarSlot($data->getHotbarSlot()); - $this->inventoryManager->addPredictedSlotChanges($data->getActions()); - //TODO: use transactiondata for rollbacks here (resending entire inventory is very wasteful) - switch($data->getActionType()){ - case ReleaseItemTransactionData::ACTION_RELEASE: - if(!$this->player->releaseHeldItem()){ - $this->inventoryManager->syncContents($this->player->getInventory()); - } - return true; + if($data->getActionType() == ReleaseItemTransactionData::ACTION_RELEASE){ + $this->player->releaseHeldItem(); + return true; } return false; From 7ae6425d05cce581854f44c13845635f2ce0eef2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 Dec 2022 23:11:48 +0000 Subject: [PATCH 6/7] Release 4.12.1 --- changelogs/4.12.md | 6 ++++++ src/VersionInfo.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelogs/4.12.md b/changelogs/4.12.md index 62ead0b40..efd53e528 100644 --- a/changelogs/4.12.md +++ b/changelogs/4.12.md @@ -12,3 +12,9 @@ Released 30th November 2022. ## General - Added support for Minecraft: Bedrock Edition 1.19.50. - Removed support for older versions. + +# 4.12.1 +Released 4th December 2022. + +## Fixes +- Fixed items glitching when dragging a stack of items across the crafting grid (desync issues). diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 6bb89aba5..1a3e2ba48 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "4.12.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; private function __construct(){ From 0a9b52618d4ab570be914dc8083445714ca5df2b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 4 Dec 2022 23:11:48 +0000 Subject: [PATCH 7/7] 4.12.2 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 1a3e2ba48..3519fd039 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "4.12.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "4.12.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; private function __construct(){