Merge branch 'next-minor' into next-major

This commit is contained in:
Dylan K. Taylor 2022-08-21 19:19:07 +01:00
commit 1ecb10acba
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
30 changed files with 350 additions and 125 deletions

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.21.1
uses: shivammathur/setup-php@2.21.2
with:
php-version: 8.0

View File

@ -18,7 +18,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: shivammathur/setup-php@2.21.1
uses: shivammathur/setup-php@2.21.2
with:
php-version: 8.0

View File

@ -13,11 +13,11 @@ jobs:
strategy:
matrix:
image: [ubuntu-20.04]
php: [8.0.18]
php: [8.0.22, 8.1.9]
steps:
- name: Build and prepare PHP cache
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
@ -31,13 +31,13 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.18]
php: [8.0.22, 8.1.9]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
@ -69,13 +69,13 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.18]
php: [8.0.22, 8.1.9]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
@ -107,7 +107,7 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.18]
php: [8.0.22, 8.1.9]
steps:
- uses: actions/checkout@v3
@ -115,7 +115,7 @@ jobs:
submodules: true
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
@ -147,13 +147,13 @@ jobs:
fail-fast: false
matrix:
image: [ubuntu-20.04]
php: [8.0.18]
php: [8.0.22, 8.1.9]
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: pmmp/setup-php-action@aa636a4fe0c1c035fd9a3f05e360eadd86e06440
uses: pmmp/setup-php-action@82a44d659bf5046612c69f036af3e14dc32e3fa8
with:
php-version: ${{ matrix.php }}
install-path: "./bin"
@ -198,10 +198,10 @@ jobs:
- uses: actions/checkout@v3
- name: Setup PHP and tools
uses: shivammathur/setup-php@2.21.1
uses: shivammathur/setup-php@2.21.2
with:
php-version: 8.0
tools: php-cs-fixer:3.2
tools: php-cs-fixer:3.8
- name: Run PHP-CS-Fixer
run: php-cs-fixer fix --dry-run --diff --ansi

View File

@ -22,7 +22,8 @@
declare(strict_types=1);
const VERSIONS = [
"8.0"
"8.0",
"8.1"
];
$workflowFile = file_get_contents(__DIR__ . '/main.yml');

View File

@ -70,6 +70,10 @@ BODY,
'scope' => 'namespaced',
'include' => ['@all'],
],
'new_with_braces' => [
'named_class' => true,
'anonymous_class' => false,
],
'no_closing_tag' => true,
'no_empty_phpdoc' => true,
'no_extra_blank_lines' => true,

View File

@ -7,10 +7,11 @@ GitHub is public and anyone can see the issues you post on the issue tracker, in
**WARNING: You may put live servers at risk by reporting a vulnerability on the GitHub issue tracker.**
**Contact us** by sending an email to [**team@pmmp.io**](mailto:team@pmmp.io?subject=Security%20Vulnerability%20in%20PocketMine-MP). Include the following information:
**Contact us** by sending an email to [**security@pmmp.io**](mailto:security@pmmp.io). Include the following information:
- Version of PocketMine-MP
- Detailed description of the vulnerability (e.g. how to exploit it, what the effects are)
- Your GitHub username, if you wish to be credited for reporting the problem in the security advisory
Please note that we can't guarantee a reply to every email.

View File

@ -21,3 +21,11 @@ Released 14th August 2022.
- Fixed Turtle Master potions not giving any effects.
- Unimplemented items are no longer craftable.
- Fixed incorrect items appearing in item frames (due to an obsolete workaround for 1.19.10).
# 4.7.2
Released 16th August 2022.
## Fixes
- Fixed crash when processing player skins with invalid geometry data.
- Fixed spectator players being able to pick blocks using mousewheel click.
- Improved supporting requirements for sugarcane.

12
composer.lock generated
View File

