Merge branch 'minor-next' into major-next

This commit is contained in:
Dylan K. Taylor 2025-01-08 15:25:12 +00:00
commit 02ac512b4e
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
46 changed files with 978 additions and 435 deletions

View File

@ -129,7 +129,7 @@ function buildPhar(string $pharPath, string $basePath, array $includedPaths, arr
} }
function main() : void{ function main() : void{
if(ini_get("phar.readonly") == 1){ if(ini_get("phar.readonly") === "1"){
echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL; echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
exit(1); exit(1);
} }

View File

@ -52,9 +52,9 @@
"symfony/filesystem": "~6.4.0" "symfony/filesystem": "~6.4.0"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.11.11", "phpstan/phpstan": "2.1.1",
"phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^1.2.0", "phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^10.5.24" "phpunit/phpunit": "^10.5.24"
}, },
"autoload": { "autoload": {

94
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "732102eca72dc1d29e7b67dfbce07653", "content-hash": "994ccffe45f066768542019f6f9d237b",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -256,16 +256,16 @@
}, },
{ {
"name": "pocketmine/bedrock-protocol", "name": "pocketmine/bedrock-protocol",
"version": "35.0.0+bedrock-1.21.50", "version": "35.0.3+bedrock-1.21.50",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/BedrockProtocol.git", "url": "https://github.com/pmmp/BedrockProtocol.git",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435" "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c4d62581cb62d29ec426914c6b4d7e0ff835da9c",
"reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -278,10 +278,10 @@
"ramsey/uuid": "^4.1" "ramsey/uuid": "^4.1"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.11.9", "phpstan/phpstan": "2.1.0",
"phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-phpunit": "^2.0.0",
"phpstan/phpstan-strict-rules": "^1.0.0", "phpstan/phpstan-strict-rules": "^2.0.0",
"phpunit/phpunit": "^9.5 || ^10.0" "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -296,9 +296,9 @@
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
"support": { "support": {
"issues": "https://github.com/pmmp/BedrockProtocol/issues", "issues": "https://github.com/pmmp/BedrockProtocol/issues",
"source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.50" "source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.3+bedrock-1.21.50"
}, },
"time": "2024-12-04T13:02:00+00:00" "time": "2025-01-07T23:06:29+00:00"
}, },
{ {
"name": "pocketmine/binaryutils", "name": "pocketmine/binaryutils",
@ -576,16 +576,16 @@
}, },
{ {
"name": "pocketmine/nbt", "name": "pocketmine/nbt",
"version": "1.0.0", "version": "1.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/NBT.git", "url": "https://github.com/pmmp/NBT.git",
"reference": "20540271cb59e04672cb163dca73366f207974f1" "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1", "url": "https://api.github.com/repos/pmmp/NBT/zipball/53db37487bc5ddbfbd84247966e1a073bdcfdb7d",
"reference": "20540271cb59e04672cb163dca73366f207974f1", "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -595,8 +595,8 @@
}, },
"require-dev": { "require-dev": {
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "1.10.25", "phpstan/phpstan": "2.1.0",
"phpstan/phpstan-strict-rules": "^1.0", "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.5"
}, },
"type": "library", "type": "library",
@ -612,9 +612,9 @@
"description": "PHP library for working with Named Binary Tags", "description": "PHP library for working with Named Binary Tags",
"support": { "support": {
"issues": "https://github.com/pmmp/NBT/issues", "issues": "https://github.com/pmmp/NBT/issues",
"source": "https://github.com/pmmp/NBT/tree/1.0.0" "source": "https://github.com/pmmp/NBT/tree/1.0.1"
}, },
"time": "2023-07-14T13:01:49+00:00" "time": "2025-01-07T22:47:46+00:00"
}, },
{ {
"name": "pocketmine/raklib", "name": "pocketmine/raklib",
@ -1386,20 +1386,20 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.11.11", "version": "2.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2|^8.0" "php": "^7.4|^8.0"
}, },
"conflict": { "conflict": {
"phpstan/phpstan-shim": "*" "phpstan/phpstan-shim": "*"
@ -1440,34 +1440,33 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-08-19T14:37:29+00:00" "time": "2025-01-05T16:43:48+00:00"
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
"version": "1.4.0", "version": "2.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git", "url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11" "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11", "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/e32ac656788a5bf3dedda89e6a2cad5643bf1a18",
"reference": "f3ea021866f4263f07ca3636bf22c64be9610c11", "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.4 || ^8.0",
"phpstan/phpstan": "^1.11" "phpstan/phpstan": "^2.0.4"
}, },
"conflict": { "conflict": {
"phpunit/phpunit": "<7.0" "phpunit/phpunit": "<7.0"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^1.5.1", "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.6"
}, },
"type": "phpstan-extension", "type": "phpstan-extension",
"extra": { "extra": {
@ -1490,34 +1489,33 @@
"description": "PHPUnit extensions and rules for PHPStan", "description": "PHPUnit extensions and rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues", "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0" "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.3"
}, },
"time": "2024-04-20T06:39:00+00:00" "time": "2024-12-19T09:14:43+00:00"
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "1.6.0", "version": "2.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1" "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3",
"reference": "363f921dd8441777d4fc137deb99beb486c77df1", "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0", "php": "^7.4 || ^8.0",
"phpstan/phpstan": "^1.11" "phpstan/phpstan": "^2.0.4"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.6"
}, },
"type": "phpstan-extension", "type": "phpstan-extension",
"extra": { "extra": {
@ -1539,9 +1537,9 @@
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.6.0" "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.1"
}, },
"time": "2024-04-20T06:37:51+00:00" "time": "2024-12-12T20:21:10+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -17,6 +17,7 @@ rules:
parameters: parameters:
level: 9 level: 9
checkMissingCallableSignature: true checkMissingCallableSignature: true
rememberPossiblyImpureFunctionValues: false #risky to remember these, better for performance to avoid repeated calls anyway
treatPhpDocTypesAsCertain: false treatPhpDocTypesAsCertain: false
bootstrapFiles: bootstrapFiles:
- tests/phpstan/bootstrap.php - tests/phpstan/bootstrap.php
@ -29,6 +30,7 @@ parameters:
paths: paths:
- build - build
- src - src
- tests/phpstan/DummyPluginOwned.php
- tests/phpstan/rules - tests/phpstan/rules
- tests/phpunit - tests/phpunit
- tests/plugins/TesterPlugin - tests/plugins/TesterPlugin
@ -42,6 +44,7 @@ parameters:
- pocketmine\DEBUG - pocketmine\DEBUG
- pocketmine\IS_DEVELOPMENT_BUILD - pocketmine\IS_DEVELOPMENT_BUILD
stubFiles: stubFiles:
- tests/phpstan/stubs/chunkutils2.stub
- tests/phpstan/stubs/JsonMapper.stub - tests/phpstan/stubs/JsonMapper.stub
- tests/phpstan/stubs/leveldb.stub - tests/phpstan/stubs/leveldb.stub
- tests/phpstan/stubs/pmmpthread.stub - tests/phpstan/stubs/pmmpthread.stub

View File

