mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-12 06:25:32 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
commit
0330b25768
8
.github/workflows/build-docker-image.yml
vendored
8
.github/workflows/build-docker-image.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build image for tag
|
||||
uses: docker/build-push-action@v6.16.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
- name: Build image for major tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.16.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Build image for minor tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.16.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Build image for latest tag
|
||||
if: steps.channel.outputs.CHANNEL == 'stable'
|
||||
uses: docker/build-push-action@v6.16.0
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
with:
|
||||
push: true
|
||||
context: ./pocketmine-mp
|
||||
|
@ -36,7 +36,7 @@
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60",
|
||||
"pocketmine/bedrock-data": "~5.0.0+bedrock-1.21.80",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50",
|
||||
"pocketmine/bedrock-protocol": "~38.0.0+bedrock-1.21.80",
|
||||
"pocketmine/bedrock-protocol": "~38.1.0+bedrock-1.21.80",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
@ -45,14 +45,14 @@
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.1.0",
|
||||
"pocketmine/raklib": "~1.1.2",
|
||||
"pocketmine/raklib": "~1.2.0",
|
||||
"pocketmine/raklib-ipc": "~1.0.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
"ramsey/uuid": "~4.7.0",
|
||||
"ramsey/uuid": "~4.8.0",
|
||||
"symfony/filesystem": "~6.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "2.1.16",
|
||||
"phpstan/phpstan": "2.1.17",
|
||||
"phpstan/phpstan-phpunit": "^2.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.0",
|
||||
"phpunit/phpunit": "^10.5.24"
|
||||
|
117
composer.lock
generated
117
composer.lock
generated
@ -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": "b106b34fbd6c8abdfd45931bcb18bb69",
|
||||
"content-hash": "7c3052613e98e566d8b00ae3c9119057",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -67,16 +67,16 @@
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.12.3",
|
||||
"version": "0.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
|
||||
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -115,7 +115,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.12.3"
|
||||
"source": "https://github.com/brick/math/tree/0.13.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -123,7 +123,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-28T13:11:00+00:00"
|
||||
"time": "2025-03-29T13:50:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "netresearch/jsonmapper",
|
||||
@ -256,16 +256,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "38.0.1+bedrock-1.21.80",
|
||||
"version": "38.1.0+bedrock-1.21.80",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f"
|
||||
"reference": "a1fa215563517050045309bb779a67f75843b867"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f",
|
||||
"reference": "0c1c13e970a2e1ded1609d0b442b4fcfd24cd21f",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/a1fa215563517050045309bb779a67f75843b867",
|
||||
"reference": "a1fa215563517050045309bb779a67f75843b867",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -296,9 +296,9 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/38.0.1+bedrock-1.21.80"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/38.1.0+bedrock-1.21.80"
|
||||
},
|
||||
"time": "2025-05-17T11:56:33+00:00"
|
||||
"time": "2025-05-28T22:19:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -618,16 +618,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f"
|
||||
"reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/4145a31cd812fe8931c3c9c691fcd2ded2f47e7f",
|
||||
"reference": "4145a31cd812fe8931c3c9c691fcd2ded2f47e7f",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/a28d05216d34dbd00e8aed827a58df6b4c11510b",
|
||||
"reference": "a28d05216d34dbd00e8aed827a58df6b4c11510b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -655,9 +655,9 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/1.1.2"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/1.2.0"
|
||||
},
|
||||
"time": "2025-04-06T03:38:21+00:00"
|
||||
"time": "2025-06-08T17:36:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib-ipc",
|
||||
@ -818,20 +818,20 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.7.6",
|
||||
"version": "4.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "91039bc1faa45ba123c4328958e620d382ec7088"
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
|
||||
"reference": "91039bc1faa45ba123c4328958e620d382ec7088",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
|
||||
"ext-json": "*",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
@ -840,26 +840,23 @@
|
||||
"rhumsaa/uuid": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"captainhook/captainhook": "^5.10",
|
||||
"captainhook/captainhook": "^5.25",
|
||||
"captainhook/plugin-composer": "^5.3",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
|
||||
"doctrine/annotations": "^1.8",
|
||||
"ergebnis/composer-normalize": "^2.15",
|
||||
"mockery/mockery": "^1.3",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||
"ergebnis/composer-normalize": "^2.47",
|
||||
"mockery/mockery": "^1.6",
|
||||
"paragonie/random-lib": "^2",
|
||||
"php-mock/php-mock": "^2.2",
|
||||
"php-mock/php-mock-mockery": "^1.3",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.1",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-mockery": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.1",
|
||||
"phpunit/phpunit": "^8.5 || ^9",
|
||||
"ramsey/composer-repl": "^1.4",
|
||||
"slevomat/coding-standard": "^8.4",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"vimeo/psalm": "^4.9"
|
||||
"php-mock/php-mock": "^2.6",
|
||||
"php-mock/php-mock-mockery": "^1.5",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.4.0",
|
||||
"phpbench/phpbench": "^1.2.14",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-mockery": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"slevomat/coding-standard": "^8.18",
|
||||
"squizlabs/php_codesniffer": "^3.13"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
|
||||
@ -894,19 +891,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.6"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.8.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ramsey",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-27T21:32:50+00:00"
|
||||
"time": "2025-06-01T06:28:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
@ -1038,16 +1025,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.4.0",
|
||||
"version": "v5.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494"
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1090,9 +1077,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
|
||||
},
|
||||
"time": "2024-12-30T11:07:19+00:00"
|
||||
"time": "2025-05-31T08:24:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -1214,16 +1201,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.16",
|
||||
"version": "2.1.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9"
|
||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9",
|
||||
"reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053",
|
||||
"reference": "89b5ef665716fa2a52ecd2633f21007a6a349053",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1268,7 +1255,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-16T09:40:10+00:00"
|
||||
"time": "2025-05-21T20:55:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
|
@ -25,11 +25,12 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CropGrowthHelper;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use function array_rand;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Stem extends Crops{
|
||||
protected int $facing = Facing::UP;
|
||||
@ -90,8 +91,10 @@ abstract class Stem extends Crops{
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
//TODO: bit annoying we have to pass an Item instance here
|
||||
//this should not be affected by Fortune, but still follows a binomial distribution
|
||||
return [
|
||||
$this->asItem()->setCount(mt_rand(0, 2))
|
||||
$this->asItem()->setCount(FortuneDropHelper::binomial(VanillaItems::AIR(), 0, chance: ($this->age + 1) / 15))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{
|
||||
$slotItem->setCount($slotItem->getCount() + $amount);
|
||||
$this->setItem($i, $slotItem);
|
||||
if($newItem->getCount() <= 0){
|
||||
break;
|
||||
return $newItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,7 +270,7 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{
|
||||
$slotItem->setCount($amount);
|
||||
$this->setItem($slotIndex, $slotItem);
|
||||
if($newItem->getCount() <= 0){
|
||||
break;
|
||||
return $newItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\item\enchantment\EnchantingOption;
|
||||
use pocketmine\item\enchantment\EnchantmentInstance;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\cache\CreativeInventoryCache;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
|
||||
@ -228,17 +229,25 @@ class InventoryManager{
|
||||
return null;
|
||||
}
|
||||
|
||||
private function addPredictedSlotChange(Inventory $inventory, int $slot, ItemStack $item) : void{
|
||||
private function addPredictedSlotChangeInternal(Inventory $inventory, int $slot, ItemStack $item) : void{
|
||||
$this->inventories[spl_object_id($inventory)]->predictions[$slot] = $item;
|
||||
}
|
||||
|
||||
public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{
|
||||
public function addPredictedSlotChange(Inventory $inventory, int $slot, Item $item) : void{
|
||||
$typeConverter = $this->session->getTypeConverter();
|
||||
$itemStack = $typeConverter->coreItemStackToNet($item);
|
||||
$this->addPredictedSlotChangeInternal($inventory, $slot, $itemStack);
|
||||
}
|
||||
|
||||
public function addTransactionPredictedSlotChanges(InventoryTransaction $tx) : void{
|
||||
foreach($tx->getActions() as $action){
|
||||
if($action instanceof SlotChangeAction){
|
||||
//TODO: ItemStackRequestExecutor can probably build these predictions with much lower overhead
|
||||
$itemStack = $typeConverter->coreItemStackToNet($action->getTargetItem());
|
||||
$this->addPredictedSlotChange($action->getInventory(), $action->getSlot(), $itemStack);
|
||||
$this->addPredictedSlotChange(
|
||||
$action->getInventory(),
|
||||
$action->getSlot(),
|
||||
$action->getTargetItem()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,7 +276,7 @@ class InventoryManager{
|
||||
}
|
||||
|
||||
[$inventory, $slot] = $info;
|
||||
$this->addPredictedSlotChange($inventory, $slot, $action->newItem->getItemStack());
|
||||
$this->addPredictedSlotChangeInternal($inventory, $slot, $action->newItem->getItemStack());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,8 @@ class InGamePacketHandler extends PacketHandler{
|
||||
protected ?float $lastPlayerAuthInputPitch = null;
|
||||
protected ?BitSet $lastPlayerAuthInputFlags = null;
|
||||
|
||||
protected ?BlockPosition $lastBlockAttacked = null;
|
||||
|
||||
public bool $forceMoveSync = false;
|
||||
|
||||
protected ?string $lastRequestedFullSkinId = null;
|
||||
@ -211,7 +213,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
|
||||
$inputFlags = $packet->getInputFlags();
|
||||
if($inputFlags !== $this->lastPlayerAuthInputFlags){
|
||||
if($this->lastPlayerAuthInputFlags === null || !$inputFlags->equals($this->lastPlayerAuthInputFlags)){
|
||||
$this->lastPlayerAuthInputFlags = $inputFlags;
|
||||
|
||||
$sneaking = $inputFlags->get(PlayerAuthInputFlags::SNEAKING);
|
||||
@ -248,6 +250,28 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
$packetHandled = true;
|
||||
|
||||
$useItemTransaction = $packet->getItemInteractionData();
|
||||
if($useItemTransaction !== null){
|
||||
if(count($useItemTransaction->getTransactionData()->getActions()) > 100){
|
||||
throw new PacketHandlingException("Too many actions in item use transaction");
|
||||
}
|
||||
|
||||
$this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId());
|
||||
$this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions());
|
||||
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
|
||||
$packetHandled = false;
|
||||
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
|
||||
}else{
|
||||
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
|
||||
}
|
||||
$this->inventoryManager->setCurrentItemStackRequestId(null);
|
||||
}
|
||||
|
||||
$itemStackRequest = $packet->getItemStackRequest();
|
||||
$itemStackResponseBuilder = $itemStackRequest !== null ? $this->handleSingleItemStackRequest($itemStackRequest) : null;
|
||||
|
||||
//itemstack request or transaction may set predictions for the outcome of these actions, so these need to be
|
||||
//processed last
|
||||
$blockActions = $packet->getBlockActions();
|
||||
if($blockActions !== null){
|
||||
if(count($blockActions) > 100){
|
||||
@ -268,27 +292,9 @@ class InGamePacketHandler extends PacketHandler{
|
||||
}
|
||||
}
|
||||
|
||||
$useItemTransaction = $packet->getItemInteractionData();
|
||||
if($useItemTransaction !== null){
|
||||
if(count($useItemTransaction->getTransactionData()->getActions()) > 100){
|
||||
throw new PacketHandlingException("Too many actions in item use transaction");
|
||||
}
|
||||
|
||||
$this->inventoryManager->setCurrentItemStackRequestId($useItemTransaction->getRequestId());
|
||||
$this->inventoryManager->addRawPredictedSlotChanges($useItemTransaction->getTransactionData()->getActions());
|
||||
if(!$this->handleUseItemTransaction($useItemTransaction->getTransactionData())){
|
||||
$packetHandled = false;
|
||||
$this->session->getLogger()->debug("Unhandled transaction in PlayerAuthInputPacket (type " . $useItemTransaction->getTransactionData()->getActionType() . ")");
|
||||
}else{
|
||||
$this->inventoryManager->syncMismatchedPredictedSlotChanges();
|
||||
}
|
||||
$this->inventoryManager->setCurrentItemStackRequestId(null);
|
||||
}
|
||||
|
||||
$itemStackRequest = $packet->getItemStackRequest();
|
||||
if($itemStackRequest !== null){
|
||||
$result = $this->handleSingleItemStackRequest($itemStackRequest);
|
||||
$this->session->sendDataPacket(ItemStackResponsePacket::create([$result]));
|
||||
$itemStackResponse = $itemStackResponseBuilder?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $itemStackRequest->getRequestId());
|
||||
$this->session->sendDataPacket(ItemStackResponsePacket::create([$itemStackResponse]));
|
||||
}
|
||||
|
||||
return $packetHandled;
|
||||
@ -498,13 +504,6 @@ class InGamePacketHandler extends PacketHandler{
|
||||
//if only the client would tell us what blocks it thinks changed...
|
||||
$this->syncBlocksNearby($vBlockPos, $data->getFace());
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$blockPos = $data->getBlockPosition();
|
||||
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
|
||||
if(!$this->player->breakBlock($vBlockPos)){
|
||||
$this->syncBlocksNearby($vBlockPos, null);
|
||||
}
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
if($this->player->isUsingItem()){
|
||||
if(!$this->player->consumeHeldItem()){
|
||||
@ -580,7 +579,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function handleSingleItemStackRequest(ItemStackRequest $request) : ItemStackResponse{
|
||||
private function handleSingleItemStackRequest(ItemStackRequest $request) : ?ItemStackResponseBuilder{
|
||||
if(count($request->getActions()) > 60){
|
||||
//recipe book auto crafting can affect all slots of the inventory when consuming inputs or producing outputs
|
||||
//this means there could be as many as 50 CraftingConsumeInput actions or Place (taking the result) actions
|
||||
@ -597,7 +596,11 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$executor = new ItemStackRequestExecutor($this->player, $this->inventoryManager, $request);
|
||||
try{
|
||||
$transaction = $executor->generateInventoryTransaction();
|
||||
$result = $this->executeInventoryTransaction($transaction, $request->getRequestId());
|
||||
if($transaction !== null){
|
||||
$result = $this->executeInventoryTransaction($transaction, $request->getRequestId());
|
||||
}else{
|
||||
$result = true; //predictions only, just send responses
|
||||
}
|
||||
}catch(ItemStackRequestProcessException $e){
|
||||
$result = false;
|
||||
$this->session->getLogger()->debug("ItemStackRequest #" . $request->getRequestId() . " failed: " . $e->getMessage());
|
||||
@ -605,10 +608,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
$this->inventoryManager->requestSyncAll();
|
||||
}
|
||||
|
||||
if(!$result){
|
||||
return new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId());
|
||||
}
|
||||
return $executor->buildItemStackResponse();
|
||||
return $result ? $executor->getItemStackResponseBuilder() : null;
|
||||
}
|
||||
|
||||
public function handleItemStackRequest(ItemStackRequestPacket $packet) : bool{
|
||||
@ -618,7 +618,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
throw new PacketHandlingException("Too many requests in ItemStackRequestPacket");
|
||||
}
|
||||
foreach($packet->getRequests() as $request){
|
||||
$responses[] = $this->handleSingleItemStackRequest($request);
|
||||
$responses[] = $this->handleSingleItemStackRequest($request)?->build() ?? new ItemStackResponse(ItemStackResponse::RESULT_ERROR, $request->getRequestId());
|
||||
}
|
||||
|
||||
$this->session->sendDataPacket(ItemStackResponsePacket::create($responses));
|
||||
@ -681,16 +681,27 @@ class InGamePacketHandler extends PacketHandler{
|
||||
|
||||
switch($action){
|
||||
case PlayerAction::START_BREAK:
|
||||
case PlayerAction::CONTINUE_DESTROY_BLOCK: //destroy the next block while holding down left click
|
||||
self::validateFacing($face);
|
||||
if($this->lastBlockAttacked !== null && $blockPosition->equals($this->lastBlockAttacked)){
|
||||
//the client will send CONTINUE_DESTROY_BLOCK for the currently targeted block directly before it
|
||||
//sends PREDICT_DESTROY_BLOCK, but also when it starts to break the block
|
||||
//this seems like a bug in the client and would cause spurious left-click events if we allowed it to
|
||||
//be delivered to the player
|
||||
$this->session->getLogger()->debug("Ignoring PlayerAction $action on $pos because we were already destroying this block");
|
||||
break;
|
||||
}
|
||||
if(!$this->player->attackBlock($pos, $face)){
|
||||
$this->syncBlocksNearby($pos, $face);
|
||||
}
|
||||
$this->lastBlockAttacked = $blockPosition;
|
||||
|
||||
break;
|
||||
|
||||
case PlayerAction::ABORT_BREAK:
|
||||
case PlayerAction::STOP_BREAK:
|
||||
$this->player->stopBreakBlock($pos);
|
||||
$this->lastBlockAttacked = null;
|
||||
break;
|
||||
case PlayerAction::START_SLEEPING:
|
||||
//unused
|
||||
@ -701,11 +712,17 @@ class InGamePacketHandler extends PacketHandler{
|
||||
case PlayerAction::CRACK_BREAK:
|
||||
self::validateFacing($face);
|
||||
$this->player->continueBreakBlock($pos, $face);
|
||||
$this->lastBlockAttacked = $blockPosition;
|
||||
break;
|
||||
case PlayerAction::INTERACT_BLOCK: //TODO: ignored (for now)
|
||||
break;
|
||||
case PlayerAction::CREATIVE_PLAYER_DESTROY_BLOCK:
|
||||
//TODO: do we need to handle this?
|
||||
case PlayerAction::PREDICT_DESTROY_BLOCK:
|
||||
if(!$this->player->breakBlock($pos)){
|
||||
$this->syncBlocksNearby($pos, $face);
|
||||
}
|
||||
$this->lastBlockAttacked = null;
|
||||
break;
|
||||
case PlayerAction::START_ITEM_USE_ON:
|
||||
case PlayerAction::STOP_ITEM_USE_ON:
|
||||
|
@ -33,9 +33,11 @@ use pocketmine\inventory\transaction\EnchantingTransaction;
|
||||
use pocketmine\inventory\transaction\InventoryTransaction;
|
||||
use pocketmine\inventory\transaction\TransactionBuilder;
|
||||
use pocketmine\inventory\transaction\TransactionBuilderInventory;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\InventoryManager;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingConsumeInputStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftingCreateSpecificResultStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\CraftRecipeAutoStackRequestAction;
|
||||
@ -47,6 +49,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\DropStackReque
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequest;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequestSlotInfo;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\MineBlockStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\PlaceStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\SwapStackRequestAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\TakeStackRequestAction;
|
||||
@ -362,6 +365,16 @@ class ItemStackRequestExecutor{
|
||||
$this->setNextCreatedItem($nextResultItem);
|
||||
}elseif($action instanceof DeprecatedCraftingResultsStackRequestAction){
|
||||
//no obvious use
|
||||
}elseif($action instanceof MineBlockStackRequestAction){
|
||||
$slot = $action->getHotbarSlot();
|
||||
$this->requestSlotInfos[] = new ItemStackRequestSlotInfo(new FullContainerName(ContainerUIIds::HOTBAR), $slot, $action->getStackId());
|
||||
$inventory = $this->player->getInventory();
|
||||
$usedItem = $inventory->slotExists($slot) ? $inventory->getItem($slot) : null;
|
||||
$predictedDamage = $action->getPredictedDurability();
|
||||
if($usedItem instanceof Durable && $predictedDamage >= 0 && $predictedDamage <= $usedItem->getMaxDurability()){
|
||||
$usedItem->setDamage($predictedDamage);
|
||||
$this->inventoryManager->addPredictedSlotChange($inventory, $slot, $usedItem);
|
||||
}
|
||||
}else{
|
||||
throw new ItemStackRequestProcessException("Unhandled item stack request action");
|
||||
}
|
||||
@ -370,7 +383,7 @@ class ItemStackRequestExecutor{
|
||||
/**
|
||||
* @throws ItemStackRequestProcessException
|
||||
*/
|
||||
public function generateInventoryTransaction() : InventoryTransaction{
|
||||
public function generateInventoryTransaction() : ?InventoryTransaction{
|
||||
foreach(Utils::promoteKeys($this->request->getActions()) as $k => $action){
|
||||
try{
|
||||
$this->processItemStackRequestAction($action);
|
||||
@ -380,6 +393,9 @@ class ItemStackRequestExecutor{
|
||||
}
|
||||
$this->setNextCreatedItem(null);
|
||||
$inventoryActions = $this->builder->generateActions();
|
||||
if(count($inventoryActions) === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
$transaction = $this->specialTransaction ?? new InventoryTransaction($this->player);
|
||||
foreach($inventoryActions as $action){
|
||||
@ -389,12 +405,16 @@ class ItemStackRequestExecutor{
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
public function buildItemStackResponse() : ItemStackResponse{
|
||||
public function getItemStackResponseBuilder() : ItemStackResponseBuilder{
|
||||
$builder = new ItemStackResponseBuilder($this->request->getRequestId(), $this->inventoryManager);
|
||||
foreach($this->requestSlotInfos as $requestInfo){
|
||||
$builder->addSlot($requestInfo->getContainerName()->getContainerId(), $requestInfo->getSlotId());
|
||||
}
|
||||
|
||||
return $builder->build();
|
||||
return $builder;
|
||||
}
|
||||
|
||||
public function buildItemStackResponse() : ItemStackResponse{
|
||||
return $this->getItemStackResponseBuilder()->build();
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ class PreSpawnPacketHandler extends PacketHandler{
|
||||
$this->server->getMotd(),
|
||||
"",
|
||||
false,
|
||||
new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false),
|
||||
new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V3, 0, true),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
|
@ -1649,7 +1649,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$newReplica = clone $oldHeldItem;
|
||||
$newReplica->setCount($newHeldItem->getCount());
|
||||
if($newReplica instanceof Durable && $newHeldItem instanceof Durable){
|
||||
$newReplica->setDamage($newHeldItem->getDamage());
|
||||
$newDamage = $newHeldItem->getDamage();
|
||||
if($newDamage >= 0 && $newDamage <= $newReplica->getMaxDurability()){
|
||||
$newReplica->setDamage($newDamage);
|
||||
}
|
||||
}
|
||||
$damagedOrDeducted = $newReplica->equalsExact($newHeldItem);
|
||||
|
||||
|
@ -2963,6 +2963,8 @@ class World implements ChunkManager{
|
||||
if(count($chunkData->getEntityNBT()) !== 0){
|
||||
$this->timings->syncChunkLoadEntities->startTiming();
|
||||
$entityFactory = EntityFactory::getInstance();
|
||||
|
||||
$deletedEntities = [];
|
||||
foreach($chunkData->getEntityNBT() as $k => $nbt){
|
||||
try{
|
||||
$entity = $entityFactory->createFromData($this, $nbt);
|
||||
@ -2979,18 +2981,23 @@ class World implements ChunkManager{
|
||||
}elseif($saveIdTag instanceof IntTag){ //legacy MCPE format
|
||||
$saveId = "legacy(" . $saveIdTag->getValue() . ")";
|
||||
}
|
||||
$logger->warning("Deleted unknown entity type $saveId");
|
||||
$deletedEntities[$saveId] = ($deletedEntities[$saveId] ?? 0) + 1;
|
||||
}
|
||||
//TODO: we can't prevent entities getting added to unloaded chunks if they were saved in the wrong place
|
||||
//here, because entities currently add themselves to the world
|
||||
}
|
||||
|
||||
foreach(Utils::promoteKeys($deletedEntities) as $saveId => $count){
|
||||
$logger->warning("Deleted unknown entity type $saveId x$count");
|
||||
}
|
||||
$this->timings->syncChunkLoadEntities->stopTiming();
|
||||
}
|
||||
|
||||
if(count($chunkData->getTileNBT()) !== 0){
|
||||
$this->timings->syncChunkLoadTileEntities->startTiming();
|
||||
$tileFactory = TileFactory::getInstance();
|
||||
|
||||
$deletedTiles = [];
|
||||
foreach($chunkData->getTileNBT() as $k => $nbt){
|
||||
try{
|
||||
$tile = $tileFactory->createFromData($this, $nbt);
|
||||
@ -3000,7 +3007,8 @@ class World implements ChunkManager{
|
||||
continue;
|
||||
}
|
||||
if($tile === null){
|
||||
$logger->warning("Deleted unknown tile entity type " . $nbt->getString("id", "<unknown>"));
|
||||
$saveId = $nbt->getString("id", "<unknown>");
|
||||
$deletedTiles[$saveId] = ($deletedTiles[$saveId] ?? 0) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -3016,6 +3024,10 @@ class World implements ChunkManager{
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Utils::promoteKeys($deletedTiles) as $saveId => $count){
|
||||
$logger->warning("Deleted unknown tile entity type $saveId x$count");
|
||||
}
|
||||
|
||||
$this->timings->syncChunkLoadTileEntities->stopTiming();
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\format\io\BaseWorldProvider;
|
||||
@ -204,23 +205,29 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
|
||||
}catch(BlockStateDeserializeException $e){
|
||||
//while not ideal, this is not a fatal error
|
||||
$blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
|
||||
$errorMessage = "Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
|
||||
$blockDecodeErrors[$errorMessage][] = $i;
|
||||
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
|
||||
continue;
|
||||
}
|
||||
try{
|
||||
$palette[] = $this->blockStateDeserializer->deserialize($blockStateData);
|
||||
}catch(UnsupportedBlockStateException $e){
|
||||
$blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage();
|
||||
$blockDecodeErrors[$e->getMessage()][] = $i;
|
||||
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
|
||||
}catch(BlockStateDeserializeException $e){
|
||||
$blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
|
||||
$errorMessage = "Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString();
|
||||
$blockDecodeErrors[$errorMessage][] = $i;
|
||||
$palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
|
||||
}
|
||||
}
|
||||
|
||||
if(count($blockDecodeErrors) > 0){
|
||||
$logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors));
|
||||
$finalErrors = [];
|
||||
foreach(Utils::promoteKeys($blockDecodeErrors) as $errorMessage => $paletteOffsets){
|
||||
$finalErrors[] = "$errorMessage (palette offsets: " . implode(", ", $paletteOffsets) . ")";
|
||||
}
|
||||
$logger->error("Errors decoding blocks:\n - " . implode("\n - ", $finalErrors));
|
||||
}
|
||||
|
||||
//TODO: exceptions
|
||||
|
@ -702,12 +702,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/inventory/transaction/InventoryTransaction.php
|
||||
|
||||
-
|
||||
message: '#^Cannot cast mixed to int\.$#'
|
||||
identifier: cast.int
|
||||
count: 2
|
||||
path: ../../../src/item/Item.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$buffer of method pocketmine\\nbt\\BaseNbtSerializer\:\:read\(\) expects string, mixed given\.$#'
|
||||
identifier: argument.type
|
||||
|
Loading…
x
Reference in New Issue
Block a user