@ -586,16 +586,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.8.3",
"version": "2.8.6",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "113c115a3b8976917eb22b74dccab464831b6483"
"reference": "87f1afaa4824998ece14d71ce37e94bc1f1d3116"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483",
"reference": "113c115a3b8976917eb22b74dccab464831b6483",
"url": "https://api.github.com/repos/pmmp/Language/zipball/87f1afaa4824998ece14d71ce37e94bc1f1d3116",
"reference": "87f1afaa4824998ece14d71ce37e94bc1f1d3116",
"shasum": ""
},
"type": "library",
@ -603,9 +603,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.8.3"
"source": "https://github.com/pmmp/Language/tree/2.8.6"
},
"time": "2022-05-11T13:51:37+00:00"
"time": "2022-08-18T15:25:44+00:00"
},
{
"name": "pocketmine/log",

View File

@ -1,4 +1,5 @@
includes:
- tests/phpstan/analyse-for-current-php-version.neon.php
- tests/phpstan/configs/actual-problems.neon
- tests/phpstan/configs/gc-hacks.neon
- tests/phpstan/configs/impossible-generics.neon

View File

@ -37,6 +37,7 @@ namespace pocketmine {
use Webmozart\PathUtil\Path;
use function defined;
use function extension_loaded;
use function function_exists;
use function getcwd;
use function phpversion;
use function preg_match;
@ -160,7 +161,7 @@ namespace pocketmine {
if(PHP_DEBUG !== 0){
$logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance.");
}
if(extension_loaded("xdebug")){
if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){
$logger->warning("Xdebug extension is enabled. This has a major impact on performance.");
}
if(((int) ini_get('zend.assertions')) !== -1){

View File

@ -127,7 +127,8 @@ class Sugarcane extends Flowable{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}elseif($this->canBeSupportedBy($down)){
foreach(Facing::HORIZONTAL as $side){
if($down->getSide($side) instanceof Water){
$sideBlock = $down->getSide($side);
if($sideBlock instanceof Water || $sideBlock instanceof FrostedIce){
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -28,7 +28,6 @@ use pocketmine\utils\Limits;
use function implode;
use function in_array;
use function json_encode;
use function json_last_error_msg;
use function strlen;
use const JSON_THROW_ON_ERROR;
@ -68,9 +67,10 @@ final class Skin{
}
if($geometryData !== ""){
$decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
if($decodedGeometry === false){
throw new InvalidSkinException("Invalid geometry data (" . json_last_error_msg() . ")");
try{
$decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
}catch(\RuntimeException $e){
throw new InvalidSkinException("Invalid geometry data: " . $e->getMessage(), 0, $e);
}
/*

View File

@ -35,7 +35,7 @@ final class StringToEffectParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result = new self();
$result->register("absorption", fn() => VanillaEffects::ABSORPTION());
$result->register("blindness", fn() => VanillaEffects::BLINDNESS());

View File

@ -31,7 +31,7 @@ class HandlerListManager{
private static ?self $globalInstance = null;
public static function global() : self{
return self::$globalInstance ?? (self::$globalInstance = new self);
return self::$globalInstance ?? (self::$globalInstance = new self());
}
/** @var HandlerList[] classname => HandlerList */

View File

@ -0,0 +1,61 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory\transaction;
use pocketmine\inventory\Inventory;
use pocketmine\inventory\transaction\action\InventoryAction;
use function spl_object_id;
final class TransactionBuilder{
/** @var TransactionBuilderInventory[] */
private array $inventories = [];
/** @var InventoryAction[] */
private array $extraActions = [];
public function addAction(InventoryAction $action) : void{
$this->extraActions[spl_object_id($action)] = $action;
}
public function getInventory(Inventory $inventory) : TransactionBuilderInventory{
$id = spl_object_id($inventory);
return $this->inventories[$id] ??= new TransactionBuilderInventory($inventory);
}
/**
* @return InventoryAction[]
*/
public function generateActions() : array{
$actions = $this->extraActions;
foreach($this->inventories as $inventory){
foreach($inventory->generateActions() as $action){
$actions[spl_object_id($action)] = $action;
}
}
return $actions;
}
}

View File

@ -46,7 +46,7 @@ final class StringToItemParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result = new self();
self::registerDynamicBlocks($result);
self::registerBlocks($result);

View File

@ -35,7 +35,7 @@ final class StringToEnchantmentParser extends StringToTParser{
use SingletonTrait;
private static function make() : self{
$result = new self;
$result = new self();
$result->register("blast_protection", fn() => VanillaEnchantments::BLAST_PROTECTION());
$result->register("efficiency", fn() => VanillaEnchantments::EFFICIENCY());

View File

@ -0,0 +1,64 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe;
use pocketmine\inventory\Inventory;
final class ComplexWindowMapEntry{
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private array $reverseSlotMap = [];
/**
* @param int[] $slotMap
* @phpstan-param array<int, int> $slotMap
*/
public function __construct(
private Inventory $inventory,
private array $slotMap
){
foreach($slotMap as $slot => $index){
$this->reverseSlotMap[$index] = $slot;
}
}
public function getInventory() : Inventory{ return $this->inventory; }
/**
* @return int[]
* @phpstan-return array<int, int>
*/
public function getSlotMap() : array{ return $this->slotMap; }
public function mapNetToCore(int $slot) : ?int{
return $this->slotMap[$slot] ?? null;
}
public function mapCoreToNet(int $slot) : ?int{
return $this->reverseSlotMap[$slot] ?? null;
}
}

View File

@ -55,6 +55,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes;
use pocketmine\network\PacketHandlingException;
use pocketmine\player\Player;
@ -63,6 +64,7 @@ use pocketmine\utils\ObjectSet;
use function array_map;
use function array_search;
use function get_class;
use function is_int;
use function max;
use function spl_object_id;
@ -72,6 +74,17 @@ use function spl_object_id;
class InventoryManager{
/** @var Inventory[] */
private array $windowMap = [];
/**
* @var ComplexWindowMapEntry[]
* @phpstan-var array<int, ComplexWindowMapEntry>
*/
private array $complexWindows = [];
/**
* @var ComplexWindowMapEntry[]
* @phpstan-var array<int, ComplexWindowMapEntry>
*/
private array $complexSlotToWindowMap = [];
private int $lastInventoryNetworkId = ContainerIds::FIRST;
/**
@ -98,7 +111,8 @@ class InventoryManager{
$this->add(ContainerIds::INVENTORY, $this->player->getInventory());
$this->add(ContainerIds::OFFHAND, $this->player->getOffHandInventory());
$this->add(ContainerIds::ARMOR, $this->player->getArmorInventory());
$this->add(ContainerIds::UI, $this->player->getCursorInventory());
$this->addComplex(UIInventorySlotOffset::CURSOR, $this->player->getCursorInventory());
$this->addComplex(UIInventorySlotOffset::CRAFTING2X2_INPUT, $this->player->getCraftingGrid());
$this->player->getInventory()->getHeldItemIndexChangeListeners()->add(function() : void{
$this->syncSelectedHotbarSlot();
@ -115,8 +129,27 @@ class InventoryManager{
return $this->lastInventoryNetworkId;
}
/**
* @param int[]|int $slotMap
* @phpstan-param array<int, int>|int $slotMap
*/
private function addComplex(array|int $slotMap, Inventory $inventory) : void{
$entry = new ComplexWindowMapEntry($inventory, is_int($slotMap) ? [$slotMap => 0] : $slotMap);
$this->complexWindows[spl_object_id($inventory)] = $entry;
foreach($entry->getSlotMap() as $netSlot => $coreSlot){
$this->complexSlotToWindowMap[$netSlot] = $entry;
}
}
private function remove(int $id) : void{
unset($this->windowMap[$id], $this->initiatedSlotChanges[$id]);
$inventory = $this->windowMap[$id];
$splObjectId = spl_object_id($inventory);
unset($this->windowMap[$id], $this->initiatedSlotChanges[$id], $this->complexWindows[$splObjectId]);
foreach($this->complexSlotToWindowMap as $netSlot => $entry){
if($entry->getInventory() === $inventory){
unset($this->complexSlotToWindowMap[$netSlot]);
}
}
}
public function getWindowId(Inventory $inventory) : ?int{
@ -127,8 +160,22 @@ class InventoryManager{
return $this->lastInventoryNetworkId;
}
public function getWindow(int $windowId) : ?Inventory{
return $this->windowMap[$windowId] ?? null;
/**
* @phpstan-return array{Inventory, int}
*/
public function locateWindowAndSlot(int $windowId, int $netSlotId) : ?array{
if($windowId === ContainerIds::UI){
$entry = $this->complexSlotToWindowMap[$netSlotId] ?? null;
if($entry === null){
return null;
}
$coreSlotId = $entry->mapNetToCore($netSlotId);
return $coreSlotId !== null ? [$entry->getInventory(), $coreSlotId] : null;
}
if(isset($this->windowMap[$windowId])){
return [$this->windowMap[$windowId], $netSlotId];
}
return null;
}
public function onTransactionStart(InventoryTransaction $tx) : void{
@ -181,11 +228,32 @@ class InventoryManager{
}
}
/**
* @return int[]|null
* @phpstan-return array<int, int>|null
*/
private function createComplexSlotMapping(Inventory $inventory) : ?array{
//TODO: make this dynamic so plugins can add mappings for stuff not implemented by PM
return match(true){
$inventory instanceof AnvilInventory => UIInventorySlotOffset::ANVIL,
$inventory instanceof EnchantInventory => UIInventorySlotOffset::ENCHANTING_TABLE,
$inventory instanceof LoomInventory => UIInventorySlotOffset::LOOM,
$inventory instanceof StonecutterInventory => [UIInventorySlotOffset::STONE_CUTTER_INPUT => StonecutterInventory::SLOT_INPUT],
$inventory instanceof CraftingTableInventory => UIInventorySlotOffset::CRAFTING3X3_INPUT,
$inventory instanceof CartographyTableInventory => UIInventorySlotOffset::CARTOGRAPHY_TABLE,
$inventory instanceof SmithingTableInventory => UIInventorySlotOffset::SMITHING_TABLE,
default => null,
};
}
public function onCurrentWindowChange(Inventory $inventory) : void{
$this->onCurrentWindowRemove();
$this->openWindowDeferred(function() use ($inventory) : void{
$windowId = $this->addDynamic($inventory);
if(($slotMap = $this->createComplexSlotMapping($inventory)) !== null){
$this->addComplex($slotMap, $inventory);
}
foreach($this->containerOpenCallbacks as $callback){
$pks = $callback($windowId, $inventory);
@ -284,10 +352,17 @@ class InventoryManager{
}
public function syncSlot(Inventory $inventory, int $slot) : void{
$windowId = $this->getWindowId($inventory);
if($windowId !== null){
$slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null;
if($slotMap !== null){
$windowId = ContainerIds::UI;
$netSlot = $slotMap->mapCoreToNet($slot) ?? null;
}else{
$windowId = $this->getWindowId($inventory);
$netSlot = $slot;
}
if($windowId !== null && $netSlot !== null){
$currentItem = $inventory->getItem($slot);
$clientSideItem = $this->initiatedSlotChanges[$windowId][$slot] ?? null;
$clientSideItem = $this->initiatedSlotChanges[$windowId][$netSlot] ?? null;
if($clientSideItem === null || !$clientSideItem->equalsExact($currentItem)){
$itemStackWrapper = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($currentItem));
if($windowId === ContainerIds::OFFHAND){
@ -298,30 +373,37 @@ class InventoryManager{
//BDS (Bedrock Dedicated Server) also seems to work this way.
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, [$itemStackWrapper]));
}else{
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $slot, $itemStackWrapper));
$this->session->sendDataPacket(InventorySlotPacket::create($windowId, $netSlot, $itemStackWrapper));
}
}
unset($this->initiatedSlotChanges[$windowId][$slot]);
unset($this->initiatedSlotChanges[$windowId][$netSlot]);
}
}
public function syncContents(Inventory $inventory) : void{
$windowId = $this->getWindowId($inventory);
$slotMap = $this->complexWindows[spl_object_id($inventory)] ?? null;
if($slotMap !== null){
$windowId = ContainerIds::UI;
}else{
$windowId = $this->getWindowId($inventory);
}
$typeConverter = TypeConverter::getInstance();
if($windowId !== null){
unset($this->initiatedSlotChanges[$windowId]);
$typeConverter = TypeConverter::getInstance();
if($windowId === ContainerIds::UI){
//TODO: HACK!
//Since 1.13, cursor is now part of a larger "UI inventory", and sending contents for this larger inventory does
//not work the way it's intended to. Even if it did, it would be necessary to send all 51 slots just to update
//this one, which is just not worth it.
//This workaround isn't great, but it's at least simple.
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
0,
ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($inventory->getItem(0)))
));
if($slotMap !== null){
foreach($inventory->getContents(true) as $slotId => $item){
$packetSlot = $slotMap->mapCoreToNet($slotId) ?? null;
if($packetSlot === null){
continue;
}
unset($this->initiatedSlotChanges[$windowId][$packetSlot]);
$this->session->sendDataPacket(InventorySlotPacket::create(
$windowId,
$packetSlot,
ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($inventory->getItem($slotId)))
));
}
}else{
unset($this->initiatedSlotChanges[$windowId]);
$this->session->sendDataPacket(InventoryContentPacket::create($windowId, array_map(function(Item $itemStack) use ($typeConverter) : ItemStackWrapper{
return ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($itemStack));
}, $inventory->getContents(true))));
@ -333,16 +415,20 @@ class InventoryManager{
foreach($this->windowMap as $inventory){
$this->syncContents($inventory);
}
foreach($this->complexWindows as $entry){
$this->syncContents($entry->getInventory());
}
}
public function syncMismatchedPredictedSlotChanges() : void{
foreach($this->initiatedSlotChanges as $windowId => $slots){
if(!isset($this->windowMap[$windowId])){
continue;
}
$inventory = $this->windowMap[$windowId];
foreach($slots as $netSlot => $expectedItem){
$located = $this->locateWindowAndSlot($windowId, $netSlot);
if($located === null){
continue;
}
[$inventory, $slot] = $located;
foreach($slots as $slot => $expectedItem){
if(!$inventory->slotExists($slot)){
continue; //TODO: size desync ???
}

View File

@ -23,13 +23,6 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\convert;
use pocketmine\block\inventory\AnvilInventory;
use pocketmine\block\inventory\CartographyTableInventory;
use pocketmine\block\inventory\CraftingTableInventory;
use pocketmine\block\inventory\EnchantInventory;
use pocketmine\block\inventory\LoomInventory;
use pocketmine\block\inventory\SmithingTableInventory;
use pocketmine\block\inventory\StonecutterInventory;
use pocketmine\block\VanillaBlocks;
use pocketmine\crafting\ExactRecipeIngredient;
use pocketmine\crafting\MetaWildcardRecipeIngredient;
@ -242,40 +235,12 @@ class TypeConverter{
}
switch($action->sourceType){
case NetworkInventoryAction::SOURCE_CONTAINER:
$window = null;
if($action->windowId === ContainerIds::UI && $action->inventorySlot > 0){
if($action->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){
return null; //useless noise
}
$pSlot = $action->inventorySlot;
$slot = UIInventorySlotOffset::CRAFTING2X2_INPUT[$pSlot] ?? null;
if($slot !== null){
$window = $player->getCraftingGrid();
}elseif(($current = $player->getCurrentWindow()) !== null){
$slotMap = match(true){
$current instanceof AnvilInventory => UIInventorySlotOffset::ANVIL,
$current instanceof EnchantInventory => UIInventorySlotOffset::ENCHANTING_TABLE,
$current instanceof LoomInventory => UIInventorySlotOffset::LOOM,
$current instanceof StonecutterInventory => [UIInventorySlotOffset::STONE_CUTTER_INPUT => StonecutterInventory::SLOT_INPUT],
$current instanceof CraftingTableInventory => UIInventorySlotOffset::CRAFTING3X3_INPUT,
$current instanceof CartographyTableInventory => UIInventorySlotOffset::CARTOGRAPHY_TABLE,
$current instanceof SmithingTableInventory => UIInventorySlotOffset::SMITHING_TABLE,
default => null
};
if($slotMap !== null){
$window = $current;
$slot = $slotMap[$pSlot] ?? null;
}
}
if($slot === null){
throw new TypeConversionException("Unmatched UI inventory slot offset $pSlot");
}
}else{
$window = $inventoryManager->getWindow($action->windowId);
$slot = $action->inventorySlot;
if($action->windowId === ContainerIds::UI && $action->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){
return null; //useless noise
}
if($window !== null){
$located = $inventoryManager->locateWindowAndSlot($action->windowId, $action->inventorySlot);
if($located !== null){
[$window, $slot] = $located;
return new SlotChangeAction($window, $slot, $old, $new);
}

View File

@ -165,13 +165,13 @@ class LoginPacketHandler extends PacketHandler{
if(!is_array($claims["extraData"])){
throw new PacketHandlingException("'extraData' key should be an array");
}
$mapper = new \JsonMapper;
$mapper = new \JsonMapper();
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
try{
/** @var AuthenticationData $extraData */
$extraData = $mapper->map($claims["extraData"], new AuthenticationData);
$extraData = $mapper->map($claims["extraData"], new AuthenticationData());
}catch(\JsonMapper_Exception $e){
throw PacketHandlingException::wrap($e);
}
@ -193,12 +193,12 @@ class LoginPacketHandler extends PacketHandler{
throw PacketHandlingException::wrap($e);
}
$mapper = new \JsonMapper;
$mapper = new \JsonMapper();
$mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
$mapper->bExceptionOnMissingData = true;
$mapper->bExceptionOnUndefinedProperty = true;
try{
$clientData = $mapper->map($clientDataClaims, new ClientData);
$clientData = $mapper->map($clientDataClaims, new ClientData());
}catch(\JsonMapper_Exception $e){
throw PacketHandlingException::wrap($e);
}

View File

@ -84,8 +84,8 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{
$this->sleeper = new SleeperNotifier();
$mainToThreadBuffer = new \Threaded;
$threadToMainBuffer = new \Threaded;
$mainToThreadBuffer = new \Threaded();
$threadToMainBuffer = new \Threaded();
$this->rakLib = new RakLibServer(
$this->server->getLogger(),

View File

@ -31,7 +31,7 @@ class PermissionManager{
public static function getInstance() : PermissionManager{
if(self::$instance === null){
self::$instance = new self;
self::$instance = new self();
}
return self::$instance;

View File

@ -86,7 +86,7 @@ use pocketmine\inventory\PlayerCursorInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\inventory\transaction\action\DropItemAction;
use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\inventory\transaction\TransactionBuilderInventory;
use pocketmine\inventory\transaction\TransactionBuilder;
use pocketmine\inventory\transaction\TransactionCancelledException;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\ConsumableItem;
@ -1583,7 +1583,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$ev = new PlayerBlockPickEvent($this, $block, $item);
$existingSlot = $this->inventory->first($item);
if($existingSlot === -1 && $this->hasFiniteResources()){
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
$ev->cancel();
}
$ev->call();
@ -2444,29 +2444,23 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$inventories[] = $this->currentWindow;
}
$transaction = new InventoryTransaction($this);
$mainInventoryTransactionBuilder = new TransactionBuilderInventory($this->inventory);
$builder = new TransactionBuilder();
foreach($inventories as $inventory){
$contents = $inventory->getContents();
if(count($contents) > 0){
$drops = $mainInventoryTransactionBuilder->addItem(...$contents);
$drops = $builder->getInventory($this->inventory)->addItem(...$contents);
foreach($drops as $drop){
$transaction->addAction(new DropItemAction($drop));
$builder->addAction(new DropItemAction($drop));
}
$clearedInventoryTransactionBuilder = new TransactionBuilderInventory($inventory);
$clearedInventoryTransactionBuilder->clearAll();
foreach($clearedInventoryTransactionBuilder->generateActions() as $action){
$transaction->addAction($action);
}
$builder->getInventory($inventory)->clearAll();
}
}
foreach($mainInventoryTransactionBuilder->generateActions() as $action){
$transaction->addAction($action);
}
if(count($transaction->getActions()) !== 0){
$actions = $builder->generateActions();
if(count($actions) !== 0){
$transaction = new InventoryTransaction($this, $actions);
try{
$transaction->execute();
$this->logger->debug("Successfully evacuated items from temporary inventories");

View File

@ -158,7 +158,7 @@ class AsyncPool{
throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once");
}
$task->progressUpdates = new \Threaded;
$task->progressUpdates = new \Threaded();
$task->setSubmitted();
$this->getWorker($worker)->stack($task);

View File

@ -28,7 +28,7 @@ trait SingletonTrait{
private static $instance = null;
private static function make() : self{
return new self;
return new self();
}
public static function getInstance() : self{

View File

@ -48,7 +48,7 @@ final class WorldCreationOptions{
}
public static function create() : self{
return new self;
return new self();
}
/** @phpstan-return class-string<Generator> */

View File

@ -123,6 +123,7 @@ class Normal extends Generator{
private function pickBiome(int $x, int $z) : Biome{
$hash = $x * 2345803 ^ $z * 9236449 ^ $this->seed;
$hash *= $hash + 223;
$hash = (int) $hash;
$xNoise = $hash >> 20 & 3;
$zNoise = $hash >> 22 & 3;
if($xNoise == 3){

View File

@ -0,0 +1,32 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
/*
* This hack works around a "feature" in PHPStan which locks in analysis version based on platform.php in composer.json
* See https://github.com/phpstan/phpstan/issues/7701 for details
*/
return [
'parameters' => [
'phpVersion' => PHP_VERSION_ID
]
];

View File

@ -80,3 +80,8 @@ parameters:
count: 2
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: "#^Casting to int something that's already int\\.$#"
count: 1
path: ../../../src/world/generator/normal/Normal.php