mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 00:33:59 +00:00
Merge branch 'next-minor' into next-major
This commit is contained in:
commit
1ecb10acba
2
.github/workflows/discord-release-notify.yml
vendored
2
.github/workflows/discord-release-notify.yml
vendored
@ -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
|
||||
|
||||
|
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@ -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
|
||||
|
||||
|
24
.github/workflows/main.yml
vendored
24
.github/workflows/main.yml
vendored
@ -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
|
||||
|
3
.github/workflows/update-php-versions.php
vendored
3
.github/workflows/update-php-versions.php
vendored
@ -22,7 +22,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const VERSIONS = [
|
||||
"8.0"
|
||||
"8.0",
|
||||
"8.1"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
12
composer.lock
generated
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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){
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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());
|
||||
|
@ -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 */
|
||||
|
61
src/inventory/transaction/TransactionBuilder.php
Normal file
61
src/inventory/transaction/TransactionBuilder.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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());
|
||||
|
64
src/network/mcpe/ComplexWindowMapEntry.php
Normal file
64
src/network/mcpe/ComplexWindowMapEntry.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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 ???
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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{
|
||||
|
@ -48,7 +48,7 @@ final class WorldCreationOptions{
|
||||
}
|
||||
|
||||
public static function create() : self{
|
||||
return new self;
|
||||
return new self();
|
||||
}
|
||||
|
||||
/** @phpstan-return class-string<Generator> */
|
||||
|
@ -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){
|
||||
|
32
tests/phpstan/analyse-for-current-php-version.neon.php
Normal file
32
tests/phpstan/analyse-for-current-php-version.neon.php
Normal 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
|
||||
]
|
||||
];
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user