@ -139,6 +139,7 @@ use function file_put_contents;
use function filemtime; use function filemtime;
use function fopen; use function fopen;
use function get_class; use function get_class;
use function gettype;
use function ini_set; use function ini_set;
use function is_array; use function is_array;
use function is_dir; use function is_dir;
@ -923,6 +924,7 @@ class Server{
TimingsHandler::getCollectCallbacks()->add(function() : array{ TimingsHandler::getCollectCallbacks()->add(function() : array{
$promises = []; $promises = [];
foreach($this->asyncPool->getRunningWorkers() as $workerId){ foreach($this->asyncPool->getRunningWorkers() as $workerId){
/** @phpstan-var PromiseResolver<list<string>> $resolver */
$resolver = new PromiseResolver(); $resolver = new PromiseResolver();
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId); $this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
@ -1018,7 +1020,11 @@ class Server{
copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile); copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile);
} }
try{ try{
$pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile))); $array = yaml_parse(Filesystem::fileGetContents($graylistFile));
if(!is_array($array)){
throw new \InvalidArgumentException("Expected array for root, but have " . gettype($array));
}
$pluginGraylist = PluginGraylist::fromArray($array);
}catch(\InvalidArgumentException $e){ }catch(\InvalidArgumentException $e){
$this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage()); $this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage());
$this->forceShutdownExit(); $this->forceShutdownExit();
@ -1180,7 +1186,7 @@ class Server{
if($this->worldManager->getDefaultWorld() === null){ if($this->worldManager->getDefaultWorld() === null){
$default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); $default = $this->configGroup->getConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");
if(trim($default) == ""){ if(trim($default) === ""){
$this->logger->warning("level-name cannot be null, using default"); $this->logger->warning("level-name cannot be null, using default");
$default = "world"; $default = "world";
$this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); $this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world");

View File

@ -51,9 +51,8 @@ final class CommandStringHelper{
foreach($matches[0] as $k => $_){ foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){ for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){ if($matches[$i][$k] !== ""){
/** @var string $match */ //phpstan can't understand preg_match and friends by itself :(
$match = $matches[$i][$k]; $match = $matches[$i][$k];
$args[(int) $k] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg()); $args[] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
break; break;
} }
} }

View File

@ -110,7 +110,6 @@ class CraftingManager{
/** /**
* @param Item[] $items * @param Item[] $items
* @phpstan-param list<Item> $items
* *
* @return Item[] * @return Item[]
* @phpstan-return list<Item> * @phpstan-return list<Item>
@ -135,7 +134,6 @@ class CraftingManager{
/** /**
* @param Item[] $outputs * @param Item[] $outputs
* @phpstan-param list<Item> $outputs
*/ */
private static function hashOutputs(array $outputs) : string{ private static function hashOutputs(array $outputs) : string{
$outputs = self::pack($outputs); $outputs = self::pack($outputs);

View File

@ -37,7 +37,6 @@ use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function array_key_last; use function array_key_last;
use function array_map; use function array_map;
use function array_values;
use function assert; use function assert;
use function count; use function count;
use function get_debug_type; use function get_debug_type;
@ -138,8 +137,8 @@ final class BlockStateUpgradeSchemaUtils{
$convertedRemappedValuesIndex = []; $convertedRemappedValuesIndex = [];
foreach(Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){ foreach(Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){
foreach($mappingValues as $k => $oldNew){ foreach($mappingValues as $oldNew){
$convertedRemappedValuesIndex[$mappingKey][$k] = new BlockStateUpgradeSchemaValueRemap( $convertedRemappedValuesIndex[$mappingKey][] = new BlockStateUpgradeSchemaValueRemap(
self::jsonModelToTag($oldNew->old), self::jsonModelToTag($oldNew->old),
self::jsonModelToTag($oldNew->new) self::jsonModelToTag($oldNew->new)
); );
@ -361,7 +360,7 @@ final class BlockStateUpgradeSchemaUtils{
//remaps with the same number of criteria should be sorted alphabetically, but this is not strictly necessary //remaps with the same number of criteria should be sorted alphabetically, but this is not strictly necessary
return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []); return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
}); });
$result->remappedStates[$oldBlockName] = array_values($keyedRemaps); $result->remappedStates[$oldBlockName] = $keyedRemaps; //usort strips keys, so this is already a list
} }
if(isset($result->remappedStates)){ if(isset($result->remappedStates)){
ksort($result->remappedStates); ksort($result->remappedStates);

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\data\runtime; namespace pocketmine\data\runtime;
use function array_values;
use function ceil; use function ceil;
use function count; use function count;
use function log; use function log;
@ -60,7 +59,7 @@ final class RuntimeEnumMetadata{
usort($members, fn(\UnitEnum $a, \UnitEnum $b) => $a->name <=> $b->name); //sort by name to ensure consistent ordering (and thus consistent bit assignments) usort($members, fn(\UnitEnum $a, \UnitEnum $b) => $a->name <=> $b->name); //sort by name to ensure consistent ordering (and thus consistent bit assignments)
$this->bits = (int) ceil(log(count($members), 2)); $this->bits = (int) ceil(log(count($members), 2));
$this->intToEnum = array_values($members); $this->intToEnum = $members; //usort strips keys so this is already a list
$reversed = []; $reversed = [];
foreach($this->intToEnum as $int => $enum){ foreach($this->intToEnum as $int => $enum){

View File

@ -44,7 +44,6 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
use function assert;
use function atan2; use function atan2;
use function ceil; use function ceil;
use function count; use function count;
@ -170,8 +169,6 @@ abstract class Projectile extends Entity{
$start = $this->location->asVector3(); $start = $this->location->asVector3();
$end = $start->add($dx, $dy, $dz); $end = $start->add($dx, $dy, $dz);
$blockHit = null;
$entityHit = null;
$hitResult = null; $hitResult = null;
$world = $this->getWorld(); $world = $this->getWorld();
@ -181,8 +178,7 @@ abstract class Projectile extends Entity{
$blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end); $blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end);
if($blockHitResult !== null){ if($blockHitResult !== null){
$end = $blockHitResult->hitVector; $end = $blockHitResult->hitVector;
$blockHit = $block; $hitResult = [$block, $blockHitResult];
$hitResult = $blockHitResult;
break; break;
} }
} }
@ -206,8 +202,7 @@ abstract class Projectile extends Entity{
if($distance < $entityDistance){ if($distance < $entityDistance){
$entityDistance = $distance; $entityDistance = $distance;
$entityHit = $entity; $hitResult = [$entity, $entityHitResult];
$hitResult = $entityHitResult;
$end = $entityHitResult->hitVector; $end = $entityHitResult->hitVector;
} }
} }
@ -223,26 +218,18 @@ abstract class Projectile extends Entity{
$this->recalculateBoundingBox(); $this->recalculateBoundingBox();
if($hitResult !== null){ if($hitResult !== null){
/** @var ProjectileHitEvent|null $ev */ [$objectHit, $rayTraceResult] = $hitResult;
$ev = null; if($objectHit instanceof Entity){
if($entityHit !== null){ $ev = new ProjectileHitEntityEvent($this, $rayTraceResult, $objectHit);
$ev = new ProjectileHitEntityEvent($this, $hitResult, $entityHit); $specificHitFunc = fn() => $this->onHitEntity($objectHit, $rayTraceResult);
}elseif($blockHit !== null){
$ev = new ProjectileHitBlockEvent($this, $hitResult, $blockHit);
}else{ }else{
assert(false, "unknown hit type"); $ev = new ProjectileHitBlockEvent($this, $rayTraceResult, $objectHit);
$specificHitFunc = fn() => $this->onHitBlock($objectHit, $rayTraceResult);
} }
if($ev !== null){
$ev->call(); $ev->call();
$this->onHit($ev); $this->onHit($ev);
$specificHitFunc();
if($ev instanceof ProjectileHitEntityEvent){
$this->onHitEntity($ev->getEntityHit(), $ev->getRayTraceResult());
}elseif($ev instanceof ProjectileHitBlockEvent){
$this->onHitBlock($ev->getBlockHit(), $ev->getRayTraceResult());
}
}
$this->isCollided = $this->onGround = true; $this->isCollided = $this->onGround = true;
$this->motion = Vector3::zero(); $this->motion = Vector3::zero();
@ -290,10 +277,11 @@ abstract class Projectile extends Entity{
$damage = $this->getResultDamage(); $damage = $this->getResultDamage();
if($damage >= 0){ if($damage >= 0){
if($this->getOwningEntity() === null){ $owner = $this->getOwningEntity();
if($owner === null){
$ev = new EntityDamageByEntityEvent($this, $entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); $ev = new EntityDamageByEntityEvent($this, $entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage);
}else{ }else{
$ev = new EntityDamageByChildEntityEvent($this->getOwningEntity(), $this, $entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); $ev = new EntityDamageByChildEntityEvent($owner, $this, $entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage);
} }
$entityHit->attack($ev); $entityHit->attack($ev);

View File

@ -119,7 +119,7 @@ class HandlerListManager{
public function getHandlersFor(string $event) : array{ public function getHandlersFor(string $event) : array{
$cache = $this->handlerCaches[$event] ?? null; $cache = $this->handlerCaches[$event] ?? null;
//getListFor() will populate the cache for the next call //getListFor() will populate the cache for the next call
return $cache?->list ?? $this->getListFor($event)->getListenerList(); return $cache->list ?? $this->getListFor($event)->getListenerList();
} }
/** /**

View File

@ -52,9 +52,15 @@ class PlayerPreLoginEvent extends Event{
self::KICK_FLAG_BANNED self::KICK_FLAG_BANNED
]; ];
/** @var Translatable[]|string[] reason const => associated message */ /**
* @var Translatable[]|string[] reason const => associated message
* @phpstan-var array<int, Translatable|string>
*/
protected array $disconnectReasons = []; protected array $disconnectReasons = [];
/** @var Translatable[]|string[] */ /**
* @var Translatable[]|string[]
* @phpstan-var array<int, Translatable|string>
*/
protected array $disconnectScreenMessages = []; protected array $disconnectScreenMessages = [];
public function __construct( public function __construct(
@ -93,6 +99,7 @@ class PlayerPreLoginEvent extends Event{
* Returns an array of kick flags currently assigned. * Returns an array of kick flags currently assigned.
* *
* @return int[] * @return int[]
* @phpstan-return list<int>
*/ */
public function getKickFlags() : array{ public function getKickFlags() : array{
return array_keys($this->disconnectReasons); return array_keys($this->disconnectReasons);

View File

@ -231,7 +231,7 @@ class InventoryTransaction{
/** /**
* @param SlotChangeAction[] $possibleActions * @param SlotChangeAction[] $possibleActions
* @phpstan-param list<SlotChangeAction> $possibleActions * @phpstan-param array<int, SlotChangeAction> $possibleActions
*/ */
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{ protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
assert(count($possibleActions) > 0); assert(count($possibleActions) > 0);

View File

@ -101,8 +101,9 @@ abstract class WritableBookBase extends Item{
* @return $this * @return $this
*/ */
public function deletePage(int $pageId) : self{ public function deletePage(int $pageId) : self{
unset($this->pages[$pageId]); $newPages = $this->pages;
$this->pages = array_values($this->pages); unset($newPages[$pageId]);
$this->pages = array_values($newPages);
return $this; return $this;
} }

View File

@ -32,6 +32,7 @@ use function array_merge;
use function array_search; use function array_search;
use function array_shift; use function array_shift;
use function array_unique; use function array_unique;
use function array_values;
use function count; use function count;
/** /**
@ -103,7 +104,8 @@ final class ItemEnchantmentTagRegistry{
foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){ foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){
if(($nestedKey = array_search($tag, $nestedTags, true)) !== false){ if(($nestedKey = array_search($tag, $nestedTags, true)) !== false){
unset($this->tagMap[$key][$nestedKey]); unset($nestedTags[$nestedKey]);
$this->tagMap[$key] = array_values($nestedTags);
} }
} }
} }
@ -115,7 +117,7 @@ final class ItemEnchantmentTagRegistry{
*/ */
public function removeNested(string $tag, array $nestedTags) : void{ public function removeNested(string $tag, array $nestedTags) : void{
$this->assertNotInternalTag($tag); $this->assertNotInternalTag($tag);
$this->tagMap[$tag] = array_diff($this->tagMap[$tag], $nestedTags); $this->tagMap[$tag] = array_values(array_diff($this->tagMap[$tag], $nestedTags));
} }
/** /**

View File

@ -25,18 +25,22 @@ namespace pocketmine\item\enchantment;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\lang\Translatable; use pocketmine\lang\Translatable;
use function array_flip; use function array_fill_keys;
use function floor; use function floor;
class ProtectionEnchantment extends Enchantment{ class ProtectionEnchantment extends Enchantment{
protected float $typeModifier; protected float $typeModifier;
/** @var int[]|null */ /**
* @var true[]|null
* @phpstan-var array<int, true>
*/
protected ?array $applicableDamageTypes = null; protected ?array $applicableDamageTypes = null;
/** /**
* ProtectionEnchantment constructor. * ProtectionEnchantment constructor.
* *
* @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower * @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower
* @phpstan-param list<int>|null $applicableDamageTypes
* *
* @param int[]|null $applicableDamageTypes EntityDamageEvent::CAUSE_* constants which this enchantment type applies to, or null if it applies to all types of damage. * @param int[]|null $applicableDamageTypes EntityDamageEvent::CAUSE_* constants which this enchantment type applies to, or null if it applies to all types of damage.
* @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange) * @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange)
@ -46,7 +50,7 @@ class ProtectionEnchantment extends Enchantment{
$this->typeModifier = $typeModifier; $this->typeModifier = $typeModifier;
if($applicableDamageTypes !== null){ if($applicableDamageTypes !== null){
$this->applicableDamageTypes = array_flip($applicableDamageTypes); $this->applicableDamageTypes = array_fill_keys($applicableDamageTypes, true);
} }
} }

View File

@ -101,11 +101,12 @@ class BanEntry{
} }
public function getString() : string{ public function getString() : string{
$expires = $this->getExpires();
return implode("|", [ return implode("|", [
$this->getName(), $this->getName(),
$this->getCreated()->format(self::$format), $this->getCreated()->format(self::$format),
$this->getSource(), $this->getSource(),
$this->getExpires() === null ? "Forever" : $this->getExpires()->format(self::$format), $expires === null ? "Forever" : $expires->format(self::$format),
$this->getReason() $this->getReason()
]); ]);
} }

View File

@ -148,7 +148,6 @@ use function count;
use function explode; use function explode;
use function floor; use function floor;
use function get_class; use function get_class;
use function is_int;
use function max; use function max;
use function mb_strlen; use function mb_strlen;
use function microtime; use function microtime;
@ -831,7 +830,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$X = null; $X = null;
$Z = null; $Z = null;
World::getXZ($index, $X, $Z); World::getXZ($index, $X, $Z);
assert(is_int($X) && is_int($Z));
++$count; ++$count;
@ -1351,7 +1349,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->nextChunkOrderRun = 0; $this->nextChunkOrderRun = 0;
} }
if(!$revert && $distanceSquared != 0){ if(!$revert && $distanceSquared !== 0.0){
$dx = $newPos->x - $oldPos->x; $dx = $newPos->x - $oldPos->x;
$dy = $newPos->y - $oldPos->y; $dy = $newPos->y - $oldPos->y;
$dz = $newPos->z - $oldPos->z; $dz = $newPos->z - $oldPos->z;
@ -2324,7 +2322,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason); $ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason);
$ev->call(); $ev->call();
if(($quitMessage = $ev->getQuitMessage()) != ""){ if(($quitMessage = $ev->getQuitMessage()) !== ""){
$this->server->broadcastMessage($quitMessage); $this->server->broadcastMessage($quitMessage);
} }
$this->save(); $this->save();
@ -2465,7 +2463,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
$this->xpManager->setXpAndProgress(0, 0.0); $this->xpManager->setXpAndProgress(0, 0.0);
} }
if($ev->getDeathMessage() != ""){ if($ev->getDeathMessage() !== ""){
$this->server->broadcastMessage($ev->getDeathMessage()); $this->server->broadcastMessage($ev->getDeathMessage());
} }

View File

@ -24,7 +24,8 @@ declare(strict_types=1);
namespace pocketmine\plugin; namespace pocketmine\plugin;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function array_flip; use function array_fill_keys;
use function array_keys;
use function is_array; use function is_array;
use function is_float; use function is_float;
use function is_int; use function is_int;
@ -32,23 +33,28 @@ use function is_string;
class PluginGraylist{ class PluginGraylist{
/** @var string[] */ /**
* @var true[]
* @phpstan-var array<string, true>
*/
private array $plugins; private array $plugins;
private bool $isWhitelist = false; private bool $isWhitelist = false;
/** /**
* @param string[] $plugins * @param string[] $plugins
* @phpstan-param list<string> $plugins
*/ */
public function __construct(array $plugins = [], bool $whitelist = false){ public function __construct(array $plugins = [], bool $whitelist = false){
$this->plugins = array_flip($plugins); $this->plugins = array_fill_keys($plugins, true);
$this->isWhitelist = $whitelist; $this->isWhitelist = $whitelist;
} }
/** /**
* @return string[] * @return string[]
* @phpstan-return list<string>
*/ */
public function getPlugins() : array{ public function getPlugins() : array{
return array_flip($this->plugins); return array_keys($this->plugins);
} }
public function isWhitelist() : bool{ public function isWhitelist() : bool{

View File

@ -47,10 +47,16 @@ class ResourcePackManager{
private string $path; private string $path;
private bool $serverForceResources = false; private bool $serverForceResources = false;
/** @var ResourcePack[] */ /**
* @var ResourcePack[]
* @phpstan-var list<ResourcePack>
*/
private array $resourcePacks = []; private array $resourcePacks = [];
/** @var ResourcePack[] */ /**
* @var ResourcePack[]
* @phpstan-var array<string, ResourcePack>
*/
private array $uuidList = []; private array $uuidList = [];
/** /**
@ -165,6 +171,7 @@ class ResourcePackManager{
/** /**
* Returns an array of resource packs in use, sorted in order of priority. * Returns an array of resource packs in use, sorted in order of priority.
* @return ResourcePack[] * @return ResourcePack[]
* @phpstan-return list<ResourcePack>
*/ */
public function getResourceStack() : array{ public function getResourceStack() : array{
return $this->resourcePacks; return $this->resourcePacks;

View File

@ -100,7 +100,7 @@ class ZippedResourcePack implements ResourcePack{
try{ try{
$manifest = (new CommentedJsonDecoder())->decode($manifestData); $manifest = (new CommentedJsonDecoder())->decode($manifestData);
}catch(\RuntimeException $e){ }catch(\RuntimeException $e){
throw new ResourcePackException("Failed to parse manifest.json: " . $e->getMessage(), $e->getCode(), $e); throw new ResourcePackException("Failed to parse manifest.json: " . $e->getMessage(), 0, $e);
} }
if(!($manifest instanceof \stdClass)){ if(!($manifest instanceof \stdClass)){
throw new ResourcePackException("manifest.json should contain a JSON object, not " . gettype($manifest)); throw new ResourcePackException("manifest.json should contain a JSON object, not " . gettype($manifest));

View File

@ -77,7 +77,10 @@ class BulkCurlTask extends AsyncTask{
* @phpstan-var \Closure(list<InternetRequestResult|InternetException>) : void * @phpstan-var \Closure(list<InternetRequestResult|InternetException>) : void
*/ */
$callback = $this->fetchLocal(self::TLS_KEY_COMPLETION_CALLBACK); $callback = $this->fetchLocal(self::TLS_KEY_COMPLETION_CALLBACK);
/** @var InternetRequestResult[]|InternetException[] $results */ /**
* @var InternetRequestResult[]|InternetException[] $results
* @phpstan-var list<InternetRequestResult|InternetException> $results
*/
$results = $this->getResult(); $results = $this->getResult();
$callback($results); $callback($results);
} }

View File

@ -123,6 +123,7 @@ class TimingsHandler{
/** /**
* @return string[] * @return string[]
* @phpstan-return list<string>
*/ */
private static function printFooter() : array{ private static function printFooter() : array{
$result = []; $result = [];
@ -160,6 +161,7 @@ class TimingsHandler{
} }
} }
/** @phpstan-var PromiseResolver<list<string>> $resolver */
$resolver = new PromiseResolver(); $resolver = new PromiseResolver();
Promise::all($otherThreadRecordPromises)->onCompletion( Promise::all($otherThreadRecordPromises)->onCompletion(
function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{ function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{

View File

@ -131,7 +131,7 @@ final class TimingsRecord{
} }
public function stopTiming(int $now) : void{ public function stopTiming(int $now) : void{
if($this->start == 0){ if($this->start === 0){
return; return;
} }
if(self::$currentRecord !== $this){ if(self::$currentRecord !== $this){

View File

@ -167,6 +167,7 @@ final class Utils{
/** /**
* @phpstan-return \Closure(object) : object * @phpstan-return \Closure(object) : object
* @deprecated
*/ */
public static function cloneCallback() : \Closure{ public static function cloneCallback() : \Closure{
return static function(object $o){ return static function(object $o){
@ -179,15 +180,13 @@ final class Utils{
* @phpstan-template TValue of object * @phpstan-template TValue of object
* *
* @param object[] $array * @param object[] $array
* @phpstan-param array<TKey, TValue> $array * @phpstan-param array<TKey, TValue>|list<TValue> $array
* *
* @return object[] * @return object[]
* @phpstan-return array<TKey, TValue> * @phpstan-return ($array is list<TValue> ? list<TValue> : array<TKey, TValue>)
*/ */
public static function cloneObjectArray(array $array) : array{ public static function cloneObjectArray(array $array) : array{
/** @phpstan-var \Closure(TValue) : TValue $callback */ return array_map(fn(object $o) => clone $o, $array);
$callback = self::cloneCallback();
return array_map($callback, $array);
} }
/** /**
@ -406,6 +405,7 @@ final class Utils{
/** /**
* @param mixed[][] $trace * @param mixed[][] $trace
* @phpstan-param list<array<string, mixed>>|null $trace
* @return string[] * @return string[]
*/ */
public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{ public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{
@ -469,7 +469,15 @@ final class Utils{
} }
$params = implode(", ", $paramsList); $params = implode(", ", $paramsList);
} }
$messages[] = "#$i " . (isset($trace[$i]["file"]) ? Filesystem::cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" || $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable($params) . ")"; $messages[] = "#$i " .
(isset($trace[$i]["file"]) ? Filesystem::cleanPath($trace[$i]["file"]) : "") .
"(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " .
(isset($trace[$i]["class"]) ?
$trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" || $trace[$i]["type"] === "->") ? "->" : "::") :
""
) .
$trace[$i]["function"] .
"(" . Utils::printable($params) . ")";
} }
return $messages; return $messages;
} }

View File

@ -113,6 +113,7 @@ use function array_keys;
use function array_map; use function array_map;
use function array_merge; use function array_merge;
use function array_sum; use function array_sum;
use function array_values;
use function assert; use function assert;
use function cos; use function cos;
use function count; use function count;
@ -678,7 +679,6 @@ class World implements ChunkManager{
* Used for broadcasting sounds and particles with specific targets. * Used for broadcasting sounds and particles with specific targets.
* *
* @param Player[] $allowed * @param Player[] $allowed
* @phpstan-param list<Player> $allowed
* *
* @return array<int, Player> * @return array<int, Player>
*/ */
@ -1089,7 +1089,6 @@ class World implements ChunkManager{
/** /**
* @param Vector3[] $blocks * @param Vector3[] $blocks
* @phpstan-param list<Vector3> $blocks
* *
* @return ClientboundPacket[] * @return ClientboundPacket[]
* @phpstan-return list<ClientboundPacket> * @phpstan-return list<ClientboundPacket>
@ -1456,8 +1455,8 @@ class World implements ChunkManager{
$this->provider->saveChunk($chunkX, $chunkZ, new ChunkData( $this->provider->saveChunk($chunkX, $chunkZ, new ChunkData(
$chunk->getSubChunks(), $chunk->getSubChunks(),
$chunk->isPopulated(), $chunk->isPopulated(),
array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($chunkX, $chunkZ), fn(Entity $e) => $e->canSaveWithChunk())), array_map(fn(Entity $e) => $e->saveNBT(), array_values(array_filter($this->getChunkEntities($chunkX, $chunkZ), fn(Entity $e) => $e->canSaveWithChunk()))),
array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()), array_map(fn(Tile $t) => $t->saveNBT(), array_values($chunk->getTiles())),
), $chunk->getTerrainDirtyFlags()); ), $chunk->getTerrainDirtyFlags());
$chunk->clearTerrainDirtyFlags(); $chunk->clearTerrainDirtyFlags();
} }
@ -3000,8 +2999,8 @@ class World implements ChunkManager{
$this->provider->saveChunk($x, $z, new ChunkData( $this->provider->saveChunk($x, $z, new ChunkData(
$chunk->getSubChunks(), $chunk->getSubChunks(),
$chunk->isPopulated(), $chunk->isPopulated(),
array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($x, $z), fn(Entity $e) => $e->canSaveWithChunk())), array_map(fn(Entity $e) => $e->saveNBT(), array_values(array_filter($this->getChunkEntities($x, $z), fn(Entity $e) => $e->canSaveWithChunk()))),
array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()), array_map(fn(Tile $t) => $t->saveNBT(), array_values($chunk->getTiles())),
), $chunk->getTerrainDirtyFlags()); ), $chunk->getTerrainDirtyFlags());
}finally{ }finally{
$this->timings->syncChunkSave->stopTiming(); $this->timings->syncChunkSave->stopTiming();

View File

@ -83,11 +83,11 @@ abstract class BaseWorldProvider implements WorldProvider{
} }
try{ try{
$newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData); $newPalette[] = $this->blockStateDeserializer->deserialize($newStateData);
}catch(BlockStateDeserializeException $e){ }catch(BlockStateDeserializeException $e){
//this should never happen anyway - if the upgrader returned an invalid state, we have bigger problems //this should never happen anyway - if the upgrader returned an invalid state, we have bigger problems
$blockDecodeErrors[] = "Palette offset $k / Failed to deserialize upgraded state $id:$meta: " . $e->getMessage(); $blockDecodeErrors[] = "Palette offset $k / Failed to deserialize upgraded state $id:$meta: " . $e->getMessage();
$newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); $newPalette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData());
} }
} }

View File

@ -711,7 +711,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$nbt = new LittleEndianNbtSerializer(); $nbt = new LittleEndianNbtSerializer();
/** @var CompoundTag[] $entities */
$entities = []; $entities = [];
if(($entityData = $this->db->get($index . ChunkDataKey::ENTITIES)) !== false && $entityData !== ""){ if(($entityData = $this->db->get($index . ChunkDataKey::ENTITIES)) !== false && $entityData !== ""){
try{ try{
@ -721,7 +720,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
} }
} }
/** @var CompoundTag[] $tiles */
$tiles = []; $tiles = [];
if(($tileData = $this->db->get($index . ChunkDataKey::BLOCK_ENTITIES)) !== false && $tileData !== ""){ if(($tileData = $this->db->get($index . ChunkDataKey::BLOCK_ENTITIES)) !== false && $tileData !== ""){
try{ try{

View File

@ -33,10 +33,8 @@ use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\LoadedChunkData; use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\io\WorldData; use pocketmine\world\format\io\WorldData;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function assert;
use function file_exists; use function file_exists;
use function is_dir; use function is_dir;
use function is_int;
use function morton2d_encode; use function morton2d_encode;
use function rename; use function rename;
use function scandir; use function scandir;
@ -60,7 +58,12 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
public static function isValid(string $path) : bool{ public static function isValid(string $path) : bool{
if(file_exists(Path::join($path, "level.dat")) && is_dir($regionPath = Path::join($path, "region"))){ if(file_exists(Path::join($path, "level.dat")) && is_dir($regionPath = Path::join($path, "region"))){
foreach(scandir($regionPath, SCANDIR_SORT_NONE) as $file){ $files = scandir($regionPath, SCANDIR_SORT_NONE);
if($files === false){
//we can't tell the type if we don't have read perms
return false;
}
foreach($files as $file){
$extPos = strrpos($file, "."); $extPos = strrpos($file, ".");
if($extPos !== false && substr($file, $extPos + 1) === static::getRegionFileExtension()){ if($extPos !== false && substr($file, $extPos + 1) === static::getRegionFileExtension()){
//we don't care if other region types exist, we only care if this format is possible //we don't care if other region types exist, we only care if this format is possible
@ -199,7 +202,6 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData{ public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData{
$regionX = $regionZ = null; $regionX = $regionZ = null;
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
assert(is_int($regionX) && is_int($regionZ));
if(!file_exists($this->pathToRegion($regionX, $regionZ))){ if(!file_exists($this->pathToRegion($regionX, $regionZ))){
return null; return null;
@ -213,6 +215,9 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
return null; return null;
} }
/**
* @phpstan-return \RegexIterator<mixed, string, \FilesystemIterator>
*/
private function createRegionIterator() : \RegexIterator{ private function createRegionIterator() : \RegexIterator{
return new \RegexIterator( return new \RegexIterator(
new \FilesystemIterator( new \FilesystemIterator(

View File

@ -27,7 +27,6 @@ use pocketmine\data\bedrock\BiomeIds;
use pocketmine\item\LegacyStringToItemParser; use pocketmine\item\LegacyStringToItemParser;
use pocketmine\item\LegacyStringToItemParserException; use pocketmine\item\LegacyStringToItemParserException;
use function array_map; use function array_map;
use function count;
use function explode; use function explode;
use function preg_match; use function preg_match;
use function preg_match_all; use function preg_match_all;
@ -75,8 +74,7 @@ final class FlatGeneratorOptions{
$y = 0; $y = 0;
$itemParser = LegacyStringToItemParser::getInstance(); $itemParser = LegacyStringToItemParser::getInstance();
foreach($split as $line){ foreach($split as $line){
preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches); if(preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches) !== 1){
if(count($matches) !== 3){
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\""); throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\"");
} }
@ -119,7 +117,7 @@ final class FlatGeneratorOptions{
} }
} }
} }
$options[(string) $option] = $params; $options[$option] = $params;
} }
return new self($structure, $biomeId, $options); return new self($structure, $biomeId, $options);
} }

View File

@ -126,10 +126,10 @@ class Normal extends Generator{
$hash = (int) $hash; $hash = (int) $hash;
$xNoise = $hash >> 20 & 3; $xNoise = $hash >> 20 & 3;
$zNoise = $hash >> 22 & 3; $zNoise = $hash >> 22 & 3;
if($xNoise == 3){ if($xNoise === 3){
$xNoise = 1; $xNoise = 1;
} }
if($zNoise == 3){ if($zNoise === 3){
$zNoise = 1; $zNoise = 1;
} }

View File

@ -0,0 +1,28 @@
<?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\plugin;
class DummyPluginOwned{
use PluginOwnedTrait;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Method pocketmine\\\\event\\\\RegisteredListener\\:\\:__construct\\(\\) has parameter \\$handler with no signature specified for Closure\\.$#" message: '#^Method pocketmine\\event\\RegisteredListener\:\:__construct\(\) has parameter \$handler with no signature specified for Closure\.$#'
identifier: missingType.callable
count: 1 count: 1
path: ../../../src/event/RegisteredListener.php path: ../../../src/event/RegisteredListener.php
- -
message: "#^Method pocketmine\\\\event\\\\RegisteredListener\\:\\:getHandler\\(\\) return type has no signature specified for Closure\\.$#" message: '#^Method pocketmine\\event\\RegisteredListener\:\:getHandler\(\) return type has no signature specified for Closure\.$#'
identifier: missingType.callable
count: 1 count: 1
path: ../../../src/event/RegisteredListener.php path: ../../../src/event/RegisteredListener.php

View File

@ -1,112 +1,266 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#" message: '#^Access to an undefined property object\:\:\$crashId\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Access to an undefined property object\:\:\$crashUrl\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Access to an undefined property object\:\:\$error\.$#'
identifier: property.notFound
count: 1
path: ../../../src/Server.php
-
message: '#^Method pocketmine\\block\\Block\:\:readStateFromWorld\(\) is marked as impure but does not have any side effects\.$#'
identifier: impureMethod.pure
count: 1
path: ../../../src/block/Block.php
-
message: '#^Method pocketmine\\block\\DoubleTallGrass\:\:traitGetDropsForIncompatibleTool\(\) return type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1 count: 1
path: ../../../src/block/DoubleTallGrass.php path: ../../../src/block/DoubleTallGrass.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:ACACIA_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:BIRCH_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CHERRY_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CRIMSON_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:DARK_OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:JUNGLE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:MANGROVE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:PALE_OAK_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:WARPED_SIGN\\(\\)\\.$#" message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_SIGN\(\)\.$#'
identifier: callable.nonNativeMethod
count: 1 count: 1
path: ../../../src/block/VanillaBlocks.php path: ../../../src/block/VanillaBlocks.php
- -
message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" message: '#^Strict comparison using \=\=\= between \*NEVER\* and 5 will always evaluate to false\.$#'
identifier: identical.alwaysFalse
count: 1
path: ../../../src/command/defaults/TeleportCommand.php
-
message: '#^Method pocketmine\\crafting\\ShapedRecipe\:\:getIngredientMap\(\) should return list\<list\<pocketmine\\crafting\\RecipeIngredient\|null\>\> but returns array\<int\<0, max\>, non\-empty\-array\<int\<0, max\>, pocketmine\\crafting\\RecipeIngredient\|null\>\>\.$#'
identifier: return.type
count: 1
path: ../../../src/crafting/ShapedRecipe.php
-
message: '#^Property pocketmine\\crash\\CrashDumpData\:\:\$parameters \(list\<string\>\) does not accept array\.$#'
identifier: assign.propertyType
count: 1
path: ../../../src/crash/CrashDump.php
-
message: '#^Call to function assert\(\) with false and ''unknown hit type'' will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1 count: 1
path: ../../../src/entity/projectile/Projectile.php path: ../../../src/entity/projectile/Projectile.php
- -
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\PthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#" message: '#^Property pocketmine\\item\\WritableBookBase\:\:\$pages \(list\<pocketmine\\item\\WritableBookPage\>\) does not accept non\-empty\-array\<int, pocketmine\\item\\WritableBookPage\>\.$#'
identifier: assign.propertyType
count: 1
path: ../../../src/item/WritableBookBase.php
-
message: '#^Parameter \#3 \$input of class pocketmine\\network\\mcpe\\protocol\\types\\recipe\\ShapedRecipe constructor expects list\<list\<pocketmine\\network\\mcpe\\protocol\\types\\recipe\\RecipeIngredient\>\>, array\<int\<0, max\>, non\-empty\-array\<int\<0, max\>, pocketmine\\network\\mcpe\\protocol\\types\\recipe\\RecipeIngredient\>\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/network/mcpe/cache/CraftingDataCache.php
-
message: '#^Method pocketmine\\network\\mcpe\\compression\\ZlibCompressor\:\:getNetworkId\(\) never returns 1 so it can be removed from the return type\.$#'
identifier: return.unusedType
count: 1
path: ../../../src/network/mcpe/compression/ZlibCompressor.php
-
message: '#^Method pocketmine\\network\\mcpe\\compression\\ZlibCompressor\:\:getNetworkId\(\) never returns 255 so it can be removed from the return type\.$#'
identifier: return.unusedType
count: 1
path: ../../../src/network/mcpe/compression/ZlibCompressor.php
-
message: '#^Parameter \#1 \$states of class pocketmine\\network\\mcpe\\convert\\BlockStateDictionary constructor expects list\<pocketmine\\network\\mcpe\\convert\\BlockStateDictionaryEntry\>, array\<int\<0, max\>, pocketmine\\network\\mcpe\\convert\\BlockStateDictionaryEntry\> given\.$#'
identifier: argument.type
count: 1
path: ../../../src/network/mcpe/convert/BlockStateDictionary.php
-
message: '#^Cannot access offset ''default'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 1
path: ../../../src/network/mcpe/convert/LegacySkinAdapter.php
-
message: '#^Property pocketmine\\network\\mcpe\\raklib\\PthreadsChannelWriter\:\:\$buffer is never read, only written\.$#'
identifier: property.onlyWritten
count: 1 count: 1
path: ../../../src/network/mcpe/raklib/PthreadsChannelWriter.php path: ../../../src/network/mcpe/raklib/PthreadsChannelWriter.php
- -
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\SnoozeAwarePthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#" message: '#^Property pocketmine\\network\\mcpe\\raklib\\SnoozeAwarePthreadsChannelWriter\:\:\$buffer is never read, only written\.$#'
identifier: property.onlyWritten
count: 1 count: 1
path: ../../../src/network/mcpe/raklib/SnoozeAwarePthreadsChannelWriter.php path: ../../../src/network/mcpe/raklib/SnoozeAwarePthreadsChannelWriter.php
- -
message: "#^Dead catch \\- RuntimeException is never thrown in the try block\\.$#" message: '#^Dead catch \- RuntimeException is never thrown in the try block\.$#'
identifier: catch.neverThrown
count: 1 count: 1
path: ../../../src/plugin/PluginManager.php path: ../../../src/plugin/PluginManager.php
- -
message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\<T of object\\> but returns pocketmine\\\\utils\\\\ObjectSet\\<object\\>\\.$#" message: '#^Binary operation "\." between mixed and ''/''\|''\\\\'' results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/thread/ThreadSafeClassLoader.php
-
message: '#^Binary operation "\." between mixed and non\-falsy\-string results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/thread/ThreadSafeClassLoader.php
-
message: '#^Method pocketmine\\timings\\TimingsHandler\:\:lazyGetSet\(\) should return pocketmine\\utils\\ObjectSet\<T of object\> but returns pocketmine\\utils\\ObjectSet\<object\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/timings/TimingsHandler.php path: ../../../src/timings/TimingsHandler.php
- -
message: "#^Casting to int something that's already int\\.$#" message: '#^Parameter &\$where @param\-out type of method pocketmine\\timings\\TimingsHandler\:\:lazyGetSet\(\) expects pocketmine\\utils\\ObjectSet\<T of object\>, pocketmine\\utils\\ObjectSet\<object\> given\.$#'
identifier: paramOut.type
count: 1
path: ../../../src/timings/TimingsHandler.php
-
message: '#^Binary operation "\*" between mixed and 3600 results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Binary operation "\*" between mixed and 60 results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Binary operation "\+" between \(float\|int\) and mixed results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: ../../../src/utils/Timezone.php
-
message: '#^Property pocketmine\\world\\format\\io\\region\\RegionLoader\:\:\$locationTable \(list\<pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\) does not accept non\-empty\-array\<int, pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\.$#'
identifier: assign.propertyType
count: 2
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: '#^Property pocketmine\\world\\format\\io\\region\\RegionLoader\:\:\$locationTable \(list\<pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\) does not accept non\-empty\-array\<int\<0, max\>, pocketmine\\world\\format\\io\\region\\RegionLocationTableEntry\|null\>\.$#'
identifier: assign.propertyType
count: 3
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: '#^Method pocketmine\\world\\format\\io\\region\\RegionWorldProvider\:\:createRegionIterator\(\) should return RegexIterator\<mixed, string, FilesystemIterator\> but returns RegexIterator\<mixed, mixed, Traversable\<TKey, TValue\>\>\.$#'
identifier: return.type
count: 1
path: ../../../src/world/format/io/region/RegionWorldProvider.php
-
message: '#^Casting to int something that''s already int\.$#'
identifier: cast.useless
count: 1 count: 1
path: ../../../src/world/generator/normal/Normal.php path: ../../../src/world/generator/normal/Normal.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFalse\\(\\) with false will always evaluate to true\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertFalse\(\) with false will always evaluate to true\.$#'
identifier: staticMethod.alreadyNarrowedType
count: 1 count: 1
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false and 'All promise should…' will always evaluate to false\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with false and ''All promise should…'' will always evaluate to false\.$#'
identifier: staticMethod.impossibleType
count: 1 count: 1
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false will always evaluate to false\\.$#" message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with false will always evaluate to false\.$#'
identifier: staticMethod.impossibleType
count: 2 count: 2
path: ../../phpunit/promise/PromiseTest.php path: ../../phpunit/promise/PromiseTest.php
- -
message: "#^Strict comparison using \\=\\=\\= between 0 and 0 will always evaluate to true\\.$#" message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#'
identifier: identical.alwaysTrue
count: 1 count: 1
path: ../rules/UnsafeForeachArrayOfStringRule.php path: ../rules/UnsafeForeachArrayOfStringRule.php

View File

@ -1,22 +1,32 @@
parameters: parameters:
ignoreErrors: ignoreErrors:
- -
message: "#^Cannot call method collectGarbage\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" message: '#^Cannot call method collectGarbage\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#'
identifier: method.nonObject
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:getSubChunks\\(\\) should return array\\<int, pocketmine\\\\world\\\\format\\\\SubChunk\\> but returns array\\<int, pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\>\\.$#" message: '#^Method pocketmine\\world\\format\\Chunk\:\:getSubChunks\(\) should return array\<int, pocketmine\\world\\format\\SubChunk\> but returns array\<int, pocketmine\\world\\format\\SubChunk\|null\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\)\\: mixed\\)\\|null, Closure\\(pocketmine\\\\world\\\\format\\\\SubChunk\\)\\: pocketmine\\\\world\\\\format\\\\SubChunk given\\.$#" message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(pocketmine\\world\\format\\SubChunk\|null\)\: mixed\)\|null, Closure\(pocketmine\\world\\format\\SubChunk\)\: pocketmine\\world\\format\\SubChunk given\.$#'
identifier: argument.type
count: 1 count: 1
path: ../../../src/world/format/Chunk.php path: ../../../src/world/format/Chunk.php
- -
message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return non\\-empty\\-array\\<int, int\\> but returns array\\<int, int\\|null\\>\\.$#" message: '#^Method pocketmine\\world\\format\\HeightArray\:\:getValues\(\) should return non\-empty\-list\<int\> but returns array\<int, int\|null\>\.$#'
identifier: return.type
count: 1 count: 1
path: ../../../src/world/format/HeightArray.php path: ../../../src/world/format/HeightArray.php
-
message: '#^Offset int might not exist on SplFixedArray\<float\>\|null\.$#'
identifier: offsetAccess.notFound
count: 4
path: ../../../src/world/generator/noise/Noise.php

View File

@ -44,6 +44,7 @@ final class DisallowForeachByReferenceRule implements Rule{
return [ return [
RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.") RuleErrorBuilder::message("Foreach by-reference is not allowed, because it has surprising behaviour.")
->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.") ->tip("If the value variable is used outside of the foreach construct (e.g. in a second foreach), the iterable's contents will be unexpectedly altered.")
->identifier('pocketmine.foreach.byRef')
->build() ->build()
]; ];
} }

View File

@ -101,7 +101,7 @@ final class UnsafeForeachArrayOfStringRule implements Rule{
RuleErrorBuilder::message(sprintf( RuleErrorBuilder::message(sprintf(
"Unsafe foreach on array with key type %s (they might be casted to int).", "Unsafe foreach on array with key type %s (they might be casted to int).",
$iterableType->getIterableKeyType()->describe(VerbosityLevel::value()) $iterableType->getIterableKeyType()->describe(VerbosityLevel::value())
))->tip($tip)->build() ))->tip($tip)->identifier('pocketmine.foreach.stringKeys')->build()
]; ];
} }
return []; return [];

View File

@ -0,0 +1,15 @@
<?php
namespace pocketmine\world\format;
final class PalettedBlockArray{
/**
* @param list<int> $palette
*/
public static function fromData(int $bitsPerBlock, string $wordArray, array $palette): PalettedBlockArray {}
/**
* @return list<int>
*/
public function getPalette(): array {}
}

View File

@ -62,6 +62,7 @@ class AsyncPoolTest extends TestCase{
} }
public function testThreadSafeSetResult() : void{ public function testThreadSafeSetResult() : void{
/** @phpstan-var PromiseResolver<ThreadSafeArray<array-key, mixed>> $resolver */
$resolver = new PromiseResolver(); $resolver = new PromiseResolver();
$resolver->getPromise()->onCompletion( $resolver->getPromise()->onCompletion(
function(ThreadSafeArray $result) : void{ function(ThreadSafeArray $result) : void{

View File

@ -23,6 +23,6 @@ declare(strict_types=1);
namespace pocketmine\utils\fixtures; namespace pocketmine\utils\fixtures;
trait TestTrait{ trait TestTrait{ // @phpstan-ignore trait.unused
} }

View File

@ -523,10 +523,12 @@ function processRemappedStates(array $upgradeTable) : array{
} }
} }
} }
$orderedUnchanged = [];
foreach(Utils::stringifyKeys($unchangedStatesByNewName) as $newName => $unchangedStates){ foreach(Utils::stringifyKeys($unchangedStatesByNewName) as $newName => $unchangedStates){
ksort($unchangedStates); sort($unchangedStates);
$unchangedStatesByNewName[$newName] = $unchangedStates; $orderedUnchanged[$newName] = $unchangedStates;
} }
$unchangedStatesByNewName = $orderedUnchanged;
$notFlattenedProperties = []; $notFlattenedProperties = [];
@ -656,7 +658,8 @@ function processRemappedStates(array $upgradeTable) : array{
usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{ usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{
return count($b->oldState) <=> count($a->oldState); return count($b->oldState) <=> count($a->oldState);
}); });
return array_values($list); //usort discards keys, so this is already a list<BlockStateUpgradeSchemaBlockRemap>
return $list;
} }
/** /**

View File

@ -76,7 +76,12 @@ function find_regions_recursive(string $dir, array &$files) : void{
in_array(pathinfo($fullPath, PATHINFO_EXTENSION), SUPPORTED_EXTENSIONS, true) && in_array(pathinfo($fullPath, PATHINFO_EXTENSION), SUPPORTED_EXTENSIONS, true) &&
is_file($fullPath) is_file($fullPath)
){ ){
$files[$fullPath] = filesize($fullPath); $size = filesize($fullPath);
if($size === false){
//If we can't get the size of the file, we probably don't have perms to read it, so ignore it
continue;
}
$files[$fullPath] = $size;
}elseif(is_dir($fullPath)){ }elseif(is_dir($fullPath)){
find_regions_recursive($fullPath, $files); find_regions_recursive($fullPath, $files);
} }
@ -165,7 +170,8 @@ function main(array $argv) : int{
clearstatcache(); clearstatcache();
$newSize = 0; $newSize = 0;
foreach(Utils::stringifyKeys($files) as $file => $oldSize){ foreach(Utils::stringifyKeys($files) as $file => $oldSize){
$newSize += file_exists($file) ? filesize($file) : 0; $size = file_exists($file) ? filesize($file) : 0;
$newSize += $size !== false ? $size : 0;
} }
$diff = $currentSize - $newSize; $diff = $currentSize - $newSize;
$logger->info("Finished compaction of " . count($files) . " files. Freed " . number_format($diff) . " bytes of space (" . round(($diff / $currentSize) * 100, 2) . "% reduction)."); $logger->info("Finished compaction of " . count($files) . " files. Freed " . number_format($diff) . " bytes of space (" . round(($diff / $currentSize) * 100, 2) . "% reduction).");

View File

@ -454,7 +454,7 @@ class ParserPacketHandler extends PacketHandler{
//this sorts the data into a canonical order to make diffs between versions reliable //this sorts the data into a canonical order to make diffs between versions reliable
//how the data is ordered doesn't matter as long as it's reproducible //how the data is ordered doesn't matter as long as it's reproducible
foreach($recipes as $_type => $entries){ foreach(Utils::promoteKeys($recipes) as $_type => $entries){
$_sortedRecipes = []; $_sortedRecipes = [];
$_seen = []; $_seen = [];
foreach($entries as $entry){ foreach($entries as $entry){
@ -475,10 +475,10 @@ class ParserPacketHandler extends PacketHandler{
} }
ksort($recipes, SORT_STRING); ksort($recipes, SORT_STRING);
foreach($recipes as $type => $entries){ foreach(Utils::promoteKeys($recipes) as $type => $entries){
echo "$type: " . count($entries) . "\n"; echo "$type: " . count($entries) . "\n";
} }
foreach($recipes as $type => $entries){ foreach(Utils::promoteKeys($recipes) as $type => $entries){
file_put_contents(Path::join($recipesPath, $type . '.json'), json_encode($entries, JSON_PRETTY_PRINT) . "\n"); file_put_contents(Path::join($recipesPath, $type . '.json'), json_encode($entries, JSON_PRETTY_PRINT) . "\n");
} }

View File

@ -31,10 +31,12 @@ namespace pocketmine\tools\generate_item_upgrade_schema;
use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\utils\Filesystem; use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use function count; use function count;
use function dirname; use function dirname;
use function file_put_contents; use function file_put_contents;
use function fwrite;
use function is_array; use function is_array;
use function json_decode; use function json_decode;
use function json_encode; use function json_encode;
@ -45,6 +47,7 @@ use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR; use const JSON_THROW_ON_ERROR;
use const SCANDIR_SORT_ASCENDING; use const SCANDIR_SORT_ASCENDING;
use const SORT_STRING; use const SORT_STRING;
use const STDERR;
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
@ -56,7 +59,7 @@ if(count($argv) !== 4){
[, $mappingTableFile, $upgradeSchemasDir, $outputFile] = $argv; [, $mappingTableFile, $upgradeSchemasDir, $outputFile] = $argv;
$target = json_decode(Filesystem::fileGetContents($mappingTableFile), true, JSON_THROW_ON_ERROR); $target = json_decode(Filesystem::fileGetContents($mappingTableFile), true, JSON_THROW_ON_ERROR);
if(!is_array($target)){ if(!is_array($target) || !isset($target["simple"]) || !is_array($target["simple"]) || !isset($target["complex"]) || !is_array($target["complex"])){
\GlobalLogger::get()->error("Invalid mapping table file"); \GlobalLogger::get()->error("Invalid mapping table file");
exit(1); exit(1);
} }
@ -93,7 +96,7 @@ foreach($files as $file){
$newDiff = []; $newDiff = [];
foreach($target["simple"] as $oldId => $newId){ foreach(Utils::promoteKeys($target["simple"]) as $oldId => $newId){
$previousNewId = $merged["simple"][$oldId] ?? null; $previousNewId = $merged["simple"][$oldId] ?? null;
if( if(
$previousNewId === $newId || //if previous schemas already accounted for this $previousNewId === $newId || //if previous schemas already accounted for this
@ -107,8 +110,12 @@ if(isset($newDiff["renamedIds"])){
ksort($newDiff["renamedIds"], SORT_STRING); ksort($newDiff["renamedIds"], SORT_STRING);
} }
foreach($target["complex"] as $oldId => $mappings){ foreach(Utils::promoteKeys($target["complex"]) as $oldId => $mappings){
foreach($mappings as $meta => $newId){ if(!is_array($mappings)){
fwrite(STDERR, "Complex mapping for $oldId is not an array\n");
exit(1);
}
foreach(Utils::promoteKeys($mappings) as $meta => $newId){
if(($merged["complex"][$oldId][$meta] ?? null) !== $newId){ if(($merged["complex"][$oldId][$meta] ?? null) !== $newId){
if($oldId === "minecraft:spawn_egg" && $meta === 130 && ($newId === "minecraft:axolotl_bucket" || $newId === "minecraft:axolotl_spawn_egg")){ if($oldId === "minecraft:spawn_egg" && $meta === 130 && ($newId === "minecraft:axolotl_bucket" || $newId === "minecraft:axolotl_spawn_egg")){
//TODO: hack for vanilla bug workaround //TODO: hack for vanilla bug workaround

View File

@ -53,6 +53,10 @@ use const STR_PAD_LEFT;
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
/**
* @phpstan-param positive-int $scale
* @phpstan-param positive-int $radius
*/
function newImage(int $scale, int $radius) : \GdImage{ function newImage(int $scale, int $radius) : \GdImage{
$image = Utils::assumeNotFalse(imagecreatetruecolor($scale * $radius * 2, $scale * $radius * 2)); $image = Utils::assumeNotFalse(imagecreatetruecolor($scale * $radius * 2, $scale * $radius * 2));
imagesavealpha($image, true); imagesavealpha($image, true);
@ -149,6 +153,18 @@ if($radius === null){
fwrite(STDERR, "Please specify a radius using --radius\n"); fwrite(STDERR, "Please specify a radius using --radius\n");
exit(1); exit(1);
} }
if($radius < 1){
fwrite(STDERR, "Radius cannot be less than 1\n");
exit(1);
}
if($scale < 1){
fwrite(STDERR, "Scale cannot be less than 1\n");
exit(1);
}
if($nChunksPerStep < 1){
fwrite(STDERR, "Chunks per step cannot be less than 1\n");
exit(1);
}
$outputDirectory = null; $outputDirectory = null;
if(isset($opts["output"])){ if(isset($opts["output"])){