From bdb5845cecc777ded4e37071a9ae976a8dcbe0d1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 5 Aug 2024 22:33:30 +0100 Subject: [PATCH 001/290] Allow name flattening rules where multiple old values map to the same new ID this allows more compaction in certain cases, such as tallgrass recently. instead of blacklisting any mapping which reuses the same flattened infix, we select the flatten property which produces the smallest number of distinct rules, which produces the most compact schema possible. this change also permits potentially flattening other types of properties such as for corals (live/dead and type), although only one may be selected at a time. --- tools/generate-blockstate-upgrade-schema.php | 30 +++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index 54984d459..299d97d05 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -42,6 +42,7 @@ use function array_key_last; use function array_keys; use function array_map; use function array_shift; +use function array_unique; use function array_values; use function count; use function dirname; @@ -423,6 +424,10 @@ function processRemappedStates(array $upgradeTable) : array{ $filter = $pair->old->getStates(); foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){ + if($unchangedPropertyName === $propertyName){ + $notFlattenedProperties[$propertyName] = true; + continue 2; + } unset($filter[$unchangedPropertyName]); } unset($filter[$propertyName]); @@ -436,26 +441,31 @@ function processRemappedStates(array $upgradeTable) : array{ $notFlattenedProperties[$propertyName] = true; continue; } - foreach(Utils::stringifyKeys($valuesToIds) as $otherRawValue => $otherNewId){ - if($otherRawValue === $rawValue){ - continue; - } - if($otherNewId === $pair->new->getName()){ - //this old value maps to the same new ID as another old value - bad candidate for flattening - $notFlattenedProperties[$propertyName] = true; - continue 2; - } - } } $candidateFlattenedValues[$propertyName][$rawFilter][$rawValue] = $pair->new->getName(); } } + foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ + foreach($filters as $valuesToIds){ + if(count(array_unique($valuesToIds)) === 1){ + //this property doesn't influence the new ID + $notFlattenedProperties[$propertyName] = true; + continue 2; + } + } + } foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){ unset($candidateFlattenedValues[$propertyName]); } $flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues); $flattenProperty = array_key_first($flattenedProperties); + //Properties with fewer rules take up less space for the same result + foreach(Utils::stringifyKeys($flattenedProperties) as $propertyName => $rules){ + if(count($rules) < count($flattenedProperties[$flattenProperty])){ + $flattenProperty = $propertyName; + } + } $list = []; From be2437ac6e6cd851f0cd282f54983cb2e9fcba6f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 5 Aug 2024 22:38:02 +0100 Subject: [PATCH 002/290] Support for flattening TAG_Byte and TAG_Int properties this allows optimisation in upcoming versions. --- .../BlockStateUpgradeSchemaFlattenedName.php | 10 ++++- .../upgrade/BlockStateUpgradeSchemaUtils.php | 33 ++++++++++---- .../block/upgrade/BlockStateUpgrader.php | 23 +++++++--- ...ckStateUpgradeSchemaModelFlattenedName.php | 7 ++- tools/generate-blockstate-upgrade-schema.php | 45 ++++++++++++++++--- 5 files changed, 94 insertions(+), 24 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php index 1c95dd9c7..e2f7f798d 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; +use pocketmine\nbt\tag\ByteTag; +use pocketmine\nbt\tag\IntTag; +use pocketmine\nbt\tag\StringTag; use function ksort; use const SORT_STRING; @@ -31,12 +34,14 @@ final class BlockStateUpgradeSchemaFlattenedName{ /** * @param string[] $flattenedValueRemaps * @phpstan-param array $flattenedValueRemaps + * @phpstan-param class-string|class-string|class-string|null $flattenedPropertyType */ public function __construct( public string $prefix, public string $flattenedProperty, public string $suffix, - public array $flattenedValueRemaps + public array $flattenedValueRemaps, + public ?string $flattenedPropertyType = null ){ ksort($this->flattenedValueRemaps, SORT_STRING); } @@ -45,6 +50,7 @@ final class BlockStateUpgradeSchemaFlattenedName{ return $this->prefix === $that->prefix && $this->flattenedProperty === $that->flattenedProperty && $this->suffix === $that->suffix && - $this->flattenedValueRemaps === $that->flattenedValueRemaps; + $this->flattenedValueRemaps === $that->flattenedValueRemaps && + $this->flattenedPropertyType === $that->flattenedPropertyType; } } diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php index 832631490..d66b7e68c 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php @@ -157,18 +157,29 @@ final class BlockStateUpgradeSchemaUtils{ foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){ foreach($remaps as $remap){ - if(isset($remap->newName) === isset($remap->newFlattenedName)){ + if(isset($remap->newName)){ + $remapName = $remap->newName; + }elseif(isset($remap->newFlattenedName)){ + $flattenRule = $remap->newFlattenedName; + $remapName = new BlockStateUpgradeSchemaFlattenedName( + $flattenRule->prefix, + $flattenRule->flattenedProperty, + $flattenRule->suffix, + $flattenRule->flattenedValueRemaps ?? [], + match($flattenRule->flattenedPropertyType){ + "string", null => StringTag::class, + "int" => IntTag::class, + "byte" => ByteTag::class, + default => throw new \UnexpectedValueException("Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'") + } + ); + }else{ throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set"); } $result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap( array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []), - $remap->newName ?? new BlockStateUpgradeSchemaFlattenedName( - $remap->newFlattenedName->prefix, - $remap->newFlattenedName->flattenedProperty, - $remap->newFlattenedName->suffix, - $remap->newFlattenedName->flattenedValueRemaps ?? [], - ), + $remapName, array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []), $remap->copiedState ?? [] ); @@ -303,7 +314,13 @@ final class BlockStateUpgradeSchemaUtils{ $remap->newName->prefix, $remap->newName->flattenedProperty, $remap->newName->suffix, - $remap->newName->flattenedValueRemaps + $remap->newName->flattenedValueRemaps, + match($remap->newName->flattenedPropertyType){ + StringTag::class => null, //omit for TAG_String, as this is the common case + ByteTag::class => "byte", + IntTag::class => "int", + default => throw new \LogicException("Unexpected tag type " . $remap->newName->flattenedPropertyType . " in flattened property type") + } ), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState), $remap->copiedState diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index 4a305d8bc..ddb1e1359 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -24,10 +24,14 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; use pocketmine\data\bedrock\block\BlockStateData; +use pocketmine\nbt\tag\ByteTag; +use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\Tag; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use function count; +use function get_class; use function is_string; use function ksort; use function max; @@ -141,14 +145,21 @@ final class BlockStateUpgrader{ $newName = $remap->newName; }else{ $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; - if($flattenedValue instanceof StringTag){ - $embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue(); - $newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix); - unset($oldState[$remap->newName->flattenedProperty]); - }else{ - //flattened property is not a TAG_String, so this transformation is not applicable + $expectedType = $remap->newName->flattenedPropertyType; + if(!$flattenedValue instanceof $expectedType){ + //flattened property is not of the expected type, so this transformation is not applicable continue; } + $embedKey = match(get_class($flattenedValue)){ + StringTag::class => $flattenedValue->getValue(), + ByteTag::class => (string) $flattenedValue->getValue(), + IntTag::class => (string) $flattenedValue->getValue(), + //flattenedPropertyType is always one of these three types, but PHPStan doesn't know that + default => throw new AssumptionFailedError("flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)), + }; + $embedValue = $remap->newName->flattenedValueRemaps[$embedKey] ?? $embedKey; + $newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix); + unset($oldState[$remap->newName->flattenedProperty]); } $newState = $remap->newState; diff --git a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php index 001192f47..6d03bbc12 100644 --- a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php +++ b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php @@ -31,6 +31,7 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab public string $prefix; /** @required */ public string $flattenedProperty; + public ?string $flattenedPropertyType = null; /** @required */ public string $suffix; /** @@ -43,11 +44,12 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab * @param string[] $flattenedValueRemaps * @phpstan-param array $flattenedValueRemaps */ - public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){ + public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps, ?string $flattenedPropertyType = null){ $this->prefix = $prefix; $this->flattenedProperty = $flattenedProperty; $this->suffix = $suffix; $this->flattenedValueRemaps = $flattenedValueRemaps; + $this->flattenedPropertyType = $flattenedPropertyType; } /** @@ -58,6 +60,9 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab if(count($this->flattenedValueRemaps) === 0){ unset($result["flattenedValueRemaps"]); } + if($this->flattenedPropertyType === null){ + unset($result["flattenedPropertyType"]); + } return $result; } } diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index 299d97d05..d2d0a8c41 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -30,6 +30,8 @@ use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap; use pocketmine\nbt\LittleEndianNbtSerializer; +use pocketmine\nbt\tag\ByteTag; +use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\Tag; use pocketmine\nbt\TreeRoot; @@ -48,7 +50,10 @@ use function count; use function dirname; use function file_put_contents; use function fwrite; +use function get_class; +use function get_debug_type; use function implode; +use function is_numeric; use function json_encode; use function ksort; use function min; @@ -312,11 +317,13 @@ function findCommonSuffix(array $strings) : string{ /** * @param string[][][] $candidateFlattenedValues * @phpstan-param array>> $candidateFlattenedValues + * @param string[] $candidateFlattenPropertyTypes + * @phpstan-param array|class-string|class-string> $candidateFlattenPropertyTypes * * @return BlockStateUpgradeSchemaFlattenedName[][] * @phpstan-return array> */ -function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{ +function buildFlattenPropertyRules(array $candidateFlattenedValues, array $candidateFlattenPropertyTypes) : array{ $flattenPropertyRules = []; foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){ @@ -340,11 +347,26 @@ function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{ } } + $allNumeric = true; + if(count($valueMap) > 0){ + foreach(Utils::stringifyKeys($valueMap) as $value => $newValue){ + if(!is_numeric($value)){ + $allNumeric = false; + break; + } + } + if($allNumeric){ + //add a dummy key to force the JSON to be an object and not a list + $valueMap["dummy"] = "map_not_list"; + } + } + $flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName( $idPrefix, $propertyName, $idSuffix, - $valueMap + $valueMap, + $candidateFlattenPropertyTypes[$propertyName], ); } } @@ -407,16 +429,25 @@ function processRemappedStates(array $upgradeTable) : array{ $notFlattenedProperties = []; $candidateFlattenedValues = []; + $candidateFlattenedPropertyTypes = []; foreach($upgradeTable as $pair){ foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){ if(isset($notFlattenedProperties[$propertyName])){ continue; } - if(!$propertyValue instanceof StringTag){ + if(!$propertyValue instanceof StringTag && !$propertyValue instanceof IntTag && !$propertyValue instanceof ByteTag){ $notFlattenedProperties[$propertyName] = true; continue; } - $rawValue = $propertyValue->getValue(); + $previousType = $candidateFlattenedPropertyTypes[$propertyName] ?? null; + if($previousType !== null && $previousType !== get_class($propertyValue)){ + //mismatched types for the same property name - this has never happened so far, but it's not impossible + $notFlattenedProperties[$propertyName] = true; + continue; + } + $candidateFlattenedPropertyTypes[$propertyName] = get_class($propertyValue); + + $rawValue = (string) $propertyValue->getValue(); if($rawValue === ""){ $notFlattenedProperties[$propertyName] = true; continue; @@ -458,7 +489,7 @@ function processRemappedStates(array $upgradeTable) : array{ unset($candidateFlattenedValues[$propertyName]); } - $flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues); + $flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues, $candidateFlattenedPropertyTypes); $flattenProperty = array_key_first($flattenedProperties); //Properties with fewer rules take up less space for the same result foreach(Utils::stringifyKeys($flattenedProperties) as $propertyName => $rules){ @@ -485,8 +516,8 @@ function processRemappedStates(array $upgradeTable) : array{ ksort($cleanedNewState); if($flattenProperty !== null){ $flattenedValue = $cleanedOldState[$flattenProperty] ?? null; - if(!$flattenedValue instanceof StringTag){ - throw new AssumptionFailedError("This should always be a TAG_String ($newName $flattenProperty)"); + if(!$flattenedValue instanceof StringTag && !$flattenedValue instanceof IntTag && !$flattenedValue instanceof ByteTag){ + throw new AssumptionFailedError("Non-flattenable type of tag ($newName $flattenProperty) but have " . get_debug_type($flattenedValue)); } unset($cleanedOldState[$flattenProperty]); } From d0d7a995fb60a493462323208ff2b887003cef44 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 5 Aug 2024 22:38:32 +0100 Subject: [PATCH 003/290] Add a TODO in BlockStateUpgrader this issue can be worked around by adding a dummy schema, but it's a bit clunky. --- src/data/bedrock/block/upgrade/BlockStateUpgrader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index ddb1e1359..b0612585c 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -83,6 +83,8 @@ final class BlockStateUpgrader{ * version doesn't tell us which of the schemas have already been applied. * If there's only one schema for a version (the norm), we can safely assume it's already been applied if * the version is the same, and skip over it. + * TODO: this causes issues when testing isolated schemas since there will only be one schema for a version. + * The second check should be disabled for that case. */ if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){ continue; From 2aa64dc15ef7bf0b35fb233e8d5348c258d9034d Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 5 Aug 2024 17:13:23 -0500 Subject: [PATCH 004/290] Simplify phpstan-doc type hint for better readability --- .../block/upgrade/BlockStateUpgradeSchemaFlattenedName.php | 2 +- tools/generate-blockstate-upgrade-schema.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php index e2f7f798d..8259f690d 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php @@ -34,7 +34,7 @@ final class BlockStateUpgradeSchemaFlattenedName{ /** * @param string[] $flattenedValueRemaps * @phpstan-param array $flattenedValueRemaps - * @phpstan-param class-string|class-string|class-string|null $flattenedPropertyType + * @phpstan-param ?class-string $flattenedPropertyType */ public function __construct( public string $prefix, diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index d2d0a8c41..43bcc71f1 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -318,7 +318,7 @@ function findCommonSuffix(array $strings) : string{ * @param string[][][] $candidateFlattenedValues * @phpstan-param array>> $candidateFlattenedValues * @param string[] $candidateFlattenPropertyTypes - * @phpstan-param array|class-string|class-string> $candidateFlattenPropertyTypes + * @phpstan-param array> $candidateFlattenPropertyTypes * * @return BlockStateUpgradeSchemaFlattenedName[][] * @phpstan-return array> From 26761c2b87608afb6b2bbdfa5daa96bda53adcb9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 9 Aug 2024 12:37:08 +0100 Subject: [PATCH 005/290] Blockstate schema tool now supports testing schemas as well as generating them this is useful when making changes to the generator, since regenerated schemas can now be tested for validity. This helps to find bugs in the generator. --- ...hp => blockstate-upgrade-schema-utils.php} | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) rename tools/{generate-blockstate-upgrade-schema.php => blockstate-upgrade-schema-utils.php} (90%) diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/blockstate-upgrade-schema-utils.php similarity index 90% rename from tools/generate-blockstate-upgrade-schema.php rename to tools/blockstate-upgrade-schema-utils.php index 43bcc71f1..c8dafcb67 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -21,9 +21,10 @@ declare(strict_types=1); -namespace pocketmine\tools\generate_blockstate_upgrade_schema; +namespace pocketmine\tools\blockstate_upgrade_schema_utils; use pocketmine\data\bedrock\block\BlockStateData; +use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName; @@ -607,32 +608,71 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad return $result; } +/** + * @param BlockStateMapping[][] $upgradeTable + * @phpstan-param array> $upgradeTable + */ +function testBlockStateUpgradeSchema(array $upgradeTable, BlockStateUpgradeSchema $schema) : bool{ + //TODO: HACK! + //the upgrader won't apply the schema if it's the same version and there's only one schema with a matching version + //ID (for performance reasons), which is a problem for testing isolated schemas + //add a dummy schema to bypass this optimization + $dummySchema = new BlockStateUpgradeSchema($schema->maxVersionMajor, $schema->maxVersionMinor, $schema->maxVersionPatch, $schema->maxVersionRevision, $schema->getSchemaId() + 1); + $upgrader = new BlockStateUpgrader([$schema, $dummySchema]); + + foreach($upgradeTable as $mappingsByOldName){ + foreach($mappingsByOldName as $mapping){ + $expectedNewState = $mapping->new; + + $actualNewState = $upgrader->upgrade($mapping->old); + + if(!$expectedNewState->equals($actualNewState)){ + \GlobalLogger::get()->error("Expected: " . $expectedNewState->toNbt()); + \GlobalLogger::get()->error("Actual: " . $actualNewState->toNbt()); + return false; + } + } + } + + return true; +} + /** * @param string[] $argv */ function main(array $argv) : int{ - if(count($argv) !== 3){ - fwrite(STDERR, "Required arguments: input file path, output file path\n"); + if(count($argv) !== 4 || ($argv[1] !== "generate" && $argv[1] !== "test")){ + fwrite(STDERR, "Required arguments: \n"); return 1; } - $input = $argv[1]; - $output = $argv[2]; + $mode = $argv[1]; + $upgradeTableFile = $argv[2]; + $schemaFile = $argv[3]; - $table = loadUpgradeTable($input, false); + $table = loadUpgradeTable($upgradeTableFile, false); ksort($table, SORT_STRING); - $diff = generateBlockStateUpgradeSchema($table); - if($diff->isEmpty()){ - \GlobalLogger::get()->warning("All states appear to be the same! No schema generated."); - return 0; + if($mode === "generate"){ + $diff = generateBlockStateUpgradeSchema($table); + if($diff->isEmpty()){ + \GlobalLogger::get()->warning("All states appear to be the same! No schema generated."); + return 0; + } + file_put_contents( + $schemaFile, + json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n" + ); + \GlobalLogger::get()->info("Schema file $schemaFile generated successfully."); + }else{ + $schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($schemaFile), 0); + if(!testBlockStateUpgradeSchema($table, $schema)){ + \GlobalLogger::get()->error("Schema $schemaFile does not produce the results predicted by $upgradeTableFile"); + return 1; + } + \GlobalLogger::get()->info("Schema $schemaFile is valid according to $upgradeTableFile"); } - file_put_contents( - $output, - json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n" - ); - \GlobalLogger::get()->info("Schema file $output generated successfully."); return 0; } From 33dc995cc748cd6967c32a65844d88d28df45037 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 9 Aug 2024 13:12:46 +0100 Subject: [PATCH 006/290] blockstate-upgrade-schema-utils: added a command to update old schemas to a newer format this is useful when the generator was updated with new features & optimisations, to reduce the size and/or improve readability of existing schemas. --- tools/blockstate-upgrade-schema-utils.php | 137 ++++++++++++++++------ 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index c8dafcb67..a9069d429 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -36,6 +36,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\Tag; use pocketmine\nbt\TreeRoot; +use pocketmine\network\mcpe\convert\BlockStateDictionary; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; @@ -90,18 +91,18 @@ function encodeProperty(Tag $tag) : string{ } /** + * @param TreeRoot[] $oldNewStateList + * @phpstan-param list $oldNewStateList + * * @return BlockStateMapping[][] * @phpstan-return array> */ -function loadUpgradeTable(string $file, bool $reverse) : array{ - $contents = Filesystem::fileGetContents($file); - $data = (new NetworkNbtSerializer())->readMultiple($contents); - +function buildUpgradeTableFromData(array $oldNewStateList, bool $reverse) : array{ $result = []; - for($i = 0; isset($data[$i]); $i += 2){ - $oldTag = $data[$i]->mustGetCompoundTag(); - $newTag = $data[$i + 1]->mustGetCompoundTag(); + for($i = 0; isset($oldNewStateList[$i]); $i += 2){ + $oldTag = $oldNewStateList[$i]->mustGetCompoundTag(); + $newTag = $oldNewStateList[$i + 1]->mustGetCompoundTag(); $old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag); $new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag); @@ -114,6 +115,17 @@ function loadUpgradeTable(string $file, bool $reverse) : array{ return $result; } +/** + * @return BlockStateMapping[][] + * @phpstan-return array> + */ +function loadUpgradeTableFromFile(string $file, bool $reverse) : array{ + $contents = Filesystem::fileGetContents($file); + $data = (new NetworkNbtSerializer())->readMultiple($contents); + + return buildUpgradeTableFromData($data, $reverse); +} + /** * @param BlockStateData[] $states * @phpstan-param array $states @@ -640,41 +652,100 @@ function testBlockStateUpgradeSchema(array $upgradeTable, BlockStateUpgradeSchem /** * @param string[] $argv */ -function main(array $argv) : int{ - if(count($argv) !== 4 || ($argv[1] !== "generate" && $argv[1] !== "test")){ - fwrite(STDERR, "Required arguments: \n"); - return 1; - } - - $mode = $argv[1]; +function cmdGenerate(array $argv) : int{ $upgradeTableFile = $argv[2]; $schemaFile = $argv[3]; - $table = loadUpgradeTable($upgradeTableFile, false); + $table = loadUpgradeTableFromFile($upgradeTableFile, false); ksort($table, SORT_STRING); - if($mode === "generate"){ - $diff = generateBlockStateUpgradeSchema($table); - if($diff->isEmpty()){ - \GlobalLogger::get()->warning("All states appear to be the same! No schema generated."); - return 0; - } - file_put_contents( - $schemaFile, - json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n" - ); - \GlobalLogger::get()->info("Schema file $schemaFile generated successfully."); - }else{ - $schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($schemaFile), 0); - if(!testBlockStateUpgradeSchema($table, $schema)){ - \GlobalLogger::get()->error("Schema $schemaFile does not produce the results predicted by $upgradeTableFile"); - return 1; - } - \GlobalLogger::get()->info("Schema $schemaFile is valid according to $upgradeTableFile"); + $diff = generateBlockStateUpgradeSchema($table); + if($diff->isEmpty()){ + \GlobalLogger::get()->warning("All states appear to be the same! No schema generated."); + return 0; } + file_put_contents( + $schemaFile, + json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n" + ); + \GlobalLogger::get()->info("Schema file $schemaFile generated successfully."); + return 0; +} + +/** + * @param string[] $argv + */ +function cmdTest(array $argv) : int{ + $upgradeTableFile = $argv[2]; + $schemaFile = $argv[3]; + + $table = loadUpgradeTableFromFile($upgradeTableFile, false); + + ksort($table, SORT_STRING); + + $schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($schemaFile), 0); + if(!testBlockStateUpgradeSchema($table, $schema)){ + \GlobalLogger::get()->error("Schema $schemaFile does not produce the results predicted by $upgradeTableFile"); + return 1; + } + \GlobalLogger::get()->info("Schema $schemaFile is valid according to $upgradeTableFile"); return 0; } +/** + * @param string[] $argv + */ +function cmdUpdate(array $argv) : int{ + [, , $oldSchemaFile, $oldPaletteFile, $newSchemaFile] = $argv; + + $palette = BlockStateDictionary::loadPaletteFromString(Filesystem::fileGetContents($oldPaletteFile)); + $schema = BlockStateUpgradeSchemaUtils::loadSchemaFromString(Filesystem::fileGetContents($oldSchemaFile), 0); + //TODO: HACK! + //the upgrader won't apply the schema if it's the same version and there's only one schema with a matching version + //ID (for performance reasons), which is a problem for testing isolated schemas + //add a dummy schema to bypass this optimization + $dummySchema = new BlockStateUpgradeSchema($schema->maxVersionMajor, $schema->maxVersionMinor, $schema->maxVersionPatch, $schema->maxVersionRevision, $schema->getSchemaId() + 1); + $upgrader = new BlockStateUpgrader([$schema, $dummySchema]); + + $tags = []; + foreach($palette as $stateData){ + $tags[] = new TreeRoot($stateData->toNbt()); + $tags[] = new TreeRoot($upgrader->upgrade($stateData)->toNbt()); + } + + $upgradeTable = buildUpgradeTableFromData($tags, false); + $newSchema = generateBlockStateUpgradeSchema($upgradeTable); + file_put_contents( + $newSchemaFile, + json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($newSchema), JSON_PRETTY_PRINT) . "\n" + ); + \GlobalLogger::get()->info("Schema file $newSchemaFile updated to new format (from $oldSchemaFile) successfully."); + return 0; +} + +/** + * @param string[] $argv + */ +function main(array $argv) : int{ + $options = [ + "generate" => [["palette upgrade table file", "schema output file"], cmdGenerate(...)], + "test" => [["palette upgrade table file", "schema output file"], cmdTest(...)], + "update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)] + ]; + + $selected = $argv[1] ?? null; + if($selected === null || !isset($options[$selected])){ + fwrite(STDERR, "Available commands:\n"); + foreach($options as $command => [$args, $callback]){ + fwrite(STDERR, " - $command " . implode(" ", array_map(fn(string $a) => "<$a>", $args)) . "\n"); + } + return 1; + } + + $callback = $options[$selected][1]; + return $callback($argv); +} + exit(main($argv)); From 25c66e4c8b397ea677f3ad9e48c13ad97e79ceb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:36:14 +0000 Subject: [PATCH 007/290] Bump phpstan/phpstan in the development-patch-updates group (#6420) --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 28ef5e9c2..5c1282c45 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "1.11.9", + "phpstan/phpstan": "1.11.10", "phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-strict-rules": "^1.2.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index e3211e7c5..128c28e67 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cddee8096f4b575629ff671000543d3f", + "content-hash": "38063b4fcc2e80a5bccb8d5cc8623cf8", "packages": [ { "name": "adhocore/json-comment", @@ -1389,16 +1389,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.9", + "version": "1.11.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e370bcddadaede0c1716338b262346f40d296f82" + "reference": "640410b32995914bde3eed26fa89552f9c2c082f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", - "reference": "e370bcddadaede0c1716338b262346f40d296f82", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", + "reference": "640410b32995914bde3eed26fa89552f9c2c082f", "shasum": "" }, "require": { @@ -1443,7 +1443,7 @@ "type": "github" } ], - "time": "2024-08-01T16:25:18+00:00" + "time": "2024-08-08T09:02:50+00:00" }, { "name": "phpstan/phpstan-phpunit", From d077bda30ce21187e51cc2a41400b90e14a58039 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:35:01 +0000 Subject: [PATCH 008/290] Bump docker/build-push-action from 6.5.0 to 6.6.1 (#6419) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index b3d6661e9..4f045bdc9 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: push: true context: ./pocketmine-mp From 13f5cc9f873a887d87dcfc0d894700f07752fbc8 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:11:22 +0300 Subject: [PATCH 009/290] ChiseledBookshelf: fixed function visibility (#6421) --- src/block/tile/ChiseledBookshelf.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/tile/ChiseledBookshelf.php b/src/block/tile/ChiseledBookshelf.php index f9d61e7eb..6455208fe 100644 --- a/src/block/tile/ChiseledBookshelf.php +++ b/src/block/tile/ChiseledBookshelf.php @@ -59,7 +59,7 @@ class ChiseledBookshelf extends Tile implements Container{ $this->loadItems($nbt); } - public function writeSaveData(CompoundTag $nbt) : void{ + protected function writeSaveData(CompoundTag $nbt) : void{ $this->saveItems($nbt); } From 585dc835e7c50f49e301d41d055821987fb62e5d Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:19:07 +0300 Subject: [PATCH 010/290] Fixed anvil placing rotation. (#6375) Co-authored-by: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> --- src/block/Anvil.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 916c390ef..4b4afef61 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -91,7 +91,7 @@ class Anvil extends Transparent implements Fallable{ public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ - $this->facing = Facing::rotateY($player->getHorizontalFacing(), true); + $this->facing = Facing::rotateY($player->getHorizontalFacing(), false); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } From 929cd631355ecac724a1636ebf905309dd5cb1fd Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Tue, 13 Aug 2024 10:55:29 -0500 Subject: [PATCH 011/290] Release 5.17.1 --- changelogs/5.17.md | 13 +++++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/changelogs/5.17.md b/changelogs/5.17.md index 1b4084710..b26f38b8a 100644 --- a/changelogs/5.17.md +++ b/changelogs/5.17.md @@ -23,3 +23,16 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Fixes - Bowl can now be used as fuel. - Bells always drops themselves even when using an incompatible tool. + +# 5.17.1 +Released 13th August 2024. + +## Documentation +- Added a note about `BlockStateData::CURRENT_VERSION`. + +## Fixes +- Fixed anvil placement rotation to match vanilla. +- Fixed outdated `BedrockWorldData` version, this was preventing use newer worlds. + +## Internals +- Dependabot: PHPStan and patch updates are now grouped into a single PR. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index bc4154e86..1d0a9a608 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.17.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From f5ab2979a070d0ad03faf2e431290b97b690a637 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Tue, 13 Aug 2024 10:56:03 -0500 Subject: [PATCH 012/290] 5.17.2 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 1d0a9a608..5162560bf 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.17.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.17.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 8c3cf7a6874953a871452d239e0b204ae950fbec Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Wed, 14 Aug 2024 22:48:10 -0500 Subject: [PATCH 013/290] Use VISIBLE_MOB_EFFECTS actor metadata property to send effect bubbles (#6414) Close #6402 --- composer.json | 2 +- composer.lock | 12 ++++++------ src/entity/Living.php | 28 ++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 5c1282c45..21e3619a5 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "pocketmine/bedrock-block-upgrade-schema": "~4.2.0+bedrock-1.21.0", "pocketmine/bedrock-data": "~2.11.0+bedrock-1.21.0", "pocketmine/bedrock-item-upgrade-schema": "~1.10.0+bedrock-1.21.0", - "pocketmine/bedrock-protocol": "~32.1.0+bedrock-1.21.2", + "pocketmine/bedrock-protocol": "~32.2.0+bedrock-1.21.2", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 128c28e67..9d175040a 100644 --- a/composer.lock +++ b/composer.lock @@ -205,16 +205,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "32.1.0+bedrock-1.21.2", + "version": "32.2.0+bedrock-1.21.2", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "bb23db51365bdc91d3135c3885986a691ae1cb44" + "reference": "229e5f3ae676a8601c576b7a57e56060b611d68d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bb23db51365bdc91d3135c3885986a691ae1cb44", - "reference": "bb23db51365bdc91d3135c3885986a691ae1cb44", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/229e5f3ae676a8601c576b7a57e56060b611d68d", + "reference": "229e5f3ae676a8601c576b7a57e56060b611d68d", "shasum": "" }, "require": { @@ -245,9 +245,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/32.1.0+bedrock-1.21.2" + "source": "https://github.com/pmmp/BedrockProtocol/tree/32.2.0+bedrock-1.21.2" }, - "time": "2024-07-10T01:38:43+00:00" + "time": "2024-08-10T19:23:18+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/entity/Living.php b/src/entity/Living.php index b0d14957c..81f46424f 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -68,6 +68,7 @@ use function atan2; use function ceil; use function count; use function floor; +use function ksort; use function lcg_value; use function max; use function min; @@ -76,6 +77,7 @@ use function mt_rand; use function round; use function sqrt; use const M_PI; +use const SORT_NUMERIC; abstract class Living extends Entity{ protected const DEFAULT_BREATH_TICKS = 300; @@ -883,8 +885,30 @@ abstract class Living extends Entity{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); - $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectManager->hasOnlyAmbientEffects() ? 1 : 0); - $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectManager->getBubbleColor()->toARGB())); + $visibleEffects = []; + foreach ($this->effectManager->all() as $effect) { + if (!$effect->isVisible() || !$effect->getType()->hasBubbles()) { + continue; + } + $visibleEffects[EffectIdMap::getInstance()->toId($effect->getType())] = $effect->isAmbient(); + } + + //TODO: HACK! the client may not be able to identify effects if they are not sorted. + ksort($visibleEffects, SORT_NUMERIC); + + $effectsData = 0; + $packedEffectsCount = 0; + foreach ($visibleEffects as $effectId => $isAmbient) { + $effectsData = ($effectsData << 7) | + (($effectId & 0x3f) << 1) | //Why not use 7 bits instead of only 6? mojang... + ($isAmbient ? 1 : 0); + + if (++$packedEffectsCount >= 8) { + break; + } + } + $properties->setLong(EntityMetadataProperties::VISIBLE_MOB_EFFECTS, $effectsData); + $properties->setShort(EntityMetadataProperties::AIR, $this->breathTicks); $properties->setShort(EntityMetadataProperties::MAX_AIR, $this->maxBreathTicks); From 0f365886e0b3d9a929f5433f62bbc9247b1dda64 Mon Sep 17 00:00:00 2001 From: Dries C Date: Fri, 16 Aug 2024 12:26:49 +0200 Subject: [PATCH 014/290] Assemble 1.21.20 (#6423) Co-authored-by: Dylan T. Co-authored-by: IvanCraft623 --- composer.json | 8 +- composer.lock | 46 ++-- src/data/bedrock/block/BlockStateData.php | 4 +- src/data/bedrock/block/BlockStateNames.php | 13 - .../bedrock/block/BlockStateStringValues.php | 66 ----- src/data/bedrock/block/BlockTypeNames.php | 117 +++++++-- .../convert/BlockObjectToStateSerializer.php | 204 +++++++-------- .../convert/BlockStateDeserializerHelper.php | 61 ----- .../convert/BlockStateSerializerHelper.php | 39 +-- .../BlockStateToObjectDeserializer.php | 232 +++++++++--------- src/data/bedrock/item/ItemTypeNames.php | 12 + src/network/mcpe/InventoryManager.php | 11 +- src/network/mcpe/NetworkSession.php | 2 +- .../mcpe/StandardEntityEventBroadcaster.php | 4 +- .../mcpe/handler/ItemStackRequestExecutor.php | 8 +- .../mcpe/handler/ItemStackResponseBuilder.php | 3 +- src/world/format/io/data/BedrockWorldData.php | 6 +- 17 files changed, 386 insertions(+), 450 deletions(-) diff --git a/composer.json b/composer.json index 21e3619a5..5d1167b6d 100644 --- a/composer.json +++ b/composer.json @@ -33,10 +33,10 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", - "pocketmine/bedrock-block-upgrade-schema": "~4.2.0+bedrock-1.21.0", - "pocketmine/bedrock-data": "~2.11.0+bedrock-1.21.0", - "pocketmine/bedrock-item-upgrade-schema": "~1.10.0+bedrock-1.21.0", - "pocketmine/bedrock-protocol": "~32.2.0+bedrock-1.21.2", + "pocketmine/bedrock-block-upgrade-schema": "~4.3.0+bedrock-1.21.20", + "pocketmine/bedrock-data": "~2.12.0+bedrock-1.21.20", + "pocketmine/bedrock-item-upgrade-schema": "~1.11.0+bedrock-1.21.20", + "pocketmine/bedrock-protocol": "~33.0.0+bedrock-1.21.20", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 9d175040a..5eb9a2158 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "38063b4fcc2e80a5bccb8d5cc8623cf8", + "content-hash": "fab1e131dfd049da39a87d4e562f1870", "packages": [ { "name": "adhocore/json-comment", @@ -127,16 +127,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "4.2.0", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "8a327197b3b462fa282f40f76b070ffe585a25d2" + "reference": "53d3a41c37ce90d58b33130cdadad08e442d7c47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/8a327197b3b462fa282f40f76b070ffe585a25d2", - "reference": "8a327197b3b462fa282f40f76b070ffe585a25d2", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/53d3a41c37ce90d58b33130cdadad08e442d7c47", + "reference": "53d3a41c37ce90d58b33130cdadad08e442d7c47", "shasum": "" }, "type": "library", @@ -147,22 +147,22 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.2.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.3.0" }, - "time": "2024-06-13T17:28:26+00:00" + "time": "2024-08-13T18:04:27+00:00" }, { "name": "pocketmine/bedrock-data", - "version": "2.11.0+bedrock-1.21.0", + "version": "2.12.0+bedrock-1.21.20", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "cae40bde98081b388c4d3ab59d45b8d1cf5d8761" + "reference": "d4ee3d08964fa16fbbdd04af1fb52bbde540b665" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/cae40bde98081b388c4d3ab59d45b8d1cf5d8761", - "reference": "cae40bde98081b388c4d3ab59d45b8d1cf5d8761", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d4ee3d08964fa16fbbdd04af1fb52bbde540b665", + "reference": "d4ee3d08964fa16fbbdd04af1fb52bbde540b665", "shasum": "" }, "type": "library", @@ -173,22 +173,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.0" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.20" }, - "time": "2024-06-13T17:17:55+00:00" + "time": "2024-08-15T12:50:26+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "b4687afa19f91eacebd46c40d487f4cc515be504" + "reference": "35c18d093fc2b12da8737b2edb2c3ad6a14a53dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/b4687afa19f91eacebd46c40d487f4cc515be504", - "reference": "b4687afa19f91eacebd46c40d487f4cc515be504", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/35c18d093fc2b12da8737b2edb2c3ad6a14a53dd", + "reference": "35c18d093fc2b12da8737b2edb2c3ad6a14a53dd", "shasum": "" }, "type": "library", @@ -199,22 +199,22 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.10.0" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.11.0" }, - "time": "2024-05-15T15:15:55+00:00" + "time": "2024-08-13T18:06:25+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "32.2.0+bedrock-1.21.2", + "version": "33.0.0+bedrock-1.21.20", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "229e5f3ae676a8601c576b7a57e56060b611d68d" + "reference": "e2264137c5cd0522de2c6ee4921a3a803818ea32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/229e5f3ae676a8601c576b7a57e56060b611d68d", - "reference": "229e5f3ae676a8601c576b7a57e56060b611d68d", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e2264137c5cd0522de2c6ee4921a3a803818ea32", + "reference": "e2264137c5cd0522de2c6ee4921a3a803818ea32", "shasum": "" }, "require": { diff --git a/src/data/bedrock/block/BlockStateData.php b/src/data/bedrock/block/BlockStateData.php index 2c67ded73..1973b55af 100644 --- a/src/data/bedrock/block/BlockStateData.php +++ b/src/data/bedrock/block/BlockStateData.php @@ -45,8 +45,8 @@ final class BlockStateData{ public const CURRENT_VERSION = (1 << 24) | //major (21 << 16) | //minor - (0 << 8) | //patch - (3); //revision + (20 << 8) | //patch + (6); //revision public const TAG_NAME = "name"; public const TAG_STATES = "states"; diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index 4564f696e..e9c33bee2 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -42,7 +42,6 @@ final class BlockStateNames{ public const BIG_DRIPLEAF_HEAD = "big_dripleaf_head"; public const BIG_DRIPLEAF_TILT = "big_dripleaf_tilt"; public const BITE_COUNTER = "bite_counter"; - public const BLOCK_LIGHT_LEVEL = "block_light_level"; public const BLOOM = "bloom"; public const BOOKS_STORED = "books_stored"; public const BREWING_STAND_SLOT_A_BIT = "brewing_stand_slot_a_bit"; @@ -61,15 +60,12 @@ final class BlockStateNames{ public const CONDITIONAL_BIT = "conditional_bit"; public const CORAL_DIRECTION = "coral_direction"; public const CORAL_FAN_DIRECTION = "coral_fan_direction"; - public const CORAL_HANG_TYPE_BIT = "coral_hang_type_bit"; public const COVERED_BIT = "covered_bit"; public const CRACKED_STATE = "cracked_state"; public const CRAFTING = "crafting"; - public const DAMAGE = "damage"; public const DEAD_BIT = "dead_bit"; public const DEPRECATED = "deprecated"; public const DIRECTION = "direction"; - public const DIRT_TYPE = "dirt_type"; public const DISARMED_BIT = "disarmed_bit"; public const DOOR_HINGE_BIT = "door_hinge_bit"; public const DRAG_DOWN = "drag_down"; @@ -100,7 +96,6 @@ final class BlockStateNames{ public const MC_FACING_DIRECTION = "minecraft:facing_direction"; public const MC_VERTICAL_HALF = "minecraft:vertical_half"; public const MOISTURIZED_AMOUNT = "moisturized_amount"; - public const MONSTER_EGG_STONE_TYPE = "monster_egg_stone_type"; public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits"; public const OCCUPIED_BIT = "occupied_bit"; public const OMINOUS = "ominous"; @@ -112,7 +107,6 @@ final class BlockStateNames{ public const PILLAR_AXIS = "pillar_axis"; public const PORTAL_AXIS = "portal_axis"; public const POWERED_BIT = "powered_bit"; - public const PRISMARINE_BLOCK_TYPE = "prismarine_block_type"; public const PROPAGULE_STAGE = "propagule_stage"; public const RAIL_DATA_BIT = "rail_data_bit"; public const RAIL_DIRECTION = "rail_direction"; @@ -120,18 +114,11 @@ final class BlockStateNames{ public const REPEATER_DELAY = "repeater_delay"; public const RESPAWN_ANCHOR_CHARGE = "respawn_anchor_charge"; public const ROTATION = "rotation"; - public const SAND_STONE_TYPE = "sand_stone_type"; - public const SAND_TYPE = "sand_type"; public const SCULK_SENSOR_PHASE = "sculk_sensor_phase"; public const SEA_GRASS_TYPE = "sea_grass_type"; public const SPONGE_TYPE = "sponge_type"; public const STABILITY = "stability"; public const STABILITY_CHECK = "stability_check"; - public const STONE_BRICK_TYPE = "stone_brick_type"; - public const STONE_SLAB_TYPE = "stone_slab_type"; - public const STONE_SLAB_TYPE_2 = "stone_slab_type_2"; - public const STONE_SLAB_TYPE_3 = "stone_slab_type_3"; - public const STONE_SLAB_TYPE_4 = "stone_slab_type_4"; public const STRIPPED_BIT = "stripped_bit"; public const STRUCTURE_BLOCK_TYPE = "structure_block_type"; public const STRUCTURE_VOID_TYPE = "structure_void_type"; diff --git a/src/data/bedrock/block/BlockStateStringValues.php b/src/data/bedrock/block/BlockStateStringValues.php index 8538c3deb..1794e240d 100644 --- a/src/data/bedrock/block/BlockStateStringValues.php +++ b/src/data/bedrock/block/BlockStateStringValues.php @@ -66,14 +66,6 @@ final class BlockStateStringValues{ public const CRACKED_STATE_MAX_CRACKED = "max_cracked"; public const CRACKED_STATE_NO_CRACKS = "no_cracks"; - public const DAMAGE_BROKEN = "broken"; - public const DAMAGE_SLIGHTLY_DAMAGED = "slightly_damaged"; - public const DAMAGE_UNDAMAGED = "undamaged"; - public const DAMAGE_VERY_DAMAGED = "very_damaged"; - - public const DIRT_TYPE_COARSE = "coarse"; - public const DIRT_TYPE_NORMAL = "normal"; - public const DRIPSTONE_THICKNESS_BASE = "base"; public const DRIPSTONE_THICKNESS_FRUSTUM = "frustum"; public const DRIPSTONE_THICKNESS_MERGE = "merge"; @@ -111,13 +103,6 @@ final class BlockStateStringValues{ public const MC_VERTICAL_HALF_BOTTOM = "bottom"; public const MC_VERTICAL_HALF_TOP = "top"; - public const MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK = "chiseled_stone_brick"; - public const MONSTER_EGG_STONE_TYPE_COBBLESTONE = "cobblestone"; - public const MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK = "cracked_stone_brick"; - public const MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK = "mossy_stone_brick"; - public const MONSTER_EGG_STONE_TYPE_STONE = "stone"; - public const MONSTER_EGG_STONE_TYPE_STONE_BRICK = "stone_brick"; - public const ORIENTATION_DOWN_EAST = "down_east"; public const ORIENTATION_DOWN_NORTH = "down_north"; public const ORIENTATION_DOWN_SOUTH = "down_south"; @@ -139,18 +124,6 @@ final class BlockStateStringValues{ public const PORTAL_AXIS_X = "x"; public const PORTAL_AXIS_Z = "z"; - public const PRISMARINE_BLOCK_TYPE_BRICKS = "bricks"; - public const PRISMARINE_BLOCK_TYPE_DARK = "dark"; - public const PRISMARINE_BLOCK_TYPE_DEFAULT = "default"; - - public const SAND_STONE_TYPE_CUT = "cut"; - public const SAND_STONE_TYPE_DEFAULT = "default"; - public const SAND_STONE_TYPE_HEIROGLYPHS = "heiroglyphs"; - public const SAND_STONE_TYPE_SMOOTH = "smooth"; - - public const SAND_TYPE_NORMAL = "normal"; - public const SAND_TYPE_RED = "red"; - public const SEA_GRASS_TYPE_DEFAULT = "default"; public const SEA_GRASS_TYPE_DOUBLE_BOT = "double_bot"; public const SEA_GRASS_TYPE_DOUBLE_TOP = "double_top"; @@ -158,45 +131,6 @@ final class BlockStateStringValues{ public const SPONGE_TYPE_DRY = "dry"; public const SPONGE_TYPE_WET = "wet"; - public const STONE_BRICK_TYPE_CHISELED = "chiseled"; - public const STONE_BRICK_TYPE_CRACKED = "cracked"; - public const STONE_BRICK_TYPE_DEFAULT = "default"; - public const STONE_BRICK_TYPE_MOSSY = "mossy"; - public const STONE_BRICK_TYPE_SMOOTH = "smooth"; - - public const STONE_SLAB_TYPE_BRICK = "brick"; - public const STONE_SLAB_TYPE_COBBLESTONE = "cobblestone"; - public const STONE_SLAB_TYPE_NETHER_BRICK = "nether_brick"; - public const STONE_SLAB_TYPE_QUARTZ = "quartz"; - public const STONE_SLAB_TYPE_SANDSTONE = "sandstone"; - public const STONE_SLAB_TYPE_SMOOTH_STONE = "smooth_stone"; - public const STONE_SLAB_TYPE_STONE_BRICK = "stone_brick"; - public const STONE_SLAB_TYPE_WOOD = "wood"; - - public const STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE = "mossy_cobblestone"; - public const STONE_SLAB_TYPE_2_PRISMARINE_BRICK = "prismarine_brick"; - public const STONE_SLAB_TYPE_2_PRISMARINE_DARK = "prismarine_dark"; - public const STONE_SLAB_TYPE_2_PRISMARINE_ROUGH = "prismarine_rough"; - public const STONE_SLAB_TYPE_2_PURPUR = "purpur"; - public const STONE_SLAB_TYPE_2_RED_NETHER_BRICK = "red_nether_brick"; - public const STONE_SLAB_TYPE_2_RED_SANDSTONE = "red_sandstone"; - public const STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE = "smooth_sandstone"; - - public const STONE_SLAB_TYPE_3_ANDESITE = "andesite"; - public const STONE_SLAB_TYPE_3_DIORITE = "diorite"; - public const STONE_SLAB_TYPE_3_END_STONE_BRICK = "end_stone_brick"; - public const STONE_SLAB_TYPE_3_GRANITE = "granite"; - public const STONE_SLAB_TYPE_3_POLISHED_ANDESITE = "polished_andesite"; - public const STONE_SLAB_TYPE_3_POLISHED_DIORITE = "polished_diorite"; - public const STONE_SLAB_TYPE_3_POLISHED_GRANITE = "polished_granite"; - public const STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE = "smooth_red_sandstone"; - - public const STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE = "cut_red_sandstone"; - public const STONE_SLAB_TYPE_4_CUT_SANDSTONE = "cut_sandstone"; - public const STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK = "mossy_stone_brick"; - public const STONE_SLAB_TYPE_4_SMOOTH_QUARTZ = "smooth_quartz"; - public const STONE_SLAB_TYPE_4_STONE = "stone"; - public const STRUCTURE_BLOCK_TYPE_CORNER = "corner"; public const STRUCTURE_BLOCK_TYPE_DATA = "data"; public const STRUCTURE_BLOCK_TYPE_EXPORT = "export"; diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index 25433e6eb..f74f858ce 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -56,6 +56,8 @@ final class BlockTypeNames{ public const AMETHYST_CLUSTER = "minecraft:amethyst_cluster"; public const ANCIENT_DEBRIS = "minecraft:ancient_debris"; public const ANDESITE = "minecraft:andesite"; + public const ANDESITE_DOUBLE_SLAB = "minecraft:andesite_double_slab"; + public const ANDESITE_SLAB = "minecraft:andesite_slab"; public const ANDESITE_STAIRS = "minecraft:andesite_stairs"; public const ANVIL = "minecraft:anvil"; public const AZALEA = "minecraft:azalea"; @@ -146,8 +148,10 @@ final class BlockTypeNames{ public const BRAIN_CORAL = "minecraft:brain_coral"; public const BRAIN_CORAL_BLOCK = "minecraft:brain_coral_block"; public const BRAIN_CORAL_FAN = "minecraft:brain_coral_fan"; + public const BRAIN_CORAL_WALL_FAN = "minecraft:brain_coral_wall_fan"; public const BREWING_STAND = "minecraft:brewing_stand"; public const BRICK_BLOCK = "minecraft:brick_block"; + public const BRICK_DOUBLE_SLAB = "minecraft:brick_double_slab"; public const BRICK_SLAB = "minecraft:brick_slab"; public const BRICK_STAIRS = "minecraft:brick_stairs"; public const BROWN_CANDLE = "minecraft:brown_candle"; @@ -167,6 +171,7 @@ final class BlockTypeNames{ public const BUBBLE_CORAL = "minecraft:bubble_coral"; public const BUBBLE_CORAL_BLOCK = "minecraft:bubble_coral_block"; public const BUBBLE_CORAL_FAN = "minecraft:bubble_coral_fan"; + public const BUBBLE_CORAL_WALL_FAN = "minecraft:bubble_coral_wall_fan"; public const BUDDING_AMETHYST = "minecraft:budding_amethyst"; public const CACTUS = "minecraft:cactus"; public const CAKE = "minecraft:cake"; @@ -205,11 +210,16 @@ final class BlockTypeNames{ public const CHERRY_WALL_SIGN = "minecraft:cherry_wall_sign"; public const CHERRY_WOOD = "minecraft:cherry_wood"; public const CHEST = "minecraft:chest"; + public const CHIPPED_ANVIL = "minecraft:chipped_anvil"; public const CHISELED_BOOKSHELF = "minecraft:chiseled_bookshelf"; public const CHISELED_COPPER = "minecraft:chiseled_copper"; public const CHISELED_DEEPSLATE = "minecraft:chiseled_deepslate"; public const CHISELED_NETHER_BRICKS = "minecraft:chiseled_nether_bricks"; public const CHISELED_POLISHED_BLACKSTONE = "minecraft:chiseled_polished_blackstone"; + public const CHISELED_QUARTZ_BLOCK = "minecraft:chiseled_quartz_block"; + public const CHISELED_RED_SANDSTONE = "minecraft:chiseled_red_sandstone"; + public const CHISELED_SANDSTONE = "minecraft:chiseled_sandstone"; + public const CHISELED_STONE_BRICKS = "minecraft:chiseled_stone_bricks"; public const CHISELED_TUFF = "minecraft:chiseled_tuff"; public const CHISELED_TUFF_BRICKS = "minecraft:chiseled_tuff_bricks"; public const CHORUS_FLOWER = "minecraft:chorus_flower"; @@ -218,12 +228,14 @@ final class BlockTypeNames{ public const CLIENT_REQUEST_PLACEHOLDER_BLOCK = "minecraft:client_request_placeholder_block"; public const COAL_BLOCK = "minecraft:coal_block"; public const COAL_ORE = "minecraft:coal_ore"; + public const COARSE_DIRT = "minecraft:coarse_dirt"; public const COBBLED_DEEPSLATE = "minecraft:cobbled_deepslate"; public const COBBLED_DEEPSLATE_DOUBLE_SLAB = "minecraft:cobbled_deepslate_double_slab"; public const COBBLED_DEEPSLATE_SLAB = "minecraft:cobbled_deepslate_slab"; public const COBBLED_DEEPSLATE_STAIRS = "minecraft:cobbled_deepslate_stairs"; public const COBBLED_DEEPSLATE_WALL = "minecraft:cobbled_deepslate_wall"; public const COBBLESTONE = "minecraft:cobblestone"; + public const COBBLESTONE_DOUBLE_SLAB = "minecraft:cobblestone_double_slab"; public const COBBLESTONE_SLAB = "minecraft:cobblestone_slab"; public const COBBLESTONE_WALL = "minecraft:cobblestone_wall"; public const COCOA = "minecraft:cocoa"; @@ -238,14 +250,12 @@ final class BlockTypeNames{ public const COPPER_GRATE = "minecraft:copper_grate"; public const COPPER_ORE = "minecraft:copper_ore"; public const COPPER_TRAPDOOR = "minecraft:copper_trapdoor"; - public const CORAL_FAN_HANG = "minecraft:coral_fan_hang"; - public const CORAL_FAN_HANG2 = "minecraft:coral_fan_hang2"; - public const CORAL_FAN_HANG3 = "minecraft:coral_fan_hang3"; public const CORNFLOWER = "minecraft:cornflower"; public const CRACKED_DEEPSLATE_BRICKS = "minecraft:cracked_deepslate_bricks"; public const CRACKED_DEEPSLATE_TILES = "minecraft:cracked_deepslate_tiles"; public const CRACKED_NETHER_BRICKS = "minecraft:cracked_nether_bricks"; public const CRACKED_POLISHED_BLACKSTONE_BRICKS = "minecraft:cracked_polished_blackstone_bricks"; + public const CRACKED_STONE_BRICKS = "minecraft:cracked_stone_bricks"; public const CRAFTER = "minecraft:crafter"; public const CRAFTING_TABLE = "minecraft:crafting_table"; public const CRIMSON_BUTTON = "minecraft:crimson_button"; @@ -270,6 +280,12 @@ final class BlockTypeNames{ public const CUT_COPPER = "minecraft:cut_copper"; public const CUT_COPPER_SLAB = "minecraft:cut_copper_slab"; public const CUT_COPPER_STAIRS = "minecraft:cut_copper_stairs"; + public const CUT_RED_SANDSTONE = "minecraft:cut_red_sandstone"; + public const CUT_RED_SANDSTONE_DOUBLE_SLAB = "minecraft:cut_red_sandstone_double_slab"; + public const CUT_RED_SANDSTONE_SLAB = "minecraft:cut_red_sandstone_slab"; + public const CUT_SANDSTONE = "minecraft:cut_sandstone"; + public const CUT_SANDSTONE_DOUBLE_SLAB = "minecraft:cut_sandstone_double_slab"; + public const CUT_SANDSTONE_SLAB = "minecraft:cut_sandstone_slab"; public const CYAN_CANDLE = "minecraft:cyan_candle"; public const CYAN_CANDLE_CAKE = "minecraft:cyan_candle_cake"; public const CYAN_CARPET = "minecraft:cyan_carpet"; @@ -281,6 +297,8 @@ final class BlockTypeNames{ public const CYAN_STAINED_GLASS_PANE = "minecraft:cyan_stained_glass_pane"; public const CYAN_TERRACOTTA = "minecraft:cyan_terracotta"; public const CYAN_WOOL = "minecraft:cyan_wool"; + public const DAMAGED_ANVIL = "minecraft:damaged_anvil"; + public const DANDELION = "minecraft:dandelion"; public const DARK_OAK_BUTTON = "minecraft:dark_oak_button"; public const DARK_OAK_DOOR = "minecraft:dark_oak_door"; public const DARK_OAK_DOUBLE_SLAB = "minecraft:dark_oak_double_slab"; @@ -296,6 +314,9 @@ final class BlockTypeNames{ public const DARK_OAK_STAIRS = "minecraft:dark_oak_stairs"; public const DARK_OAK_TRAPDOOR = "minecraft:dark_oak_trapdoor"; public const DARK_OAK_WOOD = "minecraft:dark_oak_wood"; + public const DARK_PRISMARINE = "minecraft:dark_prismarine"; + public const DARK_PRISMARINE_DOUBLE_SLAB = "minecraft:dark_prismarine_double_slab"; + public const DARK_PRISMARINE_SLAB = "minecraft:dark_prismarine_slab"; public const DARK_PRISMARINE_STAIRS = "minecraft:dark_prismarine_stairs"; public const DARKOAK_STANDING_SIGN = "minecraft:darkoak_standing_sign"; public const DARKOAK_WALL_SIGN = "minecraft:darkoak_wall_sign"; @@ -304,18 +325,23 @@ final class BlockTypeNames{ public const DEAD_BRAIN_CORAL = "minecraft:dead_brain_coral"; public const DEAD_BRAIN_CORAL_BLOCK = "minecraft:dead_brain_coral_block"; public const DEAD_BRAIN_CORAL_FAN = "minecraft:dead_brain_coral_fan"; + public const DEAD_BRAIN_CORAL_WALL_FAN = "minecraft:dead_brain_coral_wall_fan"; public const DEAD_BUBBLE_CORAL = "minecraft:dead_bubble_coral"; public const DEAD_BUBBLE_CORAL_BLOCK = "minecraft:dead_bubble_coral_block"; public const DEAD_BUBBLE_CORAL_FAN = "minecraft:dead_bubble_coral_fan"; + public const DEAD_BUBBLE_CORAL_WALL_FAN = "minecraft:dead_bubble_coral_wall_fan"; public const DEAD_FIRE_CORAL = "minecraft:dead_fire_coral"; public const DEAD_FIRE_CORAL_BLOCK = "minecraft:dead_fire_coral_block"; public const DEAD_FIRE_CORAL_FAN = "minecraft:dead_fire_coral_fan"; + public const DEAD_FIRE_CORAL_WALL_FAN = "minecraft:dead_fire_coral_wall_fan"; public const DEAD_HORN_CORAL = "minecraft:dead_horn_coral"; public const DEAD_HORN_CORAL_BLOCK = "minecraft:dead_horn_coral_block"; public const DEAD_HORN_CORAL_FAN = "minecraft:dead_horn_coral_fan"; + public const DEAD_HORN_CORAL_WALL_FAN = "minecraft:dead_horn_coral_wall_fan"; public const DEAD_TUBE_CORAL = "minecraft:dead_tube_coral"; public const DEAD_TUBE_CORAL_BLOCK = "minecraft:dead_tube_coral_block"; public const DEAD_TUBE_CORAL_FAN = "minecraft:dead_tube_coral_fan"; + public const DEAD_TUBE_CORAL_WALL_FAN = "minecraft:dead_tube_coral_wall_fan"; public const DEADBUSH = "minecraft:deadbush"; public const DECORATED_POT = "minecraft:decorated_pot"; public const DEEPSLATE = "minecraft:deepslate"; @@ -338,19 +364,18 @@ final class BlockTypeNames{ public const DEEPSLATE_TILE_WALL = "minecraft:deepslate_tile_wall"; public const DEEPSLATE_TILES = "minecraft:deepslate_tiles"; public const DENY = "minecraft:deny"; + public const DEPRECATED_ANVIL = "minecraft:deprecated_anvil"; public const DETECTOR_RAIL = "minecraft:detector_rail"; public const DIAMOND_BLOCK = "minecraft:diamond_block"; public const DIAMOND_ORE = "minecraft:diamond_ore"; public const DIORITE = "minecraft:diorite"; + public const DIORITE_DOUBLE_SLAB = "minecraft:diorite_double_slab"; + public const DIORITE_SLAB = "minecraft:diorite_slab"; public const DIORITE_STAIRS = "minecraft:diorite_stairs"; public const DIRT = "minecraft:dirt"; public const DIRT_WITH_ROOTS = "minecraft:dirt_with_roots"; public const DISPENSER = "minecraft:dispenser"; public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab"; - public const DOUBLE_STONE_BLOCK_SLAB = "minecraft:double_stone_block_slab"; - public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2"; - public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3"; - public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4"; public const DRAGON_EGG = "minecraft:dragon_egg"; public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block"; public const DRIPSTONE_BLOCK = "minecraft:dripstone_block"; @@ -484,6 +509,8 @@ final class BlockTypeNames{ public const END_PORTAL_FRAME = "minecraft:end_portal_frame"; public const END_ROD = "minecraft:end_rod"; public const END_STONE = "minecraft:end_stone"; + public const END_STONE_BRICK_DOUBLE_SLAB = "minecraft:end_stone_brick_double_slab"; + public const END_STONE_BRICK_SLAB = "minecraft:end_stone_brick_slab"; public const ENDER_CHEST = "minecraft:ender_chest"; public const EXPOSED_CHISELED_COPPER = "minecraft:exposed_chiseled_copper"; public const EXPOSED_COPPER = "minecraft:exposed_copper"; @@ -502,6 +529,7 @@ final class BlockTypeNames{ public const FIRE_CORAL = "minecraft:fire_coral"; public const FIRE_CORAL_BLOCK = "minecraft:fire_coral_block"; public const FIRE_CORAL_FAN = "minecraft:fire_coral_fan"; + public const FIRE_CORAL_WALL_FAN = "minecraft:fire_coral_wall_fan"; public const FLETCHING_TABLE = "minecraft:fletching_table"; public const FLOWER_POT = "minecraft:flower_pot"; public const FLOWERING_AZALEA = "minecraft:flowering_azalea"; @@ -522,6 +550,8 @@ final class BlockTypeNames{ public const GOLD_ORE = "minecraft:gold_ore"; public const GOLDEN_RAIL = "minecraft:golden_rail"; public const GRANITE = "minecraft:granite"; + public const GRANITE_DOUBLE_SLAB = "minecraft:granite_double_slab"; + public const GRANITE_SLAB = "minecraft:granite_slab"; public const GRANITE_STAIRS = "minecraft:granite_stairs"; public const GRASS_BLOCK = "minecraft:grass_block"; public const GRASS_PATH = "minecraft:grass_path"; @@ -594,8 +624,15 @@ final class BlockTypeNames{ public const HORN_CORAL = "minecraft:horn_coral"; public const HORN_CORAL_BLOCK = "minecraft:horn_coral_block"; public const HORN_CORAL_FAN = "minecraft:horn_coral_fan"; + public const HORN_CORAL_WALL_FAN = "minecraft:horn_coral_wall_fan"; public const ICE = "minecraft:ice"; + public const INFESTED_CHISELED_STONE_BRICKS = "minecraft:infested_chiseled_stone_bricks"; + public const INFESTED_COBBLESTONE = "minecraft:infested_cobblestone"; + public const INFESTED_CRACKED_STONE_BRICKS = "minecraft:infested_cracked_stone_bricks"; public const INFESTED_DEEPSLATE = "minecraft:infested_deepslate"; + public const INFESTED_MOSSY_STONE_BRICKS = "minecraft:infested_mossy_stone_bricks"; + public const INFESTED_STONE = "minecraft:infested_stone"; + public const INFESTED_STONE_BRICKS = "minecraft:infested_stone_bricks"; public const INFO_UPDATE = "minecraft:info_update"; public const INFO_UPDATE2 = "minecraft:info_update2"; public const INVISIBLE_BEDROCK = "minecraft:invisible_bedrock"; @@ -633,7 +670,22 @@ final class BlockTypeNames{ public const LAVA = "minecraft:lava"; public const LECTERN = "minecraft:lectern"; public const LEVER = "minecraft:lever"; - public const LIGHT_BLOCK = "minecraft:light_block"; + public const LIGHT_BLOCK_0 = "minecraft:light_block_0"; + public const LIGHT_BLOCK_1 = "minecraft:light_block_1"; + public const LIGHT_BLOCK_10 = "minecraft:light_block_10"; + public const LIGHT_BLOCK_11 = "minecraft:light_block_11"; + public const LIGHT_BLOCK_12 = "minecraft:light_block_12"; + public const LIGHT_BLOCK_13 = "minecraft:light_block_13"; + public const LIGHT_BLOCK_14 = "minecraft:light_block_14"; + public const LIGHT_BLOCK_15 = "minecraft:light_block_15"; + public const LIGHT_BLOCK_2 = "minecraft:light_block_2"; + public const LIGHT_BLOCK_3 = "minecraft:light_block_3"; + public const LIGHT_BLOCK_4 = "minecraft:light_block_4"; + public const LIGHT_BLOCK_5 = "minecraft:light_block_5"; + public const LIGHT_BLOCK_6 = "minecraft:light_block_6"; + public const LIGHT_BLOCK_7 = "minecraft:light_block_7"; + public const LIGHT_BLOCK_8 = "minecraft:light_block_8"; + public const LIGHT_BLOCK_9 = "minecraft:light_block_9"; public const LIGHT_BLUE_CANDLE = "minecraft:light_blue_candle"; public const LIGHT_BLUE_CANDLE_CAKE = "minecraft:light_blue_candle_cake"; public const LIGHT_BLUE_CARPET = "minecraft:light_blue_carpet"; @@ -713,12 +765,16 @@ final class BlockTypeNames{ public const MELON_BLOCK = "minecraft:melon_block"; public const MELON_STEM = "minecraft:melon_stem"; public const MOB_SPAWNER = "minecraft:mob_spawner"; - public const MONSTER_EGG = "minecraft:monster_egg"; public const MOSS_BLOCK = "minecraft:moss_block"; public const MOSS_CARPET = "minecraft:moss_carpet"; public const MOSSY_COBBLESTONE = "minecraft:mossy_cobblestone"; + public const MOSSY_COBBLESTONE_DOUBLE_SLAB = "minecraft:mossy_cobblestone_double_slab"; + public const MOSSY_COBBLESTONE_SLAB = "minecraft:mossy_cobblestone_slab"; public const MOSSY_COBBLESTONE_STAIRS = "minecraft:mossy_cobblestone_stairs"; + public const MOSSY_STONE_BRICK_DOUBLE_SLAB = "minecraft:mossy_stone_brick_double_slab"; + public const MOSSY_STONE_BRICK_SLAB = "minecraft:mossy_stone_brick_slab"; public const MOSSY_STONE_BRICK_STAIRS = "minecraft:mossy_stone_brick_stairs"; + public const MOSSY_STONE_BRICKS = "minecraft:mossy_stone_bricks"; public const MOVING_BLOCK = "minecraft:moving_block"; public const MUD = "minecraft:mud"; public const MUD_BRICK_DOUBLE_SLAB = "minecraft:mud_brick_double_slab"; @@ -729,6 +785,7 @@ final class BlockTypeNames{ public const MUDDY_MANGROVE_ROOTS = "minecraft:muddy_mangrove_roots"; public const MYCELIUM = "minecraft:mycelium"; public const NETHER_BRICK = "minecraft:nether_brick"; + public const NETHER_BRICK_DOUBLE_SLAB = "minecraft:nether_brick_double_slab"; public const NETHER_BRICK_FENCE = "minecraft:nether_brick_fence"; public const NETHER_BRICK_SLAB = "minecraft:nether_brick_slab"; public const NETHER_BRICK_STAIRS = "minecraft:nether_brick_stairs"; @@ -739,6 +796,8 @@ final class BlockTypeNames{ public const NETHERITE_BLOCK = "minecraft:netherite_block"; public const NETHERRACK = "minecraft:netherrack"; public const NETHERREACTOR = "minecraft:netherreactor"; + public const NORMAL_STONE_DOUBLE_SLAB = "minecraft:normal_stone_double_slab"; + public const NORMAL_STONE_SLAB = "minecraft:normal_stone_slab"; public const NORMAL_STONE_STAIRS = "minecraft:normal_stone_stairs"; public const NOTEBLOCK = "minecraft:noteblock"; public const OAK_DOUBLE_SLAB = "minecraft:oak_double_slab"; @@ -781,6 +840,7 @@ final class BlockTypeNames{ public const PACKED_MUD = "minecraft:packed_mud"; public const PEARLESCENT_FROGLIGHT = "minecraft:pearlescent_froglight"; public const PEONY = "minecraft:peony"; + public const PETRIFIED_OAK_DOUBLE_SLAB = "minecraft:petrified_oak_double_slab"; public const PETRIFIED_OAK_SLAB = "minecraft:petrified_oak_slab"; public const PINK_CANDLE = "minecraft:pink_candle"; public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake"; @@ -802,6 +862,8 @@ final class BlockTypeNames{ public const PODZOL = "minecraft:podzol"; public const POINTED_DRIPSTONE = "minecraft:pointed_dripstone"; public const POLISHED_ANDESITE = "minecraft:polished_andesite"; + public const POLISHED_ANDESITE_DOUBLE_SLAB = "minecraft:polished_andesite_double_slab"; + public const POLISHED_ANDESITE_SLAB = "minecraft:polished_andesite_slab"; public const POLISHED_ANDESITE_STAIRS = "minecraft:polished_andesite_stairs"; public const POLISHED_BASALT = "minecraft:polished_basalt"; public const POLISHED_BLACKSTONE = "minecraft:polished_blackstone"; @@ -822,8 +884,12 @@ final class BlockTypeNames{ public const POLISHED_DEEPSLATE_STAIRS = "minecraft:polished_deepslate_stairs"; public const POLISHED_DEEPSLATE_WALL = "minecraft:polished_deepslate_wall"; public const POLISHED_DIORITE = "minecraft:polished_diorite"; + public const POLISHED_DIORITE_DOUBLE_SLAB = "minecraft:polished_diorite_double_slab"; + public const POLISHED_DIORITE_SLAB = "minecraft:polished_diorite_slab"; public const POLISHED_DIORITE_STAIRS = "minecraft:polished_diorite_stairs"; public const POLISHED_GRANITE = "minecraft:polished_granite"; + public const POLISHED_GRANITE_DOUBLE_SLAB = "minecraft:polished_granite_double_slab"; + public const POLISHED_GRANITE_SLAB = "minecraft:polished_granite_slab"; public const POLISHED_GRANITE_STAIRS = "minecraft:polished_granite_stairs"; public const POLISHED_TUFF = "minecraft:polished_tuff"; public const POLISHED_TUFF_DOUBLE_SLAB = "minecraft:polished_tuff_double_slab"; @@ -837,7 +903,12 @@ final class BlockTypeNames{ public const POWERED_COMPARATOR = "minecraft:powered_comparator"; public const POWERED_REPEATER = "minecraft:powered_repeater"; public const PRISMARINE = "minecraft:prismarine"; + public const PRISMARINE_BRICK_DOUBLE_SLAB = "minecraft:prismarine_brick_double_slab"; + public const PRISMARINE_BRICK_SLAB = "minecraft:prismarine_brick_slab"; + public const PRISMARINE_BRICKS = "minecraft:prismarine_bricks"; public const PRISMARINE_BRICKS_STAIRS = "minecraft:prismarine_bricks_stairs"; + public const PRISMARINE_DOUBLE_SLAB = "minecraft:prismarine_double_slab"; + public const PRISMARINE_SLAB = "minecraft:prismarine_slab"; public const PRISMARINE_STAIRS = "minecraft:prismarine_stairs"; public const PUMPKIN = "minecraft:pumpkin"; public const PUMPKIN_STEM = "minecraft:pumpkin_stem"; @@ -853,10 +924,14 @@ final class BlockTypeNames{ public const PURPLE_TERRACOTTA = "minecraft:purple_terracotta"; public const PURPLE_WOOL = "minecraft:purple_wool"; public const PURPUR_BLOCK = "minecraft:purpur_block"; + public const PURPUR_DOUBLE_SLAB = "minecraft:purpur_double_slab"; + public const PURPUR_SLAB = "minecraft:purpur_slab"; public const PURPUR_STAIRS = "minecraft:purpur_stairs"; public const QUARTZ_BLOCK = "minecraft:quartz_block"; public const QUARTZ_BRICKS = "minecraft:quartz_bricks"; + public const QUARTZ_DOUBLE_SLAB = "minecraft:quartz_double_slab"; public const QUARTZ_ORE = "minecraft:quartz_ore"; + public const QUARTZ_PILLAR = "minecraft:quartz_pillar"; public const QUARTZ_SLAB = "minecraft:quartz_slab"; public const QUARTZ_STAIRS = "minecraft:quartz_stairs"; public const RAIL = "minecraft:rail"; @@ -872,8 +947,13 @@ final class BlockTypeNames{ public const RED_MUSHROOM = "minecraft:red_mushroom"; public const RED_MUSHROOM_BLOCK = "minecraft:red_mushroom_block"; public const RED_NETHER_BRICK = "minecraft:red_nether_brick"; + public const RED_NETHER_BRICK_DOUBLE_SLAB = "minecraft:red_nether_brick_double_slab"; + public const RED_NETHER_BRICK_SLAB = "minecraft:red_nether_brick_slab"; public const RED_NETHER_BRICK_STAIRS = "minecraft:red_nether_brick_stairs"; + public const RED_SAND = "minecraft:red_sand"; public const RED_SANDSTONE = "minecraft:red_sandstone"; + public const RED_SANDSTONE_DOUBLE_SLAB = "minecraft:red_sandstone_double_slab"; + public const RED_SANDSTONE_SLAB = "minecraft:red_sandstone_slab"; public const RED_SANDSTONE_STAIRS = "minecraft:red_sandstone_stairs"; public const RED_SHULKER_BOX = "minecraft:red_shulker_box"; public const RED_STAINED_GLASS = "minecraft:red_stained_glass"; @@ -894,6 +974,7 @@ final class BlockTypeNames{ public const ROSE_BUSH = "minecraft:rose_bush"; public const SAND = "minecraft:sand"; public const SANDSTONE = "minecraft:sandstone"; + public const SANDSTONE_DOUBLE_SLAB = "minecraft:sandstone_double_slab"; public const SANDSTONE_SLAB = "minecraft:sandstone_slab"; public const SANDSTONE_STAIRS = "minecraft:sandstone_stairs"; public const SCAFFOLDING = "minecraft:scaffolding"; @@ -915,10 +996,20 @@ final class BlockTypeNames{ public const SMITHING_TABLE = "minecraft:smithing_table"; public const SMOKER = "minecraft:smoker"; public const SMOOTH_BASALT = "minecraft:smooth_basalt"; + public const SMOOTH_QUARTZ = "minecraft:smooth_quartz"; + public const SMOOTH_QUARTZ_DOUBLE_SLAB = "minecraft:smooth_quartz_double_slab"; + public const SMOOTH_QUARTZ_SLAB = "minecraft:smooth_quartz_slab"; public const SMOOTH_QUARTZ_STAIRS = "minecraft:smooth_quartz_stairs"; + public const SMOOTH_RED_SANDSTONE = "minecraft:smooth_red_sandstone"; + public const SMOOTH_RED_SANDSTONE_DOUBLE_SLAB = "minecraft:smooth_red_sandstone_double_slab"; + public const SMOOTH_RED_SANDSTONE_SLAB = "minecraft:smooth_red_sandstone_slab"; public const SMOOTH_RED_SANDSTONE_STAIRS = "minecraft:smooth_red_sandstone_stairs"; + public const SMOOTH_SANDSTONE = "minecraft:smooth_sandstone"; + public const SMOOTH_SANDSTONE_DOUBLE_SLAB = "minecraft:smooth_sandstone_double_slab"; + public const SMOOTH_SANDSTONE_SLAB = "minecraft:smooth_sandstone_slab"; public const SMOOTH_SANDSTONE_STAIRS = "minecraft:smooth_sandstone_stairs"; public const SMOOTH_STONE = "minecraft:smooth_stone"; + public const SMOOTH_STONE_DOUBLE_SLAB = "minecraft:smooth_stone_double_slab"; public const SMOOTH_STONE_SLAB = "minecraft:smooth_stone_slab"; public const SNIFFER_EGG = "minecraft:sniffer_egg"; public const SNOW = "minecraft:snow"; @@ -953,15 +1044,13 @@ final class BlockTypeNames{ public const STICKY_PISTON = "minecraft:sticky_piston"; public const STICKY_PISTON_ARM_COLLISION = "minecraft:sticky_piston_arm_collision"; public const STONE = "minecraft:stone"; - public const STONE_BLOCK_SLAB2 = "minecraft:stone_block_slab2"; - public const STONE_BLOCK_SLAB3 = "minecraft:stone_block_slab3"; - public const STONE_BLOCK_SLAB4 = "minecraft:stone_block_slab4"; + public const STONE_BRICK_DOUBLE_SLAB = "minecraft:stone_brick_double_slab"; public const STONE_BRICK_SLAB = "minecraft:stone_brick_slab"; public const STONE_BRICK_STAIRS = "minecraft:stone_brick_stairs"; + public const STONE_BRICKS = "minecraft:stone_bricks"; public const STONE_BUTTON = "minecraft:stone_button"; public const STONE_PRESSURE_PLATE = "minecraft:stone_pressure_plate"; public const STONE_STAIRS = "minecraft:stone_stairs"; - public const STONEBRICK = "minecraft:stonebrick"; public const STONECUTTER = "minecraft:stonecutter"; public const STONECUTTER_BLOCK = "minecraft:stonecutter_block"; public const STRIPPED_ACACIA_LOG = "minecraft:stripped_acacia_log"; @@ -1006,6 +1095,7 @@ final class BlockTypeNames{ public const TUBE_CORAL = "minecraft:tube_coral"; public const TUBE_CORAL_BLOCK = "minecraft:tube_coral_block"; public const TUBE_CORAL_FAN = "minecraft:tube_coral_fan"; + public const TUBE_CORAL_WALL_FAN = "minecraft:tube_coral_wall_fan"; public const TUFF = "minecraft:tuff"; public const TUFF_BRICK_DOUBLE_SLAB = "minecraft:tuff_brick_double_slab"; public const TUFF_BRICK_SLAB = "minecraft:tuff_brick_slab"; @@ -1124,7 +1214,6 @@ final class BlockTypeNames{ public const YELLOW_CARPET = "minecraft:yellow_carpet"; public const YELLOW_CONCRETE = "minecraft:yellow_concrete"; public const YELLOW_CONCRETE_POWDER = "minecraft:yellow_concrete_powder"; - public const YELLOW_FLOWER = "minecraft:yellow_flower"; public const YELLOW_GLAZED_TERRACOTTA = "minecraft:yellow_glazed_terracotta"; public const YELLOW_SHULKER_BOX = "minecraft:yellow_shulker_box"; public const YELLOW_STAINED_GLASS = "minecraft:yellow_stained_glass"; diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index ab98e75f3..df1695729 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -562,6 +562,17 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK, } )); + + $this->map(Blocks::WALL_CORAL_FAN(), fn(WallCoralFan $block) => Writer::create( + match($block->getCoralType()){ + CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_WALL_FAN : Ids::TUBE_CORAL_WALL_FAN, + CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_WALL_FAN : Ids::BRAIN_CORAL_WALL_FAN, + CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_WALL_FAN : Ids::BUBBLE_CORAL_WALL_FAN, + CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_WALL_FAN : Ids::FIRE_CORAL_WALL_FAN, + CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_WALL_FAN : Ids::HORN_CORAL_WALL_FAN, + }) + ->writeCoralFacing($block->getFacing()) + ); } private function registerCauldronSerializers() : void{ @@ -783,6 +794,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE); $this->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); $this->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); + $this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); + $this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); + $this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); $this->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT); $this->mapSimple(Blocks::CLAY(), Ids::CLAY); $this->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK); @@ -795,10 +809,14 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES); $this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); $this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS); + $this->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS); $this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); $this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS); $this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); - $this->mapSimple(Blocks::DANDELION(), Ids::YELLOW_FLOWER); + $this->mapSimple(Blocks::DANDELION(), Ids::DANDELION); + $this->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE); + $this->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE); + $this->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE); $this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); $this->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS); $this->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE); @@ -957,6 +975,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE); $this->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK); $this->mapSimple(Blocks::ICE(), Ids::ICE); + $this->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS); + $this->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE); + $this->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS); + $this->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS); + $this->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE); + $this->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS); $this->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE); $this->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2); $this->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK); @@ -973,6 +997,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); $this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); $this->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE); + $this->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS); $this->mapSimple(Blocks::MUD(), Ids::MUD); $this->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS); $this->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM); @@ -995,6 +1020,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE); $this->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE); $this->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE); + $this->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE); + $this->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS); $this->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS); $this->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK); $this->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK); @@ -1002,8 +1029,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK); $this->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM); $this->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK); + $this->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND); + $this->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); $this->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); $this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); + $this->mapSimple(Blocks::SAND(), Ids::SAND); + $this->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); $this->mapSimple(Blocks::SCULK(), Ids::SCULK); $this->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN); $this->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT); @@ -1011,13 +1042,16 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::SLIME(), Ids::SLIME); $this->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE); $this->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT); + $this->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE); + $this->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE); $this->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE); $this->mapSimple(Blocks::SNOW(), Ids::SNOW); $this->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND); $this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL); $this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); $this->mapSimple(Blocks::STONE(), Ids::STONE); - $this->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( + $this->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS); + $this->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( $this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); $this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER); $this->mapSimple(Blocks::TUFF(), Ids::TUFF); @@ -1056,19 +1090,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }) ->writeBlockFace($block->getFacing()) ); - $this->map(Blocks::ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_ANDESITE)); + $this->mapSlab(Blocks::ANDESITE_SLAB(), Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB); $this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS))); $this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_ANDESITE)); - $this->map(Blocks::ANVIL(), function(Anvil $block) : Writer{ - return Writer::create(Ids::ANVIL) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::DAMAGE, match($damage = $block->getDamage()){ - 0 => StringValues::DAMAGE_UNDAMAGED, - 1 => StringValues::DAMAGE_SLIGHTLY_DAMAGED, - 2 => StringValues::DAMAGE_VERY_DAMAGED, - default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"), - }); - }); + $this->map(Blocks::ANVIL(), fn(Anvil $block) : Writer => Writer::create( + match($damage = $block->getDamage()){ + 0 => Ids::ANVIL, + 1 => Ids::CHIPPED_ANVIL, + 2 => Ids::DAMAGED_ANVIL, + default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"), + }) + ->writeCardinalHorizontalFacing($block->getFacing()) + ); $this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{ return Writer::create(Ids::BAMBOO) ->writeBool(StateNames::AGE_BIT, $block->isReady()) @@ -1148,7 +1181,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST)) ->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST)); }); - $this->map(Blocks::BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::BRICK_SLAB, StringValues::STONE_SLAB_TYPE_BRICK)); + $this->mapSlab(Blocks::BRICK_SLAB(), Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); $this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_BRICK)); $this->map(Blocks::BROWN_MUSHROOM_BLOCK(), fn(BrownMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::BROWN_MUSHROOM_BLOCK))); @@ -1193,10 +1226,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeLegacyHorizontalFacing($block->getFacing()) ->writeInt(StateNames::BOOKS_STORED, $flags); }); - $this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_CHISELED, $block->getAxis())); - $this->map(Blocks::CHISELED_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS)); - $this->map(Blocks::CHISELED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS)); - $this->map(Blocks::CHISELED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CHISELED)); + $this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::CHISELED_QUARTZ_BLOCK))); $this->map(Blocks::CHORUS_FLOWER(), function(ChorusFlower $block) : Writer{ return Writer::create(Ids::CHORUS_FLOWER) ->writeInt(StateNames::AGE, $block->getAge()); @@ -1204,7 +1234,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB); $this->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS); $this->map(Blocks::COBBLED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::COBBLED_DEEPSLATE_WALL))); - $this->map(Blocks::COBBLESTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::COBBLESTONE_SLAB, StringValues::STONE_SLAB_TYPE_COBBLESTONE)); + $this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_COBBLESTONE)); $this->map(Blocks::COPPER(), function(Copper $block) : Writer{ @@ -1287,14 +1317,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); }); $this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR, new Writer(Ids::CHEMISTRY_TABLE))); - $this->map(Blocks::CRACKED_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_CRACKED)); - $this->map(Blocks::CUT_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_CUT)); - $this->map(Blocks::CUT_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE)); - $this->map(Blocks::CUT_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_CUT)); - $this->map(Blocks::CUT_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE)); - $this->map(Blocks::DARK_PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE) - ->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DARK)); - $this->map(Blocks::DARK_PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK)); + $this->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB); + $this->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB); + $this->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB); $this->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS); $this->map(Blocks::DAYLIGHT_SENSOR(), function(DaylightSensor $block) : Writer{ return Writer::create($block->isInverted() ? Ids::DAYLIGHT_DETECTOR_INVERTED : Ids::DAYLIGHT_DETECTOR) @@ -1316,20 +1341,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::RAIL_DATA_BIT, $block->isActivated()) ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); }); - $this->map(Blocks::DIORITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_DIORITE)); + $this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB); $this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_DIORITE)); $this->map(Blocks::DIRT(), function(Dirt $block) : Writer{ - $dirtType = $block->getDirtType(); - if($dirtType === DirtType::ROOTED){ - return new Writer(Ids::DIRT_WITH_ROOTS); - } - return Writer::create(Ids::DIRT) - ->writeString(StateNames::DIRT_TYPE, match($dirtType){ - DirtType::COARSE => StringValues::DIRT_TYPE_COARSE, - DirtType::NORMAL => StringValues::DIRT_TYPE_NORMAL, - //ROOTED was already checked above - }); + return Writer::create(match($block->getDirtType()){ + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }); }); $this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS))); $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR, new Writer(Ids::CHEMISTRY_TABLE))); @@ -1346,10 +1366,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::END_ROD) ->writeEndRodFacingDirection($block->getFacing()); }); - $this->map(Blocks::END_STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_END_STONE_BRICK)); + $this->mapSlab(Blocks::END_STONE_BRICK_SLAB(), Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); $this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_END_BRICK)); - $this->map(Blocks::FAKE_WOODEN_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::PETRIFIED_OAK_SLAB, StringValues::STONE_SLAB_TYPE_WOOD)); + $this->mapSlab(Blocks::FAKE_WOODEN_SLAB(), Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB); $this->map(Blocks::FARMLAND(), function(Farmland $block) : Writer{ return Writer::create(Ids::FARMLAND) ->writeInt(StateNames::MOISTURIZED_AMOUNT, $block->getWetness()); @@ -1380,7 +1400,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeFacingFlags($block->getFaces()); }); $this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME)); - $this->map(Blocks::GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_GRANITE)); + $this->mapSlab(Blocks::GRANITE_SLAB(), Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB); $this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); $this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_GRANITE)); $this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_RG))); @@ -1394,18 +1414,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::TOGGLE_BIT, $block->isPowered()) ->writeFacingWithoutUp($block->getFacing()); }); - $this->map(Blocks::INFESTED_CHISELED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK)); - $this->map(Blocks::INFESTED_COBBLESTONE(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_COBBLESTONE)); - $this->map(Blocks::INFESTED_CRACKED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK)); - $this->map(Blocks::INFESTED_MOSSY_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK)); - $this->map(Blocks::INFESTED_STONE(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_STONE)); - $this->map(Blocks::INFESTED_STONE_BRICK(), fn() => Writer::create(Ids::MONSTER_EGG) - ->writeString(StateNames::MONSTER_EGG_STONE_TYPE, StringValues::MONSTER_EGG_STONE_TYPE_STONE_BRICK)); $this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR))); $this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR))); $this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME)); @@ -1440,8 +1448,25 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); }); $this->map(Blocks::LIGHT(), function(Light $block) : Writer{ - return Writer::create(Ids::LIGHT_BLOCK) - ->writeInt(StateNames::BLOCK_LIGHT_LEVEL, $block->getLightLevel()); + return Writer::create(match($block->getLightLevel()){ + 0 => Ids::LIGHT_BLOCK_0, + 1 => Ids::LIGHT_BLOCK_1, + 2 => Ids::LIGHT_BLOCK_2, + 3 => Ids::LIGHT_BLOCK_3, + 4 => Ids::LIGHT_BLOCK_4, + 5 => Ids::LIGHT_BLOCK_5, + 6 => Ids::LIGHT_BLOCK_6, + 7 => Ids::LIGHT_BLOCK_7, + 8 => Ids::LIGHT_BLOCK_8, + 9 => Ids::LIGHT_BLOCK_9, + 10 => Ids::LIGHT_BLOCK_10, + 11 => Ids::LIGHT_BLOCK_11, + 12 => Ids::LIGHT_BLOCK_12, + 13 => Ids::LIGHT_BLOCK_13, + 14 => Ids::LIGHT_BLOCK_14, + 15 => Ids::LIGHT_BLOCK_15, + default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()), + }); }); $this->map(Blocks::LIGHTNING_ROD(), function(LightningRod $block) : Writer{ return Writer::create(Ids::LIGHTNING_ROD) @@ -1462,11 +1487,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::SKULL) ->writeFacingWithoutDown($block->getFacing()); }); - $this->map(Blocks::MOSSY_COBBLESTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE)); + $this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE)); - $this->map(Blocks::MOSSY_STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_MOSSY)); - $this->map(Blocks::MOSSY_STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK)); + $this->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); $this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK)); $this->mapSlab(Blocks::MUD_BRICK_SLAB(), Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB); @@ -1476,7 +1500,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writePillarAxis($block->getAxis())); $this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK) ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); - $this->map(Blocks::NETHER_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::NETHER_BRICK_SLAB, StringValues::STONE_SLAB_TYPE_NETHER_BRICK)); + $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); $this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_NETHER_BRICK)); $this->map(Blocks::NETHER_PORTAL(), function(NetherPortal $block) : Writer{ @@ -1511,7 +1535,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); }); - $this->map(Blocks::POLISHED_ANDESITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE)); + $this->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB); $this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS); $this->map(Blocks::POLISHED_BASALT(), function(SimplePillar $block) : Writer{ return Writer::create(Ids::POLISHED_BASALT) @@ -1528,9 +1552,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB); $this->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS); $this->map(Blocks::POLISHED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_DEEPSLATE_WALL))); - $this->map(Blocks::POLISHED_DIORITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_DIORITE)); + $this->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB); $this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS); - $this->map(Blocks::POLISHED_GRANITE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE)); + $this->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB); $this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS); $this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES))); $this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{ @@ -1538,13 +1562,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); }); - $this->map(Blocks::PRISMARINE(), fn() => Writer::create(Ids::PRISMARINE) - ->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_DEFAULT)); - $this->map(Blocks::PRISMARINE_BRICKS(), fn() => Writer::create(Ids::PRISMARINE) - ->writeString(StateNames::PRISMARINE_BLOCK_TYPE, StringValues::PRISMARINE_BLOCK_TYPE_BRICKS)); - $this->map(Blocks::PRISMARINE_BRICKS_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_BRICK)); + $this->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); - $this->map(Blocks::PRISMARINE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PRISMARINE_ROUGH)); + $this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB); $this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_PRISMARINE)); $this->map(Blocks::PUMPKIN(), function() : Writer{ @@ -1563,11 +1583,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeString(StateNames::CHISEL_TYPE, StringValues::CHISEL_TYPE_LINES) ->writePillarAxis($block->getAxis()); }); - $this->map(Blocks::PURPUR_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_PURPUR)); + $this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB); $this->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); - $this->map(Blocks::QUARTZ(), fn() => Helper::encodeQuartz(StringValues::CHISEL_TYPE_DEFAULT, Axis::Y)); - $this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_LINES, $block->getAxis())); - $this->map(Blocks::QUARTZ_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::QUARTZ_SLAB, StringValues::STONE_SLAB_TYPE_QUARTZ)); + $this->map(Blocks::QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK))); + $this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::QUARTZ_PILLAR))); + $this->mapSlab(Blocks::QUARTZ_SLAB(), Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB); $this->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); $this->map(Blocks::RAIL(), function(Rail $block) : Writer{ return Writer::create(Ids::RAIL) @@ -1595,21 +1615,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); }); $this->map(Blocks::RED_MUSHROOM_BLOCK(), fn(RedMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::RED_MUSHROOM_BLOCK))); - $this->map(Blocks::RED_NETHER_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_RED_NETHER_BRICK)); + $this->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); $this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK)); - $this->map(Blocks::RED_SAND(), fn() => Writer::create(Ids::SAND) - ->writeString(StateNames::SAND_TYPE, StringValues::SAND_TYPE_RED)); - $this->map(Blocks::RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_DEFAULT)); - $this->map(Blocks::RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_RED_SANDSTONE)); + $this->mapSlab(Blocks::RED_SANDSTONE_SLAB(), Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE)); $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_RG))); $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); - $this->map(Blocks::SAND(), fn() => Writer::create(Ids::SAND) - ->writeString(StateNames::SAND_TYPE, StringValues::SAND_TYPE_NORMAL)); - $this->map(Blocks::SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_DEFAULT)); - $this->map(Blocks::SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::SANDSTONE_SLAB, StringValues::STONE_SLAB_TYPE_SANDSTONE)); + $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); $this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_SANDSTONE)); $this->map(Blocks::SEA_PICKLE(), function(SeaPickle $block) : Writer{ @@ -1623,16 +1637,14 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); }); $this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER)); - $this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(StringValues::CHISEL_TYPE_SMOOTH, Axis::Y)); - $this->map(Blocks::SMOOTH_QUARTZ_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ)); + $this->map(Blocks::SMOOTH_QUARTZ(), fn() => Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ))); + $this->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB); $this->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); - $this->map(Blocks::SMOOTH_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_SMOOTH)); - $this->map(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab3($block, StringValues::STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE)); + $this->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS); - $this->map(Blocks::SMOOTH_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_SMOOTH)); - $this->map(Blocks::SMOOTH_SANDSTONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab2($block, StringValues::STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE)); + $this->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS); - $this->map(Blocks::SMOOTH_STONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::SMOOTH_STONE_SLAB, StringValues::STONE_SLAB_TYPE_SMOOTH_STONE)); + $this->mapSlab(Blocks::SMOOTH_STONE_SLAB(), Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB); $this->map(Blocks::SNOW_LAYER(), function(SnowLayer $block) : Writer{ return Writer::create(Ids::SNOW_LAYER) ->writeBool(StateNames::COVERED_BIT, false) @@ -1656,13 +1668,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK) ->writeCardinalHorizontalFacing($block->getFacing())); - $this->map(Blocks::STONE_BRICKS(), fn() => Helper::encodeStoneBricks(StringValues::STONE_BRICK_TYPE_DEFAULT)); - $this->map(Blocks::STONE_BRICK_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab1($block, Ids::STONE_BRICK_SLAB, StringValues::STONE_SLAB_TYPE_STONE_BRICK)); + $this->mapSlab(Blocks::STONE_BRICK_SLAB(), Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); $this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_STONE_BRICK)); $this->map(Blocks::STONE_BUTTON(), fn(StoneButton $block) => Helper::encodeButton($block, new Writer(Ids::STONE_BUTTON))); $this->map(Blocks::STONE_PRESSURE_PLATE(), fn(StonePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::STONE_PRESSURE_PLATE))); - $this->map(Blocks::STONE_SLAB(), fn(Slab $block) => Helper::encodeStoneSlab4($block, StringValues::STONE_SLAB_TYPE_4_STONE)); + $this->mapSlab(Blocks::STONE_SLAB(), Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB); $this->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS); $this->map(Blocks::SUGARCANE(), function(Sugarcane $block) : Writer{ return Writer::create(Ids::REEDS) @@ -1719,17 +1730,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::WALL_BANNER) ->writeHorizontalFacing($block->getFacing()); }); - $this->map(Blocks::WALL_CORAL_FAN(), function(WallCoralFan $block) : Writer{ - $coralType = $block->getCoralType(); - return Writer::create(match($coralType){ - CoralType::TUBE, CoralType::BRAIN => Ids::CORAL_FAN_HANG, - CoralType::BUBBLE, CoralType::FIRE => Ids::CORAL_FAN_HANG2, - CoralType::HORN => Ids::CORAL_FAN_HANG3, - }) - ->writeBool(StateNames::CORAL_HANG_TYPE_BIT, $coralType === CoralType::BRAIN || $coralType === CoralType::FIRE) - ->writeBool(StateNames::DEAD_BIT, $block->isDead()) - ->writeCoralFacing($block->getFacing()); - }); $this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER)); $this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{ return Writer::create(Ids::WEEPING_VINES) diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 6dda9fb81..0c8df24f2 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -326,65 +326,4 @@ final class BlockStateDeserializerHelper{ default => throw $in->badValueException(BlockStateNames::WALL_BLOCK_TYPE, $type), }, $in); } - - /** @throws BlockStateDeserializeException */ - public static function mapStoneSlab1Type(BlockStateReader $in) : Slab{ - //* stone_slab_type (StringTag) = brick, cobblestone, nether_brick, quartz, sandstone, smooth_stone, stone_brick, wood - return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE)){ - StringValues::STONE_SLAB_TYPE_BRICK => VanillaBlocks::BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_QUARTZ => VanillaBlocks::QUARTZ_SLAB(), - StringValues::STONE_SLAB_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_SMOOTH_STONE => VanillaBlocks::SMOOTH_STONE_SLAB(), - StringValues::STONE_SLAB_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_WOOD => VanillaBlocks::FAKE_WOODEN_SLAB(), - default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE, $type), - }; - } - - /** @throws BlockStateDeserializeException */ - public static function mapStoneSlab2Type(BlockStateReader $in) : Slab{ - // * stone_slab_type_2 (StringTag) = mossy_cobblestone, prismarine_brick, prismarine_dark, prismarine_rough, purpur, red_nether_brick, red_sandstone, smooth_sandstone - return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_2)){ - StringValues::STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_2_PRISMARINE_BRICK => VanillaBlocks::PRISMARINE_BRICKS_SLAB(), - StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK => VanillaBlocks::DARK_PRISMARINE_SLAB(), - StringValues::STONE_SLAB_TYPE_2_PRISMARINE_ROUGH => VanillaBlocks::PRISMARINE_SLAB(), - StringValues::STONE_SLAB_TYPE_2_PURPUR => VanillaBlocks::PURPUR_SLAB(), - StringValues::STONE_SLAB_TYPE_2_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_2_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE => VanillaBlocks::SMOOTH_SANDSTONE_SLAB(), - default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_2, $type), - }; - } - - /** @throws BlockStateDeserializeException */ - public static function mapStoneSlab3Type(BlockStateReader $in) : Slab{ - // * stone_slab_type_3 (StringTag) = andesite, diorite, end_stone_brick, granite, polished_andesite, polished_diorite, polished_granite, smooth_red_sandstone - return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_3)){ - StringValues::STONE_SLAB_TYPE_3_ANDESITE => VanillaBlocks::ANDESITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_DIORITE => VanillaBlocks::DIORITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_END_STONE_BRICK => VanillaBlocks::END_STONE_BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_3_GRANITE => VanillaBlocks::GRANITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE => VanillaBlocks::POLISHED_ANDESITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_POLISHED_DIORITE => VanillaBlocks::POLISHED_DIORITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE => VanillaBlocks::POLISHED_GRANITE_SLAB(), - StringValues::STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE => VanillaBlocks::SMOOTH_RED_SANDSTONE_SLAB(), - default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_3, $type), - }; - } - - /** @throws BlockStateDeserializeException */ - public static function mapStoneSlab4Type(BlockStateReader $in) : Slab{ - // * stone_slab_type_4 (StringTag) = cut_red_sandstone, cut_sandstone, mossy_stone_brick, smooth_quartz, stone - return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_4)){ - StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE => VanillaBlocks::CUT_RED_SANDSTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE => VanillaBlocks::CUT_SANDSTONE_SLAB(), - StringValues::STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_SLAB(), - StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ => VanillaBlocks::SMOOTH_QUARTZ_SLAB(), - StringValues::STONE_SLAB_TYPE_4_STONE => VanillaBlocks::STONE_SLAB(), - default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_4, $type), - }; - } } diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index a1f417d89..9dfb17ca0 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -160,16 +160,11 @@ final class BlockStateSerializerHelper{ ->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType())); } - public static function encodeQuartz(string $type, int $axis) : Writer{ - return Writer::create(Ids::QUARTZ_BLOCK) - ->writeString(BlockStateNames::CHISEL_TYPE, $type) + public static function encodeQuartz(int $axis, Writer $out) : Writer{ + return $out ->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway } - public static function encodeSandstone(string $id, string $type) : Writer{ - return Writer::create($id)->writeString(BlockStateNames::SAND_STONE_TYPE, $type); - } - public static function encodeSapling(Sapling $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::AGE_BIT, $block->isReady()); @@ -214,36 +209,6 @@ final class BlockStateSerializerHelper{ ->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing); } - public static function encodeStoneBricks(string $type) : Writer{ - return Writer::create(Ids::STONEBRICK) - ->writeString(BlockStateNames::STONE_BRICK_TYPE, $type); - } - - private static function encodeStoneSlab(Slab $block, string $singleId, string $doubleId, string $typeKey, string $typeValue) : Writer{ - return self::encodeSlab($block, $singleId, $doubleId) - ->writeString($typeKey, $typeValue); - } - - public static function encodeStoneSlab1(Slab $block, string $singleId, string $doubleTypeValue) : Writer{ - //1.21 made this a mess by flattening single slab IDs but not double ones - return $block->getSlabType() === SlabType::DOUBLE ? - self::encodeDoubleSlab($block, Ids::DOUBLE_STONE_BLOCK_SLAB) - ->writeString(BlockStateNames::STONE_SLAB_TYPE, $doubleTypeValue) : - self::encodeSingleSlab($block, $singleId); - } - - public static function encodeStoneSlab2(Slab $block, string $typeValue) : Writer{ - return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB2, Ids::DOUBLE_STONE_BLOCK_SLAB2, BlockStateNames::STONE_SLAB_TYPE_2, $typeValue); - } - - public static function encodeStoneSlab3(Slab $block, string $typeValue) : Writer{ - return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB3, Ids::DOUBLE_STONE_BLOCK_SLAB3, BlockStateNames::STONE_SLAB_TYPE_3, $typeValue); - } - - public static function encodeStoneSlab4(Slab $block, string $typeValue) : Writer{ - return self::encodeStoneSlab($block, Ids::STONE_BLOCK_SLAB4, Ids::DOUBLE_STONE_BLOCK_SLAB4, BlockStateNames::STONE_SLAB_TYPE_4, $typeValue); - } - public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{ return $out ->write5MinusHorizontalFacing($block->getFacing()) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 4c9ec4b79..b85951d31 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; use pocketmine\block\AmethystCluster; +use pocketmine\block\Anvil; use pocketmine\block\Bamboo; use pocketmine\block\Block; use pocketmine\block\CaveVines; use pocketmine\block\ChorusFlower; use pocketmine\block\DoublePitcherCrop; -use pocketmine\block\Light; +use pocketmine\block\Opaque; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; use pocketmine\block\Slab; @@ -83,6 +84,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->registerFlatWoodBlockDeserializers(); $this->registerLeavesDeserializers(); $this->registerSaplingDeserializers(); + $this->registerLightDeserializers(); $this->registerSimpleDeserializers(); $this->registerDeserializers(); } @@ -457,6 +459,17 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map($aliveId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(false)); $this->map($deadId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(true)); } + + foreach([ + [CoralType::BRAIN, Ids::BRAIN_CORAL_WALL_FAN, Ids::DEAD_BRAIN_CORAL_WALL_FAN], + [CoralType::BUBBLE, Ids::BUBBLE_CORAL_WALL_FAN, Ids::DEAD_BUBBLE_CORAL_WALL_FAN], + [CoralType::FIRE, Ids::FIRE_CORAL_WALL_FAN, Ids::DEAD_FIRE_CORAL_WALL_FAN], + [CoralType::HORN, Ids::HORN_CORAL_WALL_FAN, Ids::DEAD_HORN_CORAL_WALL_FAN], + [CoralType::TUBE, Ids::TUBE_CORAL_WALL_FAN, Ids::DEAD_TUBE_CORAL_WALL_FAN], + ] as [$coralType, $aliveId, $deadId]){ + $this->map($aliveId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(false)); + $this->map($deadId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(true)); + } } private function registerCauldronDeserializers() : void{ @@ -654,6 +667,29 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } } + private function registerLightDeserializers() : void{ + foreach([ + Ids::LIGHT_BLOCK_0 => 0, + Ids::LIGHT_BLOCK_1 => 1, + Ids::LIGHT_BLOCK_2 => 2, + Ids::LIGHT_BLOCK_3 => 3, + Ids::LIGHT_BLOCK_4 => 4, + Ids::LIGHT_BLOCK_5 => 5, + Ids::LIGHT_BLOCK_6 => 6, + Ids::LIGHT_BLOCK_7 => 7, + Ids::LIGHT_BLOCK_8 => 8, + Ids::LIGHT_BLOCK_9 => 9, + Ids::LIGHT_BLOCK_10 => 10, + Ids::LIGHT_BLOCK_11 => 11, + Ids::LIGHT_BLOCK_12 => 12, + Ids::LIGHT_BLOCK_13 => 13, + Ids::LIGHT_BLOCK_14 => 14, + Ids::LIGHT_BLOCK_15 => 15, + ] as $id => $level){ + $this->mapSimple($id, fn() => Blocks::LIGHT()->setLightLevel($level)); + } + } + private function registerSimpleDeserializers() : void{ $this->mapSimple(Ids::AIR, fn() => Blocks::AIR()); $this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST()); @@ -673,6 +709,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::CHISELED_DEEPSLATE, fn() => Blocks::CHISELED_DEEPSLATE()); $this->mapSimple(Ids::CHISELED_NETHER_BRICKS, fn() => Blocks::CHISELED_NETHER_BRICKS()); $this->mapSimple(Ids::CHISELED_POLISHED_BLACKSTONE, fn() => Blocks::CHISELED_POLISHED_BLACKSTONE()); + $this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE()); + $this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE()); + $this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS()); $this->mapSimple(Ids::CHORUS_PLANT, fn() => Blocks::CHORUS_PLANT()); $this->mapSimple(Ids::CLAY, fn() => Blocks::CLAY()); $this->mapSimple(Ids::COAL_BLOCK, fn() => Blocks::COAL()); @@ -684,9 +723,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::CRACKED_DEEPSLATE_TILES, fn() => Blocks::CRACKED_DEEPSLATE_TILES()); $this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS()); $this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS()); + $this->mapSimple(Ids::CRACKED_STONE_BRICKS, fn() => Blocks::CRACKED_STONE_BRICKS()); $this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE()); $this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS()); $this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN()); + $this->mapSimple(Ids::CUT_RED_SANDSTONE, fn() => Blocks::CUT_RED_SANDSTONE()); + $this->mapSimple(Ids::CUT_SANDSTONE, fn() => Blocks::CUT_SANDSTONE()); + $this->mapSimple(Ids::DARK_PRISMARINE, fn() => Blocks::DARK_PRISMARINE()); $this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH()); $this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS()); $this->mapSimple(Ids::DEEPSLATE_COAL_ORE, fn() => Blocks::DEEPSLATE_COAL_ORE()); @@ -845,6 +888,12 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::HARDENED_CLAY, fn() => Blocks::HARDENED_CLAY()); $this->mapSimple(Ids::HONEYCOMB_BLOCK, fn() => Blocks::HONEYCOMB()); $this->mapSimple(Ids::ICE, fn() => Blocks::ICE()); + $this->mapSimple(Ids::INFESTED_CHISELED_STONE_BRICKS, fn() => Blocks::INFESTED_CHISELED_STONE_BRICK()); + $this->mapSimple(Ids::INFESTED_COBBLESTONE, fn() => Blocks::INFESTED_COBBLESTONE()); + $this->mapSimple(Ids::INFESTED_CRACKED_STONE_BRICKS, fn() => Blocks::INFESTED_CRACKED_STONE_BRICK()); + $this->mapSimple(Ids::INFESTED_MOSSY_STONE_BRICKS, fn() => Blocks::INFESTED_MOSSY_STONE_BRICK()); + $this->mapSimple(Ids::INFESTED_STONE, fn() => Blocks::INFESTED_STONE()); + $this->mapSimple(Ids::INFESTED_STONE_BRICKS, fn() => Blocks::INFESTED_STONE_BRICK()); $this->mapSimple(Ids::INFO_UPDATE, fn() => Blocks::INFO_UPDATE()); $this->mapSimple(Ids::INFO_UPDATE2, fn() => Blocks::INFO_UPDATE2()); $this->mapSimple(Ids::INVISIBLE_BEDROCK, fn() => Blocks::INVISIBLE_BEDROCK()); @@ -859,6 +908,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON()); $this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER()); $this->mapSimple(Ids::MOSSY_COBBLESTONE, fn() => Blocks::MOSSY_COBBLESTONE()); + $this->mapSimple(Ids::MOSSY_STONE_BRICKS, fn() => Blocks::MOSSY_STONE_BRICKS()); $this->mapSimple(Ids::MUD, fn() => Blocks::MUD()); $this->mapSimple(Ids::MUD_BRICKS, fn() => Blocks::MUD_BRICKS()); $this->mapSimple(Ids::MYCELIUM, fn() => Blocks::MYCELIUM()); @@ -880,6 +930,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::POLISHED_DEEPSLATE, fn() => Blocks::POLISHED_DEEPSLATE()); $this->mapSimple(Ids::POLISHED_DIORITE, fn() => Blocks::POLISHED_DIORITE()); $this->mapSimple(Ids::POLISHED_GRANITE, fn() => Blocks::POLISHED_GRANITE()); + $this->mapSimple(Ids::PRISMARINE, fn() => Blocks::PRISMARINE()); + $this->mapSimple(Ids::PRISMARINE_BRICKS, fn() => Blocks::PRISMARINE_BRICKS()); $this->mapSimple(Ids::QUARTZ_BRICKS, fn() => Blocks::QUARTZ_BRICKS()); $this->mapSimple(Ids::QUARTZ_ORE, fn() => Blocks::NETHER_QUARTZ_ORE()); $this->mapSimple(Ids::RAW_COPPER_BLOCK, fn() => Blocks::RAW_COPPER()); @@ -887,9 +939,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::RAW_IRON_BLOCK, fn() => Blocks::RAW_IRON()); $this->mapSimple(Ids::RED_MUSHROOM, fn() => Blocks::RED_MUSHROOM()); $this->mapSimple(Ids::RED_NETHER_BRICK, fn() => Blocks::RED_NETHER_BRICKS()); + $this->mapSimple(Ids::RED_SAND, fn() => Blocks::RED_SAND()); + $this->mapSimple(Ids::RED_SANDSTONE, fn() => Blocks::RED_SANDSTONE()); $this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE()); $this->mapSimple(Ids::REINFORCED_DEEPSLATE, fn() => Blocks::REINFORCED_DEEPSLATE()); $this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6()); + $this->mapSimple(Ids::SAND, fn() => Blocks::SAND()); + $this->mapSimple(Ids::SANDSTONE, fn() => Blocks::SANDSTONE()); $this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK()); $this->mapSimple(Ids::SEA_LANTERN, fn() => Blocks::SEA_LANTERN()); $this->mapSimple(Ids::SHORT_GRASS, fn() => Blocks::TALL_GRASS()); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( @@ -897,6 +953,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::SLIME, fn() => Blocks::SLIME()); $this->mapSimple(Ids::SMITHING_TABLE, fn() => Blocks::SMITHING_TABLE()); $this->mapSimple(Ids::SMOOTH_BASALT, fn() => Blocks::SMOOTH_BASALT()); + $this->mapSimple(Ids::SMOOTH_RED_SANDSTONE, fn() => Blocks::SMOOTH_RED_SANDSTONE()); + $this->mapSimple(Ids::SMOOTH_SANDSTONE, fn() => Blocks::SMOOTH_SANDSTONE()); $this->mapSimple(Ids::SMOOTH_STONE, fn() => Blocks::SMOOTH_STONE()); $this->mapSimple(Ids::SNOW, fn() => Blocks::SNOW()); $this->mapSimple(Ids::SOUL_SAND, fn() => Blocks::SOUL_SAND()); @@ -904,6 +962,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM()); $this->mapSimple(Ids::STONE, fn() => Blocks::STONE()); $this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER()); + $this->mapSimple(Ids::STONE_BRICKS, fn() => Blocks::STONE_BRICKS()); $this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS()); $this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER()); $this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF()); @@ -913,7 +972,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD()); $this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB()); $this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE()); - $this->mapSimple(Ids::YELLOW_FLOWER, fn() => Blocks::DANDELION()); + $this->mapSimple(Ids::DANDELION, fn() => Blocks::DANDELION()); $this->mapSimple(Ids::ALLIUM, fn() => Blocks::ALLIUM()); $this->mapSimple(Ids::CORNFLOWER, fn() => Blocks::CORNFLOWER()); @@ -939,16 +998,21 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setStage(AmethystCluster::STAGE_CLUSTER) ->setFacing($in->readBlockFace()); }); + $this->mapSlab(Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB, fn() => Blocks::ANDESITE_SLAB()); $this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS()); $this->map(Ids::ANVIL, function(Reader $in) : Block{ return Blocks::ANVIL() - ->setDamage(match($value = $in->readString(StateNames::DAMAGE)){ - StringValues::DAMAGE_UNDAMAGED => 0, - StringValues::DAMAGE_SLIGHTLY_DAMAGED => 1, - StringValues::DAMAGE_VERY_DAMAGED => 2, - StringValues::DAMAGE_BROKEN => 0, - default => throw $in->badValueException(StateNames::DAMAGE, $value), - }) + ->setDamage(Anvil::UNDAMAGED) + ->setFacing($in->readCardinalHorizontalFacing()); + }); + $this->map(Ids::CHIPPED_ANVIL, function(Reader $in) : Block{ + return Blocks::ANVIL() + ->setDamage(Anvil::SLIGHTLY_DAMAGED) + ->setFacing($in->readCardinalHorizontalFacing()); + }); + $this->map(Ids::DAMAGED_ANVIL, function(Reader $in) : Block{ + return Blocks::ANVIL() + ->setDamage(Anvil::VERY_DAMAGED) ->setFacing($in->readCardinalHorizontalFacing()); }); $this->map(Ids::BAMBOO, function(Reader $in) : Block{ @@ -1029,6 +1093,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setSlot(BrewingStandSlot::SOUTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT)) ->setSlot(BrewingStandSlot::NORTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT)); }); + $this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB()); $this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS()); $this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in)); $this->map(Ids::CACTUS, function(Reader $in) : Block{ @@ -1078,6 +1143,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $block; }); + $this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{ + return Blocks::CHISELED_QUARTZ() + ->setAxis($in->readPillarAxis()); + }); $this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{ return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){ StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(), @@ -1095,9 +1164,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::CHORUS_FLOWER() ->setAge($in->readBoundedInt(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE)); }); + $this->map(Ids::COARSE_DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE)); $this->mapSlab(Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::COBBLED_DEEPSLATE_SLAB()); $this->mapStairs(Ids::COBBLED_DEEPSLATE_STAIRS, fn() => Blocks::COBBLED_DEEPSLATE_STAIRS()); $this->map(Ids::COBBLED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLED_DEEPSLATE_WALL(), $in)); + $this->mapSlab(Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::COBBLESTONE_SLAB()); $this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::mapLegacyWallType($in)); $this->map(Ids::COCOA, function(Reader $in) : Block{ return Blocks::COCOA_POD() @@ -1118,15 +1189,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); $this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); $this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE)); - $this->map(Ids::CORAL_FAN_HANG, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in) - ->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::BRAIN : CoralType::TUBE)); - $this->map(Ids::CORAL_FAN_HANG2, fn(Reader $in) => Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in) - ->setCoralType($in->readBool(StateNames::CORAL_HANG_TYPE_BIT) ? CoralType::FIRE : CoralType::BUBBLE)); - $this->map(Ids::CORAL_FAN_HANG3, function(Reader $in) : Block{ - $in->ignored(StateNames::CORAL_HANG_TYPE_BIT); //the game always writes this, even though it's not used - return Helper::decodeWallCoralFan(Blocks::WALL_CORAL_FAN(), $in) - ->setCoralType(CoralType::HORN); - }); + $this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB()); + $this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB()); + $this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB()); $this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS()); $this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) ->setInverted(false)); @@ -1148,15 +1213,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setActivated($in->readBool(StateNames::RAIL_DATA_BIT)) ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); }); + $this->mapSlab(Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB, fn() => Blocks::DIORITE_SLAB()); $this->mapStairs(Ids::DIORITE_STAIRS, fn() => Blocks::DIORITE_STAIRS()); - $this->map(Ids::DIRT, function(Reader $in) : Block{ - return Blocks::DIRT() - ->setDirtType(match($value = $in->readString(StateNames::DIRT_TYPE)){ - StringValues::DIRT_TYPE_NORMAL => DirtType::NORMAL, - StringValues::DIRT_TYPE_COARSE => DirtType::COARSE, - default => throw $in->badValueException(StateNames::DIRT_TYPE, $value), - }); - }); + $this->map(Ids::DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL)); $this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); $this->map(Ids::LARGE_FERN, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LARGE_FERN(), $in)); $this->map(Ids::TALL_GRASS, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::DOUBLE_TALLGRASS(), $in)); @@ -1174,6 +1233,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::END_ROD() ->setFacing($in->readEndRodFacingDirection()); }); + $this->mapSlab(Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::END_STONE_BRICK_SLAB()); $this->map(Ids::ENDER_CHEST, function(Reader $in) : Block{ return Blocks::ENDER_CHEST() ->setFacing($in->readCardinalHorizontalFacing()); @@ -1213,6 +1273,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); }); + $this->mapSlab(Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB, fn() => Blocks::GRANITE_SLAB()); $this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS()); $this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{ $in->ignored(StateNames::DEPRECATED); @@ -1260,10 +1321,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ default => throw $in->badValueException(StateNames::LEVER_DIRECTION, $value), }); }); - $this->map(Ids::LIGHT_BLOCK, function(Reader $in) : Block{ - return Blocks::LIGHT() - ->setLightLevel($in->readBoundedInt(StateNames::BLOCK_LIGHT_LEVEL, Light::MIN_LIGHT_LEVEL, Light::MAX_LIGHT_LEVEL)); - }); $this->map(Ids::LIGHTNING_ROD, function(Reader $in) : Block{ return Blocks::LIGHTNING_ROD() ->setFacing($in->readFacingDirection()); @@ -1307,18 +1364,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing($in->readBlockFace()); }); $this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in)); - $this->map(Ids::MONSTER_EGG, function(Reader $in) : Block{ - return match($type = $in->readString(StateNames::MONSTER_EGG_STONE_TYPE)){ - StringValues::MONSTER_EGG_STONE_TYPE_CHISELED_STONE_BRICK => Blocks::INFESTED_CHISELED_STONE_BRICK(), - StringValues::MONSTER_EGG_STONE_TYPE_COBBLESTONE => Blocks::INFESTED_COBBLESTONE(), - StringValues::MONSTER_EGG_STONE_TYPE_CRACKED_STONE_BRICK => Blocks::INFESTED_CRACKED_STONE_BRICK(), - StringValues::MONSTER_EGG_STONE_TYPE_MOSSY_STONE_BRICK => Blocks::INFESTED_MOSSY_STONE_BRICK(), - StringValues::MONSTER_EGG_STONE_TYPE_STONE => Blocks::INFESTED_STONE(), - StringValues::MONSTER_EGG_STONE_TYPE_STONE_BRICK => Blocks::INFESTED_STONE_BRICK(), - default => throw $in->badValueException(StateNames::MONSTER_EGG_STONE_TYPE, $type), - }; - }); + $this->mapSlab(Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::MOSSY_COBBLESTONE_SLAB()); $this->mapStairs(Ids::MOSSY_COBBLESTONE_STAIRS, fn() => Blocks::MOSSY_COBBLESTONE_STAIRS()); + $this->mapSlab(Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::MOSSY_STONE_BRICK_SLAB()); $this->mapStairs(Ids::MOSSY_STONE_BRICK_STAIRS, fn() => Blocks::MOSSY_STONE_BRICK_STAIRS()); $this->mapSlab(Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB, fn() => Blocks::MUD_BRICK_SLAB()); $this->mapStairs(Ids::MUD_BRICK_STAIRS, fn() => Blocks::MUD_BRICK_STAIRS()); @@ -1327,11 +1375,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::MUDDY_MANGROVE_ROOTS() ->setAxis($in->readPillarAxis()); }); + $this->mapSlab(Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::NETHER_BRICK_SLAB()); $this->mapStairs(Ids::NETHER_BRICK_STAIRS, fn() => Blocks::NETHER_BRICK_STAIRS()); $this->map(Ids::NETHER_WART, function(Reader $in) : Block{ return Blocks::NETHER_WART() ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); }); + $this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB()); $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); $this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis())); $this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED)); @@ -1339,6 +1389,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED)); $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED)); $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis())); + $this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB()); $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ //Pink petals only uses 0-3, but GROWTH state can go up to 7 $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); @@ -1362,6 +1413,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::PITCHER_PLANT() ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); }); + $this->mapSlab(Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_ANDESITE_SLAB()); $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ return Blocks::POLISHED_BASALT() @@ -1378,7 +1430,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DEEPSLATE_SLAB()); $this->mapStairs(Ids::POLISHED_DEEPSLATE_STAIRS, fn() => Blocks::POLISHED_DEEPSLATE_STAIRS()); $this->map(Ids::POLISHED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_DEEPSLATE_WALL(), $in)); + $this->mapSlab(Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DIORITE_SLAB()); $this->mapStairs(Ids::POLISHED_DIORITE_STAIRS, fn() => Blocks::POLISHED_DIORITE_STAIRS()); + $this->mapSlab(Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_GRANITE_SLAB()); $this->mapStairs(Ids::POLISHED_GRANITE_STAIRS, fn() => Blocks::POLISHED_GRANITE_STAIRS()); $this->map(Ids::PORTAL, function(Reader $in) : Block{ return Blocks::NETHER_PORTAL() @@ -1393,15 +1447,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::POWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in)); $this->map(Ids::POWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in) ->setPowered(true)); - $this->map(Ids::PRISMARINE, function(Reader $in) : Block{ - return match($type = $in->readString(StateNames::PRISMARINE_BLOCK_TYPE)){ - StringValues::PRISMARINE_BLOCK_TYPE_BRICKS => Blocks::PRISMARINE_BRICKS(), - StringValues::PRISMARINE_BLOCK_TYPE_DARK => Blocks::DARK_PRISMARINE(), - StringValues::PRISMARINE_BLOCK_TYPE_DEFAULT => Blocks::PRISMARINE(), - default => throw $in->badValueException(StateNames::PRISMARINE_BLOCK_TYPE, $type), - }; - }); + $this->mapSlab(Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_BRICKS_SLAB()); $this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS()); + $this->mapSlab(Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_SLAB()); $this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS()); $this->map(Ids::PUMPKIN, function(Reader $in) : Block{ $in->ignored(StateNames::MC_CARDINAL_DIRECTION); //obsolete @@ -1422,39 +1470,26 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }; } }); + $this->mapSlab(Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB, fn() => Blocks::PURPUR_SLAB()); $this->mapStairs(Ids::PURPUR_STAIRS, fn() => Blocks::PURPUR_STAIRS()); - $this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Block{ - switch($type = $in->readString(StateNames::CHISEL_TYPE)){ - case StringValues::CHISEL_TYPE_CHISELED: - return Blocks::CHISELED_QUARTZ()->setAxis($in->readPillarAxis()); - case StringValues::CHISEL_TYPE_DEFAULT: - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::QUARTZ(); - case StringValues::CHISEL_TYPE_LINES: - return Blocks::QUARTZ_PILLAR()->setAxis($in->readPillarAxis()); - case StringValues::CHISEL_TYPE_SMOOTH: - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::SMOOTH_QUARTZ(); - default: - throw $in->badValueException(StateNames::CHISEL_TYPE, $type); - } + $this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Opaque{ + $in->ignored(StateNames::PILLAR_AXIS); + return Blocks::QUARTZ(); }); + $this->map(Ids::QUARTZ_PILLAR, function(Reader $in) : Block{ + return Blocks::QUARTZ_PILLAR() + ->setAxis($in->readPillarAxis()); + }); + $this->mapSlab(Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB, fn() => Blocks::QUARTZ_SLAB()); $this->mapStairs(Ids::QUARTZ_STAIRS, fn() => Blocks::QUARTZ_STAIRS()); $this->map(Ids::RAIL, function(Reader $in) : Block{ return Blocks::RAIL() ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 9)); }); $this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in)); + $this->mapSlab(Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::RED_NETHER_BRICK_SLAB()); $this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS()); - $this->map(Ids::RED_SANDSTONE, function(Reader $in) : Block{ - return match($type = $in->readString(StateNames::SAND_STONE_TYPE)){ - StringValues::SAND_STONE_TYPE_CUT => Blocks::CUT_RED_SANDSTONE(), - StringValues::SAND_STONE_TYPE_DEFAULT => Blocks::RED_SANDSTONE(), - StringValues::SAND_STONE_TYPE_HEIROGLYPHS => Blocks::CHISELED_RED_SANDSTONE(), - StringValues::SAND_STONE_TYPE_SMOOTH => Blocks::SMOOTH_RED_SANDSTONE(), - default => throw $in->badValueException(StateNames::SAND_STONE_TYPE, $type), - }; - }); + $this->mapSlab(Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::RED_SANDSTONE_SLAB()); $this->mapStairs(Ids::RED_SANDSTONE_STAIRS, fn() => Blocks::RED_SANDSTONE_STAIRS()); $this->map(Ids::REDSTONE_LAMP, function() : Block{ return Blocks::REDSTONE_LAMP() @@ -1477,22 +1512,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::SUGARCANE() ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); }); - $this->map(Ids::SAND, function(Reader $in) : Block{ - return match($value = $in->readString(StateNames::SAND_TYPE)){ - StringValues::SAND_TYPE_NORMAL => Blocks::SAND(), - StringValues::SAND_TYPE_RED => Blocks::RED_SAND(), - default => throw $in->badValueException(StateNames::SAND_TYPE, $value), - }; - }); - $this->map(Ids::SANDSTONE, function(Reader $in) : Block{ - return match($type = $in->readString(StateNames::SAND_STONE_TYPE)){ - StringValues::SAND_STONE_TYPE_CUT => Blocks::CUT_SANDSTONE(), - StringValues::SAND_STONE_TYPE_DEFAULT => Blocks::SANDSTONE(), - StringValues::SAND_STONE_TYPE_HEIROGLYPHS => Blocks::CHISELED_SANDSTONE(), - StringValues::SAND_STONE_TYPE_SMOOTH => Blocks::SMOOTH_SANDSTONE(), - default => throw $in->badValueException(StateNames::SAND_STONE_TYPE, $type), - }; - }); + $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); $this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{ return Blocks::SEA_PICKLE() @@ -1518,9 +1538,17 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing($in->readCardinalHorizontalFacing()) ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); }); + $this->map(Ids::SMOOTH_QUARTZ, function(Reader $in) : Block{ + $in->ignored(StateNames::PILLAR_AXIS); + return Blocks::SMOOTH_QUARTZ(); + }); + $this->mapSlab(Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB, fn() => Blocks::SMOOTH_QUARTZ_SLAB()); $this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS()); + $this->mapSlab(Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_RED_SANDSTONE_SLAB()); $this->mapStairs(Ids::SMOOTH_RED_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_RED_SANDSTONE_STAIRS()); + $this->mapSlab(Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_SANDSTONE_SLAB()); $this->mapStairs(Ids::SMOOTH_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_SANDSTONE_STAIRS()); + $this->mapSlab(Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_STONE_SLAB()); $this->map(Ids::SNOW_LAYER, function(Reader $in) : Block{ $in->ignored(StateNames::COVERED_BIT); //seems to be useless return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1); @@ -1548,35 +1576,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::BANNER() ->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15)); }); + $this->mapSlab(Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::STONE_BRICK_SLAB()); $this->mapStairs(Ids::STONE_BRICK_STAIRS, fn() => Blocks::STONE_BRICK_STAIRS()); $this->map(Ids::STONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::STONE_BUTTON(), $in)); $this->map(Ids::STONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::STONE_PRESSURE_PLATE(), $in)); - - //mess for partially flattened slabs - the single IDs were flattened but not the double ones - $this->map(Ids::DOUBLE_STONE_BLOCK_SLAB, fn(Reader $in) => Helper::decodeDoubleSlab(Helper::mapStoneSlab1Type($in), $in)); - $this->map(Ids::BRICK_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::BRICK_SLAB(), $in)); - $this->map(Ids::COBBLESTONE_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::COBBLESTONE_SLAB(), $in)); - $this->map(Ids::NETHER_BRICK_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::NETHER_BRICK_SLAB(), $in)); - $this->map(Ids::PETRIFIED_OAK_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::FAKE_WOODEN_SLAB(), $in)); - $this->map(Ids::QUARTZ_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::QUARTZ_SLAB(), $in)); - $this->map(Ids::SANDSTONE_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::SANDSTONE_SLAB(), $in)); - $this->map(Ids::SMOOTH_STONE_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::SMOOTH_STONE_SLAB(), $in)); - $this->map(Ids::STONE_BRICK_SLAB, fn(Reader $in) => Helper::decodeSingleSlab(Blocks::STONE_BRICK_SLAB(), $in)); - - $this->mapSlab(Ids::STONE_BLOCK_SLAB2, Ids::DOUBLE_STONE_BLOCK_SLAB2, fn(Reader $in) => Helper::mapStoneSlab2Type($in)); - $this->mapSlab(Ids::STONE_BLOCK_SLAB3, Ids::DOUBLE_STONE_BLOCK_SLAB3, fn(Reader $in) => Helper::mapStoneSlab3Type($in)); - $this->mapSlab(Ids::STONE_BLOCK_SLAB4, Ids::DOUBLE_STONE_BLOCK_SLAB4, fn(Reader $in) => Helper::mapStoneSlab4Type($in)); $this->mapStairs(Ids::STONE_STAIRS, fn() => Blocks::COBBLESTONE_STAIRS()); - $this->map(Ids::STONEBRICK, function(Reader $in) : Block{ - return match($type = $in->readString(StateNames::STONE_BRICK_TYPE)){ - StringValues::STONE_BRICK_TYPE_SMOOTH, //TODO: bug in vanilla - StringValues::STONE_BRICK_TYPE_DEFAULT => Blocks::STONE_BRICKS(), - StringValues::STONE_BRICK_TYPE_CHISELED => Blocks::CHISELED_STONE_BRICKS(), - StringValues::STONE_BRICK_TYPE_CRACKED => Blocks::CRACKED_STONE_BRICKS(), - StringValues::STONE_BRICK_TYPE_MOSSY => Blocks::MOSSY_STONE_BRICKS(), - default => throw $in->badValueException(StateNames::STONE_BRICK_TYPE, $type), - }; - }); $this->map(Ids::STONECUTTER_BLOCK, function(Reader $in) : Block{ return Blocks::STONECUTTER() ->setFacing($in->readCardinalHorizontalFacing()); diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index c2bc71caf..db6594c43 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -91,6 +91,7 @@ final class ItemTypeNames{ public const BROWN_DYE = "minecraft:brown_dye"; public const BRUSH = "minecraft:brush"; public const BUCKET = "minecraft:bucket"; + public const BUNDLE = "minecraft:bundle"; public const BURN_POTTERY_SHERD = "minecraft:burn_pottery_sherd"; public const CAKE = "minecraft:cake"; public const CAMEL_SPAWN_EGG = "minecraft:camel_spawn_egg"; @@ -160,6 +161,7 @@ final class ItemTypeNames{ public const DARK_OAK_DOOR = "minecraft:dark_oak_door"; public const DARK_OAK_HANGING_SIGN = "minecraft:dark_oak_hanging_sign"; public const DARK_OAK_SIGN = "minecraft:dark_oak_sign"; + public const DEBUG_STICK = "minecraft:debug_stick"; public const DIAMOND = "minecraft:diamond"; public const DIAMOND_AXE = "minecraft:diamond_axe"; public const DIAMOND_BOOTS = "minecraft:diamond_boots"; @@ -175,6 +177,10 @@ final class ItemTypeNames{ public const DOLPHIN_SPAWN_EGG = "minecraft:dolphin_spawn_egg"; public const DONKEY_SPAWN_EGG = "minecraft:donkey_spawn_egg"; public const DOUBLE_PLANT = "minecraft:double_plant"; + public const DOUBLE_STONE_BLOCK_SLAB = "minecraft:double_stone_block_slab"; + public const DOUBLE_STONE_BLOCK_SLAB2 = "minecraft:double_stone_block_slab2"; + public const DOUBLE_STONE_BLOCK_SLAB3 = "minecraft:double_stone_block_slab3"; + public const DOUBLE_STONE_BLOCK_SLAB4 = "minecraft:double_stone_block_slab4"; public const DRAGON_BREATH = "minecraft:dragon_breath"; public const DRIED_KELP = "minecraft:dried_kelp"; public const DROWNED_SPAWN_EGG = "minecraft:drowned_spawn_egg"; @@ -299,6 +305,7 @@ final class ItemTypeNames{ public const LEATHER_LEGGINGS = "minecraft:leather_leggings"; public const LEAVES = "minecraft:leaves"; public const LEAVES2 = "minecraft:leaves2"; + public const LIGHT_BLOCK = "minecraft:light_block"; public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye"; public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye"; public const LIME_DYE = "minecraft:lime_dye"; @@ -323,6 +330,7 @@ final class ItemTypeNames{ public const MINECART = "minecraft:minecart"; public const MINER_POTTERY_SHERD = "minecraft:miner_pottery_sherd"; public const MOJANG_BANNER_PATTERN = "minecraft:mojang_banner_pattern"; + public const MONSTER_EGG = "minecraft:monster_egg"; public const MOOSHROOM_SPAWN_EGG = "minecraft:mooshroom_spawn_egg"; public const MOURNER_POTTERY_SHERD = "minecraft:mourner_pottery_sherd"; public const MULE_SPAWN_EGG = "minecraft:mule_spawn_egg"; @@ -475,10 +483,14 @@ final class ItemTypeNames{ public const STICK = "minecraft:stick"; public const STONE_AXE = "minecraft:stone_axe"; public const STONE_BLOCK_SLAB = "minecraft:stone_block_slab"; + public const STONE_BLOCK_SLAB2 = "minecraft:stone_block_slab2"; + public const STONE_BLOCK_SLAB3 = "minecraft:stone_block_slab3"; + public const STONE_BLOCK_SLAB4 = "minecraft:stone_block_slab4"; public const STONE_HOE = "minecraft:stone_hoe"; public const STONE_PICKAXE = "minecraft:stone_pickaxe"; public const STONE_SHOVEL = "minecraft:stone_shovel"; public const STONE_SWORD = "minecraft:stone_sword"; + public const STONEBRICK = "minecraft:stonebrick"; public const STRAY_SPAWN_EGG = "minecraft:stray_spawn_egg"; public const STRIDER_SPAWN_EGG = "minecraft:strider_spawn_egg"; public const STRING = "minecraft:string"; diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 8c0d5b6e6..16898283a 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -500,14 +500,16 @@ class InventoryManager{ $this->session->sendDataPacket(InventorySlotPacket::create( $windowId, $netSlot, - new ItemStackWrapper(0, ItemStack::null()) + new ItemStackWrapper(0, ItemStack::null()), + 0 )); } //now send the real contents $this->session->sendDataPacket(InventorySlotPacket::create( $windowId, $netSlot, - $itemStackWrapper + $itemStackWrapper, + 0 )); } @@ -525,10 +527,11 @@ class InventoryManager{ */ $this->session->sendDataPacket(InventoryContentPacket::create( $windowId, - array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null())) + array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null())), + 0 )); //now send the real contents - $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers)); + $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, 0)); } public function syncSlot(Inventory $inventory, int $slot, ItemStack $itemStack) : void{ diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 218e10253..33a9303a3 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -743,7 +743,7 @@ class NetworkSession{ }else{ $translated = $message; } - $this->sendDataPacket(DisconnectPacket::create(0, $translated)); + $this->sendDataPacket(DisconnectPacket::create(0, $translated, "")); } /** diff --git a/src/network/mcpe/StandardEntityEventBroadcaster.php b/src/network/mcpe/StandardEntityEventBroadcaster.php index 6b8a83fe1..e2a707a3d 100644 --- a/src/network/mcpe/StandardEntityEventBroadcaster.php +++ b/src/network/mcpe/StandardEntityEventBroadcaster.php @@ -41,6 +41,7 @@ use pocketmine\network\mcpe\protocol\TakeItemActorPacket; use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute; use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use function array_map; @@ -131,7 +132,8 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{ ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())), ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())), ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())), - ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())) + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())), + new ItemStackWrapper(0, ItemStack::null()) )); } diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index a36ae9f40..a1d5e4812 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -112,10 +112,10 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ protected function getBuilderInventoryAndSlot(ItemStackRequestSlotInfo $info) : array{ - [$windowId, $slotId] = ItemStackContainerIdTranslator::translate($info->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $info->getSlotId()); + [$windowId, $slotId] = ItemStackContainerIdTranslator::translate($info->getContainerName()->getContainerId(), $this->inventoryManager->getCurrentWindowId(), $info->getSlotId()); $windowAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slotId); if($windowAndSlot === null){ - throw new ItemStackRequestProcessException("No open inventory matches container UI ID: " . $info->getContainerId() . ", slot ID: " . $info->getSlotId()); + throw new ItemStackRequestProcessException("No open inventory matches container UI ID: " . $info->getContainerName()->getContainerId() . ", slot ID: " . $info->getSlotId()); } [$inventory, $slot] = $windowAndSlot; if(!$inventory->slotExists($slot)){ @@ -142,7 +142,7 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ protected function removeItemFromSlot(ItemStackRequestSlotInfo $slotInfo, int $count) : Item{ - if($slotInfo->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $slotInfo->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ + if($slotInfo->getContainerName()->getContainerId() === ContainerUIIds::CREATED_OUTPUT && $slotInfo->getSlotId() === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){ //special case for the "created item" output slot //TODO: do we need to send a response for this slot info? return $this->takeCreatedItem($count); @@ -391,7 +391,7 @@ class ItemStackRequestExecutor{ public function buildItemStackResponse() : ItemStackResponse{ $builder = new ItemStackResponseBuilder($this->request->getRequestId(), $this->inventoryManager); foreach($this->requestSlotInfos as $requestInfo){ - $builder->addSlot($requestInfo->getContainerId(), $requestInfo->getSlotId()); + $builder->addSlot($requestInfo->getContainerName()->getContainerId(), $requestInfo->getSlotId()); } return $builder->build(); diff --git a/src/network/mcpe/handler/ItemStackResponseBuilder.php b/src/network/mcpe/handler/ItemStackResponseBuilder.php index 09af69f2a..a947eae72 100644 --- a/src/network/mcpe/handler/ItemStackResponseBuilder.php +++ b/src/network/mcpe/handler/ItemStackResponseBuilder.php @@ -27,6 +27,7 @@ use pocketmine\inventory\Inventory; use pocketmine\item\Durable; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds; +use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName; use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponse; use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseContainerInfo; use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponseSlotInfo; @@ -99,7 +100,7 @@ final class ItemStackResponseBuilder{ $responseContainerInfos = []; foreach($responseInfosByContainer as $containerInterfaceId => $responseInfos){ - $responseContainerInfos[] = new ItemStackResponseContainerInfo($containerInterfaceId, $responseInfos); + $responseContainerInfos[] = new ItemStackResponseContainerInfo(new FullContainerName($containerInterfaceId, 0), $responseInfos); } return new ItemStackResponse(ItemStackResponse::RESULT_OK, $this->requestId, $responseContainerInfos); diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 7bcc44c0b..709dadf97 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,12 +51,12 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 686; + public const CURRENT_STORAGE_NETWORK_VERSION = 712; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 2, //patch - 2, //revision + 20, //patch + 0, //revision 0 //is beta ]; From 9176b2494a4a84729f9cc4dd1f75dd3ec2c8618d Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 16 Aug 2024 07:54:59 -0500 Subject: [PATCH 015/290] Release 5.18.0 --- changelogs/5.18.md | 19 +++++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.18.md diff --git a/changelogs/5.18.md b/changelogs/5.18.md new file mode 100644 index 000000000..4b7911efe --- /dev/null +++ b/changelogs/5.18.md @@ -0,0 +1,19 @@ +# 5.18.0 +Released 16th August 2024. + +**For Minecraft: Bedrock Edition 1.21.20** + +This is a support release for Minecraft: Bedrock Edition 1.21.20. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.20. +- Removed support for earlier versions. + +## Fixes +- Use `VISIBLE_MOB_EFFECTS` actor metadata property to send effect bubbles, this fixes effect bubbles not showing diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 5162560bf..de21db380 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.17.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.18.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From e3baf3cddb74d5930331d5e8f993ff2076151e12 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 16 Aug 2024 07:56:01 -0500 Subject: [PATCH 016/290] 5.18.1 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index de21db380..b292b4c45 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.18.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.18.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From a7638cf914f6f0e982a9dd26d03072c9ba00ae8a Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 19 Aug 2024 19:02:17 +0100 Subject: [PATCH 017/290] Give an approving review to team member PRs automatically Branch protection rules currently require 2 approving reviews to merge a PR. What we really want is for 2 team members to be aware of every change. If a team member makes a PR, only one other approval should be needed. Since GitHub doesn't currently allow us to set different review thresholds for different users/teams, sending an automatic approval via GitHub Actions is the next best thing. This should reduce friction of team development work. --- .github/workflows/team-pr-auto-approve.yml | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/team-pr-auto-approve.yml diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml new file mode 100644 index 000000000..a582be328 --- /dev/null +++ b/.github/workflows/team-pr-auto-approve.yml @@ -0,0 +1,42 @@ +#Due to GitHub awkwardness, it's not easy to reduce the review requirement for collaborators. +#Our policy is that 2 collaborators should be aware of every change. +#For outside PRs, this means 2 collaborator reviews. +#For PRs made by collaborators, this means 1 reviewer + the author. +#We trust that collaborators don't need as much oversight. +name: Auto approve collaborator PRs + +on: + pull_request_target: + types: + - opened + - synchronize + - reopened + - ready_for_review + +permissions: + pull-requests: write + +jobs: + approve: + name: Auto approve + runs-on: ubuntu-latest + + steps: + - name: Check if PR author has write access + id: check-permission + uses: actions-cool/check-user-permission@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + require: write + username: ${{ github.event.pull_request.user.login }} + #technically this would be fine for dependabot but generally bots don't count as team members + check-bot: true + + #TODO: Some way to avoid unnecessary repeated reviews would be nice here + + - name: Approve PR if authorized + if: steps.check-permission.outputs.require-result == 'true' && steps.check-permission.outputs.check-result == 'false' + uses: juliangruber/approve-pull-request-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + number: ${{ github.event.pull_request.number }} From e6f9cdd990465d9269d61e51571d6cc965eabb57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:12:06 +0000 Subject: [PATCH 018/290] Bump docker/build-push-action from 6.6.1 to 6.7.0 (#6432) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 4f045bdc9..437ed963f 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: push: true context: ./pocketmine-mp From bdbcfd10cccf30c9885fd2dbe417331cc0959966 Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:52:51 +0200 Subject: [PATCH 019/290] Add ShellCheck (#6407) Co-authored-by: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> --- .github/workflows/main.yml | 12 ++++++++++++ start.sh | 2 +- tests/travis.sh | 7 +++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c92c0b8b..b5a9740b5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,3 +37,15 @@ jobs: - name: Run PHP-CS-Fixer run: php-cs-fixer fix --dry-run --diff --ansi + + shellcheck: + name: ShellCheck + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v4 + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@2.0.0 diff --git a/start.sh b/start.sh index 0121f3887..4d4787114 100755 --- a/start.sh +++ b/start.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash DIR="$(cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" -cd "$DIR" +cd "$DIR" || { echo "Couldn't change directory to $DIR"; exit 1; } while getopts "p:f:l" OPTION 2> /dev/null; do case ${OPTION} in diff --git a/tests/travis.sh b/tests/travis.sh index 094f65905..a4674fb6f 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -7,6 +7,9 @@ while getopts "t:" OPTION 2> /dev/null; do t) PM_WORKERS="$OPTARG" ;; + \?) + break + ;; esac done @@ -19,7 +22,7 @@ rm PocketMine-MP.phar 2> /dev/null mkdir "$DATA_DIR" mkdir "$PLUGINS_DIR" -cd tests/plugins/DevTools +cd tests/plugins/DevTools || { echo "Couldn't change directory to $DIR"; exit 1; } php -dphar.readonly=0 ./src/ConsoleScript.php --make ./ --relative ./ --out "$PLUGINS_DIR/DevTools.phar" cd ../../.. composer make-server @@ -45,7 +48,7 @@ if [ "$result" != "" ]; then echo "$result" echo Some tests did not complete successfully, changing build status to failed exit 1 -elif [ $(grep -c "ERROR\|CRITICAL\|EMERGENCY" "$DATA_DIR/server.log") -ne 0 ]; then +elif [ "$(grep -c "ERROR\|CRITICAL\|EMERGENCY" "$DATA_DIR/server.log")" -ne 0 ]; then echo Server log contains error messages, changing build status to failed exit 1 else From ede363eb0fd98b51b5da307c79339e7de68e62d8 Mon Sep 17 00:00:00 2001 From: Dries C Date: Thu, 22 Aug 2024 22:53:21 +0200 Subject: [PATCH 020/290] Fix shift crafting (#6433) This field was added to the action in 1.21.20. Previously, the client would behave as if clicking the crafting result slot many times. Now it behaves more like recipe book shift-clicking. --- src/network/mcpe/handler/ItemStackRequestExecutor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index a1d5e4812..54a192590 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -343,7 +343,7 @@ class ItemStackRequestExecutor{ $this->setNextCreatedItem($window->getOutput($optionId)); } }else{ - $this->beginCrafting($action->getRecipeId(), 1); + $this->beginCrafting($action->getRecipeId(), $action->getRepetitions()); } }elseif($action instanceof CraftRecipeAutoStackRequestAction){ $this->beginCrafting($action->getRecipeId(), $action->getRepetitions()); From 281afb683813f2cdf037be6e7943551c8d0fe05c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:05:11 +0000 Subject: [PATCH 021/290] Bump phpstan/phpstan in the development-patch-updates group (#6435) --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 5d1167b6d..41eaccc75 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "1.11.10", + "phpstan/phpstan": "1.11.11", "phpstan/phpstan-phpunit": "^1.1.0", "phpstan/phpstan-strict-rules": "^1.2.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index 5eb9a2158..c19c5b8ad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fab1e131dfd049da39a87d4e562f1870", + "content-hash": "85c407770e0f2a1bda7c8e23faf2026e", "packages": [ { "name": "adhocore/json-comment", @@ -1389,16 +1389,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.11.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", "shasum": "" }, "require": { @@ -1443,7 +1443,7 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-08-19T14:37:29+00:00" }, { "name": "phpstan/phpstan-phpunit", From 9381fc4172e5dce4cada1cb356050c8a2ab57b94 Mon Sep 17 00:00:00 2001 From: "Vega Nicholas S." <142091702+nicholass003@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:33:11 +0700 Subject: [PATCH 022/290] Blue Ice: No longer emits light & it's now dropped when mined with a tool with silk touch enchantment (#6438) --- src/block/BlueIce.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/block/BlueIce.php b/src/block/BlueIce.php index f7e9a0404..11e3498dd 100644 --- a/src/block/BlueIce.php +++ b/src/block/BlueIce.php @@ -27,10 +27,6 @@ use pocketmine\item\Item; class BlueIce extends Opaque{ - public function getLightLevel() : int{ - return 1; - } - public function getFrictionFactor() : float{ return 0.99; } @@ -38,4 +34,8 @@ class BlueIce extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return []; } + + public function isAffectedBySilkTouch() : bool{ + return true; + } } From e9b597af6c06ed07a7dc844f38bd32ae9ecf2b6f Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 2 Sep 2024 11:24:42 -0500 Subject: [PATCH 023/290] Release 5.18.1 --- changelogs/5.18.md | 11 +++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/changelogs/5.18.md b/changelogs/5.18.md index 4b7911efe..fdf222d39 100644 --- a/changelogs/5.18.md +++ b/changelogs/5.18.md @@ -17,3 +17,14 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Fixes - Use `VISIBLE_MOB_EFFECTS` actor metadata property to send effect bubbles, this fixes effect bubbles not showing + +# 5.18.1 +Released 2nd September 2024. + +## Fixes +- Fixed shift-crafting. +- Blue Ice block no longer emits light & it's now dropped when mined with a tool with silk touch enchantment. + +## Internals +- Pull Requests from team members now get an approval automatically. This means that if a team member makes a PR, only one other approval should be needed. +- Added [ShellCheck](https://github.com/koalaman/shellcheck) to the CI tests. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b292b4c45..0e3c30655 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.18.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 2a7b183ab826f6e75859636cb0b5d0c798196953 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 2 Sep 2024 11:25:50 -0500 Subject: [PATCH 024/290] 5.18.2 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 0e3c30655..571dd9638 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.18.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.18.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 8cb2e577a1aca3f99d6504e48f34cbe75249c2ae Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 3 Sep 2024 02:02:06 +0300 Subject: [PATCH 025/290] Implement missing last interacted slot property in chiseled bookshelf (#6440) --- src/block/ChiseledBookshelf.php | 40 ++++++++++++++++++++++++++++ src/block/tile/ChiseledBookshelf.php | 22 +++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/block/ChiseledBookshelf.php b/src/block/ChiseledBookshelf.php index 89340a8f3..73c4861bf 100644 --- a/src/block/ChiseledBookshelf.php +++ b/src/block/ChiseledBookshelf.php @@ -48,11 +48,32 @@ class ChiseledBookshelf extends Opaque{ */ private array $slots = []; + private ?ChiseledBookshelfSlot $lastInteractedSlot = null; + protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ $w->horizontalFacing($this->facing); $w->enumSet($this->slots, ChiseledBookshelfSlot::cases()); } + public function readStateFromWorld() : Block{ + $tile = $this->position->getWorld()->getTile($this->position); + if($tile instanceof TileChiseledBookshelf){ + $this->lastInteractedSlot = $tile->getLastInteractedSlot(); + }else{ + $this->lastInteractedSlot = null; + } + return $this; + } + + public function writeStateToWorld() : void{ + parent::writeStateToWorld(); + + $tile = $this->position->getWorld()->getTile($this->position); + if($tile instanceof TileChiseledBookshelf){ + $tile->setLastInteractedSlot($this->lastInteractedSlot); + } + } + /** * Returns whether the given slot is displayed as occupied. * This doesn't guarantee that there is or isn't a book in the bookshelf's inventory. @@ -92,6 +113,23 @@ class ChiseledBookshelf extends Opaque{ return $this->slots; } + /** + * Returns the last slot interacted by a player or null if no slot has been interacted with yet. + */ + public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{ + return $this->lastInteractedSlot; + } + + /** + * Sets the last slot interacted by a player. + * + * @return $this + */ + public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : self{ + $this->lastInteractedSlot = $lastInteractedSlot; + return $this; + } + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($face !== $this->facing){ return false; @@ -112,10 +150,12 @@ class ChiseledBookshelf extends Opaque{ $returnedItems[] = $inventory->getItem($slot->value); $inventory->clear($slot->value); $this->setSlot($slot, false); + $this->lastInteractedSlot = $slot; }elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){ //TODO: type tags like blocks would be better for this $inventory->setItem($slot->value, $item->pop()); $this->setSlot($slot, true); + $this->lastInteractedSlot = $slot; }else{ return true; } diff --git a/src/block/tile/ChiseledBookshelf.php b/src/block/tile/ChiseledBookshelf.php index 6455208fe..06175e27f 100644 --- a/src/block/tile/ChiseledBookshelf.php +++ b/src/block/tile/ChiseledBookshelf.php @@ -40,8 +40,12 @@ use function count; class ChiseledBookshelf extends Tile implements Container{ use ContainerTrait; + private const TAG_LAST_INTERACTED_SLOT = "LastInteractedSlot"; //TAG_Int + private SimpleInventory $inventory; + private ?ChiseledBookshelfSlot $lastInteractedSlot = null; + public function __construct(World $world, Vector3 $pos){ parent::__construct($world, $pos); $this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases())); @@ -55,12 +59,30 @@ class ChiseledBookshelf extends Tile implements Container{ return $this->inventory; } + public function getLastInteractedSlot() : ?ChiseledBookshelfSlot{ + return $this->lastInteractedSlot; + } + + public function setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : void{ + $this->lastInteractedSlot = $lastInteractedSlot; + } + public function readSaveData(CompoundTag $nbt) : void{ $this->loadItems($nbt); + + $lastInteractedSlot = $nbt->getInt(self::TAG_LAST_INTERACTED_SLOT, 0); + if($lastInteractedSlot !== 0){ + $this->lastInteractedSlot = ChiseledBookshelfSlot::tryFrom($lastInteractedSlot - 1); + } } protected function writeSaveData(CompoundTag $nbt) : void{ $this->saveItems($nbt); + + $nbt->setInt(self::TAG_LAST_INTERACTED_SLOT, $this->lastInteractedSlot !== null ? + $this->lastInteractedSlot->value + 1 : + 0 + ); } protected function loadItems(CompoundTag $tag) : void{ From 72d941fc1bbbbf9999eb89c8104ad709dc464b17 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 3 Sep 2024 11:33:05 +0100 Subject: [PATCH 026/290] Update 5.18.md --- changelogs/5.18.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.18.md b/changelogs/5.18.md index fdf222d39..35aa237af 100644 --- a/changelogs/5.18.md +++ b/changelogs/5.18.md @@ -19,7 +19,7 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - Use `VISIBLE_MOB_EFFECTS` actor metadata property to send effect bubbles, this fixes effect bubbles not showing # 5.18.1 -Released 2nd September 2024. +Released 3rd September 2024. ## Fixes - Fixed shift-crafting. From f6e2a1ecce9a78ab1cb59d3a73472da657f3bea6 Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Mon, 9 Sep 2024 09:48:38 +0200 Subject: [PATCH 027/290] Validate transaction slots (#6304) --- src/inventory/ArmorInventory.php | 23 ++++++++++ src/inventory/BaseInventory.php | 11 ++++- src/inventory/SlotValidatedInventory.php | 46 +++++++++++++++++++ .../transaction/action/SlotChangeAction.php | 9 ++++ .../validator/CallbackSlotValidator.php | 44 ++++++++++++++++++ .../action/validator/SlotValidator.php | 38 +++++++++++++++ 6 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 src/inventory/SlotValidatedInventory.php create mode 100644 src/inventory/transaction/action/validator/CallbackSlotValidator.php create mode 100644 src/inventory/transaction/action/validator/SlotValidator.php diff --git a/src/inventory/ArmorInventory.php b/src/inventory/ArmorInventory.php index dcb3c04cb..0b3ae5b7b 100644 --- a/src/inventory/ArmorInventory.php +++ b/src/inventory/ArmorInventory.php @@ -23,8 +23,13 @@ declare(strict_types=1); namespace pocketmine\inventory; +use pocketmine\block\BlockTypeIds; use pocketmine\entity\Living; +use pocketmine\inventory\transaction\action\validator\CallbackSlotValidator; +use pocketmine\inventory\transaction\TransactionValidationException; +use pocketmine\item\Armor; use pocketmine\item\Item; +use pocketmine\item\ItemBlock; class ArmorInventory extends SimpleInventory{ public const SLOT_HEAD = 0; @@ -36,6 +41,8 @@ class ArmorInventory extends SimpleInventory{ protected Living $holder ){ parent::__construct(4); + + $this->validators->add(new CallbackSlotValidator($this->validate(...))); } public function getHolder() : Living{ @@ -73,4 +80,20 @@ class ArmorInventory extends SimpleInventory{ public function setBoots(Item $boots) : void{ $this->setItem(self::SLOT_FEET, $boots); } + + private function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ + if($item instanceof Armor){ + if($item->getArmorSlot() !== $slot){ + return new TransactionValidationException("Armor item is in wrong slot"); + } + }else{ + if(!($slot === ArmorInventory::SLOT_HEAD && $item instanceof ItemBlock && ( + $item->getBlock()->getTypeId() === BlockTypeIds::CARVED_PUMPKIN || + $item->getBlock()->getTypeId() === BlockTypeIds::MOB_HEAD + ))){ + return new TransactionValidationException("Item is not accepted in an armor slot"); + } + } + return null; + } } diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php index 254e44b1e..522c827a4 100644 --- a/src/inventory/BaseInventory.php +++ b/src/inventory/BaseInventory.php @@ -36,8 +36,10 @@ use function spl_object_id; /** * This class provides everything needed to implement an inventory, minus the underlying storage system. + * + * @phpstan-import-type SlotValidators from SlotValidatedInventory */ -abstract class BaseInventory implements Inventory{ +abstract class BaseInventory implements Inventory, SlotValidatedInventory{ protected int $maxStackSize = Inventory::MAX_STACK; /** @var Player[] */ protected array $viewers = []; @@ -46,9 +48,12 @@ abstract class BaseInventory implements Inventory{ * @phpstan-var ObjectSet */ protected ObjectSet $listeners; + /** @phpstan-var SlotValidators */ + protected ObjectSet $validators; public function __construct(){ $this->listeners = new ObjectSet(); + $this->validators = new ObjectSet(); } public function getMaxStackSize() : int{ @@ -398,4 +403,8 @@ abstract class BaseInventory implements Inventory{ public function getListeners() : ObjectSet{ return $this->listeners; } + + public function getSlotValidators() : ObjectSet{ + return $this->validators; + } } diff --git a/src/inventory/SlotValidatedInventory.php b/src/inventory/SlotValidatedInventory.php new file mode 100644 index 000000000..f30ebf8a0 --- /dev/null +++ b/src/inventory/SlotValidatedInventory.php @@ -0,0 +1,46 @@ + + */ +interface SlotValidatedInventory{ + /** + * Returns a set of validators that will be used to determine whether an item can be placed in a particular slot. + * All validators need to return null for the transaction to be allowed. + * If one of the validators returns an exception, the transaction will be cancelled. + * + * There is no guarantee that the validators will be called in any particular order. + * + * @phpstan-return SlotValidators + */ + public function getSlotValidators() : ObjectSet; +} diff --git a/src/inventory/transaction/action/SlotChangeAction.php b/src/inventory/transaction/action/SlotChangeAction.php index 453f0c4d2..68c3dba1b 100644 --- a/src/inventory/transaction/action/SlotChangeAction.php +++ b/src/inventory/transaction/action/SlotChangeAction.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\inventory\transaction\action; use pocketmine\inventory\Inventory; +use pocketmine\inventory\SlotValidatedInventory; use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\Item; @@ -74,6 +75,14 @@ class SlotChangeAction extends InventoryAction{ if($this->targetItem->getCount() > $this->inventory->getMaxStackSize()){ throw new TransactionValidationException("Target item exceeds inventory max stack size"); } + if($this->inventory instanceof SlotValidatedInventory && !$this->targetItem->isNull()){ + foreach($this->inventory->getSlotValidators() as $validator){ + $ret = $validator->validate($this->inventory, $this->targetItem, $this->inventorySlot); + if($ret !== null){ + throw new TransactionValidationException("Target item is not accepted by the inventory at slot #" . $this->inventorySlot . ": " . $ret->getMessage(), 0, $ret); + } + } + } } /** diff --git a/src/inventory/transaction/action/validator/CallbackSlotValidator.php b/src/inventory/transaction/action/validator/CallbackSlotValidator.php new file mode 100644 index 000000000..1670dc623 --- /dev/null +++ b/src/inventory/transaction/action/validator/CallbackSlotValidator.php @@ -0,0 +1,44 @@ +validate)($inventory, $item, $slot); + } +} diff --git a/src/inventory/transaction/action/validator/SlotValidator.php b/src/inventory/transaction/action/validator/SlotValidator.php new file mode 100644 index 000000000..1b78c91f7 --- /dev/null +++ b/src/inventory/transaction/action/validator/SlotValidator.php @@ -0,0 +1,38 @@ + Date: Sat, 21 Sep 2024 01:47:25 +0200 Subject: [PATCH 028/290] Assemble 1.21.30 (#6453) --- composer.json | 8 +- composer.lock | 52 ++++++------ src/data/bedrock/block/BlockStateData.php | 4 +- src/data/bedrock/block/BlockStateNames.php | 7 -- .../bedrock/block/BlockStateStringValues.php | 31 ------- src/data/bedrock/block/BlockTypeNames.php | 29 ++++++- .../convert/BlockObjectToStateSerializer.php | 66 +++++++-------- .../convert/BlockStateDeserializerHelper.php | 30 ------- .../convert/BlockStateSerializerHelper.php | 11 +-- .../BlockStateToObjectDeserializer.php | 81 ++++++++++--------- src/data/bedrock/item/ItemTypeNames.php | 3 + src/network/mcpe/InventoryManager.php | 14 ++-- src/network/mcpe/NetworkSession.php | 2 +- .../mcpe/StandardEntityEventBroadcaster.php | 13 ++- .../mcpe/handler/ItemStackResponseBuilder.php | 2 +- .../handler/ResourcePacksPacketHandler.php | 2 - src/world/format/io/data/BedrockWorldData.php | 6 +- 17 files changed, 156 insertions(+), 205 deletions(-) diff --git a/composer.json b/composer.json index 41eaccc75..0b9e8ec6b 100644 --- a/composer.json +++ b/composer.json @@ -33,10 +33,10 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", - "pocketmine/bedrock-block-upgrade-schema": "~4.3.0+bedrock-1.21.20", - "pocketmine/bedrock-data": "~2.12.0+bedrock-1.21.20", - "pocketmine/bedrock-item-upgrade-schema": "~1.11.0+bedrock-1.21.20", - "pocketmine/bedrock-protocol": "~33.0.0+bedrock-1.21.20", + "pocketmine/bedrock-block-upgrade-schema": "~4.4.0+bedrock-1.21.30", + "pocketmine/bedrock-data": "~2.13.0+bedrock-1.21.30", + "pocketmine/bedrock-item-upgrade-schema": "~1.12.0+bedrock-1.21.30", + "pocketmine/bedrock-protocol": "~34.0.0+bedrock-1.21.30", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index c19c5b8ad..d023c74ac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "85c407770e0f2a1bda7c8e23faf2026e", + "content-hash": "e16d3ebe48e32bbf96348981249c0ac1", "packages": [ { "name": "adhocore/json-comment", @@ -127,16 +127,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "4.3.0", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "53d3a41c37ce90d58b33130cdadad08e442d7c47" + "reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/53d3a41c37ce90d58b33130cdadad08e442d7c47", - "reference": "53d3a41c37ce90d58b33130cdadad08e442d7c47", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/89e5f6e19c29e0d0d24835639f72a5ef157c2761", + "reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761", "shasum": "" }, "type": "library", @@ -147,22 +147,22 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.3.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.4.0" }, - "time": "2024-08-13T18:04:27+00:00" + "time": "2024-09-17T16:06:36+00:00" }, { "name": "pocketmine/bedrock-data", - "version": "2.12.0+bedrock-1.21.20", + "version": "2.13.0+bedrock-1.21.30", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "d4ee3d08964fa16fbbdd04af1fb52bbde540b665" + "reference": "23d9356b866654cbd2a62b31373118bedb4a2562" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/d4ee3d08964fa16fbbdd04af1fb52bbde540b665", - "reference": "d4ee3d08964fa16fbbdd04af1fb52bbde540b665", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/23d9356b866654cbd2a62b31373118bedb4a2562", + "reference": "23d9356b866654cbd2a62b31373118bedb4a2562", "shasum": "" }, "type": "library", @@ -173,22 +173,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.20" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.30" }, - "time": "2024-08-15T12:50:26+00:00" + "time": "2024-09-17T16:03:14+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "35c18d093fc2b12da8737b2edb2c3ad6a14a53dd" + "reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/35c18d093fc2b12da8737b2edb2c3ad6a14a53dd", - "reference": "35c18d093fc2b12da8737b2edb2c3ad6a14a53dd", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/85a0014c7dfd4a25c22a9efb0b447afb7dc6c409", + "reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409", "shasum": "" }, "type": "library", @@ -199,22 +199,22 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.11.0" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.12.0" }, - "time": "2024-08-13T18:06:25+00:00" + "time": "2024-09-11T19:48:31+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "33.0.0+bedrock-1.21.20", + "version": "34.0.0+bedrock-1.21.30", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "e2264137c5cd0522de2c6ee4921a3a803818ea32" + "reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/e2264137c5cd0522de2c6ee4921a3a803818ea32", - "reference": "e2264137c5cd0522de2c6ee4921a3a803818ea32", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/440c8078c66cc2a8f2abf58468df7df7246ee33b", + "reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b", "shasum": "" }, "require": { @@ -227,7 +227,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.11.2", + "phpstan/phpstan": "1.11.9", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5 || ^10.0" @@ -245,9 +245,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/32.2.0+bedrock-1.21.2" + "source": "https://github.com/pmmp/BedrockProtocol/tree/34.0.0+bedrock-1.21.30" }, - "time": "2024-08-10T19:23:18+00:00" + "time": "2024-09-18T20:58:42+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/data/bedrock/block/BlockStateData.php b/src/data/bedrock/block/BlockStateData.php index 1973b55af..f405e4cf6 100644 --- a/src/data/bedrock/block/BlockStateData.php +++ b/src/data/bedrock/block/BlockStateData.php @@ -45,8 +45,8 @@ final class BlockStateData{ public const CURRENT_VERSION = (1 << 24) | //major (21 << 16) | //minor - (20 << 8) | //patch - (6); //revision + (30 << 8) | //patch + (7); //revision public const TAG_NAME = "name"; public const TAG_STATES = "states"; diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index e9c33bee2..c54822671 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -34,7 +34,6 @@ final class BlockStateNames{ public const ACTIVE = "active"; public const AGE = "age"; public const AGE_BIT = "age_bit"; - public const ALLOW_UNDERWATER_BIT = "allow_underwater_bit"; public const ATTACHED_BIT = "attached_bit"; public const ATTACHMENT = "attachment"; public const BAMBOO_LEAF_SIZE = "bamboo_leaf_size"; @@ -52,10 +51,7 @@ final class BlockStateNames{ public const CAN_SUMMON = "can_summon"; public const CANDLES = "candles"; public const CAULDRON_LIQUID = "cauldron_liquid"; - public const CHEMISTRY_TABLE_TYPE = "chemistry_table_type"; - public const CHISEL_TYPE = "chisel_type"; public const CLUSTER_COUNT = "cluster_count"; - public const COLOR_BIT = "color_bit"; public const COMPOSTER_FILL_LEVEL = "composter_fill_level"; public const CONDITIONAL_BIT = "conditional_bit"; public const CORAL_DIRECTION = "coral_direction"; @@ -116,12 +112,10 @@ final class BlockStateNames{ public const ROTATION = "rotation"; public const SCULK_SENSOR_PHASE = "sculk_sensor_phase"; public const SEA_GRASS_TYPE = "sea_grass_type"; - public const SPONGE_TYPE = "sponge_type"; public const STABILITY = "stability"; public const STABILITY_CHECK = "stability_check"; public const STRIPPED_BIT = "stripped_bit"; public const STRUCTURE_BLOCK_TYPE = "structure_block_type"; - public const STRUCTURE_VOID_TYPE = "structure_void_type"; public const SUSPENDED_BIT = "suspended_bit"; public const TOGGLE_BIT = "toggle_bit"; public const TORCH_FACING_DIRECTION = "torch_facing_direction"; @@ -134,7 +128,6 @@ final class BlockStateNames{ public const UPSIDE_DOWN_BIT = "upside_down_bit"; public const VAULT_STATE = "vault_state"; public const VINE_DIRECTION_BITS = "vine_direction_bits"; - public const WALL_BLOCK_TYPE = "wall_block_type"; public const WALL_CONNECTION_TYPE_EAST = "wall_connection_type_east"; public const WALL_CONNECTION_TYPE_NORTH = "wall_connection_type_north"; public const WALL_CONNECTION_TYPE_SOUTH = "wall_connection_type_south"; diff --git a/src/data/bedrock/block/BlockStateStringValues.php b/src/data/bedrock/block/BlockStateStringValues.php index 1794e240d..9dfdcfb63 100644 --- a/src/data/bedrock/block/BlockStateStringValues.php +++ b/src/data/bedrock/block/BlockStateStringValues.php @@ -52,16 +52,6 @@ final class BlockStateStringValues{ public const CAULDRON_LIQUID_POWDER_SNOW = "powder_snow"; public const CAULDRON_LIQUID_WATER = "water"; - public const CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR = "compound_creator"; - public const CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR = "element_constructor"; - public const CHEMISTRY_TABLE_TYPE_LAB_TABLE = "lab_table"; - public const CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER = "material_reducer"; - - public const CHISEL_TYPE_CHISELED = "chiseled"; - public const CHISEL_TYPE_DEFAULT = "default"; - public const CHISEL_TYPE_LINES = "lines"; - public const CHISEL_TYPE_SMOOTH = "smooth"; - public const CRACKED_STATE_CRACKED = "cracked"; public const CRACKED_STATE_MAX_CRACKED = "max_cracked"; public const CRACKED_STATE_NO_CRACKS = "no_cracks"; @@ -128,9 +118,6 @@ final class BlockStateStringValues{ public const SEA_GRASS_TYPE_DOUBLE_BOT = "double_bot"; public const SEA_GRASS_TYPE_DOUBLE_TOP = "double_top"; - public const SPONGE_TYPE_DRY = "dry"; - public const SPONGE_TYPE_WET = "wet"; - public const STRUCTURE_BLOCK_TYPE_CORNER = "corner"; public const STRUCTURE_BLOCK_TYPE_DATA = "data"; public const STRUCTURE_BLOCK_TYPE_EXPORT = "export"; @@ -138,9 +125,6 @@ final class BlockStateStringValues{ public const STRUCTURE_BLOCK_TYPE_LOAD = "load"; public const STRUCTURE_BLOCK_TYPE_SAVE = "save"; - public const STRUCTURE_VOID_TYPE_AIR = "air"; - public const STRUCTURE_VOID_TYPE_VOID = "void"; - public const TORCH_FACING_DIRECTION_EAST = "east"; public const TORCH_FACING_DIRECTION_NORTH = "north"; public const TORCH_FACING_DIRECTION_SOUTH = "south"; @@ -158,21 +142,6 @@ final class BlockStateStringValues{ public const VAULT_STATE_INACTIVE = "inactive"; public const VAULT_STATE_UNLOCKING = "unlocking"; - public const WALL_BLOCK_TYPE_ANDESITE = "andesite"; - public const WALL_BLOCK_TYPE_BRICK = "brick"; - public const WALL_BLOCK_TYPE_COBBLESTONE = "cobblestone"; - public const WALL_BLOCK_TYPE_DIORITE = "diorite"; - public const WALL_BLOCK_TYPE_END_BRICK = "end_brick"; - public const WALL_BLOCK_TYPE_GRANITE = "granite"; - public const WALL_BLOCK_TYPE_MOSSY_COBBLESTONE = "mossy_cobblestone"; - public const WALL_BLOCK_TYPE_MOSSY_STONE_BRICK = "mossy_stone_brick"; - public const WALL_BLOCK_TYPE_NETHER_BRICK = "nether_brick"; - public const WALL_BLOCK_TYPE_PRISMARINE = "prismarine"; - public const WALL_BLOCK_TYPE_RED_NETHER_BRICK = "red_nether_brick"; - public const WALL_BLOCK_TYPE_RED_SANDSTONE = "red_sandstone"; - public const WALL_BLOCK_TYPE_SANDSTONE = "sandstone"; - public const WALL_BLOCK_TYPE_STONE_BRICK = "stone_brick"; - public const WALL_CONNECTION_TYPE_EAST_NONE = "none"; public const WALL_CONNECTION_TYPE_EAST_SHORT = "short"; public const WALL_CONNECTION_TYPE_EAST_TALL = "tall"; diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index f74f858ce..ec5565715 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -59,6 +59,7 @@ final class BlockTypeNames{ public const ANDESITE_DOUBLE_SLAB = "minecraft:andesite_double_slab"; public const ANDESITE_SLAB = "minecraft:andesite_slab"; public const ANDESITE_STAIRS = "minecraft:andesite_stairs"; + public const ANDESITE_WALL = "minecraft:andesite_wall"; public const ANVIL = "minecraft:anvil"; public const AZALEA = "minecraft:azalea"; public const AZALEA_LEAVES = "minecraft:azalea_leaves"; @@ -154,6 +155,7 @@ final class BlockTypeNames{ public const BRICK_DOUBLE_SLAB = "minecraft:brick_double_slab"; public const BRICK_SLAB = "minecraft:brick_slab"; public const BRICK_STAIRS = "minecraft:brick_stairs"; + public const BRICK_WALL = "minecraft:brick_wall"; public const BROWN_CANDLE = "minecraft:brown_candle"; public const BROWN_CANDLE_CAKE = "minecraft:brown_candle_cake"; public const BROWN_CARPET = "minecraft:brown_carpet"; @@ -191,7 +193,6 @@ final class BlockTypeNames{ public const CHAIN = "minecraft:chain"; public const CHAIN_COMMAND_BLOCK = "minecraft:chain_command_block"; public const CHEMICAL_HEAT = "minecraft:chemical_heat"; - public const CHEMISTRY_TABLE = "minecraft:chemistry_table"; public const CHERRY_BUTTON = "minecraft:cherry_button"; public const CHERRY_DOOR = "minecraft:cherry_door"; public const CHERRY_DOUBLE_SLAB = "minecraft:cherry_double_slab"; @@ -239,10 +240,13 @@ final class BlockTypeNames{ public const COBBLESTONE_SLAB = "minecraft:cobblestone_slab"; public const COBBLESTONE_WALL = "minecraft:cobblestone_wall"; public const COCOA = "minecraft:cocoa"; - public const COLORED_TORCH_BP = "minecraft:colored_torch_bp"; - public const COLORED_TORCH_RG = "minecraft:colored_torch_rg"; + public const COLORED_TORCH_BLUE = "minecraft:colored_torch_blue"; + public const COLORED_TORCH_GREEN = "minecraft:colored_torch_green"; + public const COLORED_TORCH_PURPLE = "minecraft:colored_torch_purple"; + public const COLORED_TORCH_RED = "minecraft:colored_torch_red"; public const COMMAND_BLOCK = "minecraft:command_block"; public const COMPOSTER = "minecraft:composter"; + public const COMPOUND_CREATOR = "minecraft:compound_creator"; public const CONDUIT = "minecraft:conduit"; public const COPPER_BLOCK = "minecraft:copper_block"; public const COPPER_BULB = "minecraft:copper_bulb"; @@ -365,6 +369,8 @@ final class BlockTypeNames{ public const DEEPSLATE_TILES = "minecraft:deepslate_tiles"; public const DENY = "minecraft:deny"; public const DEPRECATED_ANVIL = "minecraft:deprecated_anvil"; + public const DEPRECATED_PURPUR_BLOCK_1 = "minecraft:deprecated_purpur_block_1"; + public const DEPRECATED_PURPUR_BLOCK_2 = "minecraft:deprecated_purpur_block_2"; public const DETECTOR_RAIL = "minecraft:detector_rail"; public const DIAMOND_BLOCK = "minecraft:diamond_block"; public const DIAMOND_ORE = "minecraft:diamond_ore"; @@ -372,6 +378,7 @@ final class BlockTypeNames{ public const DIORITE_DOUBLE_SLAB = "minecraft:diorite_double_slab"; public const DIORITE_SLAB = "minecraft:diorite_slab"; public const DIORITE_STAIRS = "minecraft:diorite_stairs"; + public const DIORITE_WALL = "minecraft:diorite_wall"; public const DIRT = "minecraft:dirt"; public const DIRT_WITH_ROOTS = "minecraft:dirt_with_roots"; public const DISPENSER = "minecraft:dispenser"; @@ -499,6 +506,7 @@ final class BlockTypeNames{ public const ELEMENT_97 = "minecraft:element_97"; public const ELEMENT_98 = "minecraft:element_98"; public const ELEMENT_99 = "minecraft:element_99"; + public const ELEMENT_CONSTRUCTOR = "minecraft:element_constructor"; public const EMERALD_BLOCK = "minecraft:emerald_block"; public const EMERALD_ORE = "minecraft:emerald_ore"; public const ENCHANTING_TABLE = "minecraft:enchanting_table"; @@ -511,6 +519,7 @@ final class BlockTypeNames{ public const END_STONE = "minecraft:end_stone"; public const END_STONE_BRICK_DOUBLE_SLAB = "minecraft:end_stone_brick_double_slab"; public const END_STONE_BRICK_SLAB = "minecraft:end_stone_brick_slab"; + public const END_STONE_BRICK_WALL = "minecraft:end_stone_brick_wall"; public const ENDER_CHEST = "minecraft:ender_chest"; public const EXPOSED_CHISELED_COPPER = "minecraft:exposed_chiseled_copper"; public const EXPOSED_COPPER = "minecraft:exposed_copper"; @@ -553,6 +562,7 @@ final class BlockTypeNames{ public const GRANITE_DOUBLE_SLAB = "minecraft:granite_double_slab"; public const GRANITE_SLAB = "minecraft:granite_slab"; public const GRANITE_STAIRS = "minecraft:granite_stairs"; + public const GRANITE_WALL = "minecraft:granite_wall"; public const GRASS_BLOCK = "minecraft:grass_block"; public const GRASS_PATH = "minecraft:grass_path"; public const GRAVEL = "minecraft:gravel"; @@ -661,6 +671,7 @@ final class BlockTypeNames{ public const JUNGLE_WALL_SIGN = "minecraft:jungle_wall_sign"; public const JUNGLE_WOOD = "minecraft:jungle_wood"; public const KELP = "minecraft:kelp"; + public const LAB_TABLE = "minecraft:lab_table"; public const LADDER = "minecraft:ladder"; public const LANTERN = "minecraft:lantern"; public const LAPIS_BLOCK = "minecraft:lapis_block"; @@ -761,6 +772,7 @@ final class BlockTypeNames{ public const MANGROVE_TRAPDOOR = "minecraft:mangrove_trapdoor"; public const MANGROVE_WALL_SIGN = "minecraft:mangrove_wall_sign"; public const MANGROVE_WOOD = "minecraft:mangrove_wood"; + public const MATERIAL_REDUCER = "minecraft:material_reducer"; public const MEDIUM_AMETHYST_BUD = "minecraft:medium_amethyst_bud"; public const MELON_BLOCK = "minecraft:melon_block"; public const MELON_STEM = "minecraft:melon_stem"; @@ -771,9 +783,11 @@ final class BlockTypeNames{ public const MOSSY_COBBLESTONE_DOUBLE_SLAB = "minecraft:mossy_cobblestone_double_slab"; public const MOSSY_COBBLESTONE_SLAB = "minecraft:mossy_cobblestone_slab"; public const MOSSY_COBBLESTONE_STAIRS = "minecraft:mossy_cobblestone_stairs"; + public const MOSSY_COBBLESTONE_WALL = "minecraft:mossy_cobblestone_wall"; public const MOSSY_STONE_BRICK_DOUBLE_SLAB = "minecraft:mossy_stone_brick_double_slab"; public const MOSSY_STONE_BRICK_SLAB = "minecraft:mossy_stone_brick_slab"; public const MOSSY_STONE_BRICK_STAIRS = "minecraft:mossy_stone_brick_stairs"; + public const MOSSY_STONE_BRICK_WALL = "minecraft:mossy_stone_brick_wall"; public const MOSSY_STONE_BRICKS = "minecraft:mossy_stone_bricks"; public const MOVING_BLOCK = "minecraft:moving_block"; public const MUD = "minecraft:mud"; @@ -789,6 +803,7 @@ final class BlockTypeNames{ public const NETHER_BRICK_FENCE = "minecraft:nether_brick_fence"; public const NETHER_BRICK_SLAB = "minecraft:nether_brick_slab"; public const NETHER_BRICK_STAIRS = "minecraft:nether_brick_stairs"; + public const NETHER_BRICK_WALL = "minecraft:nether_brick_wall"; public const NETHER_GOLD_ORE = "minecraft:nether_gold_ore"; public const NETHER_SPROUTS = "minecraft:nether_sprouts"; public const NETHER_WART = "minecraft:nether_wart"; @@ -910,6 +925,7 @@ final class BlockTypeNames{ public const PRISMARINE_DOUBLE_SLAB = "minecraft:prismarine_double_slab"; public const PRISMARINE_SLAB = "minecraft:prismarine_slab"; public const PRISMARINE_STAIRS = "minecraft:prismarine_stairs"; + public const PRISMARINE_WALL = "minecraft:prismarine_wall"; public const PUMPKIN = "minecraft:pumpkin"; public const PUMPKIN_STEM = "minecraft:pumpkin_stem"; public const PURPLE_CANDLE = "minecraft:purple_candle"; @@ -925,6 +941,7 @@ final class BlockTypeNames{ public const PURPLE_WOOL = "minecraft:purple_wool"; public const PURPUR_BLOCK = "minecraft:purpur_block"; public const PURPUR_DOUBLE_SLAB = "minecraft:purpur_double_slab"; + public const PURPUR_PILLAR = "minecraft:purpur_pillar"; public const PURPUR_SLAB = "minecraft:purpur_slab"; public const PURPUR_STAIRS = "minecraft:purpur_stairs"; public const QUARTZ_BLOCK = "minecraft:quartz_block"; @@ -950,11 +967,13 @@ final class BlockTypeNames{ public const RED_NETHER_BRICK_DOUBLE_SLAB = "minecraft:red_nether_brick_double_slab"; public const RED_NETHER_BRICK_SLAB = "minecraft:red_nether_brick_slab"; public const RED_NETHER_BRICK_STAIRS = "minecraft:red_nether_brick_stairs"; + public const RED_NETHER_BRICK_WALL = "minecraft:red_nether_brick_wall"; public const RED_SAND = "minecraft:red_sand"; public const RED_SANDSTONE = "minecraft:red_sandstone"; public const RED_SANDSTONE_DOUBLE_SLAB = "minecraft:red_sandstone_double_slab"; public const RED_SANDSTONE_SLAB = "minecraft:red_sandstone_slab"; public const RED_SANDSTONE_STAIRS = "minecraft:red_sandstone_stairs"; + public const RED_SANDSTONE_WALL = "minecraft:red_sandstone_wall"; public const RED_SHULKER_BOX = "minecraft:red_shulker_box"; public const RED_STAINED_GLASS = "minecraft:red_stained_glass"; public const RED_STAINED_GLASS_PANE = "minecraft:red_stained_glass_pane"; @@ -977,6 +996,7 @@ final class BlockTypeNames{ public const SANDSTONE_DOUBLE_SLAB = "minecraft:sandstone_double_slab"; public const SANDSTONE_SLAB = "minecraft:sandstone_slab"; public const SANDSTONE_STAIRS = "minecraft:sandstone_stairs"; + public const SANDSTONE_WALL = "minecraft:sandstone_wall"; public const SCAFFOLDING = "minecraft:scaffolding"; public const SCULK = "minecraft:sculk"; public const SCULK_CATALYST = "minecraft:sculk_catalyst"; @@ -1047,6 +1067,7 @@ final class BlockTypeNames{ public const STONE_BRICK_DOUBLE_SLAB = "minecraft:stone_brick_double_slab"; public const STONE_BRICK_SLAB = "minecraft:stone_brick_slab"; public const STONE_BRICK_STAIRS = "minecraft:stone_brick_stairs"; + public const STONE_BRICK_WALL = "minecraft:stone_brick_wall"; public const STONE_BRICKS = "minecraft:stone_bricks"; public const STONE_BUTTON = "minecraft:stone_button"; public const STONE_PRESSURE_PLATE = "minecraft:stone_pressure_plate"; @@ -1108,6 +1129,7 @@ final class BlockTypeNames{ public const TUFF_WALL = "minecraft:tuff_wall"; public const TURTLE_EGG = "minecraft:turtle_egg"; public const TWISTING_VINES = "minecraft:twisting_vines"; + public const UNDERWATER_TNT = "minecraft:underwater_tnt"; public const UNDERWATER_TORCH = "minecraft:underwater_torch"; public const UNDYED_SHULKER_BOX = "minecraft:undyed_shulker_box"; public const UNKNOWN = "minecraft:unknown"; @@ -1192,6 +1214,7 @@ final class BlockTypeNames{ public const WEATHERED_DOUBLE_CUT_COPPER_SLAB = "minecraft:weathered_double_cut_copper_slab"; public const WEB = "minecraft:web"; public const WEEPING_VINES = "minecraft:weeping_vines"; + public const WET_SPONGE = "minecraft:wet_sponge"; public const WHEAT = "minecraft:wheat"; public const WHITE_CANDLE = "minecraft:white_candle"; public const WHITE_CANDLE_CAKE = "minecraft:white_candle_cake"; diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index df1695729..af6547729 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -1092,7 +1092,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ); $this->mapSlab(Blocks::ANDESITE_SLAB(), Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB); $this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS))); - $this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_ANDESITE)); + $this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::ANDESITE_WALL))); $this->map(Blocks::ANVIL(), fn(Anvil $block) : Writer => Writer::create( match($damage = $block->getDamage()){ 0 => Ids::ANVIL, @@ -1169,7 +1169,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); $this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL))); $this->map(Blocks::BLAST_FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::BLAST_FURNACE, Ids::LIT_BLAST_FURNACE)); - $this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_BP))); + $this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_BLUE))); $this->map(Blocks::BONE_BLOCK(), function(BoneBlock $block) : Writer{ return Writer::create(Ids::BONE_BLOCK) ->writeInt(StateNames::DEPRECATED, 0) @@ -1183,7 +1183,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->mapSlab(Blocks::BRICK_SLAB(), Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); - $this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_BRICK)); + $this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::BRICK_WALL))); $this->map(Blocks::BROWN_MUSHROOM_BLOCK(), fn(BrownMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::BROWN_MUSHROOM_BLOCK))); $this->map(Blocks::CACTUS(), function(Cactus $block) : Writer{ return Writer::create(Ids::CACTUS) @@ -1236,7 +1236,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::COBBLED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::COBBLED_DEEPSLATE_WALL))); $this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); - $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_COBBLESTONE)); + $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))); $this->map(Blocks::COPPER(), function(Copper $block) : Writer{ $oxidation = $block->getOxidation(); return new Writer($block->isWaxed() ? @@ -1316,7 +1316,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeInt(StateNames::AGE, $block->getAge()) ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); }); - $this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR, new Writer(Ids::CHEMISTRY_TABLE))); + $this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::COMPOUND_CREATOR))); $this->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB); $this->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB); $this->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB); @@ -1343,7 +1343,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB); $this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); - $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_DIORITE)); + $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::DIORITE_WALL))); $this->map(Blocks::DIRT(), function(Dirt $block) : Writer{ return Writer::create(match($block->getDirtType()){ DirtType::NORMAL => Ids::DIRT, @@ -1352,7 +1352,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); }); $this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS))); - $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR, new Writer(Ids::CHEMISTRY_TABLE))); + $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::ELEMENT_CONSTRUCTOR))); $this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{ return Writer::create(Ids::ENDER_CHEST) ->writeCardinalHorizontalFacing($block->getFacing()); @@ -1368,7 +1368,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->mapSlab(Blocks::END_STONE_BRICK_SLAB(), Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); - $this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_END_BRICK)); + $this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::END_STONE_BRICK_WALL))); $this->mapSlab(Blocks::FAKE_WOODEN_SLAB(), Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB); $this->map(Blocks::FARMLAND(), function(Farmland $block) : Writer{ return Writer::create(Ids::FARMLAND) @@ -1402,8 +1402,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME)); $this->mapSlab(Blocks::GRANITE_SLAB(), Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB); $this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); - $this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_GRANITE)); - $this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_RG))); + $this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::GRANITE_WALL))); + $this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_GREEN))); $this->map(Blocks::HAY_BALE(), function(HayBale $block) : Writer{ return Writer::create(Ids::HAY_BLOCK) ->writeInt(StateNames::DEPRECATED, 0) @@ -1417,7 +1417,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR))); $this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR))); $this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME)); - $this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE, new Writer(Ids::CHEMISTRY_TABLE))); + $this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::LAB_TABLE))); $this->map(Blocks::LADDER(), function(Ladder $block) : Writer{ return Writer::create(Ids::LADDER) ->writeHorizontalFacing($block->getFacing()); @@ -1481,7 +1481,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::LOOM) ->writeLegacyHorizontalFacing($block->getFacing()); }); - $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER, new Writer(Ids::CHEMISTRY_TABLE))); + $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::MATERIAL_REDUCER))); $this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM))); $this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{ return Writer::create(Ids::SKULL) @@ -1489,10 +1489,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); - $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE)); + $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_COBBLESTONE_WALL))); $this->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); - $this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK)); + $this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_STONE_BRICK_WALL))); $this->mapSlab(Blocks::MUD_BRICK_SLAB(), Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS); $this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL))); @@ -1502,7 +1502,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); - $this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_NETHER_BRICK)); + $this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::NETHER_BRICK_WALL))); $this->map(Blocks::NETHER_PORTAL(), function(NetherPortal $block) : Writer{ return Writer::create(Ids::PORTAL) ->writeString(StateNames::PORTAL_AXIS, match($block->getAxis()){ @@ -1566,21 +1566,16 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); $this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB); $this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); - $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_PRISMARINE)); + $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::PRISMARINE_WALL))); $this->map(Blocks::PUMPKIN(), function() : Writer{ return Writer::create(Ids::PUMPKIN) ->writeCardinalHorizontalFacing(Facing::SOUTH); //no longer used }); $this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM))); - $this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, true, Writer::create(Ids::COLORED_TORCH_BP))); - $this->map(Blocks::PURPUR(), function() : Writer{ - return Writer::create(Ids::PURPUR_BLOCK) - ->writeString(StateNames::CHISEL_TYPE, StringValues::CHISEL_TYPE_DEFAULT) - ->writePillarAxis(Axis::Y); //useless, but MCPE wants it - }); + $this->map(Blocks::PURPUR(), fn() => Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y)); + $this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_PURPLE))); $this->map(Blocks::PURPUR_PILLAR(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::PURPUR_BLOCK) - ->writeString(StateNames::CHISEL_TYPE, StringValues::CHISEL_TYPE_LINES) + return Writer::create(Ids::PURPUR_PILLAR) ->writePillarAxis($block->getAxis()); }); $this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB); @@ -1617,15 +1612,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::RED_MUSHROOM_BLOCK(), fn(RedMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::RED_MUSHROOM_BLOCK))); $this->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); - $this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK)); + $this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_NETHER_BRICK_WALL))); $this->mapSlab(Blocks::RED_SANDSTONE_SLAB(), Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); - $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE)); - $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeColoredTorch($block, false, Writer::create(Ids::COLORED_TORCH_RG))); + $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_SANDSTONE_WALL))); + $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_RED))); $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); - $this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_SANDSTONE)); + $this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::SANDSTONE_WALL))); $this->map(Blocks::SEA_PICKLE(), function(SeaPickle $block) : Writer{ return Writer::create(Ids::SEA_PICKLE) ->writeBool(StateNames::DEAD_BIT, !$block->isUnderwater()) @@ -1662,15 +1657,12 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::SOUL_TORCH) ->writeTorchFacing($block->getFacing()); }); - $this->map(Blocks::SPONGE(), function(Sponge $block) : Writer{ - return Writer::create(Ids::SPONGE) - ->writeString(StateNames::SPONGE_TYPE, $block->isWet() ? StringValues::SPONGE_TYPE_WET : StringValues::SPONGE_TYPE_DRY); - }); + $this->map(Blocks::SPONGE(), fn(Sponge $block) => Writer::create($block->isWet() ? Ids::WET_SPONGE : Ids::SPONGE)); $this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK) ->writeCardinalHorizontalFacing($block->getFacing())); $this->mapSlab(Blocks::STONE_BRICK_SLAB(), Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); - $this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeLegacyWall($block, StringValues::WALL_BLOCK_TYPE_STONE_BRICK)); + $this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::STONE_BRICK_WALL))); $this->map(Blocks::STONE_BUTTON(), fn(StoneButton $block) => Helper::encodeButton($block, new Writer(Ids::STONE_BUTTON))); $this->map(Blocks::STONE_PRESSURE_PLATE(), fn(StonePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::STONE_PRESSURE_PLATE))); $this->mapSlab(Blocks::STONE_SLAB(), Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB); @@ -1684,11 +1676,9 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::SWEET_BERRY_BUSH) ->writeInt(StateNames::GROWTH, $block->getAge()); }); - $this->map(Blocks::TNT(), function(TNT $block) : Writer{ - return Writer::create(Ids::TNT) - ->writeBool(StateNames::ALLOW_UNDERWATER_BIT, $block->worksUnderwater()) - ->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable()); - }); + $this->map(Blocks::TNT(), fn(TNT $block) => Writer::create($block->worksUnderwater() ? Ids::UNDERWATER_TNT : Ids::TNT) + ->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable()) + ); $this->map(Blocks::TORCH(), function(Torch $block) : Writer{ return Writer::create(Ids::TORCH) ->writeTorchFacing($block->getFacing()); diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 0c8df24f2..47c0fd4af 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -52,7 +52,6 @@ use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\SlabType; use pocketmine\block\VanillaBlocks; use pocketmine\block\Wall; -use pocketmine\block\WallCoralFan; use pocketmine\block\WallSign; use pocketmine\block\WeightedPressurePlate; use pocketmine\block\Wood; @@ -60,7 +59,6 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -288,13 +286,6 @@ final class BlockStateDeserializerHelper{ return $block; } - /** @throws BlockStateDeserializeException */ - public static function decodeWallCoralFan(WallCoralFan $block, BlockStateReader $in) : WallCoralFan{ - return $block - ->setDead($in->readBool(BlockStateNames::DEAD_BIT)) - ->setFacing($in->readCoralFacing()); - } - /** @throws BlockStateDeserializeException */ public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ return $block @@ -305,25 +296,4 @@ final class BlockStateDeserializerHelper{ return $block ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); } - - /** @throws BlockStateDeserializeException */ - public static function mapLegacyWallType(BlockStateReader $in) : Wall{ - return self::decodeWall(match($type = $in->readString(BlockStateNames::WALL_BLOCK_TYPE)){ - StringValues::WALL_BLOCK_TYPE_ANDESITE => VanillaBlocks::ANDESITE_WALL(), - StringValues::WALL_BLOCK_TYPE_BRICK => VanillaBlocks::BRICK_WALL(), - StringValues::WALL_BLOCK_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_WALL(), - StringValues::WALL_BLOCK_TYPE_DIORITE => VanillaBlocks::DIORITE_WALL(), - StringValues::WALL_BLOCK_TYPE_END_BRICK => VanillaBlocks::END_STONE_BRICK_WALL(), - StringValues::WALL_BLOCK_TYPE_GRANITE => VanillaBlocks::GRANITE_WALL(), - StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_WALL(), - StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_WALL(), - StringValues::WALL_BLOCK_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_WALL(), - StringValues::WALL_BLOCK_TYPE_PRISMARINE => VanillaBlocks::PRISMARINE_WALL(), - StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_WALL(), - StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_WALL(), - StringValues::WALL_BLOCK_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_WALL(), - StringValues::WALL_BLOCK_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_WALL(), - default => throw $in->badValueException(BlockStateNames::WALL_BLOCK_TYPE, $type), - }, $in); - } } diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index 9dfb17ca0..3e2215746 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -68,9 +68,8 @@ final class BlockStateSerializerHelper{ ->writeInt(StateNames::CANDLES, $block->getCount() - 1); } - public static function encodeChemistryTable(ChemistryTable $block, string $chemistryTableType, Writer $out) : Writer{ + public static function encodeChemistryTable(ChemistryTable $block, Writer $out) : Writer{ return $out - ->writeString(BlockStateNames::CHEMISTRY_TABLE_TYPE, $chemistryTableType) ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); } @@ -78,9 +77,8 @@ final class BlockStateSerializerHelper{ return $out->writeInt(BlockStateNames::GROWTH, $block->getAge()); } - public static function encodeColoredTorch(Torch $block, bool $highBit, Writer $out) : Writer{ + public static function encodeTorch(Torch $block, Writer $out) : Writer{ return $out - ->writeBool(BlockStateNames::COLOR_BIT, $highBit) ->writeTorchFacing($block->getFacing()); } @@ -225,11 +223,6 @@ final class BlockStateSerializerHelper{ ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); } - public static function encodeLegacyWall(Wall $block, string $type) : Writer{ - return self::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL)) - ->writeString(BlockStateNames::WALL_BLOCK_TYPE, $type); - } - public static function encodeWallSign(WallSign $block, Writer $out) : Writer{ return $out ->writeHorizontalFacing($block->getFacing()); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index b85951d31..eb0cb8d50 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -960,6 +960,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::SOUL_SAND, fn() => Blocks::SOUL_SAND()); $this->mapSimple(Ids::SOUL_SOIL, fn() => Blocks::SOUL_SOIL()); $this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM()); + $this->mapSimple(Ids::SPONGE, fn() => Blocks::SPONGE()); $this->mapSimple(Ids::STONE, fn() => Blocks::STONE()); $this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER()); $this->mapSimple(Ids::STONE_BRICKS, fn() => Blocks::STONE_BRICKS()); @@ -971,6 +972,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS()); $this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD()); $this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB()); + $this->mapSimple(Ids::WET_SPONGE, fn() => Blocks::SPONGE()->setWet(true)); $this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE()); $this->mapSimple(Ids::DANDELION, fn() => Blocks::DANDELION()); @@ -1000,6 +1002,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB, fn() => Blocks::ANDESITE_SLAB()); $this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS()); + $this->map(Ids::ANDESITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::ANDESITE_WALL(), $in)); $this->map(Ids::ANVIL, function(Reader $in) : Block{ return Blocks::ANVIL() ->setDamage(Anvil::UNDAMAGED) @@ -1095,6 +1098,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB()); $this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS()); + $this->map(Ids::BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BRICK_WALL(), $in)); $this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in)); $this->map(Ids::CACTUS, function(Reader $in) : Block{ return Blocks::CACTUS() @@ -1147,15 +1151,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::CHISELED_QUARTZ() ->setAxis($in->readPillarAxis()); }); - $this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{ - return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){ - StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(), - StringValues::CHEMISTRY_TABLE_TYPE_ELEMENT_CONSTRUCTOR => Blocks::ELEMENT_CONSTRUCTOR(), - StringValues::CHEMISTRY_TABLE_TYPE_LAB_TABLE => Blocks::LAB_TABLE(), - StringValues::CHEMISTRY_TABLE_TYPE_MATERIAL_REDUCER => Blocks::MATERIAL_REDUCER(), - default => throw $in->badValueException(StateNames::CHEMISTRY_TABLE_TYPE, $type), - })->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())); - }); $this->map(Ids::CHEST, function(Reader $in) : Block{ return Blocks::CHEST() ->setFacing($in->readCardinalHorizontalFacing()); @@ -1169,22 +1164,19 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapStairs(Ids::COBBLED_DEEPSLATE_STAIRS, fn() => Blocks::COBBLED_DEEPSLATE_STAIRS()); $this->map(Ids::COBBLED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLED_DEEPSLATE_WALL(), $in)); $this->mapSlab(Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::COBBLESTONE_SLAB()); - $this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::mapLegacyWallType($in)); + $this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLESTONE_WALL(), $in)); $this->map(Ids::COCOA, function(Reader $in) : Block{ return Blocks::COCOA_POD() ->setAge($in->readBoundedInt(StateNames::AGE, 0, 2)) ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())); }); - $this->map(Ids::COLORED_TORCH_BP, function(Reader $in) : Block{ - return $in->readBool(StateNames::COLOR_BIT) ? - Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing()) : - Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::COLORED_TORCH_RG, function(Reader $in) : Block{ - return $in->readBool(StateNames::COLOR_BIT) ? - Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing()) : - Blocks::RED_TORCH()->setFacing($in->readTorchFacing()); - }); + $this->map(Ids::COLORED_TORCH_BLUE, fn(Reader $in) => Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing())); + $this->map(Ids::COLORED_TORCH_GREEN, fn(Reader $in) => Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing())); + $this->map(Ids::COLORED_TORCH_PURPLE, fn(Reader $in) => Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing())); + $this->map(Ids::COLORED_TORCH_RED, fn(Reader $in) => Blocks::RED_TORCH()->setFacing($in->readTorchFacing())); + $this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR() + ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) + ); $this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE)); $this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); $this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); @@ -1215,6 +1207,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB, fn() => Blocks::DIORITE_SLAB()); $this->mapStairs(Ids::DIORITE_STAIRS, fn() => Blocks::DIORITE_STAIRS()); + $this->map(Ids::DIORITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DIORITE_WALL(), $in)); $this->map(Ids::DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL)); $this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); $this->map(Ids::LARGE_FERN, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LARGE_FERN(), $in)); @@ -1223,7 +1216,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::ROSE_BUSH, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::ROSE_BUSH(), $in)); $this->map(Ids::SUNFLOWER, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::SUNFLOWER(), $in)); $this->map(Ids::LILAC, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LILAC(), $in)); + $this->map(Ids::ELEMENT_CONSTRUCTOR, fn(Reader $in) => Blocks::ELEMENT_CONSTRUCTOR() + ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) + ); $this->mapStairs(Ids::END_BRICK_STAIRS, fn() => Blocks::END_STONE_BRICK_STAIRS()); + $this->map(Ids::END_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::END_STONE_BRICK_WALL(), $in)); $this->map(Ids::END_PORTAL_FRAME, function(Reader $in) : Block{ return Blocks::END_PORTAL_FRAME() ->setEye($in->readBool(StateNames::END_PORTAL_EYE_BIT)) @@ -1275,6 +1272,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB, fn() => Blocks::GRANITE_SLAB()); $this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS()); + $this->map(Ids::GRANITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::GRANITE_WALL(), $in)); $this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{ $in->ignored(StateNames::DEPRECATED); return Blocks::HAY_BALE()->setAxis($in->readPillarAxis()); @@ -1287,6 +1285,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in)); $this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in)); + $this->map(Ids::LAB_TABLE, fn(Reader $in) => Blocks::LAB_TABLE() + ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) + ); $this->map(Ids::LADDER, function(Reader $in) : Block{ return Blocks::LADDER() ->setFacing($in->readHorizontalFacing()); @@ -1358,6 +1359,9 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::LOOM() ->setFacing($in->readLegacyHorizontalFacing()); }); + $this->map(Ids::MATERIAL_REDUCER, fn(Reader $in) => Blocks::MATERIAL_REDUCER() + ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) + ); $this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{ return Blocks::AMETHYST_CLUSTER() ->setStage(AmethystCluster::STAGE_MEDIUM_BUD) @@ -1366,8 +1370,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in)); $this->mapSlab(Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::MOSSY_COBBLESTONE_SLAB()); $this->mapStairs(Ids::MOSSY_COBBLESTONE_STAIRS, fn() => Blocks::MOSSY_COBBLESTONE_STAIRS()); + $this->map(Ids::MOSSY_COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_COBBLESTONE_WALL(), $in)); $this->mapSlab(Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::MOSSY_STONE_BRICK_SLAB()); $this->mapStairs(Ids::MOSSY_STONE_BRICK_STAIRS, fn() => Blocks::MOSSY_STONE_BRICK_STAIRS()); + $this->map(Ids::MOSSY_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_STONE_BRICK_WALL(), $in)); $this->mapSlab(Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB, fn() => Blocks::MUD_BRICK_SLAB()); $this->mapStairs(Ids::MUD_BRICK_STAIRS, fn() => Blocks::MUD_BRICK_STAIRS()); $this->map(Ids::MUD_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MUD_BRICK_WALL(), $in)); @@ -1377,6 +1383,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::NETHER_BRICK_SLAB()); $this->mapStairs(Ids::NETHER_BRICK_STAIRS, fn() => Blocks::NETHER_BRICK_STAIRS()); + $this->map(Ids::NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::NETHER_BRICK_WALL(), $in)); $this->map(Ids::NETHER_WART, function(Reader $in) : Block{ return Blocks::NETHER_WART() ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); @@ -1449,6 +1456,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setPowered(true)); $this->mapSlab(Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_BRICKS_SLAB()); $this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS()); + $this->map(Ids::PRISMARINE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::PRISMARINE_WALL(), $in)); $this->mapSlab(Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_SLAB()); $this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS()); $this->map(Ids::PUMPKIN, function(Reader $in) : Block{ @@ -1457,19 +1465,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->map(Ids::PUMPKIN_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::PUMPKIN_STEM(), $in)); $this->map(Ids::PURPUR_BLOCK, function(Reader $in) : Block{ - $type = $in->readString(StateNames::CHISEL_TYPE); - if($type === StringValues::CHISEL_TYPE_LINES){ - return Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis()); - }else{ - $in->ignored(StateNames::PILLAR_AXIS); //axis only applies to pillars - return match($type){ - StringValues::CHISEL_TYPE_CHISELED, //TODO: bug in MCPE - StringValues::CHISEL_TYPE_SMOOTH, //TODO: bug in MCPE - StringValues::CHISEL_TYPE_DEFAULT => Blocks::PURPUR(), - default => throw $in->badValueException(StateNames::CHISEL_TYPE, $type), - }; - } + $in->ignored(StateNames::PILLAR_AXIS); //??? + return Blocks::PURPUR(); }); + $this->map(Ids::PURPUR_PILLAR, fn(Reader $in) => Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis())); $this->mapSlab(Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB, fn() => Blocks::PURPUR_SLAB()); $this->mapStairs(Ids::PURPUR_STAIRS, fn() => Blocks::PURPUR_STAIRS()); $this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Opaque{ @@ -1489,8 +1488,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in)); $this->mapSlab(Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::RED_NETHER_BRICK_SLAB()); $this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS()); + $this->map(Ids::RED_NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_NETHER_BRICK_WALL(), $in)); $this->mapSlab(Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::RED_SANDSTONE_SLAB()); $this->mapStairs(Ids::RED_SANDSTONE_STAIRS, fn() => Blocks::RED_SANDSTONE_STAIRS()); + $this->map(Ids::RED_SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_SANDSTONE_WALL(), $in)); $this->map(Ids::REDSTONE_LAMP, function() : Block{ return Blocks::REDSTONE_LAMP() ->setPowered(false); @@ -1514,6 +1515,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); + $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); $this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{ return Blocks::SEA_PICKLE() ->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1) @@ -1565,19 +1567,13 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::SOUL_TORCH() ->setFacing($in->readTorchFacing()); }); - $this->map(Ids::SPONGE, function(Reader $in) : Block{ - return Blocks::SPONGE()->setWet(match($type = $in->readString(StateNames::SPONGE_TYPE)){ - StringValues::SPONGE_TYPE_DRY => false, - StringValues::SPONGE_TYPE_WET => true, - default => throw $in->badValueException(StateNames::SPONGE_TYPE, $type), - }); - }); $this->map(Ids::STANDING_BANNER, function(Reader $in) : Block{ return Blocks::BANNER() ->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15)); }); $this->mapSlab(Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::STONE_BRICK_SLAB()); $this->mapStairs(Ids::STONE_BRICK_STAIRS, fn() => Blocks::STONE_BRICK_STAIRS()); + $this->map(Ids::STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::STONE_BRICK_WALL(), $in)); $this->map(Ids::STONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::STONE_BUTTON(), $in)); $this->map(Ids::STONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::STONE_PRESSURE_PLATE(), $in)); $this->mapStairs(Ids::STONE_STAIRS, fn() => Blocks::COBBLESTONE_STAIRS()); @@ -1594,7 +1590,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::TNT, function(Reader $in) : Block{ return Blocks::TNT() ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) - ->setWorksUnderwater($in->readBool(StateNames::ALLOW_UNDERWATER_BIT)); + ->setWorksUnderwater(false); }); $this->map(Ids::TORCH, function(Reader $in) : Block{ return Blocks::TORCH() @@ -1626,6 +1622,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::TWISTING_VINES() ->setAge($in->readBoundedInt(StateNames::TWISTING_VINES_AGE, 0, 25)); }); + $this->map(Ids::UNDERWATER_TNT, function(Reader $in) : Block{ + return Blocks::TNT() + ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) + ->setWorksUnderwater(true); + }); $this->map(Ids::UNDERWATER_TORCH, function(Reader $in) : Block{ return Blocks::UNDERWATER_TORCH() ->setFacing($in->readTorchFacing()); diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index db6594c43..06ae28cec 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -109,6 +109,7 @@ final class ItemTypeNames{ public const CHAINMAIL_HELMET = "minecraft:chainmail_helmet"; public const CHAINMAIL_LEGGINGS = "minecraft:chainmail_leggings"; public const CHARCOAL = "minecraft:charcoal"; + public const CHEMISTRY_TABLE = "minecraft:chemistry_table"; public const CHERRY_BOAT = "minecraft:cherry_boat"; public const CHERRY_CHEST_BOAT = "minecraft:cherry_chest_boat"; public const CHERRY_DOOR = "minecraft:cherry_door"; @@ -127,6 +128,8 @@ final class ItemTypeNames{ public const COD = "minecraft:cod"; public const COD_BUCKET = "minecraft:cod_bucket"; public const COD_SPAWN_EGG = "minecraft:cod_spawn_egg"; + public const COLORED_TORCH_BP = "minecraft:colored_torch_bp"; + public const COLORED_TORCH_RG = "minecraft:colored_torch_rg"; public const COMMAND_BLOCK_MINECART = "minecraft:command_block_minecart"; public const COMPARATOR = "minecraft:comparator"; public const COMPASS = "minecraft:compass"; diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 16898283a..99af73707 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\network\mcpe\protocol\types\Enchant; use pocketmine\network\mcpe\protocol\types\EnchantOption as ProtocolEnchantOption; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; +use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction; @@ -500,16 +501,18 @@ class InventoryManager{ $this->session->sendDataPacket(InventorySlotPacket::create( $windowId, $netSlot, - new ItemStackWrapper(0, ItemStack::null()), - 0 + new FullContainerName($this->lastInventoryNetworkId), + 0, + new ItemStackWrapper(0, ItemStack::null()) )); } //now send the real contents $this->session->sendDataPacket(InventorySlotPacket::create( $windowId, $netSlot, - $itemStackWrapper, - 0 + new FullContainerName($this->lastInventoryNetworkId), + 0, + $itemStackWrapper )); } @@ -528,10 +531,11 @@ class InventoryManager{ $this->session->sendDataPacket(InventoryContentPacket::create( $windowId, array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null())), + new FullContainerName($this->lastInventoryNetworkId), 0 )); //now send the real contents - $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, 0)); + $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, new FullContainerName($this->lastInventoryNetworkId), 0)); } public function syncSlot(Inventory $inventory, int $slot, ItemStack $itemStack) : void{ diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 33a9303a3..2dce5bbb8 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -787,7 +787,7 @@ class NetworkSession{ public function transfer(string $ip, int $port, Translatable|string|null $reason = null) : void{ $reason ??= KnownTranslationFactory::pocketmine_disconnect_transfer(); $this->tryDisconnect(function() use ($ip, $port, $reason) : void{ - $this->sendDataPacket(TransferPacket::create($ip, $port), true); + $this->sendDataPacket(TransferPacket::create($ip, $port, false), true); if($this->player !== null){ $this->player->onPostDisconnect($reason, null); } diff --git a/src/network/mcpe/StandardEntityEventBroadcaster.php b/src/network/mcpe/StandardEntityEventBroadcaster.php index e2a707a3d..3e2df3994 100644 --- a/src/network/mcpe/StandardEntityEventBroadcaster.php +++ b/src/network/mcpe/StandardEntityEventBroadcaster.php @@ -38,8 +38,8 @@ use pocketmine\network\mcpe\protocol\MobEquipmentPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\SetActorDataPacket; use pocketmine\network\mcpe\protocol\TakeItemActorPacket; -use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute; use pocketmine\network\mcpe\protocol\types\entity\PropertySyncData; +use pocketmine\network\mcpe\protocol\types\entity\UpdateAttribute; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; @@ -67,7 +67,7 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{ if(count($attributes) > 0){ $this->sendDataPacket($recipients, UpdateAttributesPacket::create( $entity->getId(), - array_map(fn(Attribute $attr) => new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []), $attributes), + array_map(fn(Attribute $attr) => new UpdateAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getDefaultValue(), []), $attributes), 0 )); } @@ -142,6 +142,13 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{ } public function onEmote(array $recipients, Human $from, string $emoteId) : void{ - $this->sendDataPacket($recipients, EmotePacket::create($from->getId(), $emoteId, "", "", EmotePacket::FLAG_SERVER | EmotePacket::FLAG_MUTE_ANNOUNCEMENT)); + $this->sendDataPacket($recipients, EmotePacket::create( + $from->getId(), + $emoteId, + 0, //seems to be irrelevant for the client, we cannot risk rebroadcasting random values received + "", + "", + EmotePacket::FLAG_SERVER | EmotePacket::FLAG_MUTE_ANNOUNCEMENT + )); } } diff --git a/src/network/mcpe/handler/ItemStackResponseBuilder.php b/src/network/mcpe/handler/ItemStackResponseBuilder.php index a947eae72..1369e3ba7 100644 --- a/src/network/mcpe/handler/ItemStackResponseBuilder.php +++ b/src/network/mcpe/handler/ItemStackResponseBuilder.php @@ -100,7 +100,7 @@ final class ItemStackResponseBuilder{ $responseContainerInfos = []; foreach($responseInfosByContainer as $containerInterfaceId => $responseInfos){ - $responseContainerInfos[] = new ItemStackResponseContainerInfo(new FullContainerName($containerInterfaceId, 0), $responseInfos); + $responseContainerInfos[] = new ItemStackResponseContainerInfo(new FullContainerName($containerInterfaceId), $responseInfos); } return new ItemStackResponse(ItemStackResponse::RESULT_OK, $this->requestId, $responseContainerInfos); diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index b99775886..5f7fb0e4b 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -115,11 +115,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ //TODO: support forcing server packs $this->session->sendDataPacket(ResourcePacksInfoPacket::create( resourcePackEntries: $resourcePackEntries, - behaviorPackEntries: [], mustAccept: $this->mustAccept, hasAddons: false, hasScripts: false, - forceServerPacks: false, cdnUrls: [] )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 709dadf97..1612e7d84 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,12 +51,12 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 712; + public const CURRENT_STORAGE_NETWORK_VERSION = 729; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 20, //patch - 0, //revision + 30, //patch + 3, //revision 0 //is beta ]; From 49c2f13cf01c045eaba55553f1907cd30ba4e550 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 20 Sep 2024 18:59:45 -0500 Subject: [PATCH 029/290] Release 5.19.0 --- changelogs/5.19.md | 16 ++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.19.md diff --git a/changelogs/5.19.md b/changelogs/5.19.md new file mode 100644 index 000000000..57d322bf7 --- /dev/null +++ b/changelogs/5.19.md @@ -0,0 +1,16 @@ +# 5.19.0 +Released 21tst September 2024. + +**For Minecraft: Bedrock Edition 1.21.30** + +This is a support release for Minecraft: Bedrock Edition 1.21.30. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.30. +- Removed support for earlier versions. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 571dd9638..d482046da 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.18.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.19.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From a5babb2c9f897be1d3d60fe36ad79b5c001b6734 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 20 Sep 2024 19:00:23 -0500 Subject: [PATCH 030/290] 5.19.1 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index d482046da..b2d2fe4ab 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.19.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.19.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From e8620ef94db7fb237ca8aec917d38dd4cb5e6223 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 22 Sep 2024 21:59:40 -0500 Subject: [PATCH 031/290] Restore travis.sh execute permission --- tests/travis.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/travis.sh diff --git a/tests/travis.sh b/tests/travis.sh old mode 100644 new mode 100755 From 4e6b34f57377e7e92892ed88d5ed3744642fcb1f Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:25:10 -0500 Subject: [PATCH 032/290] Implement new 1.21 copper blocks (#6366) Added the following new blocks: - All types of Copper Bulb - All types of Copper Door - All types of Copper Trapdoor - All types of Chiseled Copper - All types of Copper Grate --- src/block/BlockTypeIds.php | 7 +- src/block/CopperBulb.php | 69 +++++++++++++ src/block/CopperDoor.php | 53 ++++++++++ src/block/CopperGrate.php | 33 +++++++ src/block/CopperTrapdoor.php | 44 +++++++++ src/block/VanillaBlocks.php | 12 +++ .../convert/BlockObjectToStateSerializer.php | 99 +++++++++++++++++++ .../BlockStateToObjectDeserializer.php | 72 ++++++++++++++ .../ItemSerializerDeserializerRegistrar.php | 28 ++++++ src/item/StringToItemParser.php | 5 + tests/phpstan/configs/phpstan-bugs.neon | 10 ++ .../block_factory_consistency_check.json | 5 + 12 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 src/block/CopperBulb.php create mode 100644 src/block/CopperDoor.php create mode 100644 src/block/CopperGrate.php create mode 100644 src/block/CopperTrapdoor.php diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 29f4e650d..3914a4b74 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -760,8 +760,13 @@ final class BlockTypeIds{ public const POLISHED_TUFF_SLAB = 10730; public const POLISHED_TUFF_STAIRS = 10731; public const POLISHED_TUFF_WALL = 10732; + public const COPPER_BULB = 10733; + public const COPPER_DOOR = 10734; + public const COPPER_TRAPDOOR = 10735; + public const CHISELED_COPPER = 10736; + public const COPPER_GRATE = 10737; - public const FIRST_UNUSED_BLOCK_ID = 10733; + public const FIRST_UNUSED_BLOCK_ID = 10738; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/CopperBulb.php b/src/block/CopperBulb.php new file mode 100644 index 000000000..223c63527 --- /dev/null +++ b/src/block/CopperBulb.php @@ -0,0 +1,69 @@ +encodeLitState($w); + $w->bool($this->powered); + } + + /** @return $this */ + public function togglePowered(bool $powered) : self{ + if($powered === $this->powered){ + return $this; + } + if ($powered) { + $this->setLit(!$this->lit); + } + $this->setPowered($powered); + return $this; + } + + public function getLightLevel() : int{ + if ($this->lit) { + return match($this->oxidation){ + CopperOxidation::NONE => 15, + CopperOxidation::EXPOSED => 12, + CopperOxidation::WEATHERED => 8, + CopperOxidation::OXIDIZED => 4, + }; + } + + return 0; + } +} diff --git a/src/block/CopperDoor.php b/src/block/CopperDoor.php new file mode 100644 index 000000000..d53be2323 --- /dev/null +++ b/src/block/CopperDoor.php @@ -0,0 +1,53 @@ +isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) { + //copy copper properties to other half + $other = $this->getSide($this->top ? Facing::DOWN : Facing::UP); + $world = $this->position->getWorld(); + if ($other instanceof CopperDoor) { + $other->setOxidation($this->oxidation); + $other->setWaxed($this->waxed); + $world->setBlock($other->position, $other); + } + return true; + } + + return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); + } +} diff --git a/src/block/CopperGrate.php b/src/block/CopperGrate.php new file mode 100644 index 000000000..fb59d846e --- /dev/null +++ b/src/block/CopperGrate.php @@ -0,0 +1,33 @@ +isSneaking() && $this->onInteractCopper($item, $face, $clickVector, $player, $returnedItems)) { + return true; + } + + return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 13c7e869a..60540dfb8 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -181,6 +181,7 @@ use function strtolower; * @method static Wood CHERRY_WOOD() * @method static Chest CHEST() * @method static ChiseledBookshelf CHISELED_BOOKSHELF() + * @method static Copper CHISELED_COPPER() * @method static Opaque CHISELED_DEEPSLATE() * @method static Opaque CHISELED_NETHER_BRICKS() * @method static Opaque CHISELED_POLISHED_BLACKSTONE() @@ -209,7 +210,11 @@ use function strtolower; * @method static Concrete CONCRETE() * @method static ConcretePowder CONCRETE_POWDER() * @method static Copper COPPER() + * @method static CopperBulb COPPER_BULB() + * @method static CopperDoor COPPER_DOOR() + * @method static CopperGrate COPPER_GRATE() * @method static CopperOre COPPER_ORE() + * @method static CopperTrapdoor COPPER_TRAPDOOR() * @method static Coral CORAL() * @method static CoralBlock CORAL_BLOCK() * @method static FloorCoralFan CORAL_FAN() @@ -1642,9 +1647,16 @@ final class VanillaBlocks{ self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo)); self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo)); + self::register("chiseled_copper", new Copper(new BID(Ids::CHISELED_COPPER), "Chiseled Copper", $copperBreakInfo)); + self::register("copper_grate", new CopperGrate(new BID(Ids::COPPER_GRATE), "Copper Grate", $copperBreakInfo)); self::register("cut_copper", new Copper(new BID(Ids::CUT_COPPER), "Cut Copper Block", $copperBreakInfo)); self::register("cut_copper_slab", new CopperSlab(new BID(Ids::CUT_COPPER_SLAB), "Cut Copper Slab", $copperBreakInfo)); self::register("cut_copper_stairs", new CopperStairs(new BID(Ids::CUT_COPPER_STAIRS), "Cut Copper Stairs", $copperBreakInfo)); + self::register("copper_bulb", new CopperBulb(new BID(Ids::COPPER_BULB), "Copper Bulb", $copperBreakInfo)); + + $copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); + self::register("copper_door", new CopperDoor(new BID(Ids::COPPER_DOOR), "Copper Door", $copperDoorBreakInfo)); + self::register("copper_trapdoor", new CopperTrapdoor(new BID(Ids::COPPER_TRAPDOOR), "Copper Trapdoor", $copperDoorBreakInfo)); $candleBreakInfo = new Info(new BreakInfo(0.1)); self::register("candle", new Candle(new BID(Ids::CANDLE), "Candle", $candleBreakInfo)); diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 0768d21c3..f85e84551 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -58,8 +58,12 @@ use pocketmine\block\CocoaBlock; use pocketmine\block\Concrete; use pocketmine\block\ConcretePowder; use pocketmine\block\Copper; +use pocketmine\block\CopperBulb; +use pocketmine\block\CopperDoor; +use pocketmine\block\CopperGrate; use pocketmine\block\CopperSlab; use pocketmine\block\CopperStairs; +use pocketmine\block\CopperTrapdoor; use pocketmine\block\Coral; use pocketmine\block\CoralBlock; use pocketmine\block\DaylightSensor; @@ -1255,6 +1259,40 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER) ); }); + $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_CHISELED_COPPER, + Ids::WAXED_EXPOSED_CHISELED_COPPER, + Ids::WAXED_WEATHERED_CHISELED_COPPER, + Ids::WAXED_OXIDIZED_CHISELED_COPPER + ) : + Helper::selectCopperId($oxidation, + Ids::CHISELED_COPPER, + Ids::EXPOSED_CHISELED_COPPER, + Ids::WEATHERED_CHISELED_COPPER, + Ids::OXIDIZED_CHISELED_COPPER + ) + ); + }); + $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_COPPER_GRATE, + Ids::WAXED_EXPOSED_COPPER_GRATE, + Ids::WAXED_WEATHERED_COPPER_GRATE, + Ids::WAXED_OXIDIZED_COPPER_GRATE + ) : + Helper::selectCopperId($oxidation, + Ids::COPPER_GRATE, + Ids::EXPOSED_COPPER_GRATE, + Ids::WEATHERED_COPPER_GRATE, + Ids::OXIDIZED_COPPER_GRATE + ) + ); + }); $this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{ $oxidation = $block->getOxidation(); return new Writer($block->isWaxed() ? @@ -1322,6 +1360,67 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ) ); }); + $this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{ + $oxidation = $block->getOxidation(); + return Writer::create($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_COPPER_BULB, + Ids::WAXED_EXPOSED_COPPER_BULB, + Ids::WAXED_WEATHERED_COPPER_BULB, + Ids::WAXED_OXIDIZED_COPPER_BULB) : + Helper::selectCopperId($oxidation, + Ids::COPPER_BULB, + Ids::EXPOSED_COPPER_BULB, + Ids::WEATHERED_COPPER_BULB, + Ids::OXIDIZED_COPPER_BULB + )) + ->writeBool(StateNames::LIT, $block->isLit()) + ->writeBool(StateNames::POWERED_BIT, $block->isPowered()); + }); + $this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeDoor( + $block, + new Writer($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_COPPER_DOOR, + Ids::WAXED_EXPOSED_COPPER_DOOR, + Ids::WAXED_WEATHERED_COPPER_DOOR, + Ids::WAXED_OXIDIZED_COPPER_DOOR + ) : + Helper::selectCopperId( + $oxidation, + Ids::COPPER_DOOR, + Ids::EXPOSED_COPPER_DOOR, + Ids::WEATHERED_COPPER_DOOR, + Ids::OXIDIZED_COPPER_DOOR + ) + ) + ); + }); + $this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeTrapdoor( + $block, + new Writer($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_COPPER_TRAPDOOR, + Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, + Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, + Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR + ) : + Helper::selectCopperId( + $oxidation, + Ids::COPPER_TRAPDOOR, + Ids::EXPOSED_COPPER_TRAPDOOR, + Ids::WEATHERED_COPPER_TRAPDOOR, + Ids::OXIDIZED_COPPER_TRAPDOOR + ) + ) + ); + }); $this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{ return Writer::create(Ids::COCOA) ->writeInt(StateNames::AGE, $block->getAge()) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 49365799c..19b5372a0 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -1156,6 +1156,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $block; }); + $this->map(Ids::CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE)); $this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{ return Blocks::CHISELED_QUARTZ() ->setAxis($in->readPillarAxis()); @@ -1187,6 +1188,14 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) ); $this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE)); + $this->map(Ids::COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in)); + $this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE)); + $this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in)); $this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); $this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); $this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE)); @@ -1245,9 +1254,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing($in->readCardinalHorizontalFacing()); }); $this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED)); + $this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED)); + $this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED)); $this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED)); $this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED)); $this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED)); + $this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in)); + $this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in)); $this->map(Ids::FARMLAND, function(Reader $in) : Block{ return Blocks::FARMLAND() ->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7)); @@ -1401,9 +1419,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); $this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis())); $this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED)); + $this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED)); + $this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED)); $this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED)); $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED)); $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED)); + $this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in)); + $this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in)); $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis())); $this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB()); $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ @@ -1677,25 +1704,70 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ }); $this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in)); $this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE)); + $this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE)); + $this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE)); $this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); $this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); $this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE)); + $this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in)); + $this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in)); $this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED)); + $this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED)); + $this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED)); $this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED)); $this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED)); $this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED)); + $this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in)); + $this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in)); $this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED)); + $this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED)); + $this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED)); $this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED)); $this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED)); $this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED)); + $this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in)); + $this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in)); $this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED)); + $this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED)); + $this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED)); $this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED)); $this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED)); $this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED)); + $this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in)); + $this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in)); $this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED)); + $this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED)); + $this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED)); $this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED)); $this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED)); $this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED)); + $this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{ + return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED) + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)); + }); + $this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in)); + $this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in)); $this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{ return Blocks::WEEPING_VINES() ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25)); diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index de9b5ae5e..d06b41626 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -25,7 +25,9 @@ namespace pocketmine\data\bedrock\item; use pocketmine\block\Bed; use pocketmine\block\Block; +use pocketmine\block\CopperDoor; use pocketmine\block\MobHead; +use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\DyeColor; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\data\bedrock\CompoundTypeIds; @@ -56,6 +58,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->register1to1BlockWithMetaMappings(); $this->register1to1ItemWithMetaMappings(); $this->register1ToNItemMappings(); + $this->registerMiscBlockMappings(); $this->registerMiscItemMappings(); } @@ -538,4 +541,29 @@ final class ItemSerializerDeserializerRegistrar{ } $this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor()))); } + + /** + * Registers serializers and deserializers for PocketMine-MP blockitems that don't fit any other pattern. + * Ideally we want to get rid of this completely, if possible. + * + * Most of these are single PocketMine-MP blocks which map to multiple IDs depending on their properties, which is + * complex to implement in a generic way. + */ + private function registerMiscBlockMappings() : void{ + $copperDoorStateIdMap = []; + foreach ([ + [Ids::COPPER_DOOR, CopperOxidation::NONE, false], + [Ids::EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, false], + [Ids::WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, false], + [Ids::OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, false], + [Ids::WAXED_COPPER_DOOR, CopperOxidation::NONE, true], + [Ids::WAXED_EXPOSED_COPPER_DOOR, CopperOxidation::EXPOSED, true], + [Ids::WAXED_WEATHERED_COPPER_DOOR, CopperOxidation::WEATHERED, true], + [Ids::WAXED_OXIDIZED_COPPER_DOOR, CopperOxidation::OXIDIZED, true] + ] as [$id, $oxidation, $waxed]) { + $copperDoorStateIdMap[$oxidation->value][$waxed ? 1 : 0] = $id; + $this->deserializer?->mapBlock($id, fn() => Blocks::COPPER_DOOR()->setOxidation($oxidation)->setWaxed($waxed)); + } + $this->serializer?->mapBlock(Blocks::COPPER_DOOR(), fn(CopperDoor $block) => new Data($copperDoorStateIdMap[$block->getOxidation()->value][$block->isWaxed() ? 1 : 0])); + } } diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 9f5db6950..ee0f1f5c5 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -98,9 +98,14 @@ final class StringToItemParser extends StringToTParser{ foreach(["" => false, "waxed_" => true] as $waxedPrefix => $waxed){ $register = fn(string $name, \Closure $callback) => $result->registerBlock($waxedPrefix . $oxPrefix . $name, $callback); $register("copper_block", fn() => Blocks::COPPER()->setOxidation($oxidation)->setWaxed($waxed)); + $register("chiseled_copper", fn() => Blocks::CHISELED_COPPER()->setOxidation($oxidation)->setWaxed($waxed)); + $register("copper_grate", fn() => Blocks::COPPER_GRATE()->setOxidation($oxidation)->setWaxed($waxed)); $register("cut_copper_block", fn() => Blocks::CUT_COPPER()->setOxidation($oxidation)->setWaxed($waxed)); $register("cut_copper_stairs", fn() => Blocks::CUT_COPPER_STAIRS()->setOxidation($oxidation)->setWaxed($waxed)); $register("cut_copper_slab", fn() => Blocks::CUT_COPPER_SLAB()->setOxidation($oxidation)->setWaxed($waxed)); + $register("copper_bulb", fn() => Blocks::COPPER_BULB()->setOxidation($oxidation)->setWaxed($waxed)); + $register("copper_door", fn() => Blocks::COPPER_DOOR()->setOxidation($oxidation)->setWaxed($waxed)); + $register("copper_trapdoor", fn() => Blocks::COPPER_TRAPDOOR()->setOxidation($oxidation)->setWaxed($waxed)); } } diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index de38903bd..0ead377ba 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -10,6 +10,16 @@ parameters: count: 1 path: ../../../src/block/DoubleTallGrass.php + - + message: "#^Method pocketmine\\\\block\\\\CopperDoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" + count: 1 + path: ../../../src/block/utils/CopperTrait.php + + - + message: "#^Method pocketmine\\\\block\\\\CopperTrapdoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" + count: 1 + path: ../../../src/block/utils/CopperTrait.php + - message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" count: 1 diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 97c24b52e..79804d8cb 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -105,6 +105,7 @@ "CHERRY_WOOD": 6, "CHEST": 4, "CHISELED_BOOKSHELF": 256, + "CHISELED_COPPER": 8, "CHISELED_DEEPSLATE": 1, "CHISELED_NETHER_BRICKS": 1, "CHISELED_POLISHED_BLACKSTONE": 1, @@ -133,7 +134,11 @@ "CONCRETE": 16, "CONCRETE_POWDER": 16, "COPPER": 8, + "COPPER_BULB": 32, + "COPPER_DOOR": 256, + "COPPER_GRATE": 8, "COPPER_ORE": 1, + "COPPER_TRAPDOOR": 128, "CORAL": 10, "CORAL_BLOCK": 10, "CORAL_FAN": 20, From f6e6f15c639bc09e8384416f1111eb12eff2f0b1 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Wed, 25 Sep 2024 21:28:17 +0300 Subject: [PATCH 033/290] Implemented a proper way to handle items cooldown (#6405) --- src/item/ChorusFruit.php | 4 +++ src/item/EnderPearl.php | 4 +++ src/item/Item.php | 14 +++++++++ src/item/ItemCooldownTags.php | 45 +++++++++++++++++++++++++++++ src/network/mcpe/NetworkSession.php | 10 +++++++ src/player/Player.php | 13 ++++++--- 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/item/ItemCooldownTags.php diff --git a/src/item/ChorusFruit.php b/src/item/ChorusFruit.php index c12724d7c..e10c51957 100644 --- a/src/item/ChorusFruit.php +++ b/src/item/ChorusFruit.php @@ -88,4 +88,8 @@ class ChorusFruit extends Food{ public function getCooldownTicks() : int{ return 20; } + + public function getCooldownTag() : ?string{ + return ItemCooldownTags::CHORUS_FRUIT; + } } diff --git a/src/item/EnderPearl.php b/src/item/EnderPearl.php index 76bcb358e..7109d3ae0 100644 --- a/src/item/EnderPearl.php +++ b/src/item/EnderPearl.php @@ -45,4 +45,8 @@ class EnderPearl extends ProjectileItem{ public function getCooldownTicks() : int{ return 20; } + + public function getCooldownTag() : ?string{ + return ItemCooldownTags::ENDER_PEARL; + } } diff --git a/src/item/Item.php b/src/item/Item.php index 1a74345b5..205f15e13 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -654,6 +654,20 @@ class Item implements \JsonSerializable{ return 0; } + /** + * Returns a tag that identifies a group of items that should have cooldown at the same time + * regardless of their state or type. + * When cooldown starts, any other items with the same cooldown tag can't be used until the cooldown expires. + * Such behaviour can be seen in goat horns and shields. + * + * If tag is null, item state id will be used to store cooldown. + * + * @see ItemCooldownTags + */ + public function getCooldownTag() : ?string{ + return null; + } + /** * Compares an Item to this Item and check if they match. * diff --git a/src/item/ItemCooldownTags.php b/src/item/ItemCooldownTags.php new file mode 100644 index 000000000..f0ef6d169 --- /dev/null +++ b/src/item/ItemCooldownTags.php @@ -0,0 +1,45 @@ +sendDataPacket(OpenSignPacket::create(BlockPosition::fromVector3($signPosition), $frontSide)); } + public function onItemCooldownChanged(Item $item, int $ticks) : void{ + $this->sendDataPacket(PlayerStartItemCooldownPacket::create( + GlobalItemDataHandlers::getSerializer()->serializeType($item)->getName(), + $ticks + )); + } + public function tick() : void{ if(!$this->isConnected()){ $this->dispose(); diff --git a/src/player/Player.php b/src/player/Player.php index d442c6a3b..8ae206e1a 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -283,7 +283,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ protected string $locale = "en_US"; protected int $startAction = -1; - /** @var int[] ID => ticks map */ + + /** + * @phpstan-var array + * @var int[] stateId|cooldownTag => ticks map + */ protected array $usedItemsCooldown = []; private int $lastEmoteTick = 0; @@ -697,7 +701,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ */ public function getItemCooldownExpiry(Item $item) : int{ $this->checkItemCooldowns(); - return $this->usedItemsCooldown[$item->getStateId()] ?? 0; + return $this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()] ?? 0; } /** @@ -705,7 +709,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ */ public function hasItemCooldown(Item $item) : bool{ $this->checkItemCooldowns(); - return isset($this->usedItemsCooldown[$item->getStateId()]); + return isset($this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()]); } /** @@ -714,7 +718,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function resetItemCooldown(Item $item, ?int $ticks = null) : void{ $ticks = $ticks ?? $item->getCooldownTicks(); if($ticks > 0){ - $this->usedItemsCooldown[$item->getStateId()] = $this->server->getTick() + $ticks; + $this->usedItemsCooldown[$item->getCooldownTag() ?? $item->getStateId()] = $this->server->getTick() + $ticks; + $this->getNetworkSession()->onItemCooldownChanged($item, $ticks); } } From 5cc1068cd43264d3363295eb8d6901e02f467897 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:29:22 +0000 Subject: [PATCH 034/290] Bump docker/build-push-action from 6.7.0 to 6.8.0 (#6462) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 437ed963f..7f5eae32a 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.8.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.8.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.8.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.8.0 with: push: true context: ./pocketmine-mp From c8f567b0937c406097b9cc26fc26b5cd4c7ea21d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 17 Oct 2024 20:24:57 +0100 Subject: [PATCH 035/290] Fix missing arg count check --- tools/blockstate-upgrade-schema-utils.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index a9069d429..cd782b9c4 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -745,6 +745,10 @@ function main(array $argv) : int{ } $callback = $options[$selected][1]; + if(count($argv) !== count($options[$selected][0]) + 2){ + fwrite(STDERR, "Usage: {$argv[0]} $selected " . implode(" ", array_map(fn(string $a) => "<$a>", $options[$selected][0])) . "\n"); + return 1; + } return $callback($argv); } From 59d14de1d83d9babd30a9c45965feae90374d6ba Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 17 Oct 2024 20:51:17 +0100 Subject: [PATCH 036/290] generate-blockstate-upgrade-schema: fallback to exact state match when encountering ambiguous filters this popped up due to new changes in 1.20.40. Really we need to improve the way the filters are calculated, but this workaround solves the issue for now. --- tools/generate-blockstate-upgrade-schema.php | 27 ++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index 54984d459..e5eab822f 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -496,8 +496,31 @@ function processRemappedStates(array $upgradeTable) : array{ if($existing === null || $existing->equals($remap)){ $list[$rawOldState] = $remap; }else{ - //match criteria is borked - throw new AssumptionFailedError("Match criteria resulted in two ambiguous remaps"); + //TODO: ambiguous filter - this is a bug in the unchanged states calculation + //this is a real pain to fix, so workaround this for now + //this arose in 1.20.40 with brown_mushroom_block when variants 10 and 15 were remapped to mushroom_stem + //while also keeping the huge_mushroom_bits property with the same value + //this causes huge_mushroom_bits to be considered an "unchanged" state, which is *technically* correct, but + //means it can't be deleted from the filter + + //move stuff from newState to copiedState where possible, even if we can't delete it from the filter + $cleanedNewState2 = $newState; + $copiedState = []; + foreach(Utils::stringifyKeys($cleanedNewState2) as $newPropertyName => $newPropertyValue){ + if(isset($oldState[$newPropertyName]) && $oldState[$newPropertyName]->equals($newPropertyValue)){ + $copiedState[] = $newPropertyName; + unset($cleanedNewState2[$newPropertyName]); + } + } + + $list[encodeOrderedProperties($oldState)] = new BlockStateUpgradeSchemaBlockRemap( + $oldState, + $newName, + $cleanedNewState2, + $copiedState + ); + \GlobalLogger::get()->warning("Couldn't calculate an unambiguous partial remappedStates filter for some states of \"" . $pair->old->getName() . "\" - falling back to exact match"); + \GlobalLogger::get()->warning("The schema should still work, but may be larger than desired"); } } From f1b1a7022d7dc67d012d8891bc4c23c2652c825e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 17 Oct 2024 20:55:12 +0100 Subject: [PATCH 037/290] and a sanity check just in case --- tools/generate-blockstate-upgrade-schema.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index e5eab822f..741098f7a 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -513,7 +513,11 @@ function processRemappedStates(array $upgradeTable) : array{ } } - $list[encodeOrderedProperties($oldState)] = new BlockStateUpgradeSchemaBlockRemap( + $fallbackRawFilter = encodeOrderedProperties($oldState); + if(isset($list[$fallbackRawFilter])){ + throw new AssumptionFailedError("Exact match filter collision for \"" . $pair->old->getName() . "\" - this should never happen"); + } + $list[$fallbackRawFilter] = new BlockStateUpgradeSchemaBlockRemap( $oldState, $newName, $cleanedNewState2, From 7e343617b9641875c1c6cea58957d94034d0cdde Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:34:42 +0300 Subject: [PATCH 038/290] Rename ICopper to CopperMaterial (#6470) --- src/block/Copper.php | 4 ++-- src/block/CopperBulb.php | 4 ++-- src/block/CopperDoor.php | 4 ++-- src/block/CopperGrate.php | 4 ++-- src/block/CopperSlab.php | 4 ++-- src/block/CopperStairs.php | 4 ++-- src/block/CopperTrapdoor.php | 4 ++-- src/block/utils/{ICopper.php => CopperMaterial.php} | 6 +++--- .../block/convert/BlockStateDeserializerHelper.php | 10 +++++----- 9 files changed, 22 insertions(+), 22 deletions(-) rename src/block/utils/{ICopper.php => CopperMaterial.php} (85%) diff --git a/src/block/Copper.php b/src/block/Copper.php index 8e678bf42..d285e6ec0 100644 --- a/src/block/Copper.php +++ b/src/block/Copper.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; -class Copper extends Opaque implements ICopper{ +class Copper extends Opaque implements CopperMaterial{ use CopperTrait; } diff --git a/src/block/CopperBulb.php b/src/block/CopperBulb.php index 223c63527..97fc209fe 100644 --- a/src/block/CopperBulb.php +++ b/src/block/CopperBulb.php @@ -23,14 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; use pocketmine\block\utils\LightableTrait; use pocketmine\block\utils\PoweredByRedstoneTrait; use pocketmine\data\runtime\RuntimeDataDescriber; -class CopperBulb extends Opaque implements ICopper{ +class CopperBulb extends Opaque implements CopperMaterial{ use CopperTrait; use PoweredByRedstoneTrait; use LightableTrait{ diff --git a/src/block/CopperDoor.php b/src/block/CopperDoor.php index d53be2323..82a611206 100644 --- a/src/block/CopperDoor.php +++ b/src/block/CopperDoor.php @@ -23,14 +23,14 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -class CopperDoor extends Door implements ICopper{ +class CopperDoor extends Door implements CopperMaterial{ use CopperTrait{ onInteract as onInteractCopper; } diff --git a/src/block/CopperGrate.php b/src/block/CopperGrate.php index fb59d846e..d646d1333 100644 --- a/src/block/CopperGrate.php +++ b/src/block/CopperGrate.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; -class CopperGrate extends Transparent implements ICopper{ +class CopperGrate extends Transparent implements CopperMaterial{ use CopperTrait; //TODO: waterlogging! diff --git a/src/block/CopperSlab.php b/src/block/CopperSlab.php index 4194cd854..cc1838e29 100644 --- a/src/block/CopperSlab.php +++ b/src/block/CopperSlab.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; -class CopperSlab extends Slab implements ICopper{ +class CopperSlab extends Slab implements CopperMaterial{ use CopperTrait; } diff --git a/src/block/CopperStairs.php b/src/block/CopperStairs.php index dd8f44f7a..ecb224319 100644 --- a/src/block/CopperStairs.php +++ b/src/block/CopperStairs.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; -class CopperStairs extends Stair implements ICopper{ +class CopperStairs extends Stair implements CopperMaterial{ use CopperTrait; } diff --git a/src/block/CopperTrapdoor.php b/src/block/CopperTrapdoor.php index ab743af44..e7d56fa0c 100644 --- a/src/block/CopperTrapdoor.php +++ b/src/block/CopperTrapdoor.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperTrait; -use pocketmine\block\utils\ICopper; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\player\Player; -class CopperTrapdoor extends Trapdoor implements ICopper{ +class CopperTrapdoor extends Trapdoor implements CopperMaterial{ use CopperTrait{ onInteract as onInteractCopper; } diff --git a/src/block/utils/ICopper.php b/src/block/utils/CopperMaterial.php similarity index 85% rename from src/block/utils/ICopper.php rename to src/block/utils/CopperMaterial.php index a749efe63..6df22620b 100644 --- a/src/block/utils/ICopper.php +++ b/src/block/utils/CopperMaterial.php @@ -26,13 +26,13 @@ namespace pocketmine\block\utils; /** * Represents copper blocks that have oxidized and waxed variations. */ -interface ICopper{ +interface CopperMaterial{ public function getOxidation() : CopperOxidation; - public function setOxidation(CopperOxidation $oxidation) : ICopper; + public function setOxidation(CopperOxidation $oxidation) : CopperMaterial; public function isWaxed() : bool; - public function setWaxed(bool $waxed) : ICopper; + public function setWaxed(bool $waxed) : CopperMaterial; } diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index c0807c8a6..e183589c9 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -45,8 +45,8 @@ use pocketmine\block\Slab; use pocketmine\block\Stair; use pocketmine\block\Stem; use pocketmine\block\Trapdoor; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; -use pocketmine\block\utils\ICopper; use pocketmine\block\utils\SlabType; use pocketmine\block\VanillaBlocks; use pocketmine\block\Wall; @@ -98,24 +98,24 @@ final class BlockStateDeserializerHelper{ } /** - * @phpstan-template TBlock of ICopper + * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block * @phpstan-return TBlock */ - public static function decodeCopper(ICopper $block, CopperOxidation $oxidation) : ICopper{ + public static function decodeCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{ $block->setOxidation($oxidation); $block->setWaxed(false); return $block; } /** - * @phpstan-template TBlock of ICopper + * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block * @phpstan-return TBlock */ - public static function decodeWaxedCopper(ICopper $block, CopperOxidation $oxidation) : ICopper{ + public static function decodeWaxedCopper(CopperMaterial $block, CopperOxidation $oxidation) : CopperMaterial{ $block->setOxidation($oxidation); $block->setWaxed(true); return $block; From 7f9e79c83e1130f3cf74c33917a33ff6fd7a074b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 24 Oct 2024 13:30:24 +0100 Subject: [PATCH 039/290] Automatically test new schemas to ensure they produce the results predicted by the input file --- tools/blockstate-upgrade-schema-utils.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index a779ab9c3..c8dfb0ab2 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -692,6 +692,13 @@ function cmdGenerate(array $argv) : int{ \GlobalLogger::get()->warning("All states appear to be the same! No schema generated."); return 0; } + + if(!testBlockStateUpgradeSchema($table, $diff)){ + \GlobalLogger::get()->error("Generated schema does not produce the results expected by $upgradeTableFile"); + \GlobalLogger::get()->error("This is probably a bug in the schema generation code. Please report this to the developers."); + return 1; + } + file_put_contents( $schemaFile, json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($diff), JSON_PRETTY_PRINT) . "\n" @@ -744,6 +751,13 @@ function cmdUpdate(array $argv) : int{ $upgradeTable = buildUpgradeTableFromData($tags, false); $newSchema = generateBlockStateUpgradeSchema($upgradeTable); + + if(!testBlockStateUpgradeSchema($upgradeTable, $newSchema)){ + \GlobalLogger::get()->error("Updated schema does not produce the expected results!"); + \GlobalLogger::get()->error("This is probably a bug in the schema generation code. Please report this to the developers."); + return 1; + } + file_put_contents( $newSchemaFile, json_encode(BlockStateUpgradeSchemaUtils::toJsonModel($newSchema), JSON_PRETTY_PRINT) . "\n" From acbfb0a3e9a8e2d4be25836d7cd359461c7e4b73 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 24 Oct 2024 13:30:55 +0100 Subject: [PATCH 040/290] Support for updating a batch of schemas using BlockPaletteArchive --- tools/blockstate-upgrade-schema-utils.php | 50 ++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index c8dfb0ab2..b73954aff 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -41,6 +41,7 @@ use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; +use Symfony\Component\Filesystem\Path; use function array_key_first; use function array_key_last; use function array_keys; @@ -50,15 +51,19 @@ use function array_unique; use function array_values; use function count; use function dirname; +use function file_exists; use function file_put_contents; use function fwrite; use function get_class; use function get_debug_type; use function implode; +use function is_dir; use function is_numeric; use function json_encode; use function ksort; use function min; +use function preg_match; +use function scandir; use function sort; use function strlen; use function strrev; @@ -766,6 +771,48 @@ function cmdUpdate(array $argv) : int{ return 0; } +/** + * @param string[] $argv + */ +function cmdUpdateAll(array $argv) : int{ + $oldPaletteFilenames = [ + '1.9.0' => '1.09.0', + '1.19.50' => '1.19.50.23_beta', + '1.19.60' => '1.19.60.26_beta', + '1.19.70' => '1.19.70.26_beta', + '1.19.80' => '1.19.80.24_beta', + ]; + $schemaDir = $argv[2]; + $paletteArchiveDir = $argv[3]; + + $schemaFileNames = scandir($schemaDir); + if($schemaFileNames === false){ + \GlobalLogger::get()->error("Failed to read schema directory $schemaDir"); + return 1; + } + foreach($schemaFileNames as $file){ + $schemaFile = Path::join($schemaDir, $file); + if(!file_exists($schemaFile) || is_dir($schemaFile)){ + continue; + } + + if(preg_match('/^\d{4}_(.+?)_to_(.+?).json/', $file, $matches) !== 1){ + continue; + } + $oldPaletteFile = Path::join($paletteArchiveDir, ($oldPaletteFilenames[$matches[1]] ?? $matches[1]) . '.nbt'); + + //a bit clunky but it avoids having to make yet another function + //TODO: perhaps in the future we should write the result to a tmpfile until all schemas are updated, + //and then copy the results into place at the end + if(cmdUpdate([$argv[0], "update", $schemaFile, $oldPaletteFile, $schemaFile]) !== 0){ + return 1; + } + } + + \GlobalLogger::get()->info("All schemas updated successfully."); + return 0; +} + /** * @param string[] $argv */ @@ -773,7 +820,8 @@ function main(array $argv) : int{ $options = [ "generate" => [["palette upgrade table file", "schema output file"], cmdGenerate(...)], "test" => [["palette upgrade table file", "schema output file"], cmdTest(...)], - "update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)] + "update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)], + "update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)] ]; $selected = $argv[1] ?? null; From 22718c4971460fae5b4d98fabff199da77c6acf0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 24 Oct 2024 16:12:28 +0100 Subject: [PATCH 041/290] Add support for specialized flattenedProperties in schema format --- .../block/upgrade/BlockStateUpgradeSchema.php | 8 + .../BlockStateUpgradeSchemaBlockRemap.php | 6 +- ...=> BlockStateUpgradeSchemaFlattenInfo.php} | 2 +- .../upgrade/BlockStateUpgradeSchemaUtils.php | 71 +++++--- .../block/upgrade/BlockStateUpgrader.php | 62 +++++-- .../model/BlockStateUpgradeSchemaModel.php | 6 + ...BlockStateUpgradeSchemaModelBlockRemap.php | 6 +- ...ockStateUpgradeSchemaModelFlattenInfo.php} | 2 +- .../block/upgrade/BlockStateUpgraderTest.php | 19 ++ tools/blockstate-upgrade-schema-utils.php | 170 +++++++++++++----- 10 files changed, 251 insertions(+), 101 deletions(-) rename src/data/bedrock/block/upgrade/{BlockStateUpgradeSchemaFlattenedName.php => BlockStateUpgradeSchemaFlattenInfo.php} (97%) rename src/data/bedrock/block/upgrade/model/{BlockStateUpgradeSchemaModelFlattenedName.php => BlockStateUpgradeSchemaModelFlattenInfo.php} (95%) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php index 6d280ecf7..f8894cfd2 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; +use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenInfo as FlattenInfo; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap as ValueRemap; use pocketmine\nbt\tag\Tag; use function count; @@ -58,6 +59,12 @@ final class BlockStateUpgradeSchema{ */ public array $remappedPropertyValues = []; + /** + * @var FlattenInfo[] + * @phpstan-var array + */ + public array $flattenedProperties = []; + /** * @var BlockStateUpgradeSchemaBlockRemap[][] * @phpstan-var array> @@ -93,6 +100,7 @@ final class BlockStateUpgradeSchema{ $this->removedProperties, $this->renamedProperties, $this->remappedPropertyValues, + $this->flattenedProperties, $this->remappedStates, ] as $list){ if(count($list) !== 0){ diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaBlockRemap.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaBlockRemap.php index 611ad04e2..676afbaf4 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaBlockRemap.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaBlockRemap.php @@ -40,7 +40,7 @@ final class BlockStateUpgradeSchemaBlockRemap{ */ public function __construct( public array $oldState, - public string|BlockStateUpgradeSchemaFlattenedName $newName, + public string|BlockStateUpgradeSchemaFlattenInfo $newName, public array $newState, public array $copiedState ){} @@ -48,8 +48,8 @@ final class BlockStateUpgradeSchemaBlockRemap{ public function equals(self $that) : bool{ $sameName = $this->newName === $that->newName || ( - $this->newName instanceof BlockStateUpgradeSchemaFlattenedName && - $that->newName instanceof BlockStateUpgradeSchemaFlattenedName && + $this->newName instanceof BlockStateUpgradeSchemaFlattenInfo && + $that->newName instanceof BlockStateUpgradeSchemaFlattenInfo && $this->newName->equals($that->newName) ); if(!$sameName){ diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenInfo.php similarity index 97% rename from src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php rename to src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenInfo.php index 8259f690d..4a14a1291 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenInfo.php @@ -29,7 +29,7 @@ use pocketmine\nbt\tag\StringTag; use function ksort; use const SORT_STRING; -final class BlockStateUpgradeSchemaFlattenedName{ +final class BlockStateUpgradeSchemaFlattenInfo{ /** * @param string[] $flattenedValueRemaps diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php index d66b7e68c..08eba8978 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php @@ -25,7 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModel; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap; -use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenedName; +use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelFlattenInfo; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelTag; use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap; use pocketmine\nbt\tag\ByteTag; @@ -155,24 +155,17 @@ final class BlockStateUpgradeSchemaUtils{ } } + foreach(Utils::stringifyKeys($model->flattenedProperties ?? []) as $blockName => $flattenRule){ + $result->flattenedProperties[$blockName] = self::jsonModelToFlattenRule($flattenRule); + } + foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){ foreach($remaps as $remap){ if(isset($remap->newName)){ $remapName = $remap->newName; }elseif(isset($remap->newFlattenedName)){ $flattenRule = $remap->newFlattenedName; - $remapName = new BlockStateUpgradeSchemaFlattenedName( - $flattenRule->prefix, - $flattenRule->flattenedProperty, - $flattenRule->suffix, - $flattenRule->flattenedValueRemaps ?? [], - match($flattenRule->flattenedPropertyType){ - "string", null => StringTag::class, - "int" => IntTag::class, - "byte" => ByteTag::class, - default => throw new \UnexpectedValueException("Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'") - } - ); + $remapName = self::jsonModelToFlattenRule($flattenRule); }else{ throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set"); } @@ -265,6 +258,36 @@ final class BlockStateUpgradeSchemaUtils{ $model->remappedPropertyValues = $modelDedupMapping; } + private static function flattenRuleToJsonModel(BlockStateUpgradeSchemaFlattenInfo $flattenRule) : BlockStateUpgradeSchemaModelFlattenInfo{ + return new BlockStateUpgradeSchemaModelFlattenInfo( + $flattenRule->prefix, + $flattenRule->flattenedProperty, + $flattenRule->suffix, + $flattenRule->flattenedValueRemaps, + match($flattenRule->flattenedPropertyType){ + StringTag::class => null, //omit for TAG_String, as this is the common case + ByteTag::class => "byte", + IntTag::class => "int", + default => throw new \LogicException("Unexpected tag type " . $flattenRule->flattenedPropertyType . " in flattened property type") + } + ); + } + + private static function jsonModelToFlattenRule(BlockStateUpgradeSchemaModelFlattenInfo $flattenRule) : BlockStateUpgradeSchemaFlattenInfo{ + return new BlockStateUpgradeSchemaFlattenInfo( + $flattenRule->prefix, + $flattenRule->flattenedProperty, + $flattenRule->suffix, + $flattenRule->flattenedValueRemaps ?? [], + match ($flattenRule->flattenedPropertyType) { + "string", null => StringTag::class, + "int" => IntTag::class, + "byte" => ByteTag::class, + default => throw new \UnexpectedValueException("Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'") + } + ); + } + public static function toJsonModel(BlockStateUpgradeSchema $schema) : BlockStateUpgradeSchemaModel{ $result = new BlockStateUpgradeSchemaModel(); $result->maxVersionMajor = $schema->maxVersionMajor; @@ -303,25 +326,19 @@ final class BlockStateUpgradeSchemaUtils{ self::buildRemappedValuesIndex($schema, $result); + foreach(Utils::stringifyKeys($schema->flattenedProperties) as $blockName => $flattenRule){ + $result->flattenedProperties[$blockName] = self::flattenRuleToJsonModel($flattenRule); + } + if(isset($result->flattenedProperties)){ + ksort($result->flattenedProperties); + } + foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){ $keyedRemaps = []; foreach($remaps as $remap){ $modelRemap = new BlockStateUpgradeSchemaModelBlockRemap( array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState), - is_string($remap->newName) ? - $remap->newName : - new BlockStateUpgradeSchemaModelFlattenedName( - $remap->newName->prefix, - $remap->newName->flattenedProperty, - $remap->newName->suffix, - $remap->newName->flattenedValueRemaps, - match($remap->newName->flattenedPropertyType){ - StringTag::class => null, //omit for TAG_String, as this is the common case - ByteTag::class => "byte", - IntTag::class => "int", - default => throw new \LogicException("Unexpected tag type " . $remap->newName->flattenedPropertyType . " in flattened property type") - } - ), + is_string($remap->newName) ? $remap->newName : self::flattenRuleToJsonModel($remap->newName), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState), $remap->copiedState ); diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index b0612585c..e95f3c80f 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -110,10 +110,21 @@ final class BlockStateUpgrader{ } $oldName = $blockStateData->getName(); - $newName = $schema->renamedIds[$oldName] ?? null; + $states = $blockStateData->getStates(); + + if(isset($schema->renamedIds[$oldName]) && isset($schema->flattenedProperties[$oldName])){ + //TODO: this probably ought to be validated when the schema is constructed + throw new AssumptionFailedError("Both renamedIds and flattenedProperties are set for the same block ID \"$oldName\" - don't know what to do"); + } + if(isset($schema->renamedIds[$oldName])){ + $newName = $schema->renamedIds[$oldName] ?? null; + }elseif(isset($schema->flattenedProperties[$oldName])){ + [$newName, $states] = $this->applyPropertyFlattened($schema->flattenedProperties[$oldName], $oldName, $states); + }else{ + $newName = null; + } $stateChanges = 0; - $states = $blockStateData->getStates(); $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges); $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges); @@ -146,22 +157,9 @@ final class BlockStateUpgrader{ if(is_string($remap->newName)){ $newName = $remap->newName; }else{ - $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; - $expectedType = $remap->newName->flattenedPropertyType; - if(!$flattenedValue instanceof $expectedType){ - //flattened property is not of the expected type, so this transformation is not applicable - continue; - } - $embedKey = match(get_class($flattenedValue)){ - StringTag::class => $flattenedValue->getValue(), - ByteTag::class => (string) $flattenedValue->getValue(), - IntTag::class => (string) $flattenedValue->getValue(), - //flattenedPropertyType is always one of these three types, but PHPStan doesn't know that - default => throw new AssumptionFailedError("flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)), - }; - $embedValue = $remap->newName->flattenedValueRemaps[$embedKey] ?? $embedKey; - $newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix); - unset($oldState[$remap->newName->flattenedProperty]); + //yes, overwriting $oldState here is intentional, although we probably don't actually need it anyway + //it shouldn't make any difference unless the flattened property appears in copiedState for some reason + [$newName, $oldState] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState); } $newState = $remap->newState; @@ -279,4 +277,32 @@ final class BlockStateUpgrader{ return $states; } + + /** + * @param Tag[] $states + * @phpstan-param array $states + * + * @return (string|Tag[])[] + * @phpstan-return array{0: string, 1: array} + */ + private function applyPropertyFlattened(BlockStateUpgradeSchemaFlattenInfo $flattenInfo, string $oldName, array $states) : array{ + $flattenedValue = $states[$flattenInfo->flattenedProperty] ?? null; + $expectedType = $flattenInfo->flattenedPropertyType; + if(!$flattenedValue instanceof $expectedType){ + //flattened property is not of the expected type, so this transformation is not applicable + return [$oldName, $states]; + } + $embedKey = match(get_class($flattenedValue)){ + StringTag::class => $flattenedValue->getValue(), + ByteTag::class => (string) $flattenedValue->getValue(), + IntTag::class => (string) $flattenedValue->getValue(), + //flattenedPropertyType is always one of these three types, but PHPStan doesn't know that + default => throw new AssumptionFailedError("flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)), + }; + $embedValue = $flattenInfo->flattenedValueRemaps[$embedKey] ?? $embedKey; + $newName = sprintf("%s%s%s", $flattenInfo->prefix, $embedValue, $flattenInfo->suffix); + unset($states[$flattenInfo->flattenedProperty]); + + return [$newName, $states]; + } } diff --git a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModel.php b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModel.php index 1a4a14c87..7d91438e4 100644 --- a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModel.php +++ b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModel.php @@ -75,6 +75,12 @@ final class BlockStateUpgradeSchemaModel implements \JsonSerializable{ */ public array $remappedPropertyValuesIndex; + /** + * @var BlockStateUpgradeSchemaModelFlattenInfo[] + * @phpstan-var array + */ + public array $flattenedProperties; + /** * @var BlockStateUpgradeSchemaModelBlockRemap[][] * @phpstan-var array> diff --git a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelBlockRemap.php b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelBlockRemap.php index 0f518479e..6accf1f02 100644 --- a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelBlockRemap.php +++ b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelBlockRemap.php @@ -43,7 +43,7 @@ final class BlockStateUpgradeSchemaModelBlockRemap{ * Either this or newName must be present * Due to technical limitations of jsonmapper, we can't use a union type here */ - public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName; + public BlockStateUpgradeSchemaModelFlattenInfo $newFlattenedName; /** * @var BlockStateUpgradeSchemaModelTag[]|null @@ -67,9 +67,9 @@ final class BlockStateUpgradeSchemaModelBlockRemap{ * @phpstan-param array $newState * @phpstan-param list $copiedState */ - public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenedName $newNameRule, array $newState, array $copiedState){ + public function __construct(array $oldState, string|BlockStateUpgradeSchemaModelFlattenInfo $newNameRule, array $newState, array $copiedState){ $this->oldState = count($oldState) === 0 ? null : $oldState; - if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){ + if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenInfo){ $this->newFlattenedName = $newNameRule; }else{ $this->newName = $newNameRule; diff --git a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenInfo.php similarity index 95% rename from src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php rename to src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenInfo.php index 6d03bbc12..6da590287 100644 --- a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php +++ b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenInfo.php @@ -25,7 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade\model; use function count; -final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{ +final class BlockStateUpgradeSchemaModelFlattenInfo implements \JsonSerializable{ /** @required */ public string $prefix; diff --git a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php index 4d4d321ec..91afd8ed9 100644 --- a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php +++ b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php @@ -24,8 +24,10 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; use PHPUnit\Framework\TestCase; +use pocketmine\block\Block; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\nbt\tag\IntTag; +use pocketmine\nbt\tag\StringTag; use const PHP_INT_MAX; class BlockStateUpgraderTest extends TestCase{ @@ -210,6 +212,23 @@ class BlockStateUpgraderTest extends TestCase{ self::assertSame($upgradedStateData->getState(self::TEST_PROPERTY_2)?->getValue(), $valueAfter); } + public function testFlattenProperty() : void{ + $schema = $this->getNewSchema(); + $schema->flattenedProperties[self::TEST_BLOCK] = new BlockStateUpgradeSchemaFlattenInfo( + "minecraft:", + "test", + "_suffix", + [], + StringTag::class + ); + + $stateData = new BlockStateData(self::TEST_BLOCK, ["test" => new StringTag("value1")], 0); + $upgradedStateData = $this->upgrade($stateData, fn() => $stateData); + + self::assertSame("minecraft:value1_suffix", $upgradedStateData->getName()); + self::assertEmpty($upgradedStateData->getStates()); + } + /** * @phpstan-return \Generator */ diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index b73954aff..e1a5e7c3b 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -27,7 +27,7 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap; -use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName; +use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenInfo; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap; use pocketmine\nbt\LittleEndianNbtSerializer; @@ -183,6 +183,11 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra $removedProperties = []; $renamedProperties = []; + $uniqueNewIds = []; + foreach($upgradeTable as $pair){ + $uniqueNewIds[$pair->new->getName()] = $pair->new->getName(); + } + foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){ if(count($newPropertyValues) === 1){ $newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)]; @@ -278,6 +283,61 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra } } + if(count($uniqueNewIds) > 1){ + //detect possible flattening + $flattenedProperty = null; + $flattenedPropertyType = null; + $flattenedPropertyMap = []; + foreach($removedProperties as $removedProperty){ + $valueMap = []; + foreach($upgradeTable as $pair){ + $oldValue = $pair->old->getState($removedProperty); + if($oldValue === null){ + throw new AssumptionFailedError("We already checked that all states had consistent old properties"); + } + //TODO: lots of similar logic to the remappedStates builder below + if(!$oldValue instanceof ByteTag && !$oldValue instanceof IntTag && !$oldValue instanceof StringTag){ + //unknown property type - bad candidate for flattening + continue 2; + } + if($flattenedPropertyType === null){ + $flattenedPropertyType = get_class($oldValue); + }elseif(!$oldValue instanceof $flattenedPropertyType){ + //property type mismatch - bad candidate for flattening + continue 2; + } + + $rawValue = (string) $oldValue->getValue(); + $existingNewId = $valueMap[$rawValue] ?? null; + if($existingNewId !== null && $existingNewId !== $pair->new->getName()){ + //this property value is associated with multiple new IDs - bad candidate for flattening + continue 2; + } + $valueMap[$rawValue] = $pair->new->getName(); + } + + if($flattenedProperty !== null){ + //found multiple candidates for flattening - fallback to remappedStates + return false; + } + //we found a suitable candidate + $flattenedProperty = $removedProperty; + $flattenedPropertyMap = $valueMap; + break; + } + + if($flattenedProperty === null){ + //can't figure out how the new IDs are related to the old states - fallback to remappedStates + return false; + } + if($flattenedPropertyType === null){ + throw new AssumptionFailedError("This should never happen at this point"); + } + + $result->flattenedProperties[$oldName] = buildFlattenPropertyRule($flattenedPropertyMap, $flattenedProperty, $flattenedPropertyType); + unset($removedProperties[$flattenedProperty]); + } + //finally, write the results to the schema if(count($remappedPropertyValues) !== 0){ @@ -332,60 +392,69 @@ function findCommonSuffix(array $strings) : string{ return strrev(findCommonPrefix($reversed)); } +/** + * @param string[] $valueToId + * @phpstan-param array $valueToId + * @phpstan-param class-string $propertyType + */ +function buildFlattenPropertyRule(array $valueToId, string $propertyName, string $propertyType) : BlockStateUpgradeSchemaFlattenInfo{ + $ids = array_values($valueToId); + + //TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that + //"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft. + //This also causes more remaps to be generated than necessary, since some of the values are already + //contained in the new ID. + $idPrefix = findCommonPrefix($ids); + $idSuffix = findCommonSuffix($ids); + if(strlen($idSuffix) < 2){ + $idSuffix = ""; + } + + $valueMap = []; + foreach(Utils::stringifyKeys($valueToId) as $value => $newId){ + $newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null); + if($newValue !== $value){ + $valueMap[$value] = $newValue; + } + } + + $allNumeric = true; + if(count($valueMap) > 0){ + foreach(Utils::stringifyKeys($valueMap) as $value => $newValue){ + if(!is_numeric($value)){ + $allNumeric = false; + break; + } + } + if($allNumeric){ + //add a dummy key to force the JSON to be an object and not a list + $valueMap["dummy"] = "map_not_list"; + } + } + + return new BlockStateUpgradeSchemaFlattenInfo( + $idPrefix, + $propertyName, + $idSuffix, + $valueMap, + $propertyType, + ); +} + /** * @param string[][][] $candidateFlattenedValues * @phpstan-param array>> $candidateFlattenedValues * @param string[] $candidateFlattenPropertyTypes * @phpstan-param array> $candidateFlattenPropertyTypes * - * @return BlockStateUpgradeSchemaFlattenedName[][] - * @phpstan-return array> + * @return BlockStateUpgradeSchemaFlattenInfo[][] + * @phpstan-return array> */ function buildFlattenPropertyRules(array $candidateFlattenedValues, array $candidateFlattenPropertyTypes) : array{ $flattenPropertyRules = []; foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){ - $ids = array_values($valueToId); - - //TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that - //"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft. - //This also causes more remaps to be generated than necessary, since some of the values are already - //contained in the new ID. - $idPrefix = findCommonPrefix($ids); - $idSuffix = findCommonSuffix($ids); - if(strlen($idSuffix) < 2){ - $idSuffix = ""; - } - - $valueMap = []; - foreach(Utils::stringifyKeys($valueToId) as $value => $newId){ - $newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null); - if($newValue !== $value){ - $valueMap[$value] = $newValue; - } - } - - $allNumeric = true; - if(count($valueMap) > 0){ - foreach(Utils::stringifyKeys($valueMap) as $value => $newValue){ - if(!is_numeric($value)){ - $allNumeric = false; - break; - } - } - if($allNumeric){ - //add a dummy key to force the JSON to be an object and not a list - $valueMap["dummy"] = "map_not_list"; - } - } - - $flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName( - $idPrefix, - $propertyName, - $idSuffix, - $valueMap, - $candidateFlattenPropertyTypes[$propertyName], - ); + $flattenPropertyRules[$propertyName][$filter] = buildFlattenPropertyRule($valueToId, $propertyName, $candidateFlattenPropertyTypes[$propertyName]); } } ksort($flattenPropertyRules, SORT_STRING); @@ -642,10 +711,15 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad throw new \RuntimeException("States with the same ID should be fully consistent"); } }else{ - //block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap - //even if some of the states stay under the same ID, the compression techniques used by this function - //implicitly rely on knowing the full set of old states and their new transformations - $result->remappedStates[$oldName] = processRemappedStates($blockStateMappings); + //try processing this as a regular state group first + //if a property was flattened into the ID, the remaining states will normally be consistent + //if not we fall back to remap states and state filters + if(!processStateGroup($oldName, $blockStateMappings, $result)){ + //block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap + //even if some of the states stay under the same ID, the compression techniques used by this function + //implicitly rely on knowing the full set of old states and their new transformations + $result->remappedStates[$oldName] = processRemappedStates($blockStateMappings); + } } } From d01203d7c4d48be9c7a92dbfa385ac8844ea1841 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 24 Oct 2024 18:50:28 +0100 Subject: [PATCH 042/290] Reduce code duplication --- tools/blockstate-upgrade-schema-utils.php | 81 +++++++++++------------ 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index e1a5e7c3b..b7a9a4169 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -295,25 +295,9 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra if($oldValue === null){ throw new AssumptionFailedError("We already checked that all states had consistent old properties"); } - //TODO: lots of similar logic to the remappedStates builder below - if(!$oldValue instanceof ByteTag && !$oldValue instanceof IntTag && !$oldValue instanceof StringTag){ - //unknown property type - bad candidate for flattening + if(!checkFlattenPropertySuitability($oldValue, $flattenedPropertyType, $pair->new->getName(), $valueMap)){ continue 2; } - if($flattenedPropertyType === null){ - $flattenedPropertyType = get_class($oldValue); - }elseif(!$oldValue instanceof $flattenedPropertyType){ - //property type mismatch - bad candidate for flattening - continue 2; - } - - $rawValue = (string) $oldValue->getValue(); - $existingNewId = $valueMap[$rawValue] ?? null; - if($existingNewId !== null && $existingNewId !== $pair->new->getName()){ - //this property value is associated with multiple new IDs - bad candidate for flattening - continue 2; - } - $valueMap[$rawValue] = $pair->new->getName(); } if($flattenedProperty !== null){ @@ -392,6 +376,37 @@ function findCommonSuffix(array $strings) : string{ return strrev(findCommonPrefix($reversed)); } +/** + * @param string[] $valueToIdMap + * @phpstan-param ?class-string $expectedType + * @phpstan-param-out class-string $expectedType + * @phpstan-param array $valueToIdMap + * @phpstan-param-out array $valueToIdMap + */ +function checkFlattenPropertySuitability(Tag $oldValue, ?string &$expectedType, string $actualNewId, array &$valueToIdMap) : bool{ + //TODO: lots of similar logic to the remappedStates builder below + if(!$oldValue instanceof ByteTag && !$oldValue instanceof IntTag && !$oldValue instanceof StringTag){ + //unknown property type - bad candidate for flattening + return false; + } + if($expectedType === null){ + $expectedType = get_class($oldValue); + }elseif(!$oldValue instanceof $expectedType){ + //property type mismatch - bad candidate for flattening + return false; + } + + $rawValue = (string) $oldValue->getValue(); + $existingNewId = $valueToIdMap[$rawValue] ?? null; + if($existingNewId !== null && $existingNewId !== $actualNewId){ + //this property value is associated with multiple new IDs - bad candidate for flattening + return false; + } + $valueToIdMap[$rawValue] = $actualNewId; + + return true; +} + /** * @param string[] $valueToId * @phpstan-param array $valueToId @@ -522,23 +537,6 @@ function processRemappedStates(array $upgradeTable) : array{ if(isset($notFlattenedProperties[$propertyName])){ continue; } - if(!$propertyValue instanceof StringTag && !$propertyValue instanceof IntTag && !$propertyValue instanceof ByteTag){ - $notFlattenedProperties[$propertyName] = true; - continue; - } - $previousType = $candidateFlattenedPropertyTypes[$propertyName] ?? null; - if($previousType !== null && $previousType !== get_class($propertyValue)){ - //mismatched types for the same property name - this has never happened so far, but it's not impossible - $notFlattenedProperties[$propertyName] = true; - continue; - } - $candidateFlattenedPropertyTypes[$propertyName] = get_class($propertyValue); - - $rawValue = (string) $propertyValue->getValue(); - if($rawValue === ""){ - $notFlattenedProperties[$propertyName] = true; - continue; - } $filter = $pair->old->getStates(); foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){ @@ -551,16 +549,13 @@ function processRemappedStates(array $upgradeTable) : array{ unset($filter[$propertyName]); $rawFilter = encodeOrderedProperties($filter); - if(isset($candidateFlattenedValues[$propertyName][$rawFilter])){ - $valuesToIds = $candidateFlattenedValues[$propertyName][$rawFilter]; - $existingNewId = $valuesToIds[$rawValue] ?? null; - if($existingNewId !== null && $existingNewId !== $pair->new->getName()){ - //this old value is associated with multiple new IDs - bad candidate for flattening - $notFlattenedProperties[$propertyName] = true; - continue; - } + $candidateFlattenedValues[$propertyName][$rawFilter] ??= []; + $expectedType = $candidateFlattenedPropertyTypes[$propertyName] ?? null; + if(!checkFlattenPropertySuitability($propertyValue, $expectedType, $pair->new->getName(), $candidateFlattenedValues[$propertyName][$rawFilter])){ + $notFlattenedProperties[$propertyName] = true; + continue; } - $candidateFlattenedValues[$propertyName][$rawFilter][$rawValue] = $pair->new->getName(); + $candidateFlattenedPropertyTypes[$propertyName] = $expectedType; } } foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ From 4814db4fe7e5ed9cecf9889d3841788174fd0fd3 Mon Sep 17 00:00:00 2001 From: Dries C Date: Fri, 25 Oct 2024 15:21:51 +0200 Subject: [PATCH 043/290] Assemble 1.21.40 (#6471) --- composer.json | 8 +-- composer.lock | 50 +++++++++---------- src/data/bedrock/BedrockDataFiles.php | 1 + src/data/bedrock/block/BlockStateData.php | 4 +- src/data/bedrock/block/BlockStateNames.php | 1 - src/data/bedrock/block/BlockTypeNames.php | 9 +++- .../convert/BlockObjectToStateSerializer.php | 46 +++++++---------- .../convert/BlockStateDeserializerHelper.php | 5 +- .../BlockStateToObjectDeserializer.php | 35 ++++++++----- .../ItemSerializerDeserializerRegistrar.php | 10 ---- src/data/bedrock/item/ItemTypeNames.php | 16 ++++++ src/network/mcpe/InventoryManager.php | 8 +-- .../mcpe/handler/PreSpawnPacketHandler.php | 4 +- .../handler/ResourcePacksPacketHandler.php | 3 +- tools/generate-bedrock-data-from-packets.php | 2 +- 15 files changed, 107 insertions(+), 95 deletions(-) diff --git a/composer.json b/composer.json index 0b9e8ec6b..9ecbad32f 100644 --- a/composer.json +++ b/composer.json @@ -33,10 +33,10 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", - "pocketmine/bedrock-block-upgrade-schema": "~4.4.0+bedrock-1.21.30", - "pocketmine/bedrock-data": "~2.13.0+bedrock-1.21.30", - "pocketmine/bedrock-item-upgrade-schema": "~1.12.0+bedrock-1.21.30", - "pocketmine/bedrock-protocol": "~34.0.0+bedrock-1.21.30", + "pocketmine/bedrock-block-upgrade-schema": "~4.5.0+bedrock-1.21.40", + "pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40", + "pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40", + "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index d023c74ac..d4cb38ddc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e16d3ebe48e32bbf96348981249c0ac1", + "content-hash": "5c5882370131d2ae3a043819c05e6f9c", "packages": [ { "name": "adhocore/json-comment", @@ -127,16 +127,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "4.4.0", + "version": "4.5.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761" + "reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/89e5f6e19c29e0d0d24835639f72a5ef157c2761", - "reference": "89e5f6e19c29e0d0d24835639f72a5ef157c2761", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/7943b894e050d68dd21b5c7fa609827a4e2e30f1", + "reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1", "shasum": "" }, "type": "library", @@ -147,22 +147,22 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.4.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.5.0" }, - "time": "2024-09-17T16:06:36+00:00" + "time": "2024-10-23T16:15:24+00:00" }, { "name": "pocketmine/bedrock-data", - "version": "2.13.0+bedrock-1.21.30", + "version": "2.14.0+bedrock-1.21.40", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "23d9356b866654cbd2a62b31373118bedb4a2562" + "reference": "606d32ae426164b0615898b95d10e23293bed6ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/23d9356b866654cbd2a62b31373118bedb4a2562", - "reference": "23d9356b866654cbd2a62b31373118bedb4a2562", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/606d32ae426164b0615898b95d10e23293bed6ac", + "reference": "606d32ae426164b0615898b95d10e23293bed6ac", "shasum": "" }, "type": "library", @@ -173,22 +173,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.30" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.40" }, - "time": "2024-09-17T16:03:14+00:00" + "time": "2024-10-23T19:19:16+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.12.0", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409" + "reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/85a0014c7dfd4a25c22a9efb0b447afb7dc6c409", - "reference": "85a0014c7dfd4a25c22a9efb0b447afb7dc6c409", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1dee9bbd0aaa65ed108b377b402746defe10b3b0", + "reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0", "shasum": "" }, "type": "library", @@ -199,22 +199,22 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.12.0" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.0" }, - "time": "2024-09-11T19:48:31+00:00" + "time": "2024-10-23T18:38:43+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "34.0.0+bedrock-1.21.30", + "version": "35.0.0+bedrock-1.21.40", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b" + "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/440c8078c66cc2a8f2abf58468df7df7246ee33b", - "reference": "440c8078c66cc2a8f2abf58468df7df7246ee33b", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459", + "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459", "shasum": "" }, "require": { @@ -245,9 +245,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/34.0.0+bedrock-1.21.30" + "source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.40" }, - "time": "2024-09-18T20:58:42+00:00" + "time": "2024-10-24T15:45:43+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php index 2c9350ca3..5c476ca1c 100644 --- a/src/data/bedrock/BedrockDataFiles.php +++ b/src/data/bedrock/BedrockDataFiles.php @@ -35,6 +35,7 @@ final class BedrockDataFiles{ public const BIOME_DEFINITIONS_FULL_NBT = BEDROCK_DATA_PATH . '/biome_definitions_full.nbt'; public const BIOME_ID_MAP_JSON = BEDROCK_DATA_PATH . '/biome_id_map.json'; public const BLOCK_ID_TO_ITEM_ID_MAP_JSON = BEDROCK_DATA_PATH . '/block_id_to_item_id_map.json'; + public const BLOCK_PROPERTIES_TABLE_JSON = BEDROCK_DATA_PATH . '/block_properties_table.json'; public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json'; public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt'; public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json'; diff --git a/src/data/bedrock/block/BlockStateData.php b/src/data/bedrock/block/BlockStateData.php index f405e4cf6..6624c4ae0 100644 --- a/src/data/bedrock/block/BlockStateData.php +++ b/src/data/bedrock/block/BlockStateData.php @@ -45,8 +45,8 @@ final class BlockStateData{ public const CURRENT_VERSION = (1 << 24) | //major (21 << 16) | //minor - (30 << 8) | //patch - (7); //revision + (40 << 8) | //patch + (1); //revision public const TAG_NAME = "name"; public const TAG_STATES = "states"; diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index c54822671..0f4b87426 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -114,7 +114,6 @@ final class BlockStateNames{ public const SEA_GRASS_TYPE = "sea_grass_type"; public const STABILITY = "stability"; public const STABILITY_CHECK = "stability_check"; - public const STRIPPED_BIT = "stripped_bit"; public const STRUCTURE_BLOCK_TYPE = "structure_block_type"; public const SUSPENDED_BIT = "suspended_bit"; public const TOGGLE_BIT = "toggle_bit"; diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index ec5565715..029a5f6aa 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -262,6 +262,7 @@ final class BlockTypeNames{ public const CRACKED_STONE_BRICKS = "minecraft:cracked_stone_bricks"; public const CRAFTER = "minecraft:crafter"; public const CRAFTING_TABLE = "minecraft:crafting_table"; + public const CREEPER_HEAD = "minecraft:creeper_head"; public const CRIMSON_BUTTON = "minecraft:crimson_button"; public const CRIMSON_DOOR = "minecraft:crimson_door"; public const CRIMSON_DOUBLE_SLAB = "minecraft:crimson_double_slab"; @@ -384,6 +385,7 @@ final class BlockTypeNames{ public const DISPENSER = "minecraft:dispenser"; public const DOUBLE_CUT_COPPER_SLAB = "minecraft:double_cut_copper_slab"; public const DRAGON_EGG = "minecraft:dragon_egg"; + public const DRAGON_HEAD = "minecraft:dragon_head"; public const DRIED_KELP_BLOCK = "minecraft:dried_kelp_block"; public const DRIPSTONE_BLOCK = "minecraft:dripstone_block"; public const DROPPER = "minecraft:dropper"; @@ -797,6 +799,7 @@ final class BlockTypeNames{ public const MUD_BRICK_WALL = "minecraft:mud_brick_wall"; public const MUD_BRICKS = "minecraft:mud_bricks"; public const MUDDY_MANGROVE_ROOTS = "minecraft:muddy_mangrove_roots"; + public const MUSHROOM_STEM = "minecraft:mushroom_stem"; public const MYCELIUM = "minecraft:mycelium"; public const NETHER_BRICK = "minecraft:nether_brick"; public const NETHER_BRICK_DOUBLE_SLAB = "minecraft:nether_brick_double_slab"; @@ -857,6 +860,7 @@ final class BlockTypeNames{ public const PEONY = "minecraft:peony"; public const PETRIFIED_OAK_DOUBLE_SLAB = "minecraft:petrified_oak_double_slab"; public const PETRIFIED_OAK_SLAB = "minecraft:petrified_oak_slab"; + public const PIGLIN_HEAD = "minecraft:piglin_head"; public const PINK_CANDLE = "minecraft:pink_candle"; public const PINK_CANDLE_CAKE = "minecraft:pink_candle_cake"; public const PINK_CARPET = "minecraft:pink_carpet"; @@ -874,6 +878,7 @@ final class BlockTypeNames{ public const PISTON_ARM_COLLISION = "minecraft:piston_arm_collision"; public const PITCHER_CROP = "minecraft:pitcher_crop"; public const PITCHER_PLANT = "minecraft:pitcher_plant"; + public const PLAYER_HEAD = "minecraft:player_head"; public const PODZOL = "minecraft:podzol"; public const POINTED_DRIPSTONE = "minecraft:pointed_dripstone"; public const POLISHED_ANDESITE = "minecraft:polished_andesite"; @@ -1009,7 +1014,7 @@ final class BlockTypeNames{ public const SHORT_GRASS = "minecraft:short_grass"; public const SHROOMLIGHT = "minecraft:shroomlight"; public const SILVER_GLAZED_TERRACOTTA = "minecraft:silver_glazed_terracotta"; - public const SKULL = "minecraft:skull"; + public const SKELETON_SKULL = "minecraft:skeleton_skull"; public const SLIME = "minecraft:slime"; public const SMALL_AMETHYST_BUD = "minecraft:small_amethyst_bud"; public const SMALL_DRIPLEAF_BLOCK = "minecraft:small_dripleaf_block"; @@ -1229,6 +1234,7 @@ final class BlockTypeNames{ public const WHITE_TULIP = "minecraft:white_tulip"; public const WHITE_WOOL = "minecraft:white_wool"; public const WITHER_ROSE = "minecraft:wither_rose"; + public const WITHER_SKELETON_SKULL = "minecraft:wither_skeleton_skull"; public const WOODEN_BUTTON = "minecraft:wooden_button"; public const WOODEN_DOOR = "minecraft:wooden_door"; public const WOODEN_PRESSURE_PLATE = "minecraft:wooden_pressure_plate"; @@ -1243,4 +1249,5 @@ final class BlockTypeNames{ public const YELLOW_STAINED_GLASS_PANE = "minecraft:yellow_stained_glass_pane"; public const YELLOW_TERRACOTTA = "minecraft:yellow_terracotta"; public const YELLOW_WOOL = "minecraft:yellow_wool"; + public const ZOMBIE_HEAD = "minecraft:zombie_head"; } diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index af6547729..a7fee6efb 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -151,6 +151,7 @@ use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; use pocketmine\block\utils\LeverFacing; +use pocketmine\block\utils\MobHeadType; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; use pocketmine\block\Wall; @@ -205,6 +206,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->registerFlatWoodBlockSerializers(); $this->registerLeavesSerializers(); $this->registerSaplingSerializers(); + $this->registerMobHeadSerializers(); $this->registerSimpleSerializers(); $this->registerSerializers(); } @@ -624,17 +626,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS); $this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB); $this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS); - $this->map(Blocks::CHERRY_WOOD(), function(Wood $block) : Writer{ - //we can't use the standard method for this because cherry_wood has a useless property attached to it - if(!$block->isStripped()){ - return Writer::create(Ids::CHERRY_WOOD) - ->writePillarAxis($block->getAxis()) - ->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written - }else{ - return Writer::create(Ids::STRIPPED_CHERRY_WOOD) - ->writePillarAxis($block->getAxis()); - } - }); + $this->mapLog(Blocks::CHERRY_WOOD(), Ids::CHERRY_WOOD, Ids::STRIPPED_CHERRY_WOOD); $this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON))); $this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR))); @@ -690,17 +682,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS); $this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB); $this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS); - $this->map(Blocks::MANGROVE_WOOD(), function(Wood $block) : Writer{ - //we can't use the standard method for this because mangrove_wood has a useless property attached to it - if(!$block->isStripped()){ - return Writer::create(Ids::MANGROVE_WOOD) - ->writePillarAxis($block->getAxis()) - ->writeBool(StateNames::STRIPPED_BIT, false); //this is useless, but it has to be written - }else{ - return Writer::create(Ids::STRIPPED_MANGROVE_WOOD) - ->writePillarAxis($block->getAxis()); - } - }); + $this->mapLog(Blocks::MANGROVE_WOOD(), Ids::MANGROVE_WOOD, Ids::STRIPPED_MANGROVE_WOOD); $this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON))); $this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR))); @@ -775,6 +757,18 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ } } + private function registerMobHeadSerializers() : void{ + $this->map(Blocks::MOB_HEAD(), fn(MobHead $block) => Writer::create(match ($block->getMobHeadType()){ + MobHeadType::CREEPER => Ids::CREEPER_HEAD, + MobHeadType::DRAGON => Ids::DRAGON_HEAD, + MobHeadType::PIGLIN => Ids::PIGLIN_HEAD, + MobHeadType::PLAYER => Ids::PLAYER_HEAD, + MobHeadType::SKELETON => Ids::SKELETON_SKULL, + MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL, + MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD, + })->writeFacingWithoutDown($block->getFacing())); + } + private function registerSimpleSerializers() : void{ $this->mapSimple(Blocks::AIR(), Ids::AIR); $this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); @@ -1078,7 +1072,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); }); - $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK) + $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM) ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); $this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create( match($stage = $block->getStage()){ @@ -1483,10 +1477,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ }); $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::MATERIAL_REDUCER))); $this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM))); - $this->map(Blocks::MOB_HEAD(), function(MobHead $block) : Writer{ - return Writer::create(Ids::SKULL) - ->writeFacingWithoutDown($block->getFacing()); - }); $this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_COBBLESTONE_WALL))); @@ -1498,7 +1488,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL))); $this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS) ->writePillarAxis($block->getAxis())); - $this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::BROWN_MUSHROOM_BLOCK) + $this->map(Blocks::MUSHROOM_STEM(), fn() => Writer::create(Ids::MUSHROOM_STEM) ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 47c0fd4af..c51cda768 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -50,7 +50,6 @@ use pocketmine\block\Stem; use pocketmine\block\Trapdoor; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\SlabType; -use pocketmine\block\VanillaBlocks; use pocketmine\block\Wall; use pocketmine\block\WallSign; use pocketmine\block\WeightedPressurePlate; @@ -210,8 +209,8 @@ final class BlockStateDeserializerHelper{ /** @throws BlockStateDeserializeException */ public static function decodeMushroomBlock(RedMushroomBlock $block, BlockStateReader $in) : Block{ switch($type = $in->readBoundedInt(BlockStateNames::HUGE_MUSHROOM_BITS, 0, 15)){ - case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: return VanillaBlocks::ALL_SIDED_MUSHROOM_STEM(); - case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: return VanillaBlocks::MUSHROOM_STEM(); + case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: + case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: throw new BlockStateDeserializeException("This state does not exist"); default: //invalid types get left as default $type = MushroomBlockTypeIdMap::getInstance()->fromId($type); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index eb0cb8d50..405f6e53e 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -45,6 +45,7 @@ use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; use pocketmine\block\utils\LeverFacing; +use pocketmine\block\utils\MobHeadType; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockLegacyMetadata; @@ -85,6 +86,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->registerLeavesDeserializers(); $this->registerSaplingDeserializers(); $this->registerLightDeserializers(); + $this->registerMobHeadDeserializers(); $this->registerSimpleDeserializers(); $this->registerDeserializers(); } @@ -531,10 +533,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS()); $this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB()); $this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS()); - $this->map(Ids::CHERRY_WOOD, function(Reader $in){ - $in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla - return Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in); - }); + $this->map(Ids::CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in)); $this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in)); $this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in)); @@ -591,10 +590,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS()); $this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB()); $this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS()); - $this->map(Ids::MANGROVE_WOOD, function(Reader $in){ - $in->ignored(StateNames::STRIPPED_BIT); //this is also ignored by vanilla - return Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in); - }); + $this->map(Ids::MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in)); $this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in)); //oak - due to age, many of these don't specify "oak", making for confusing reading @@ -690,6 +686,20 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } } + private function registerMobHeadDeserializers() : void{ + foreach([ + Ids::CREEPER_HEAD => MobHeadType::CREEPER, + Ids::DRAGON_HEAD => MobHeadType::DRAGON, + Ids::PIGLIN_HEAD => MobHeadType::PIGLIN, + Ids::PLAYER_HEAD => MobHeadType::PLAYER, + Ids::SKELETON_SKULL => MobHeadType::SKELETON, + Ids::WITHER_SKELETON_SKULL => MobHeadType::WITHER_SKELETON, + Ids::ZOMBIE_HEAD => MobHeadType::ZOMBIE + ] as $id => $mobHeadType){ + $this->map($id, fn(Reader $in) => Blocks::MOB_HEAD()->setMobHeadType($mobHeadType)->setFacing($in->readFacingWithoutDown())); + } + } + private function registerSimpleDeserializers() : void{ $this->mapSimple(Ids::AIR, fn() => Blocks::AIR()); $this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST()); @@ -1099,6 +1109,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB()); $this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS()); $this->map(Ids::BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BRICK_WALL(), $in)); + $this->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)){ + BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), + BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), + default => throw new BlockStateDeserializeException("This state does not exist"), + }); $this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in)); $this->map(Ids::CACTUS, function(Reader $in) : Block{ return Blocks::CACTUS() @@ -1521,10 +1536,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1) ->setUnderwater(!$in->readBool(StateNames::DEAD_BIT)); }); - $this->map(Ids::SKULL, function(Reader $in) : Block{ - return Blocks::MOB_HEAD() - ->setFacing($in->readFacingWithoutDown()); - }); $this->map(Ids::SMOKER, function(Reader $in) : Block{ return Blocks::SMOKER() ->setFacing($in->readCardinalHorizontalFacing()) diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 7a720559e..0af17bc73 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\item; use pocketmine\block\Bed; use pocketmine\block\Block; -use pocketmine\block\MobHead; use pocketmine\block\utils\DyeColor; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\data\bedrock\CompoundTypeIds; @@ -33,7 +32,6 @@ use pocketmine\data\bedrock\DyeColorIdMap; use pocketmine\data\bedrock\item\ItemTypeNames as Ids; use pocketmine\data\bedrock\item\SavedItemData as Data; use pocketmine\data\bedrock\MedicineTypeIdMap; -use pocketmine\data\bedrock\MobHeadTypeIdMap; use pocketmine\data\bedrock\PotionTypeIdMap; use pocketmine\data\bedrock\SuspiciousStewTypeIdMap; use pocketmine\item\Banner; @@ -464,14 +462,6 @@ final class ItemSerializerDeserializerRegistrar{ }, fn(Bed $block) => DyeColorIdMap::getInstance()->toId($block->getColor()) ); - $this->map1to1BlockWithMeta( - Ids::SKULL, - Blocks::MOB_HEAD(), - function(MobHead $block, int $meta) : void{ - $block->setMobHeadType(MobHeadTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown mob head type ID $meta")); - }, - fn(MobHead $block) => MobHeadTypeIdMap::getInstance()->toId($block->getMobHeadType()) - ); } /** diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 06ae28cec..03b32d482 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -66,12 +66,14 @@ final class ItemTypeNames{ public const BIRCH_DOOR = "minecraft:birch_door"; public const BIRCH_HANGING_SIGN = "minecraft:birch_hanging_sign"; public const BIRCH_SIGN = "minecraft:birch_sign"; + public const BLACK_BUNDLE = "minecraft:black_bundle"; public const BLACK_DYE = "minecraft:black_dye"; public const BLADE_POTTERY_SHERD = "minecraft:blade_pottery_sherd"; public const BLAZE_POWDER = "minecraft:blaze_powder"; public const BLAZE_ROD = "minecraft:blaze_rod"; public const BLAZE_SPAWN_EGG = "minecraft:blaze_spawn_egg"; public const BLEACH = "minecraft:bleach"; + public const BLUE_BUNDLE = "minecraft:blue_bundle"; public const BLUE_DYE = "minecraft:blue_dye"; public const BOAT = "minecraft:boat"; public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg"; @@ -88,6 +90,7 @@ final class ItemTypeNames{ public const BREWER_POTTERY_SHERD = "minecraft:brewer_pottery_sherd"; public const BREWING_STAND = "minecraft:brewing_stand"; public const BRICK = "minecraft:brick"; + public const BROWN_BUNDLE = "minecraft:brown_bundle"; public const BROWN_DYE = "minecraft:brown_dye"; public const BRUSH = "minecraft:brush"; public const BUCKET = "minecraft:bucket"; @@ -157,6 +160,7 @@ final class ItemTypeNames{ public const CRIMSON_HANGING_SIGN = "minecraft:crimson_hanging_sign"; public const CRIMSON_SIGN = "minecraft:crimson_sign"; public const CROSSBOW = "minecraft:crossbow"; + public const CYAN_BUNDLE = "minecraft:cyan_bundle"; public const CYAN_DYE = "minecraft:cyan_dye"; public const DANGER_POTTERY_SHERD = "minecraft:danger_pottery_sherd"; public const DARK_OAK_BOAT = "minecraft:dark_oak_boat"; @@ -255,7 +259,9 @@ final class ItemTypeNames{ public const GOLDEN_PICKAXE = "minecraft:golden_pickaxe"; public const GOLDEN_SHOVEL = "minecraft:golden_shovel"; public const GOLDEN_SWORD = "minecraft:golden_sword"; + public const GRAY_BUNDLE = "minecraft:gray_bundle"; public const GRAY_DYE = "minecraft:gray_dye"; + public const GREEN_BUNDLE = "minecraft:green_bundle"; public const GREEN_DYE = "minecraft:green_dye"; public const GUARDIAN_SPAWN_EGG = "minecraft:guardian_spawn_egg"; public const GUNPOWDER = "minecraft:gunpowder"; @@ -309,8 +315,11 @@ final class ItemTypeNames{ public const LEAVES = "minecraft:leaves"; public const LEAVES2 = "minecraft:leaves2"; public const LIGHT_BLOCK = "minecraft:light_block"; + public const LIGHT_BLUE_BUNDLE = "minecraft:light_blue_bundle"; public const LIGHT_BLUE_DYE = "minecraft:light_blue_dye"; + public const LIGHT_GRAY_BUNDLE = "minecraft:light_gray_bundle"; public const LIGHT_GRAY_DYE = "minecraft:light_gray_dye"; + public const LIME_BUNDLE = "minecraft:lime_bundle"; public const LIME_DYE = "minecraft:lime_dye"; public const LINGERING_POTION = "minecraft:lingering_potion"; public const LLAMA_SPAWN_EGG = "minecraft:llama_spawn_egg"; @@ -318,6 +327,7 @@ final class ItemTypeNames{ public const LOG = "minecraft:log"; public const LOG2 = "minecraft:log2"; public const MACE = "minecraft:mace"; + public const MAGENTA_BUNDLE = "minecraft:magenta_bundle"; public const MAGENTA_DYE = "minecraft:magenta_dye"; public const MAGMA_CREAM = "minecraft:magma_cream"; public const MAGMA_CUBE_SPAWN_EGG = "minecraft:magma_cube_spawn_egg"; @@ -384,6 +394,7 @@ final class ItemTypeNames{ public const OCELOT_SPAWN_EGG = "minecraft:ocelot_spawn_egg"; public const OMINOUS_BOTTLE = "minecraft:ominous_bottle"; public const OMINOUS_TRIAL_KEY = "minecraft:ominous_trial_key"; + public const ORANGE_BUNDLE = "minecraft:orange_bundle"; public const ORANGE_DYE = "minecraft:orange_dye"; public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door"; public const PAINTING = "minecraft:painting"; @@ -397,6 +408,7 @@ final class ItemTypeNames{ public const PIGLIN_BRUTE_SPAWN_EGG = "minecraft:piglin_brute_spawn_egg"; public const PIGLIN_SPAWN_EGG = "minecraft:piglin_spawn_egg"; public const PILLAGER_SPAWN_EGG = "minecraft:pillager_spawn_egg"; + public const PINK_BUNDLE = "minecraft:pink_bundle"; public const PINK_DYE = "minecraft:pink_dye"; public const PITCHER_POD = "minecraft:pitcher_pod"; public const PLANKS = "minecraft:planks"; @@ -416,6 +428,7 @@ final class ItemTypeNames{ public const PUFFERFISH_SPAWN_EGG = "minecraft:pufferfish_spawn_egg"; public const PUMPKIN_PIE = "minecraft:pumpkin_pie"; public const PUMPKIN_SEEDS = "minecraft:pumpkin_seeds"; + public const PURPLE_BUNDLE = "minecraft:purple_bundle"; public const PURPLE_DYE = "minecraft:purple_dye"; public const QUARTZ = "minecraft:quartz"; public const RABBIT = "minecraft:rabbit"; @@ -430,6 +443,7 @@ final class ItemTypeNames{ public const RAW_GOLD = "minecraft:raw_gold"; public const RAW_IRON = "minecraft:raw_iron"; public const RECOVERY_COMPASS = "minecraft:recovery_compass"; + public const RED_BUNDLE = "minecraft:red_bundle"; public const RED_DYE = "minecraft:red_dye"; public const RED_FLOWER = "minecraft:red_flower"; public const REDSTONE = "minecraft:redstone"; @@ -537,6 +551,7 @@ final class ItemTypeNames{ public const WEATHERED_COPPER_DOOR = "minecraft:weathered_copper_door"; public const WHEAT = "minecraft:wheat"; public const WHEAT_SEEDS = "minecraft:wheat_seeds"; + public const WHITE_BUNDLE = "minecraft:white_bundle"; public const WHITE_DYE = "minecraft:white_dye"; public const WILD_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:wild_armor_trim_smithing_template"; public const WIND_CHARGE = "minecraft:wind_charge"; @@ -556,6 +571,7 @@ final class ItemTypeNames{ public const WOOL = "minecraft:wool"; public const WRITABLE_BOOK = "minecraft:writable_book"; public const WRITTEN_BOOK = "minecraft:written_book"; + public const YELLOW_BUNDLE = "minecraft:yellow_bundle"; public const YELLOW_DYE = "minecraft:yellow_dye"; public const ZOGLIN_SPAWN_EGG = "minecraft:zoglin_spawn_egg"; public const ZOMBIE_HORSE_SPAWN_EGG = "minecraft:zombie_horse_spawn_egg"; diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 99af73707..c0969b61b 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -502,7 +502,7 @@ class InventoryManager{ $windowId, $netSlot, new FullContainerName($this->lastInventoryNetworkId), - 0, + new ItemStackWrapper(0, ItemStack::null()), new ItemStackWrapper(0, ItemStack::null()) )); } @@ -511,7 +511,7 @@ class InventoryManager{ $windowId, $netSlot, new FullContainerName($this->lastInventoryNetworkId), - 0, + new ItemStackWrapper(0, ItemStack::null()), $itemStackWrapper )); } @@ -532,10 +532,10 @@ class InventoryManager{ $windowId, array_fill_keys(array_keys($itemStackWrappers), new ItemStackWrapper(0, ItemStack::null())), new FullContainerName($this->lastInventoryNetworkId), - 0 + new ItemStackWrapper(0, ItemStack::null()) )); //now send the real contents - $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, new FullContainerName($this->lastInventoryNetworkId), 0)); + $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $itemStackWrappers, new FullContainerName($this->lastInventoryNetworkId), new ItemStackWrapper(0, ItemStack::null()))); } public function syncSlot(Inventory $inventory, int $slot, ItemStack $itemStack) : void{ diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index f80bacfc1..b80874938 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -39,7 +39,7 @@ use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\LevelSettings; use pocketmine\network\mcpe\protocol\types\NetworkPermissions; use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings; -use pocketmine\network\mcpe\protocol\types\PlayerMovementType; +use pocketmine\network\mcpe\protocol\types\ServerAuthMovementMode; use pocketmine\network\mcpe\protocol\types\SpawnSettings; use pocketmine\player\Player; use pocketmine\Server; @@ -98,7 +98,7 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->server->getMotd(), "", false, - new PlayerMovementSettings(PlayerMovementType::SERVER_AUTHORITATIVE_V1, 0, false), + new PlayerMovementSettings(ServerAuthMovementMode::SERVER_AUTHORITATIVE_V2, 0, false), 0, 0, "", diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index 5f7fb0e4b..5e2671394 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -117,8 +117,7 @@ class ResourcePacksPacketHandler extends PacketHandler{ resourcePackEntries: $resourcePackEntries, mustAccept: $this->mustAccept, hasAddons: false, - hasScripts: false, - cdnUrls: [] + hasScripts: false )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 16c3062b8..0cb5ac366 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -399,7 +399,7 @@ class ParserPacketHandler extends PacketHandler{ CraftingDataPacket::ENTRY_FURNACE => "smelting", CraftingDataPacket::ENTRY_FURNACE_DATA => "smelting", CraftingDataPacket::ENTRY_MULTI => "special_hardcoded", - CraftingDataPacket::ENTRY_SHULKER_BOX => "shapeless_shulker_box", + CraftingDataPacket::ENTRY_USER_DATA_SHAPELESS => "shapeless_shulker_box", CraftingDataPacket::ENTRY_SHAPELESS_CHEMISTRY => "shapeless_chemistry", CraftingDataPacket::ENTRY_SHAPED_CHEMISTRY => "shaped_chemistry", CraftingDataPacket::ENTRY_SMITHING_TRANSFORM => "smithing", From d372af351afd945ff44e3597db32ce81b2310259 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 26 Oct 2024 15:40:22 +0100 Subject: [PATCH 044/290] Fix changelog typo --- changelogs/5.19.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.19.md b/changelogs/5.19.md index 57d322bf7..6768c38e5 100644 --- a/changelogs/5.19.md +++ b/changelogs/5.19.md @@ -1,5 +1,5 @@ # 5.19.0 -Released 21tst September 2024. +Released 21st September 2024. **For Minecraft: Bedrock Edition 1.21.30** From 414e8acf8c6d451e2614201a5dabc0b2f2887486 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 26 Oct 2024 15:42:43 +0100 Subject: [PATCH 045/290] Release 5.20.0 --- changelogs/5.20.md | 19 +++++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.20.md diff --git a/changelogs/5.20.md b/changelogs/5.20.md new file mode 100644 index 000000000..8d7e971d9 --- /dev/null +++ b/changelogs/5.20.md @@ -0,0 +1,19 @@ +# 5.20.0 +Released 26th October 2024. + +**For Minecraft: Bedrock Edition 1.21.40** + +This is a support release for Minecraft: Bedrock Edition 1.21.40. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.40. +- Removed support for earlier versions. + +## Fixes +- Fixed a bug in `tools/generate-blockstate-upgrade-schema.php` that caused it to fail on 1.21.40 with the new mushroom block changes. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b2d2fe4ab..b61df1001 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.19.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.20.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From e7d8d99ca6086111a607cc79a8e8e64e6532df24 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 26 Oct 2024 15:42:43 +0100 Subject: [PATCH 046/290] 5.20.1 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index b61df1001..11c5b1c88 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.20.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.20.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 8ef5e737de5689ba83a9bf66b6efa147171452ce Mon Sep 17 00:00:00 2001 From: Dries C Date: Sun, 27 Oct 2024 02:29:34 +0200 Subject: [PATCH 047/290] Temporary resolve loading old skulls from storage (#6476) --- .../bedrock/item/upgrade/ItemDataUpgrader.php | 15 +++++++++++++++ src/world/format/io/GlobalItemDataHandlers.php | 6 +++++- src/world/format/io/data/BedrockWorldData.php | 6 +++--- src/world/format/io/leveldb/ChunkVersion.php | 1 + src/world/format/io/leveldb/LevelDB.php | 4 +++- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/data/bedrock/item/upgrade/ItemDataUpgrader.php b/src/data/bedrock/item/upgrade/ItemDataUpgrader.php index d7d097de8..7b34ffcb6 100644 --- a/src/data/bedrock/item/upgrade/ItemDataUpgrader.php +++ b/src/data/bedrock/item/upgrade/ItemDataUpgrader.php @@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock\item\upgrade; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader; +use pocketmine\data\bedrock\item\BlockItemIdMap; use pocketmine\data\bedrock\item\SavedItemData; use pocketmine\data\bedrock\item\SavedItemStackData; use pocketmine\data\SavedDataLoadingException; @@ -35,6 +36,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\network\mcpe\convert\BlockStateDictionary; use pocketmine\utils\Binary; use function assert; @@ -46,6 +48,8 @@ final class ItemDataUpgrader{ private LegacyItemIdToStringIdMap $legacyIntToStringIdMap, private R12ItemIdToBlockIdMap $r12ItemIdToBlockIdMap, private BlockDataUpgrader $blockDataUpgrader, + private BlockItemIdMap $blockItemIdMap, + private BlockStateDictionary $blockStateDictionary ){} /** @@ -148,6 +152,17 @@ final class ItemDataUpgrader{ [$newNameId, $newMeta] = $this->idMetaUpgrader->upgrade($rawNameId, $meta); + //TODO: Dirty hack to load old skulls from disk: Put this into item upgrade schema's before Mojang makes something with a non 0 default state + if($blockStateData === null && ($blockId = $this->blockItemIdMap->lookupBlockId($newNameId)) !== null){ + $networkRuntimeId = $this->blockStateDictionary->lookupStateIdFromIdMeta($blockId, 0); + + if($networkRuntimeId === null){ + throw new SavedDataLoadingException("Failed to find blockstate for blockitem $newNameId"); + } + + $blockStateData = $this->blockStateDictionary->generateDataFromStateId($networkRuntimeId); + } + //TODO: this won't account for spawn eggs from before 1.16.100 - perhaps we're lucky and they just left the meta in there anyway? //TODO: read version from VersionInfo::TAG_WORLD_DATA_VERSION - we may need it to fix up old items diff --git a/src/world/format/io/GlobalItemDataHandlers.php b/src/world/format/io/GlobalItemDataHandlers.php index ea5568c7c..f62258478 100644 --- a/src/world/format/io/GlobalItemDataHandlers.php +++ b/src/world/format/io/GlobalItemDataHandlers.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\world\format\io; +use pocketmine\data\bedrock\item\BlockItemIdMap; use pocketmine\data\bedrock\item\ItemDeserializer; use pocketmine\data\bedrock\item\ItemSerializer; use pocketmine\data\bedrock\item\upgrade\ItemDataUpgrader; @@ -30,6 +31,7 @@ use pocketmine\data\bedrock\item\upgrade\ItemIdMetaUpgrader; use pocketmine\data\bedrock\item\upgrade\ItemIdMetaUpgradeSchemaUtils; use pocketmine\data\bedrock\item\upgrade\LegacyItemIdToStringIdMap; use pocketmine\data\bedrock\item\upgrade\R12ItemIdToBlockIdMap; +use pocketmine\network\mcpe\convert\TypeConverter; use Symfony\Component\Filesystem\Path; use const PHP_INT_MAX; use const pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH; @@ -54,7 +56,9 @@ final class GlobalItemDataHandlers{ new ItemIdMetaUpgrader(ItemIdMetaUpgradeSchemaUtils::loadSchemas(Path::join(BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, 'id_meta_upgrade_schema'), PHP_INT_MAX)), LegacyItemIdToStringIdMap::getInstance(), R12ItemIdToBlockIdMap::getInstance(), - GlobalBlockStateHandlers::getUpgrader() + GlobalBlockStateHandlers::getUpgrader(), + BlockItemIdMap::getInstance(), + TypeConverter::getInstance()->getBlockTranslator()->getBlockStateDictionary() ); } } diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 1612e7d84..b2e079124 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,12 +51,12 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 729; + public const CURRENT_STORAGE_NETWORK_VERSION = 748; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 30, //patch - 3, //revision + 40, //patch + 1, //revision 0 //is beta ]; diff --git a/src/world/format/io/leveldb/ChunkVersion.php b/src/world/format/io/leveldb/ChunkVersion.php index e399570de..a69e4ff45 100644 --- a/src/world/format/io/leveldb/ChunkVersion.php +++ b/src/world/format/io/leveldb/ChunkVersion.php @@ -71,4 +71,5 @@ final class ChunkVersion{ public const v1_18_0_24_unused = 38; public const v1_18_0_25_beta = 39; public const v1_18_30 = 40; + public const v1_21_40 = 41; } diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 20b55922c..dda489d31 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -78,7 +78,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ protected const ENTRY_FLAT_WORLD_LAYERS = "game_flatworldlayers"; - protected const CURRENT_LEVEL_CHUNK_VERSION = ChunkVersion::v1_18_30; + protected const CURRENT_LEVEL_CHUNK_VERSION = ChunkVersion::v1_21_40; protected const CURRENT_LEVEL_SUBCHUNK_VERSION = SubChunkVersion::PALETTED_MULTI; private const CAVES_CLIFFS_EXPERIMENTAL_SUBCHUNK_KEY_OFFSET = 4; @@ -654,6 +654,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $hasBeenUpgraded = $chunkVersion < self::CURRENT_LEVEL_CHUNK_VERSION; switch($chunkVersion){ + case ChunkVersion::v1_21_40: + //TODO: BiomeStates became shorts instead of bytes case ChunkVersion::v1_18_30: case ChunkVersion::v1_18_0_25_beta: case ChunkVersion::v1_18_0_24_unused: From 0065fe649fa0d1e90d618a650e5b27cb1e89bca6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 31 Oct 2024 13:52:15 +0000 Subject: [PATCH 048/290] New release workflow triggered by the merge of a PR changing IS_DEVELOPMENT_BUILD to false This is more streamlined than the previous approach, and works better for a world where 1 person isn't doing all the work. Now, the flow is simpler: - Do changes (e.g. protocol update), changelog & set IS_DEVELOPMENT_BUILD to false all in a single PR, which can be squash-merged if desired - Once the PR is merged, a draft release will be prepared - RestrictedActions will automatically set IS_DEVELOPMENT_BUILD back to true and bump the version - Tag will be created when the release is published Previously, multiple PRs might be needed, and the PR containing the release changelog couldn't be squash-merged. Manual intervention was also required to create a tag and prepare a release. This PR also includes new CI checks to check for basic errors like forgotten changelog files to ensure changelog links work correctly. Note: Only PRs from PMMP Team members with **write** access to the repository can trigger release generation. Random people cannot trigger release generation by sending PRs. --- .github/workflows/draft-release-from-pr.yml | 65 +++++++++++ .github/workflows/draft-release-from-tag.yml | 13 +++ .github/workflows/draft-release-pr-check.yml | 111 +++++++++++++++++++ .github/workflows/draft-release.yml | 31 ++++-- build/dump-version-info.php | 86 ++++++++++++++ build/make-release.php | 38 ++++--- phpstan.neon.dist | 1 + 7 files changed, 321 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/draft-release-from-pr.yml create mode 100644 .github/workflows/draft-release-from-tag.yml create mode 100644 .github/workflows/draft-release-pr-check.yml create mode 100644 build/dump-version-info.php diff --git a/.github/workflows/draft-release-from-pr.yml b/.github/workflows/draft-release-from-pr.yml new file mode 100644 index 000000000..3936a2649 --- /dev/null +++ b/.github/workflows/draft-release-from-pr.yml @@ -0,0 +1,65 @@ +name: Draft release from PR + +on: + #presume that pull_request_target is safe at this point, since the PR was approved and merged + #we need write access to prepare the release & create comments + pull_request_target: + types: + - closed + branches: + - stable + - minor-next + - major-next + - "legacy/*" + paths: + - "src/VersionInfo.php" + +jobs: + check: + name: Check release + uses: ./.github/workflows/draft-release-pr-check.yml + + draft: + name: Create GitHub draft release + needs: [check] + if: needs.check.outputs.valid == 'true' + + uses: ./.github/workflows/draft-release.yml + + post-draft-url-comment: + name: Post draft release URL as comment + needs: [draft] + + runs-on: ubuntu-20.04 + + steps: + - name: Post draft release URL on PR + uses: thollander/actions-comment-pull-request@v2 + with: + message: "[Draft release ${{ needs.draft.outputs.version }}](${{ needs.draft.outputs.draft-url }}) has been created for commit ${{ github.sha }}. Please review and publish it." + + trigger-post-release-workflow: + name: Trigger post-release RestrictedActions workflow + # Not sure if needs is actually needed here + needs: [check] + if: needs.check.outputs.valid == 'true' + + runs-on: ubuntu-20.04 + + steps: + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} + private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} + owner: ${{ github.repository_owner }} + repositories: RestrictedActions + + - name: Dispatch post-release restricted action + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ steps.generate-token.outputs.token }} + repository: ${{ github.repository_owner }}/RestrictedActions + event-type: pocketmine_mp_post_release + client-payload: '{"branch": "${{ github.ref }}"}' diff --git a/.github/workflows/draft-release-from-tag.yml b/.github/workflows/draft-release-from-tag.yml new file mode 100644 index 000000000..f7a5df544 --- /dev/null +++ b/.github/workflows/draft-release-from-tag.yml @@ -0,0 +1,13 @@ +#Allows creating a release by pushing a tag +#This might be useful for retroactive releases +name: Draft release from git tag + +on: + push: + tags: "*" + +jobs: + draft: + name: Create GitHub draft release + if: "startsWith(github.event.head_commit.message, 'Release ')" + uses: ./.github/workflows/draft-release.yml diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml new file mode 100644 index 000000000..4c8d0f685 --- /dev/null +++ b/.github/workflows/draft-release-pr-check.yml @@ -0,0 +1,111 @@ +name: Release PR checks + +on: + #do checks on every PR update + pull_request: + branches: + - stable + - minor-next + - major-next + - "legacy/*" + paths: + - "src/VersionInfo.php" + + #allow this workflow to be invoked on PR merge, prior to creating the release + workflow_call: + outputs: + valid: + description: Whether this commit is valid for release + value: ${{ jobs.check-intent.outputs.valid && jobs.check-validity.result == 'success' }} + +permissions: + contents: read #for user access check + +jobs: + check-intent: + name: Check release trigger + runs-on: ubuntu-20.04 + + outputs: + valid: ${{ steps.validate.outputs.DEV_BUILD == 'false' }} + + steps: + - uses: actions/checkout@v4 + + - name: Check IS_DEVELOPMENT_BUILD flag + id: validate + run: | + echo DEV_BUILD=$(sed -n "s/^\s*public const IS_DEVELOPMENT_BUILD = \(true\|false\);$/\1/p" src/VersionInfo.php) >> $GITHUB_OUTPUT + + check-validity: + name: Validate release info + needs: [check-intent] + #don't do these checks if this isn't a release - we don't want to generate unnecessary failed statuses + if: needs.check-intent.outputs.valid == 'true' + + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@2.31.1 + with: + php-version: 8.2 + + - name: Restore Composer package cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/composer/files + ~/.cache/composer/vcs + key: "composer-v2-cache-${{ hashFiles('./composer.lock') }}" + restore-keys: | + composer-v2-cache- + + - name: Install Composer dependencies + run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs + + - name: Check author permissions + id: check-permission + uses: actions-cool/check-user-permission@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + require: write + username: ${{ github.event.pull_request.user.login }} + #technically this would be fine for dependabot but generally bots don't count as team members + check-bot: true + + - name: Abort if user permissions are insufficient + #user doesn't have permission or is a bot + if: steps.check-permission.outputs.require-result != 'true' || steps.check-permission.outputs.check-result != 'false' + run: | + echo "::error::This user is not authorized to trigger releases" + exit 1 + + - name: Check changelog file is present + id: file-presence + run: | + CHANGELOG_FILE="changelogs/$(php build/dump-version-info.php changelog_file_name)" + if [ ! -f "${{ github.workspace }}/$CHANGELOG_FILE" ]; then + echo "::error::$CHANGELOG_FILE does not exist" + exit 1 + fi + echo FILE="$CHANGELOG_FILE" >> $GITHUB_OUTPUT + + - name: Check header is present in changelog file + run: | + FILE="${{ steps.file-presence.outputs.FILE }}" + VERSION="$(php build/dump-version-info.php base_version)" + if ! grep -Fqx "# $VERSION" "${{ github.workspace }}/$FILE"; then + echo "::error::Header for $VERSION not found in $FILE" + exit 1 + fi + + - name: Check version is valid for the selected channel + run: | + CHANNEL="$(php build/dump-version-info.php channel)" + if [ "$(php build/dump-version-info.php suffix_valid)" != "true" ]; then + echo "::error::Version $(php build/dump-version-info.php base_version) is not allowed on the $CHANNEL channel" + exit 1 + fi diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index c47a4399b..0a07a738b 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -1,19 +1,29 @@ name: Draft release on: - push: - tags: "*" + workflow_call: + outputs: + draft-url: + description: 'The URL of the draft release' + value: ${{ jobs.draft.outputs.draft-url }} + version: + description: 'PocketMine-MP version' + value: ${{ jobs.draft.outputs.version }} jobs: draft: name: Create GitHub draft release - if: "startsWith(github.event.head_commit.message, 'Release ')" + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: php-version: [8.2] + outputs: + draft-url: ${{ steps.create-draft.outputs.html_url }} + version: ${{ steps.get-pm-version.outputs.PM_VERSION }} + steps: - uses: actions/checkout@v4 with: @@ -53,12 +63,11 @@ jobs: - name: Get PocketMine-MP release version id: get-pm-version run: | - echo PM_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BASE_VERSION;') >> $GITHUB_OUTPUT - echo MCPE_VERSION=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\network\mcpe\protocol\ProtocolInfo::MINECRAFT_VERSION_NETWORK;') >> $GITHUB_OUTPUT - echo PM_VERSION_SHORT=$(php -r 'require "vendor/autoload.php"; $v = explode(".", \pocketmine\VersionInfo::BASE_VERSION); array_pop($v); echo implode(".", $v);') >> $GITHUB_OUTPUT - echo PM_VERSION_MD=$(php -r 'require "vendor/autoload.php"; echo str_replace(".", "", \pocketmine\VersionInfo::BASE_VERSION);') >> $GITHUB_OUTPUT - echo CHANGELOG_SUFFIX=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "" : "-" . \pocketmine\VersionInfo::BUILD_CHANNEL;') >> $GITHUB_OUTPUT - echo PRERELEASE=$(php -r 'require "vendor/autoload.php"; echo \pocketmine\VersionInfo::BUILD_CHANNEL === "stable" ? "false" : "true";') >> $GITHUB_OUTPUT + echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT + echo MCPE_VERSION=$(php build/dump-version-info.php mcpe_version) >> $GITHUB_OUTPUT + echo CHANGELOG_FILE_NAME=$(php build/dump-version-info.php changelog_file_name) >> $GITHUB_OUTPUT + echo CHANGELOG_MD_HEADER=$(php build/dump-version-info.php changelog_md_header) >> $GITHUB_OUTPUT + echo PRERELEASE=$(php build/dump-version-info.php prerelease) >> $GITHUB_OUTPUT - name: Generate PHP binary download URL id: php-binary-url @@ -91,6 +100,7 @@ jobs: - name: Create draft release uses: ncipollo/release-action@v1.14.0 + id: create-draft with: artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst commit: ${{ github.sha }} @@ -99,9 +109,10 @@ jobs: name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }} tag: ${{ steps.get-pm-version.outputs.PM_VERSION }} token: ${{ secrets.GITHUB_TOKEN }} + skipIfReleaseExists: true #for release PRs, tags will be created on release publish and trigger the tag release workflow - don't create a second draft body: | **For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}** - Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.PM_VERSION_SHORT }}${{ steps.get-pm-version.outputs.CHANGELOG_SUFFIX }}.md#${{ steps.get-pm-version.outputs.PM_VERSION_MD }}) for details. + Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.CHANGELOG_FILE_NAME }}#${{ steps.get-pm-version.outputs.CHANGELOG_MD_HEADER }}) for details. :information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}). diff --git a/build/dump-version-info.php b/build/dump-version-info.php new file mode 100644 index 000000000..8898d7cab --- /dev/null +++ b/build/dump-version-info.php @@ -0,0 +1,86 @@ + $options + */ +$options = [ + "base_version" => VersionInfo::BASE_VERSION, + "mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK, + "is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD, + "changelog_file_name" => function() : string{ + $version = VersionInfo::VERSION(); + $result = $version->getMajor() . "." . $version->getMinor(); + $suffix = $version->getSuffix(); + if($suffix !== ""){ + if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){ + fwrite(STDERR, "error: invalid current version suffix \"$suffix\"; aborting" . PHP_EOL); + exit(1); + } + $baseSuffix = $matches[1]; + $result .= "-" . strtolower($baseSuffix); + } + return $result . ".md"; + }, + "changelog_md_header" => fn() : string => str_replace(".", "", VersionInfo::BASE_VERSION), + "prerelease" => fn() : bool => VersionInfo::VERSION()->getSuffix() !== "", + "channel" => VersionInfo::BUILD_CHANNEL, + "suffix_valid" => function() : bool{ + //TODO: maybe this should be put into its own script? + $suffix = VersionInfo::VERSION()->getSuffix(); + if(VersionInfo::BUILD_CHANNEL === "stable"){ + //stable builds may not have suffixes + return $suffix === ""; + } + if(VersionInfo::BUILD_CHANNEL === "alpha" || VersionInfo::BUILD_CHANNEL === "beta"){ + $upperChannel = strtoupper(VersionInfo::BUILD_CHANNEL); + $upperSuffix = strtoupper($suffix); + return str_starts_with($upperSuffix, $upperChannel) && is_numeric(substr($upperSuffix, strlen($upperChannel))); + } + return true; + } +]; +if(count($argv) !== 2 || !isset($options[$argv[1]])){ + fwrite(STDERR, "Please provide an option (one of: " . implode(", ", array_keys($options)) . PHP_EOL); + exit(1); +} + +$result = $options[$argv[1]]; +if($result instanceof Closure){ + $result = $result(); +} +if(is_bool($result)){ + echo $result ? "true" : "false"; +}else{ + echo $result; +} diff --git a/build/make-release.php b/build/make-release.php index 7a570eb35..741f9d787 100644 --- a/build/make-release.php +++ b/build/make-release.php @@ -86,7 +86,8 @@ function systemWrapper(string $command, string $errorMessage) : void{ function main() : void{ $filteredOpts = []; - foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help"])) as $optName => $optValue){ + $postCommitOnly = false; + foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){ if($optName === "help"){ fwrite(STDOUT, "Options:\n"); @@ -96,6 +97,10 @@ function main() : void{ } exit(0); } + if($optName === "post"){ + $postCommitOnly = true; + continue; + } if(!is_string($optValue)){ fwrite(STDERR, "--$optName expects exactly 1 value\n"); exit(1); @@ -141,20 +146,25 @@ function main() : void{ $channel ??= "stable"; } - echo "About to tag version $currentVer. Next version will be $nextVer.\n"; - echo "$currentVer will be published on release channel \"$channel\".\n"; - echo "please add appropriate notes to the changelog and press enter..."; - fgets(STDIN); - systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes"); - system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result); - if($result === 0){ - echo "error: no changelog changes detected; aborting\n"; - exit(1); - } $versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php'; - replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel); - systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit"); - systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag"); + + if($postCommitOnly){ + echo "Skipping release commit & tag. Bumping to next version $nextVer directly.\n"; + }else{ + echo "About to tag version $currentVer. Next version will be $nextVer.\n"; + echo "$currentVer will be published on release channel \"$channel\".\n"; + echo "please add appropriate notes to the changelog and press enter..."; + fgets(STDIN); + systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes"); + system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result); + if($result === 0){ + echo "error: no changelog changes detected; aborting\n"; + exit(1); + } + replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel); + systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit"); + systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag"); + } replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel); systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit"); diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 2fde67d6c..b96e4348d 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -40,6 +40,7 @@ parameters: - build/php dynamicConstantNames: - pocketmine\VersionInfo::IS_DEVELOPMENT_BUILD + - pocketmine\VersionInfo::BUILD_CHANNEL - pocketmine\DEBUG - pocketmine\IS_DEVELOPMENT_BUILD stubFiles: From 94dff744941d1b9770bd96ccdcf890298f1fd1a4 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 31 Oct 2024 14:35:42 +0000 Subject: [PATCH 049/290] Prepare 5.20.1 release (#6479) --- changelogs/5.20.md | 6 ++++++ src/VersionInfo.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelogs/5.20.md b/changelogs/5.20.md index 8d7e971d9..b0da701d6 100644 --- a/changelogs/5.20.md +++ b/changelogs/5.20.md @@ -17,3 +17,9 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Fixes - Fixed a bug in `tools/generate-blockstate-upgrade-schema.php` that caused it to fail on 1.21.40 with the new mushroom block changes. + +# 5.20.1 +Released 31st October 2024. + +## Fixes +- Workaround old mob heads in world saves not being upgraded correctly and causing crashes. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 11c5b1c88..20d0154c7 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.20.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 0376e37966c2bddc8466f9b64aad7776656d0eaa Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 31 Oct 2024 14:38:30 +0000 Subject: [PATCH 050/290] 5.20.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11614028030 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 20d0154c7..5de1f5a05 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.20.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.20.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From c0b74b03419038f34bb25fa0c5dc05440bf86c5b Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 3 Nov 2024 14:05:46 +0000 Subject: [PATCH 051/290] Update BlockStateUpgrader.php --- src/data/bedrock/block/upgrade/BlockStateUpgrader.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index e95f3c80f..2dce762b8 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -157,9 +157,8 @@ final class BlockStateUpgrader{ if(is_string($remap->newName)){ $newName = $remap->newName; }else{ - //yes, overwriting $oldState here is intentional, although we probably don't actually need it anyway - //it shouldn't make any difference unless the flattened property appears in copiedState for some reason - [$newName, $oldState] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState); + //discard flatten modifications to state - the remap newState and copiedState will take care of it + [$newName, ] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState); } $newState = $remap->newState; From c63d0ef1b6187ab7bbffb70625d509620c1e3ae7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Nov 2024 14:39:41 +0000 Subject: [PATCH 052/290] Fix dodgy ignored PHPStan error --- src/network/mcpe/InventoryManager.php | 1 + tests/phpstan/configs/actual-problems.neon | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index c0969b61b..e4c303121 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -364,6 +364,7 @@ class InventoryManager{ FurnaceType::FURNACE => WindowTypes::FURNACE, FurnaceType::BLAST_FURNACE => WindowTypes::BLAST_FURNACE, FurnaceType::SMOKER => WindowTypes::SMOKER, + FurnaceType::CAMPFIRE, FurnaceType::SOUL_CAMPFIRE => throw new \LogicException("Campfire inventory cannot be displayed to a player") }, $inv instanceof EnchantInventory => WindowTypes::ENCHANTMENT, $inv instanceof BrewingStandInventory => WindowTypes::BREWING_STAND, diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 2d0e6d398..cc647da80 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -580,11 +580,6 @@ parameters: count: 1 path: ../../../src/network/mcpe/ChunkRequestTask.php - - - message: "#^Match expression does not handle remaining values\\: pocketmine\\\\crafting\\\\FurnaceType\\:\\:CAMPFIRE\\|pocketmine\\\\crafting\\\\FurnaceType\\:\\:SOUL_CAMPFIRE$#" - count: 1 - path: ../../../src/network/mcpe/InventoryManager.php - - message: "#^Cannot call method doFirstSpawn\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" count: 1 From 72fc1386319f481328d4c9b4f7efe899767ecf81 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Nov 2024 14:43:15 +0000 Subject: [PATCH 053/290] Regenerate PHPStan baselines --- tests/phpstan/configs/actual-problems.neon | 25 ---------------------- tests/phpstan/configs/phpstan-bugs.neon | 14 ++++++------ 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index cc647da80..e778cf004 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -785,11 +785,6 @@ parameters: count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - - message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#" - count: 1 - path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$fileResource \\(resource\\) does not accept resource\\|false\\.$#" count: 1 @@ -1035,11 +1030,6 @@ parameters: count: 1 path: ../../../src/world/format/io/region/RegionLoader.php - - - message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#" - count: 1 - path: ../../../src/world/format/io/region/RegionLoader.php - - message: "#^Parameter \\#2 \\$size of function ftruncate expects int\\<0, max\\>, int given\\.$#" count: 1 @@ -1190,18 +1180,3 @@ parameters: count: 1 path: ../../../src/world/light/SkyLightUpdate.php - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../../phpunit/block/BlockTest.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../../phpunit/block/regenerate_consistency_check.php - - - - message: "#^Parameter \\#1 \\$logFile of class pocketmine\\\\utils\\\\MainLogger constructor expects string, string\\|false given\\.$#" - count: 1 - path: ../../phpunit/scheduler/AsyncPoolTest.php - diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 0ead377ba..0fc3defda 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -5,20 +5,20 @@ parameters: count: 1 path: ../../../src/block/CakeWithCandle.php - - - message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: ../../../src/block/DoubleTallGrass.php - - message: "#^Method pocketmine\\\\block\\\\CopperDoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" count: 1 - path: ../../../src/block/utils/CopperTrait.php + path: ../../../src/block/CopperDoor.php - message: "#^Method pocketmine\\\\block\\\\CopperTrapdoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" count: 1 - path: ../../../src/block/utils/CopperTrait.php + path: ../../../src/block/CopperTrapdoor.php + + - + message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: ../../../src/block/DoubleTallGrass.php - message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" From 84464cde4f6f3506e0b55e136d83592de2ce1fa0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Nov 2024 14:44:50 +0000 Subject: [PATCH 054/290] Update BedrockBlockUpgradeSchema --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 9ecbad32f..ed5bb582f 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", - "pocketmine/bedrock-block-upgrade-schema": "~4.5.0+bedrock-1.21.40", + "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40", "pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40", "pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40", "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40", diff --git a/composer.lock b/composer.lock index d4cb38ddc..b2f3e7858 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5c5882370131d2ae3a043819c05e6f9c", + "content-hash": "b2fbf6e7a9d650341dc71fa4dd124681", "packages": [ { "name": "adhocore/json-comment", @@ -127,16 +127,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "4.5.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1" + "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/7943b894e050d68dd21b5c7fa609827a4e2e30f1", - "reference": "7943b894e050d68dd21b5c7fa609827a4e2e30f1", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52", + "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52", "shasum": "" }, "type": "library", @@ -147,9 +147,9 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.5.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0" }, - "time": "2024-10-23T16:15:24+00:00" + "time": "2024-11-03T14:13:50+00:00" }, { "name": "pocketmine/bedrock-data", From 96b12bddc1149d9a8610c6bffc555609790b70e8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Nov 2024 15:24:43 +0000 Subject: [PATCH 055/290] Prepare 5.21.0 release --- changelogs/5.21.md | 103 ++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +- 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.21.md diff --git a/changelogs/5.21.md b/changelogs/5.21.md new file mode 100644 index 000000000..b8131a3c8 --- /dev/null +++ b/changelogs/5.21.md @@ -0,0 +1,103 @@ +# 5.21.0 +Released 3rd November 2024. + +This is a minor feature release, including gameplay features and minor internals improvements. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## Gameplay +- Added the following new blocks: + - Campfire + - Chiseled Copper + - Chiseled Tuff + - Chiseled Tuff Bricks + - Copper Bulb + - Copper Door + - Copper Grate + - Copper Trapdoor + - Polished Tuff, Slabs, Stairs and Walls + - Soul Campfire + - Tuff Bricks, Slabs, Stairs and Walls + - Tuff Slab, Stairs and Walls +- Added the following new types of painting: + - backyard + - baroque + - bouquet + - cavebird + - changing + - cotan + - endboss + - fern + - finding + - humble + - lowmist + - meditative + - orb + - owlemons + - passage + - pond + - prairie_ride + - sunflowers + - tides + - unpacked +- Armor slots are now properly restricted (on the server side) to only contain the appropriate type of armor or headwear. +- Implemented Aqua Affinity enchantment. Since the server doesn't currently enforce any movement restrictions in water, this enchantment works based on client-side behaviour only. + +## API +### `pocketmine\block` +- The following new API methods have been added: + - `public ChiseledBookshelf->getLastInteractedSlot() : ?ChiseledBookshelfSlot` + - `public ChiseledBookshelf->setLastInteractedSlot(?ChiseledBookshelfSlot $lastInteractedSlot) : $this` +- The following new classes have been added: + - `utils\CopperMaterial` - interface implemented by all copper-like blocks with oxidation and waxed properties + - `CopperBulb` + - `CopperDoor` + - `CopperGrate` + - `CopperTrapdoor` + - `SoulCampfire` + - `Campfire` +- The following enums have new cases: + - `utils\BannerPatternType` has new cases `FLOW` and `GUSTER` + +### `pocketmine\crafting` +- The following enums have new cases: + - `FurnaceType` has new cases `CAMPFIRE` and `SOUL_CAMPFIRE` + +### `pocketmine\event` +- The following new classes have been added: + - `block\CampfireCookEvent` - called when a campfire finishes cooking an item + +### `pocketmine\inventory` +- Added support for slot validators, which permit restricting the types of items a player can put into an inventory slot. + - The following new classes have been added: + - `transaction\action\SlotValidator` - interface + - `transaction\action\CallbackSlotValidator` - class allowing a closure to be used for slot content validation + - `SlotValidatedInventory` - implemented by inventories which support the use of slot validators + +### `pocketmine\item` +- The following new API methods have been added: + - `public Item->getCooldownTag() : ?string` - returns the cooldown group this item belongs to, used for ensuring that, for example, different types of goat horns all respect a general horn cooldown +- The following new classes have been added: + - `ItemCooldownTags` - list of cooldown group tags used by PocketMine-MP + +### `pocketmine\world\sound` +- The following new classes have been added + - `CampfireSound` - sound made by campfires while lit + +## Tools +- `tools/blockstate-upgrade-schema-utils.php` (formerly `generate-blockstate-upgrade-schema.php`) has several improvements: + - Support for generating `flattenedProperties` rules as per [BedrockBlockUpgradeSchema 5.0.0](https://github.com/pmmp/BedrockBlockUpgradeSchema/releases/tag/5.0.0) + - Improved criteria for flattened property selection to minimize the amount of rules required + - Several subcommands are now available: + - `generate` - generates a schema from provided data + - `update` - regenerates an existing schema in a newer format + - `update-all` - regenerates a folder of existing schemas in a newer format (useful for updating `BedrockBlockUpgradeSchema` en masse) + - `test` - verifies that a schema produces the results expected by provided data + +## Internals +- Fixed incorrect visibility of `createEntity` in spawn eggs. +- Added support for newer `BedrockBlockUpgradeSchema` in `BlockStateUpgrader`. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 5de1f5a05..4dc9ea7f9 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.20.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.21.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From e598364f0695495cbe71ddf0b62f134b51091c6e Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 3 Nov 2024 15:30:16 +0000 Subject: [PATCH 056/290] 5.21.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11652565588 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 4dc9ea7f9..8fe6d32dd 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.21.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.21.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 2173aab967fde0e2680ab77bcd1be1e0c3a32e85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:05:38 +0000 Subject: [PATCH 057/290] Bump thollander/actions-comment-pull-request from 2 to 3 (#6482) --- .github/workflows/draft-release-from-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-release-from-pr.yml b/.github/workflows/draft-release-from-pr.yml index 3936a2649..8a347853b 100644 --- a/.github/workflows/draft-release-from-pr.yml +++ b/.github/workflows/draft-release-from-pr.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Post draft release URL on PR - uses: thollander/actions-comment-pull-request@v2 + uses: thollander/actions-comment-pull-request@v3 with: message: "[Draft release ${{ needs.draft.outputs.version }}](${{ needs.draft.outputs.draft-url }}) has been created for commit ${{ github.sha }}. Please review and publish it." From b3b8aaddffe3b3a1eee8dcc62dd08fcd10581916 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:29:01 +0000 Subject: [PATCH 058/290] Bump docker/build-push-action from 6.8.0 to 6.9.0 (#6465) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 7f5eae32a..4325c63f2 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.8.0 + uses: docker/build-push-action@v6.9.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.8.0 + uses: docker/build-push-action@v6.9.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.8.0 + uses: docker/build-push-action@v6.9.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.8.0 + uses: docker/build-push-action@v6.9.0 with: push: true context: ./pocketmine-mp From 8c04d47b1b2fb9775601af919d3263eb8690458f Mon Sep 17 00:00:00 2001 From: Muqsit Date: Sun, 10 Nov 2024 03:18:30 +0800 Subject: [PATCH 059/290] Make weakness effect only applicable for melee damage (#6489) --- src/event/entity/EntityDamageByEntityEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/entity/EntityDamageByEntityEvent.php b/src/event/entity/EntityDamageByEntityEvent.php index 5ef6c4b8e..052be9a15 100644 --- a/src/event/entity/EntityDamageByEntityEvent.php +++ b/src/event/entity/EntityDamageByEntityEvent.php @@ -57,7 +57,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{ $this->setModifier($this->getBaseDamage() * 0.3 * $strength->getEffectLevel(), self::MODIFIER_STRENGTH); } - if(($weakness = $effects->get(VanillaEffects::WEAKNESS())) !== null){ + if(($weakness = $effects->get(VanillaEffects::WEAKNESS())) !== null && $this->getCause() === EntityDamageEvent::CAUSE_ENTITY_ATTACK){ $this->setModifier(-($this->getBaseDamage() * 0.2 * $weakness->getEffectLevel()), self::MODIFIER_WEAKNESS); } } From 231eec911f762beb9c17ab18e44842eb9580f3cd Mon Sep 17 00:00:00 2001 From: WavyCraftNetwork <42108413+WavyCraftNetwork@users.noreply.github.com> Date: Sat, 9 Nov 2024 11:43:30 -0800 Subject: [PATCH 060/290] Enchanted Golden Apple: Regeneration 5 => 2 matching Java (#6445) --- src/item/GoldenAppleEnchanted.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/GoldenAppleEnchanted.php b/src/item/GoldenAppleEnchanted.php index 5b68f5f21..28e43a7a4 100644 --- a/src/item/GoldenAppleEnchanted.php +++ b/src/item/GoldenAppleEnchanted.php @@ -30,7 +30,7 @@ class GoldenAppleEnchanted extends GoldenApple{ public function getAdditionalEffects() : array{ return [ - new EffectInstance(VanillaEffects::REGENERATION(), 600, 4), + new EffectInstance(VanillaEffects::REGENERATION(), 600, 1), new EffectInstance(VanillaEffects::ABSORPTION(), 2400, 3), new EffectInstance(VanillaEffects::RESISTANCE(), 6000), new EffectInstance(VanillaEffects::FIRE_RESISTANCE(), 6000) From 05a9e9c76ebe64251245f77eb53bc90e409651b6 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sun, 10 Nov 2024 18:07:18 +0300 Subject: [PATCH 061/290] Implemented sound when drinking a potion (#6444) --- src/item/Potion.php | 3 ++- src/world/sound/BottleEmptySound.php | 35 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/world/sound/BottleEmptySound.php diff --git a/src/item/Potion.php b/src/item/Potion.php index 41b0f634a..192a25784 100644 --- a/src/item/Potion.php +++ b/src/item/Potion.php @@ -26,6 +26,7 @@ namespace pocketmine\item; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Living; use pocketmine\player\Player; +use pocketmine\world\sound\BottleEmptySound; class Potion extends Item implements ConsumableItem{ @@ -50,7 +51,7 @@ class Potion extends Item implements ConsumableItem{ } public function onConsume(Living $consumer) : void{ - + $consumer->broadcastSound(new BottleEmptySound()); } public function getAdditionalEffects() : array{ diff --git a/src/world/sound/BottleEmptySound.php b/src/world/sound/BottleEmptySound.php new file mode 100644 index 000000000..bb2a0be2a --- /dev/null +++ b/src/world/sound/BottleEmptySound.php @@ -0,0 +1,35 @@ + Date: Sun, 10 Nov 2024 22:15:30 +0300 Subject: [PATCH 062/290] Fixed server crash when applying item cooldown (#6491) This commit fixes server crash when applying a cooldown to any item which count is equals to 1. closes #6490 closes #6488 --- src/player/Player.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/player/Player.php b/src/player/Player.php index 8ae206e1a..192e26a5f 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1625,7 +1625,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } - $this->resetItemCooldown($item); + $this->resetItemCooldown($oldItem); $this->returnItemsFromAction($oldItem, $item, $returnedItems); $this->setUsingItem($item instanceof Releasable && $item->canStartUsingItem($this)); @@ -1654,7 +1654,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } $this->setUsingItem(false); - $this->resetItemCooldown($slot); + $this->resetItemCooldown($oldItem); $slot->pop(); $this->returnItemsFromAction($oldItem, $slot, [$slot->getResidue()]); @@ -1682,7 +1682,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $returnedItems = []; $result = $item->onReleaseUsing($this, $returnedItems); if($result === ItemUseResult::SUCCESS){ - $this->resetItemCooldown($item); + $this->resetItemCooldown($oldItem); $this->returnItemsFromAction($oldItem, $item, $returnedItems); return true; } From d2c3b8dacb2a7d8712cf7539ab265439be056c0b Mon Sep 17 00:00:00 2001 From: kostamax27 Date: Mon, 11 Nov 2024 16:10:19 +0100 Subject: [PATCH 063/290] Fix GC cycle count increases on player disconnect (#6487) --- src/inventory/ArmorInventory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inventory/ArmorInventory.php b/src/inventory/ArmorInventory.php index 0b3ae5b7b..8591cc65b 100644 --- a/src/inventory/ArmorInventory.php +++ b/src/inventory/ArmorInventory.php @@ -42,7 +42,7 @@ class ArmorInventory extends SimpleInventory{ ){ parent::__construct(4); - $this->validators->add(new CallbackSlotValidator($this->validate(...))); + $this->validators->add(new CallbackSlotValidator(self::validate(...))); } public function getHolder() : Living{ @@ -81,7 +81,7 @@ class ArmorInventory extends SimpleInventory{ $this->setItem(self::SLOT_FEET, $boots); } - private function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ + private static function validate(Inventory $inventory, Item $item, int $slot) : ?TransactionValidationException{ if($item instanceof Armor){ if($item->getArmorSlot() !== $slot){ return new TransactionValidationException("Armor item is in wrong slot"); From 4a702b97fd282731622f40692921e6f5763a9571 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:48:01 +0300 Subject: [PATCH 064/290] Prepare 5.21.1 release (#6493) --- changelogs/5.21.md | 9 +++++++++ src/VersionInfo.php | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelogs/5.21.md b/changelogs/5.21.md index b8131a3c8..9b3c2f89a 100644 --- a/changelogs/5.21.md +++ b/changelogs/5.21.md @@ -101,3 +101,12 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if ## Internals - Fixed incorrect visibility of `createEntity` in spawn eggs. - Added support for newer `BedrockBlockUpgradeSchema` in `BlockStateUpgrader`. + +# 5.21.1 +Released 12th November 2024. + +## Fixes +- Fixed server crash when applying a cooldown to an item with 1 count. +- Fixed garbage collector cycle count increase on player disconnect. +- Fixed weakness effect being applied to all attack types, causing damage splash potions to become weaker. +- Fixed Enchanted Golden Apple regeneration effect amplifier to match vanilla. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 8fe6d32dd..4691fb213 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.21.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 07d5046b83921e413ff32dbd5960759a3b8168c7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 12 Nov 2024 11:48:59 +0000 Subject: [PATCH 065/290] 5.21.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/11796630402 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 4691fb213..bc1b24c62 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.21.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.21.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 053a71c59d07f375890a3869f935ffc24534d2c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:28:32 +0000 Subject: [PATCH 066/290] Bump build/php from `084822a` to `a51259d` (#6495) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 084822aa9..a51259d7a 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 084822aa9e381ca05591e902a2613fe971dff3fd +Subproject commit a51259d7a6ea649d64f409fc0276baa59cf4f19a From 1f86949836766470f01d2e049483e51bcfef4515 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 12 Nov 2024 14:37:02 +0000 Subject: [PATCH 067/290] Create CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..e41741f36 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @pmmp/server-developers From 054538e6b72b2a14c6de4c4cb290e4c9c4375871 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 12 Nov 2024 17:54:31 +0000 Subject: [PATCH 068/290] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 35 ++++++++++++-------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 022518e81..c27ea7a47 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,43 +1,34 @@ -## Introduction + -### Relevant issues - +### Related issues & PRs ## Changes ### API changes + ### Behavioural changes + ## Backwards compatibility + ## Follow-up + + +## In-Game Testing - -## Tests - -I tested this PR by doing the following (tick all that apply): -- [ ] Writing PHPUnit tests (commit these in the `tests/phpunit` folder) -- [ ] Playtesting using a Minecraft client (provide screenshots or a video) -- [ ] Writing a test plugin (provide the code and sample output) -- [ ] Other (provide details) From fe70150db2f1944e748ba7c90911c886e865cfdb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Nov 2024 21:47:52 +0000 Subject: [PATCH 069/290] Update composer dependencies --- composer.lock | 186 +++++++++++++++++++++++++------------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/composer.lock b/composer.lock index b2f3e7858..eb1061ff5 100644 --- a/composer.lock +++ b/composer.lock @@ -153,16 +153,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "2.14.0+bedrock-1.21.40", + "version": "2.14.1+bedrock-1.21.40", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "606d32ae426164b0615898b95d10e23293bed6ac" + "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/606d32ae426164b0615898b95d10e23293bed6ac", - "reference": "606d32ae426164b0615898b95d10e23293bed6ac", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/4a41864ed09613ecec6791e2ae076a8ec7089cc4", + "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4", "shasum": "" }, "type": "library", @@ -173,22 +173,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.40" + "source": "https://github.com/pmmp/BedrockData/tree/2.14.1+bedrock-1.21.40" }, - "time": "2024-10-23T19:19:16+00:00" + "time": "2024-11-12T21:36:20+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.13.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0" + "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1dee9bbd0aaa65ed108b377b402746defe10b3b0", - "reference": "1dee9bbd0aaa65ed108b377b402746defe10b3b0", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1cf81305f2ffcf7dde9577c4f16a55c765192b03", + "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03", "shasum": "" }, "type": "library", @@ -199,9 +199,9 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.0" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.1" }, - "time": "2024-10-23T18:38:43+00:00" + "time": "2024-11-12T21:33:17+00:00" }, { "name": "pocketmine/bedrock-protocol", @@ -926,16 +926,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.9", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b51ef8059159330b74a4d52f68e671033c0fe463" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463", - "reference": "b51ef8059159330b74a4d52f68e671033c0fe463", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { @@ -972,7 +972,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.9" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -988,24 +988,24 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:49:33+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -1051,7 +1051,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1067,24 +1067,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1131,7 +1131,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1147,22 +1147,22 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" } ], "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -1201,7 +1201,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -1209,20 +1209,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -1233,7 +1233,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -1265,9 +1265,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -1548,32 +1548,32 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.15", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -1585,7 +1585,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -1614,7 +1614,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -1622,7 +1622,7 @@ "type": "github" } ], - "time": "2024-06-29T08:25:15+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1869,16 +1869,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.24", + "version": "10.5.38", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015" + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015", - "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", "shasum": "" }, "require": { @@ -1888,26 +1888,26 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -1950,7 +1950,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" }, "funding": [ { @@ -1966,7 +1966,7 @@ "type": "tidelift" } ], - "time": "2024-06-20T13:09:54+00:00" + "time": "2024-10-28T13:06:21+00:00" }, { "name": "sebastian/cli-parser", @@ -2138,16 +2138,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { @@ -2158,7 +2158,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -2203,7 +2203,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -2211,7 +2211,7 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { "name": "sebastian/complexity", From f3cc4a28e1c74627f56fef20efa67bc17fdd321f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Nov 2024 22:12:54 +0000 Subject: [PATCH 070/290] Easy wins for PHPStan 2.0 support --- phpstan.neon.dist | 2 -- src/block/Block.php | 2 ++ src/data/runtime/RuntimeDataDescriber.php | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b96e4348d..6e8578652 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -48,8 +48,6 @@ parameters: - tests/phpstan/stubs/leveldb.stub - tests/phpstan/stubs/pmmpthread.stub reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings - staticReflectionClassNamePatterns: - - "#^COM$#" typeAliases: #variadics don't work for this - mixed probably shouldn't work either, but for now it does #what we actually need is something that accepts an infinite number of parameters, but in the absence of that, diff --git a/src/block/Block.php b/src/block/Block.php index dbc269c63..89fe39265 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -362,6 +362,8 @@ class Block{ * * A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g. * data from a block entity). + * + * @phpstan-impure */ public function readStateFromWorld() : Block{ return $this; diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 6f1d35b98..7866dabdc 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -89,6 +89,11 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{ public function straightOnlyRailShape(int &$railShape) : void; + /** + * @phpstan-template T of \UnitEnum + * @phpstan-param T &$case + * @phpstan-param-out T &$case + */ public function enum(\UnitEnum &$case) : void; /** From 09bf203267aca9b83a09640e17bfac0fc7b58ff8 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 12 Nov 2024 22:57:14 +0000 Subject: [PATCH 071/290] Update RuntimeDataDescriber.php --- src/data/runtime/RuntimeDataDescriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 7866dabdc..6eb552a7b 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -92,7 +92,7 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{ /** * @phpstan-template T of \UnitEnum * @phpstan-param T &$case - * @phpstan-param-out T &$case + * @phpstan-param-out T $case */ public function enum(\UnitEnum &$case) : void; From fbeb01767075a6e8022822bf8791d82096bf7067 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Nov 2024 14:55:14 +0000 Subject: [PATCH 072/290] Promise: allow zero promises not supporting this has caused problems every time this function has been used in reality so far (#6092 and #6333). --- src/promise/Promise.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/promise/Promise.php b/src/promise/Promise.php index 0def7e605..27a5e50aa 100644 --- a/src/promise/Promise.php +++ b/src/promise/Promise.php @@ -69,16 +69,17 @@ final class Promise{ * * @phpstan-template TPromiseValue * @phpstan-template TKey of array-key - * @phpstan-param non-empty-array> $promises + * @phpstan-param array> $promises * * @phpstan-return Promise> */ public static function all(array $promises) : Promise{ - if(count($promises) === 0){ - throw new \InvalidArgumentException("At least one promise must be provided"); - } /** @phpstan-var PromiseResolver> $resolver */ $resolver = new PromiseResolver(); + if(count($promises) === 0){ + $resolver->resolve([]); + return $resolver->getPromise(); + } $values = []; $toResolve = count($promises); $continue = true; From 9b58d355162bc01c9c7b81e4e6bb65b2d64eb415 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:57:07 -0500 Subject: [PATCH 073/290] Implement Goat horns (#5232) Co-authored-by: ipad54 <63200545+ipad54@users.noreply.github.com> Co-authored-by: Dylan T. --- src/data/bedrock/GoatHornTypeIdMap.php | 48 +++++++++++++ src/data/bedrock/GoatHornTypeIds.php | 35 +++++++++ .../ItemSerializerDeserializerRegistrar.php | 10 +++ src/item/GoatHorn.php | 71 +++++++++++++++++++ src/item/GoatHornType.php | 36 ++++++++++ src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 8 +++ src/item/VanillaItems.php | 2 + src/world/sound/GoatHornSound.php | 46 ++++++++++++ 9 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 src/data/bedrock/GoatHornTypeIdMap.php create mode 100644 src/data/bedrock/GoatHornTypeIds.php create mode 100644 src/item/GoatHorn.php create mode 100644 src/item/GoatHornType.php create mode 100644 src/world/sound/GoatHornSound.php diff --git a/src/data/bedrock/GoatHornTypeIdMap.php b/src/data/bedrock/GoatHornTypeIdMap.php new file mode 100644 index 000000000..0510a09ce --- /dev/null +++ b/src/data/bedrock/GoatHornTypeIdMap.php @@ -0,0 +1,48 @@ + */ + use IntSaveIdMapTrait; + + private function __construct(){ + foreach(GoatHornType::cases() as $case){ + $this->register(match($case){ + GoatHornType::PONDER => GoatHornTypeIds::PONDER, + GoatHornType::SING => GoatHornTypeIds::SING, + GoatHornType::SEEK => GoatHornTypeIds::SEEK, + GoatHornType::FEEL => GoatHornTypeIds::FEEL, + GoatHornType::ADMIRE => GoatHornTypeIds::ADMIRE, + GoatHornType::CALL => GoatHornTypeIds::CALL, + GoatHornType::YEARN => GoatHornTypeIds::YEARN, + GoatHornType::DREAM => GoatHornTypeIds::DREAM + }, $case); + } + } +} diff --git a/src/data/bedrock/GoatHornTypeIds.php b/src/data/bedrock/GoatHornTypeIds.php new file mode 100644 index 000000000..048d246fe --- /dev/null +++ b/src/data/bedrock/GoatHornTypeIds.php @@ -0,0 +1,35 @@ + DyeColorIdMap::getInstance()->toInvertedId($item->getColor()) ); + $this->map1to1ItemWithMeta( + Ids::GOAT_HORN, + Items::GOAT_HORN(), + function(GoatHorn $item, int $meta) : void{ + $item->setHornType(GoatHornTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown goat horn type ID $meta")); + }, + fn(GoatHorn $item) => GoatHornTypeIdMap::getInstance()->toId($item->getHornType()) + ); $this->map1to1ItemWithMeta( Ids::MEDICINE, Items::MEDICINE(), diff --git a/src/item/GoatHorn.php b/src/item/GoatHorn.php new file mode 100644 index 000000000..088701e39 --- /dev/null +++ b/src/item/GoatHorn.php @@ -0,0 +1,71 @@ +enum($this->goatHornType); + } + + public function getHornType() : GoatHornType{ return $this->goatHornType; } + + /** + * @return $this + */ + public function setHornType(GoatHornType $type) : self{ + $this->goatHornType = $type; + return $this; + } + + public function getMaxStackSize() : int{ + return 1; + } + + public function getCooldownTicks() : int{ + return 140; + } + + public function getCooldownTag() : ?string{ + return ItemCooldownTags::GOAT_HORN; + } + + public function canStartUsingItem(Player $player) : bool{ + return true; + } + + public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{ + $position = $player->getPosition(); + $position->getWorld()->addSound($position, new GoatHornSound($this->goatHornType)); + + return ItemUseResult::SUCCESS; + } +} diff --git a/src/item/GoatHornType.php b/src/item/GoatHornType.php new file mode 100644 index 000000000..6c0c3b2f7 --- /dev/null +++ b/src/item/GoatHornType.php @@ -0,0 +1,36 @@ +register($prefix("dye"), fn() => Items::DYE()->setColor($color)); } + + foreach(GoatHornType::cases() as $goatHornType){ + $prefix = fn(string $name) => strtolower($goatHornType->name) . "_" . $name; + + $result->register($prefix("goat_horn"), fn() => Items::GOAT_HORN()->setHornType($goatHornType)); + } + foreach(SuspiciousStewType::cases() as $suspiciousStewType){ $prefix = fn(string $name) => strtolower($suspiciousStewType->name) . "_" . $name; @@ -1341,6 +1348,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("glow_berries", fn() => Items::GLOW_BERRIES()); $result->register("glow_ink_sac", fn() => Items::GLOW_INK_SAC()); $result->register("glowstone_dust", fn() => Items::GLOWSTONE_DUST()); + $result->register("goat_horn", fn() => Items::GOAT_HORN()); $result->register("gold_axe", fn() => Items::GOLDEN_AXE()); $result->register("gold_boots", fn() => Items::GOLDEN_BOOTS()); $result->register("gold_chestplate", fn() => Items::GOLDEN_CHESTPLATE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 5115ee48a..c5ab59447 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -171,6 +171,7 @@ use function strtolower; * @method static Item GLOWSTONE_DUST() * @method static GlowBerries GLOW_BERRIES() * @method static Item GLOW_INK_SAC() + * @method static GoatHorn GOAT_HORN() * @method static GoldenApple GOLDEN_APPLE() * @method static Axe GOLDEN_AXE() * @method static Armor GOLDEN_BOOTS() @@ -468,6 +469,7 @@ final class VanillaItems{ self::register("glow_berries", new GlowBerries(new IID(Ids::GLOW_BERRIES), "Glow Berries")); self::register("glow_ink_sac", new Item(new IID(Ids::GLOW_INK_SAC), "Glow Ink Sac")); self::register("glowstone_dust", new Item(new IID(Ids::GLOWSTONE_DUST), "Glowstone Dust")); + self::register("goat_horn", new GoatHorn(new IID(Ids::GOAT_HORN), "Goat Horn")); self::register("gold_ingot", new Item(new IID(Ids::GOLD_INGOT), "Gold Ingot")); self::register("gold_nugget", new Item(new IID(Ids::GOLD_NUGGET), "Gold Nugget")); self::register("golden_apple", new GoldenApple(new IID(Ids::GOLDEN_APPLE), "Golden Apple")); diff --git a/src/world/sound/GoatHornSound.php b/src/world/sound/GoatHornSound.php new file mode 100644 index 000000000..3987db3da --- /dev/null +++ b/src/world/sound/GoatHornSound.php @@ -0,0 +1,46 @@ +goatHornType){ + GoatHornType::PONDER => LevelSoundEvent::HORN_CALL0, + GoatHornType::SING => LevelSoundEvent::HORN_CALL1, + GoatHornType::SEEK => LevelSoundEvent::HORN_CALL2, + GoatHornType::FEEL => LevelSoundEvent::HORN_CALL3, + GoatHornType::ADMIRE => LevelSoundEvent::HORN_CALL4, + GoatHornType::CALL => LevelSoundEvent::HORN_CALL5, + GoatHornType::YEARN => LevelSoundEvent::HORN_CALL6, + GoatHornType::DREAM => LevelSoundEvent::HORN_CALL7 + }, $pos, false)]; + } +} From 33a7b463293af01a0234f20064b42f99ea09df51 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 14 Nov 2024 17:32:22 +0000 Subject: [PATCH 074/290] Use reflection to locate BlockTypeIds and ItemTypeIds for VanillaBlocks/VanillaItems (#6498) Use reflection to locate BlockTypeIds and ItemTypeIds for VanillaBlocks/VanillaItems Since BlockTypeIds and ItemTypeIds are derived from VanillaBlocks and VanillaItems respectively anyway (they only exist to allow identifying blocks/items without having to create instances of them), this hack is probably OK, and reduces the chances of mistakes. Previously it was explored to have these IDs generated by auto-incrementing in VanillaBlocks/Items and have the constants generated that way, but this proved to be too problematic because of unstable diffs no matter how we chose to sort the elements. See #6313 for previous research on the subject. This is obviously not a desirable hack to keep long-term. In the future it will probably make sense to redesign VanillaBlocks like so: enum VanillaBlocks { ... } VanillaBlocks::STONE (the type ID) VanillaBlocks::STONE->new() (to create a block) However, more research is needed on this, as I'd prefer not to make block creation any more verbose. --- src/block/VanillaBlocks.php | 1215 +++++++++++----------- src/block/WoodLikeBlockIdHelper.php | 263 ----- src/item/VanillaItems.php | 596 +++++------ tests/phpstan/configs/phpstan-bugs.neon | 50 + tests/phpunit/block/BlockTypeIdsTest.php | 1 + tests/phpunit/item/ItemTypeIdsTest.php | 1 + 6 files changed, 983 insertions(+), 1143 deletions(-) delete mode 100644 src/block/WoodLikeBlockIdHelper.php diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 60540dfb8..54cf90a0c 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -26,7 +26,6 @@ namespace pocketmine\block; use pocketmine\block\BlockBreakInfo as BreakInfo; use pocketmine\block\BlockIdentifier as BID; use pocketmine\block\BlockToolType as ToolType; -use pocketmine\block\BlockTypeIds as Ids; use pocketmine\block\BlockTypeInfo as Info; use pocketmine\block\BlockTypeTags as Tags; use pocketmine\block\tile\Banner as TileBanner; @@ -56,6 +55,7 @@ use pocketmine\block\tile\NormalFurnace as TileNormalFurnace; use pocketmine\block\tile\Note as TileNote; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; use pocketmine\block\tile\Smoker as TileSmoker; +use pocketmine\block\tile\Tile; use pocketmine\block\utils\AmethystTrait; use pocketmine\block\utils\LeavesType; use pocketmine\block\utils\SaplingType; @@ -64,9 +64,12 @@ use pocketmine\crafting\FurnaceType; use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\Item; use pocketmine\item\ToolTier; +use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use pocketmine\utils\CloningRegistryTrait; +use function is_int; use function mb_strtolower; +use function mb_strtoupper; use function strtolower; /** @@ -793,8 +796,28 @@ final class VanillaBlocks{ //NOOP } - protected static function register(string $name, Block $block) : void{ + /** + * @phpstan-template TBlock of Block + * @phpstan-param \Closure(BID) : TBlock $createBlock + * @phpstan-param class-string $tileClass + * @phpstan-return TBlock + */ + protected static function register(string $name, \Closure $createBlock, ?string $tileClass = null) : Block{ + //this sketchy hack allows us to avoid manually writing the constants inline + //since type IDs are generated from this class anyway, I'm OK with this hack + //nonetheless, we should try to get rid of it in a future major version (e.g by using string type IDs) + $reflect = new \ReflectionClass(BlockTypeIds::class); + $typeId = $reflect->getConstant(mb_strtoupper($name)); + if(!is_int($typeId)){ + //this allows registering new stuff without adding new type ID constants + //this reduces the number of mandatory steps to test new features in local development + \GlobalLogger::get()->error(self::class . ": No constant type ID found for $name, generating a new one"); + $typeId = BlockTypeIds::newId(); + } + $block = $createBlock(new BID($typeId, $tileClass)); self::_registryRegister($name, $block); + + return $block; } /** @@ -809,11 +832,12 @@ final class VanillaBlocks{ } protected static function setup() : void{ + self::register("air", fn(BID $id) => new Air($id, "Air", new Info(BreakInfo::indestructible(-1.0)))); + $railBreakInfo = new Info(new BreakInfo(0.7)); - self::register("activator_rail", new ActivatorRail(new BID(Ids::ACTIVATOR_RAIL), "Activator Rail", $railBreakInfo)); - self::register("air", new Air(new BID(Ids::AIR), "Air", new Info(BreakInfo::indestructible(-1.0)))); - self::register("anvil", new Anvil(new BID(Ids::ANVIL), "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("bamboo", new Bamboo(new BID(Ids::BAMBOO), "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ + self::register("activator_rail", fn(BID $id) => new ActivatorRail($id, "Activator Rail", $railBreakInfo)); + self::register("anvil", fn(BID $id) => new Anvil($id, "Anvil", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); + self::register("bamboo", fn(BID $id) => new Bamboo($id, "Bamboo", new Info(new class(2.0 /* 1.0 in PC */, ToolType::AXE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ if($item->getBlockToolType() === ToolType::SWORD){ return 0.0; @@ -821,242 +845,242 @@ final class VanillaBlocks{ return parent::getBreakTime($item); } }, [Tags::POTTABLE_PLANTS]))); - self::register("bamboo_sapling", new BambooSapling(new BID(Ids::BAMBOO_SAPLING), "Bamboo Sapling", new Info(BreakInfo::instant()))); + self::register("bamboo_sapling", fn(BID $id) => new BambooSapling($id, "Bamboo Sapling", new Info(BreakInfo::instant()))); $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); - self::register("banner", new FloorBanner(new BID(Ids::BANNER, TileBanner::class), "Banner", $bannerBreakInfo)); - self::register("wall_banner", new WallBanner(new BID(Ids::WALL_BANNER, TileBanner::class), "Wall Banner", $bannerBreakInfo)); - self::register("barrel", new Barrel(new BID(Ids::BARREL, TileBarrel::class), "Barrel", new Info(BreakInfo::axe(2.5)))); - self::register("barrier", new Transparent(new BID(Ids::BARRIER), "Barrier", new Info(BreakInfo::indestructible()))); - self::register("beacon", new Beacon(new BID(Ids::BEACON, TileBeacon::class), "Beacon", new Info(new BreakInfo(3.0)))); - self::register("bed", new Bed(new BID(Ids::BED, TileBed::class), "Bed Block", new Info(new BreakInfo(0.2)))); - self::register("bedrock", new Bedrock(new BID(Ids::BEDROCK), "Bedrock", new Info(BreakInfo::indestructible()))); + self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); + self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class); + self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class); + self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); + self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); + self::register("bed", fn(BID $id) => new Bed($id, "Bed Block", new Info(new BreakInfo(0.2))), TileBed::class); + self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible()))); - self::register("beetroots", new Beetroot(new BID(Ids::BEETROOTS), "Beetroot Block", new Info(BreakInfo::instant()))); - self::register("bell", new Bell(new BID(Ids::BELL, TileBell::class), "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); - self::register("blue_ice", new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new Info(BreakInfo::pickaxe(2.8)))); - self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD)))); - self::register("bookshelf", new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new Info(BreakInfo::axe(1.5)))); - self::register("chiseled_bookshelf", new ChiseledBookshelf(new BID(Ids::CHISELED_BOOKSHELF, TileChiseledBookshelf::class), "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5)))); - self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("beetroots", fn(BID $id) => new Beetroot($id, "Beetroot Block", new Info(BreakInfo::instant()))); + self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))), TileBell::class); + self::register("blue_ice", fn(BID $id) => new BlueIce($id, "Blue Ice", new Info(BreakInfo::pickaxe(2.8)))); + self::register("bone_block", fn(BID $id) => new BoneBlock($id, "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD)))); + self::register("bookshelf", fn(BID $id) => new Bookshelf($id, "Bookshelf", new Info(BreakInfo::axe(1.5)))); + self::register("chiseled_bookshelf", fn(BID $id) => new ChiseledBookshelf($id, "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))), TileChiseledBookshelf::class); + self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))), TileBrewingStand::class); $bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("brick_stairs", new Stair(new BID(Ids::BRICK_STAIRS), "Brick Stairs", $bricksBreakInfo)); - self::register("bricks", new Opaque(new BID(Ids::BRICKS), "Bricks", $bricksBreakInfo)); + self::register("brick_stairs", fn(BID $id) => new Stair($id, "Brick Stairs", $bricksBreakInfo)); + self::register("bricks", fn(BID $id) => new Opaque($id, "Bricks", $bricksBreakInfo)); - self::register("brown_mushroom", new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); - self::register("cactus", new Cactus(new BID(Ids::CACTUS), "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS]))); - self::register("cake", new Cake(new BID(Ids::CAKE), "Cake", new Info(new BreakInfo(0.5)))); + self::register("brown_mushroom", fn(BID $id) => new BrownMushroom($id, "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("cactus", fn(BID $id) => new Cactus($id, "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS]))); + self::register("cake", fn(BID $id) => new Cake($id, "Cake", new Info(new BreakInfo(0.5)))); $campfireBreakInfo = new Info(BreakInfo::axe(2.0)); - self::register("campfire", new Campfire(new BID(Ids::CAMPFIRE, TileCampfire::class), "Campfire", $campfireBreakInfo)); - self::register("soul_campfire", new SoulCampfire(new BID(Ids::SOUL_CAMPFIRE, TileCampfire::class), "Soul Campfire", $campfireBreakInfo)); + self::register("campfire", fn(BID $id) => new Campfire($id, "Campfire", $campfireBreakInfo), TileCampfire::class); + self::register("soul_campfire", fn(BID $id) => new SoulCampfire($id, "Soul Campfire", $campfireBreakInfo), TileCampfire::class); - self::register("carrots", new Carrot(new BID(Ids::CARROTS), "Carrot Block", new Info(BreakInfo::instant()))); + self::register("carrots", fn(BID $id) => new Carrot($id, "Carrot Block", new Info(BreakInfo::instant()))); $chestBreakInfo = new Info(BreakInfo::axe(2.5)); - self::register("chest", new Chest(new BID(Ids::CHEST, TileChest::class), "Chest", $chestBreakInfo)); - self::register("clay", new Clay(new BID(Ids::CLAY), "Clay Block", new Info(BreakInfo::shovel(0.6)))); - self::register("coal", new Coal(new BID(Ids::COAL), "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + self::register("chest", fn(BID $id) => new Chest($id, "Chest", $chestBreakInfo), TileChest::class); + self::register("clay", fn(BID $id) => new Clay($id, "Clay Block", new Info(BreakInfo::shovel(0.6)))); + self::register("coal", fn(BID $id) => new Coal($id, "Coal Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); $cobblestoneBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone", $cobblestone = new Opaque(new BID(Ids::COBBLESTONE), "Cobblestone", $cobblestoneBreakInfo)); - self::register("mossy_cobblestone", new Opaque(new BID(Ids::MOSSY_COBBLESTONE), "Mossy Cobblestone", $cobblestoneBreakInfo)); - self::register("cobblestone_stairs", new Stair(new BID(Ids::COBBLESTONE_STAIRS), "Cobblestone Stairs", $cobblestoneBreakInfo)); - self::register("mossy_cobblestone_stairs", new Stair(new BID(Ids::MOSSY_COBBLESTONE_STAIRS), "Mossy Cobblestone Stairs", $cobblestoneBreakInfo)); + $cobblestone = self::register("cobblestone", fn(BID $id) => new Opaque($id, "Cobblestone", $cobblestoneBreakInfo)); + self::register("mossy_cobblestone", fn(BID $id) => new Opaque($id, "Mossy Cobblestone", $cobblestoneBreakInfo)); + self::register("cobblestone_stairs", fn(BID $id) => new Stair($id, "Cobblestone Stairs", $cobblestoneBreakInfo)); + self::register("mossy_cobblestone_stairs", fn(BID $id) => new Stair($id, "Mossy Cobblestone Stairs", $cobblestoneBreakInfo)); - self::register("cobweb", new Cobweb(new BID(Ids::COBWEB), "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); - self::register("cocoa_pod", new CocoaBlock(new BID(Ids::COCOA_POD), "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); - self::register("coral_block", new CoralBlock(new BID(Ids::CORAL_BLOCK), "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); - self::register("daylight_sensor", new DaylightSensor(new BID(Ids::DAYLIGHT_SENSOR, TileDaylightSensor::class), "Daylight Sensor", new Info(BreakInfo::axe(0.2)))); - self::register("dead_bush", new DeadBush(new BID(Ids::DEAD_BUSH), "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); - self::register("detector_rail", new DetectorRail(new BID(Ids::DETECTOR_RAIL), "Detector Rail", $railBreakInfo)); + self::register("cobweb", fn(BID $id) => new Cobweb($id, "Cobweb", new Info(new BreakInfo(4.0, ToolType::SWORD | ToolType::SHEARS, 1)))); + self::register("cocoa_pod", fn(BID $id) => new CocoaBlock($id, "Cocoa Block", new Info(BreakInfo::axe(0.2, null, 15.0)))); + self::register("coral_block", fn(BID $id) => new CoralBlock($id, "Coral Block", new Info(BreakInfo::pickaxe(7.0, ToolTier::WOOD)))); + self::register("daylight_sensor", fn(BID $id) => new DaylightSensor($id, "Daylight Sensor", new Info(BreakInfo::axe(0.2))), TileDaylightSensor::class); + self::register("dead_bush", fn(BID $id) => new DeadBush($id, "Dead Bush", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); + self::register("detector_rail", fn(BID $id) => new DetectorRail($id, "Detector Rail", $railBreakInfo)); - self::register("diamond", new Opaque(new BID(Ids::DIAMOND), "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); - self::register("dirt", new Dirt(new BID(Ids::DIRT), "Dirt", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); - self::register("sunflower", new DoublePlant(new BID(Ids::SUNFLOWER), "Sunflower", new Info(BreakInfo::instant()))); - self::register("lilac", new DoublePlant(new BID(Ids::LILAC), "Lilac", new Info(BreakInfo::instant()))); - self::register("rose_bush", new DoublePlant(new BID(Ids::ROSE_BUSH), "Rose Bush", new Info(BreakInfo::instant()))); - self::register("peony", new DoublePlant(new BID(Ids::PEONY), "Peony", new Info(BreakInfo::instant()))); - self::register("pink_petals", new PinkPetals(new BID(Ids::PINK_PETALS), "Pink Petals", new Info(BreakInfo::instant()))); - self::register("double_tallgrass", new DoubleTallGrass(new BID(Ids::DOUBLE_TALLGRASS), "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("large_fern", new DoubleTallGrass(new BID(Ids::LARGE_FERN), "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("pitcher_plant", new DoublePlant(new BID(Ids::PITCHER_PLANT), "Pitcher Plant", new Info(BreakInfo::instant()))); - self::register("pitcher_crop", new PitcherCrop(new BID(Ids::PITCHER_CROP), "Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("double_pitcher_crop", new DoublePitcherCrop(new BID(Ids::DOUBLE_PITCHER_CROP), "Double Pitcher Crop", new Info(BreakInfo::instant()))); - self::register("dragon_egg", new DragonEgg(new BID(Ids::DRAGON_EGG), "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); - self::register("dried_kelp", new DriedKelp(new BID(Ids::DRIED_KELP), "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); - self::register("emerald", new Opaque(new BID(Ids::EMERALD), "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); - self::register("enchanting_table", new EnchantingTable(new BID(Ids::ENCHANTING_TABLE, TileEnchantingTable::class), "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0)))); - self::register("end_portal_frame", new EndPortalFrame(new BID(Ids::END_PORTAL_FRAME), "End Portal Frame", new Info(BreakInfo::indestructible()))); - self::register("end_rod", new EndRod(new BID(Ids::END_ROD), "End Rod", new Info(BreakInfo::instant()))); - self::register("end_stone", new Opaque(new BID(Ids::END_STONE), "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); + self::register("diamond", fn(BID $id) => new Opaque($id, "Diamond Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); + self::register("dirt", fn(BID $id) => new Dirt($id, "Dirt", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); + self::register("sunflower", fn(BID $id) => new DoublePlant($id, "Sunflower", new Info(BreakInfo::instant()))); + self::register("lilac", fn(BID $id) => new DoublePlant($id, "Lilac", new Info(BreakInfo::instant()))); + self::register("rose_bush", fn(BID $id) => new DoublePlant($id, "Rose Bush", new Info(BreakInfo::instant()))); + self::register("peony", fn(BID $id) => new DoublePlant($id, "Peony", new Info(BreakInfo::instant()))); + self::register("pink_petals", fn(BID $id) => new PinkPetals($id, "Pink Petals", new Info(BreakInfo::instant()))); + self::register("double_tallgrass", fn(BID $id) => new DoubleTallGrass($id, "Double Tallgrass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("large_fern", fn(BID $id) => new DoubleTallGrass($id, "Large Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("pitcher_plant", fn(BID $id) => new DoublePlant($id, "Pitcher Plant", new Info(BreakInfo::instant()))); + self::register("pitcher_crop", fn(BID $id) => new PitcherCrop($id, "Pitcher Crop", new Info(BreakInfo::instant()))); + self::register("double_pitcher_crop", fn(BID $id) => new DoublePitcherCrop($id, "Double Pitcher Crop", new Info(BreakInfo::instant()))); + self::register("dragon_egg", fn(BID $id) => new DragonEgg($id, "Dragon Egg", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("dried_kelp", fn(BID $id) => new DriedKelp($id, "Dried Kelp Block", new Info(new BreakInfo(0.5, ToolType::NONE, 0, 12.5)))); + self::register("emerald", fn(BID $id) => new Opaque($id, "Emerald Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::IRON, 30.0)))); + self::register("enchanting_table", fn(BID $id) => new EnchantingTable($id, "Enchanting Table", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 6000.0))), TileEnchantingTable::class); + self::register("end_portal_frame", fn(BID $id) => new EndPortalFrame($id, "End Portal Frame", new Info(BreakInfo::indestructible()))); + self::register("end_rod", fn(BID $id) => new EndRod($id, "End Rod", new Info(BreakInfo::instant()))); + self::register("end_stone", fn(BID $id) => new Opaque($id, "End Stone", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 45.0)))); $endBrickBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD, 4.0)); - self::register("end_stone_bricks", new Opaque(new BID(Ids::END_STONE_BRICKS), "End Stone Bricks", $endBrickBreakInfo)); - self::register("end_stone_brick_stairs", new Stair(new BID(Ids::END_STONE_BRICK_STAIRS), "End Stone Brick Stairs", $endBrickBreakInfo)); + self::register("end_stone_bricks", fn(BID $id) => new Opaque($id, "End Stone Bricks", $endBrickBreakInfo)); + self::register("end_stone_brick_stairs", fn(BID $id) => new Stair($id, "End Stone Brick Stairs", $endBrickBreakInfo)); - self::register("ender_chest", new EnderChest(new BID(Ids::ENDER_CHEST, TileEnderChest::class), "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0)))); - self::register("farmland", new Farmland(new BID(Ids::FARMLAND), "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); - self::register("fire", new Fire(new BID(Ids::FIRE), "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE]))); + self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0))), TileEnderChest::class); + self::register("farmland", fn(BID $id) => new Farmland($id, "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("fire", fn(BID $id) => new Fire($id, "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE]))); $flowerTypeInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]); - self::register("dandelion", new Flower(new BID(Ids::DANDELION), "Dandelion", $flowerTypeInfo)); - self::register("poppy", new Flower(new BID(Ids::POPPY), "Poppy", $flowerTypeInfo)); - self::register("allium", new Flower(new BID(Ids::ALLIUM), "Allium", $flowerTypeInfo)); - self::register("azure_bluet", new Flower(new BID(Ids::AZURE_BLUET), "Azure Bluet", $flowerTypeInfo)); - self::register("blue_orchid", new Flower(new BID(Ids::BLUE_ORCHID), "Blue Orchid", $flowerTypeInfo)); - self::register("cornflower", new Flower(new BID(Ids::CORNFLOWER), "Cornflower", $flowerTypeInfo)); - self::register("lily_of_the_valley", new Flower(new BID(Ids::LILY_OF_THE_VALLEY), "Lily of the Valley", $flowerTypeInfo)); - self::register("orange_tulip", new Flower(new BID(Ids::ORANGE_TULIP), "Orange Tulip", $flowerTypeInfo)); - self::register("oxeye_daisy", new Flower(new BID(Ids::OXEYE_DAISY), "Oxeye Daisy", $flowerTypeInfo)); - self::register("pink_tulip", new Flower(new BID(Ids::PINK_TULIP), "Pink Tulip", $flowerTypeInfo)); - self::register("red_tulip", new Flower(new BID(Ids::RED_TULIP), "Red Tulip", $flowerTypeInfo)); - self::register("white_tulip", new Flower(new BID(Ids::WHITE_TULIP), "White Tulip", $flowerTypeInfo)); - self::register("torchflower", new Flower(new BID(Ids::TORCHFLOWER), "Torchflower", $flowerTypeInfo)); - self::register("torchflower_crop", new TorchflowerCrop(new BID(Ids::TORCHFLOWER_CROP), "Torchflower Crop", new Info(BreakInfo::instant()))); - self::register("flower_pot", new FlowerPot(new BID(Ids::FLOWER_POT, TileFlowerPot::class), "Flower Pot", new Info(BreakInfo::instant()))); - self::register("frosted_ice", new FrostedIce(new BID(Ids::FROSTED_ICE), "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); - self::register("furnace", new Furnace(new BID(Ids::FURNACE, TileNormalFurnace::class), "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE)); - self::register("blast_furnace", new Furnace(new BID(Ids::BLAST_FURNACE, TileBlastFurnace::class), "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE)); - self::register("smoker", new Furnace(new BID(Ids::SMOKER, TileSmoker::class), "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER)); + self::register("dandelion", fn(BID $id) => new Flower($id, "Dandelion", $flowerTypeInfo)); + self::register("poppy", fn(BID $id) => new Flower($id, "Poppy", $flowerTypeInfo)); + self::register("allium", fn(BID $id) => new Flower($id, "Allium", $flowerTypeInfo)); + self::register("azure_bluet", fn(BID $id) => new Flower($id, "Azure Bluet", $flowerTypeInfo)); + self::register("blue_orchid", fn(BID $id) => new Flower($id, "Blue Orchid", $flowerTypeInfo)); + self::register("cornflower", fn(BID $id) => new Flower($id, "Cornflower", $flowerTypeInfo)); + self::register("lily_of_the_valley", fn(BID $id) => new Flower($id, "Lily of the Valley", $flowerTypeInfo)); + self::register("orange_tulip", fn(BID $id) => new Flower($id, "Orange Tulip", $flowerTypeInfo)); + self::register("oxeye_daisy", fn(BID $id) => new Flower($id, "Oxeye Daisy", $flowerTypeInfo)); + self::register("pink_tulip", fn(BID $id) => new Flower($id, "Pink Tulip", $flowerTypeInfo)); + self::register("red_tulip", fn(BID $id) => new Flower($id, "Red Tulip", $flowerTypeInfo)); + self::register("white_tulip", fn(BID $id) => new Flower($id, "White Tulip", $flowerTypeInfo)); + self::register("torchflower", fn(BID $id) => new Flower($id, "Torchflower", $flowerTypeInfo)); + self::register("torchflower_crop", fn(BID $id) => new TorchflowerCrop($id, "Torchflower Crop", new Info(BreakInfo::instant()))); + self::register("flower_pot", fn(BID $id) => new FlowerPot($id, "Flower Pot", new Info(BreakInfo::instant())), TileFlowerPot::class); + self::register("frosted_ice", fn(BID $id) => new FrostedIce($id, "Frosted Ice", new Info(BreakInfo::pickaxe(2.5)))); + self::register("furnace", fn(BID $id) => new Furnace($id, "Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::FURNACE), TileNormalFurnace::class); + self::register("blast_furnace", fn(BID $id) => new Furnace($id, "Blast Furnace", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::BLAST_FURNACE), TileBlastFurnace::class); + self::register("smoker", fn(BID $id) => new Furnace($id, "Smoker", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)), FurnaceType::SMOKER), TileSmoker::class); $glassBreakInfo = new Info(new BreakInfo(0.3)); - self::register("glass", new Glass(new BID(Ids::GLASS), "Glass", $glassBreakInfo)); - self::register("glass_pane", new GlassPane(new BID(Ids::GLASS_PANE), "Glass Pane", $glassBreakInfo)); - self::register("glowing_obsidian", new GlowingObsidian(new BID(Ids::GLOWING_OBSIDIAN), "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); - self::register("glowstone", new Glowstone(new BID(Ids::GLOWSTONE), "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); - self::register("glow_lichen", new GlowLichen(new BID(Ids::GLOW_LICHEN), "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); - self::register("gold", new Opaque(new BID(Ids::GOLD), "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); + self::register("glass", fn(BID $id) => new Glass($id, "Glass", $glassBreakInfo)); + self::register("glass_pane", fn(BID $id) => new GlassPane($id, "Glass Pane", $glassBreakInfo)); + self::register("glowing_obsidian", fn(BID $id) => new GlowingObsidian($id, "Glowing Obsidian", new Info(BreakInfo::pickaxe(10.0, ToolTier::DIAMOND, 50.0)))); + self::register("glowstone", fn(BID $id) => new Glowstone($id, "Glowstone", new Info(BreakInfo::pickaxe(0.3)))); + self::register("glow_lichen", fn(BID $id) => new GlowLichen($id, "Glow Lichen", new Info(BreakInfo::axe(0.2, null, 0.2)))); + self::register("gold", fn(BID $id) => new Opaque($id, "Gold Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::IRON, 30.0)))); $grassBreakInfo = BreakInfo::shovel(0.6); - self::register("grass", new Grass(new BID(Ids::GRASS), "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); - self::register("grass_path", new GrassPath(new BID(Ids::GRASS_PATH), "Grass Path", new Info($grassBreakInfo))); - self::register("gravel", new Gravel(new BID(Ids::GRAVEL), "Gravel", new Info(BreakInfo::shovel(0.6)))); + self::register("grass", fn(BID $id) => new Grass($id, "Grass", new Info($grassBreakInfo, [Tags::DIRT]))); + self::register("grass_path", fn(BID $id) => new GrassPath($id, "Grass Path", new Info($grassBreakInfo))); + self::register("gravel", fn(BID $id) => new Gravel($id, "Gravel", new Info(BreakInfo::shovel(0.6)))); $hardenedClayBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("hardened_clay", new HardenedClay(new BID(Ids::HARDENED_CLAY), "Hardened Clay", $hardenedClayBreakInfo)); + self::register("hardened_clay", fn(BID $id) => new HardenedClay($id, "Hardened Clay", $hardenedClayBreakInfo)); $hardenedGlassBreakInfo = new Info(new BreakInfo(10.0)); - self::register("hardened_glass", new HardenedGlass(new BID(Ids::HARDENED_GLASS), "Hardened Glass", $hardenedGlassBreakInfo)); - self::register("hardened_glass_pane", new HardenedGlassPane(new BID(Ids::HARDENED_GLASS_PANE), "Hardened Glass Pane", $hardenedGlassBreakInfo)); - self::register("hay_bale", new HayBale(new BID(Ids::HAY_BALE), "Hay Bale", new Info(new BreakInfo(0.5)))); - self::register("hopper", new Hopper(new BID(Ids::HOPPER, TileHopper::class), "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0)))); - self::register("ice", new Ice(new BID(Ids::ICE), "Ice", new Info(BreakInfo::pickaxe(0.5)))); + self::register("hardened_glass", fn(BID $id) => new HardenedGlass($id, "Hardened Glass", $hardenedGlassBreakInfo)); + self::register("hardened_glass_pane", fn(BID $id) => new HardenedGlassPane($id, "Hardened Glass Pane", $hardenedGlassBreakInfo)); + self::register("hay_bale", fn(BID $id) => new HayBale($id, "Hay Bale", new Info(new BreakInfo(0.5)))); + self::register("hopper", fn(BID $id) => new Hopper($id, "Hopper", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD, 15.0))), TileHopper::class); + self::register("ice", fn(BID $id) => new Ice($id, "Ice", new Info(BreakInfo::pickaxe(0.5)))); $updateBlockBreakInfo = new Info(new BreakInfo(1.0)); - self::register("info_update", new Opaque(new BID(Ids::INFO_UPDATE), "update!", $updateBlockBreakInfo)); - self::register("info_update2", new Opaque(new BID(Ids::INFO_UPDATE2), "ate!upd", $updateBlockBreakInfo)); - self::register("invisible_bedrock", new Transparent(new BID(Ids::INVISIBLE_BEDROCK), "Invisible Bedrock", new Info(BreakInfo::indestructible()))); + self::register("info_update", fn(BID $id) => new Opaque($id, "update!", $updateBlockBreakInfo)); + self::register("info_update2", fn(BID $id) => new Opaque($id, "ate!upd", $updateBlockBreakInfo)); + self::register("invisible_bedrock", fn(BID $id) => new Transparent($id, "Invisible Bedrock", new Info(BreakInfo::indestructible()))); $ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0)); - self::register("iron", new Opaque(new BID(Ids::IRON), "Iron Block", $ironBreakInfo)); - self::register("iron_bars", new Thin(new BID(Ids::IRON_BARS), "Iron Bars", $ironBreakInfo)); + self::register("iron", fn(BID $id) => new Opaque($id, "Iron Block", $ironBreakInfo)); + self::register("iron_bars", fn(BID $id) => new Thin($id, "Iron Bars", $ironBreakInfo)); $ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 25.0)); - self::register("iron_door", new Door(new BID(Ids::IRON_DOOR), "Iron Door", $ironDoorBreakInfo)); - self::register("iron_trapdoor", new Trapdoor(new BID(Ids::IRON_TRAPDOOR), "Iron Trapdoor", $ironDoorBreakInfo)); + self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", $ironDoorBreakInfo)); + self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", $ironDoorBreakInfo)); $itemFrameInfo = new Info(new BreakInfo(0.25)); - self::register("item_frame", new ItemFrame(new BID(Ids::ITEM_FRAME, TileItemFrame::class), "Item Frame", $itemFrameInfo)); - self::register("glowing_item_frame", new ItemFrame(new BID(Ids::GLOWING_ITEM_FRAME, TileGlowingItemFrame::class), "Glow Item Frame", $itemFrameInfo)); + self::register("item_frame", fn(BID $id) => new ItemFrame($id, "Item Frame", $itemFrameInfo), TileItemFrame::class); + self::register("glowing_item_frame", fn(BID $id) => new ItemFrame($id, "Glow Item Frame", $itemFrameInfo), TileGlowingItemFrame::class); - self::register("jukebox", new Jukebox(new BID(Ids::JUKEBOX, TileJukebox::class), "Jukebox", new Info(BreakInfo::axe(0.8)))); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not - self::register("ladder", new Ladder(new BID(Ids::LADDER), "Ladder", new Info(BreakInfo::axe(0.4)))); + self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(0.8))), TileJukebox::class); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not + self::register("ladder", fn(BID $id) => new Ladder($id, "Ladder", new Info(BreakInfo::axe(0.4)))); $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)); - self::register("lantern", new Lantern(new BID(Ids::LANTERN), "Lantern", $lanternBreakInfo, 15)); - self::register("soul_lantern", new Lantern(new BID(Ids::SOUL_LANTERN), "Soul Lantern", $lanternBreakInfo, 10)); + self::register("lantern", fn(BID $id) => new Lantern($id, "Lantern", $lanternBreakInfo, 15)); + self::register("soul_lantern", fn(BID $id) => new Lantern($id, "Soul Lantern", $lanternBreakInfo, 10)); - self::register("lapis_lazuli", new Opaque(new BID(Ids::LAPIS_LAZULI), "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); - self::register("lava", new Lava(new BID(Ids::LAVA), "Lava", new Info(BreakInfo::indestructible(500.0)))); - self::register("lectern", new Lectern(new BID(Ids::LECTERN, TileLectern::class), "Lectern", new Info(BreakInfo::axe(2.0)))); - self::register("lever", new Lever(new BID(Ids::LEVER), "Lever", new Info(new BreakInfo(0.5)))); - self::register("magma", new Magma(new BID(Ids::MAGMA), "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); - self::register("melon", new Melon(new BID(Ids::MELON), "Melon Block", new Info(BreakInfo::axe(1.0)))); - self::register("melon_stem", new MelonStem(new BID(Ids::MELON_STEM), "Melon Stem", new Info(BreakInfo::instant()))); - self::register("monster_spawner", new MonsterSpawner(new BID(Ids::MONSTER_SPAWNER, TileMonsterSpawner::class), "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); - self::register("mycelium", new Mycelium(new BID(Ids::MYCELIUM), "Mycelium", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); + self::register("lapis_lazuli", fn(BID $id) => new Opaque($id, "Lapis Lazuli Block", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE)))); + self::register("lava", fn(BID $id) => new Lava($id, "Lava", new Info(BreakInfo::indestructible(500.0)))); + self::register("lectern", fn(BID $id) => new Lectern($id, "Lectern", new Info(BreakInfo::axe(2.0))), TileLectern::class); + self::register("lever", fn(BID $id) => new Lever($id, "Lever", new Info(new BreakInfo(0.5)))); + self::register("magma", fn(BID $id) => new Magma($id, "Magma Block", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("melon", fn(BID $id) => new Melon($id, "Melon Block", new Info(BreakInfo::axe(1.0)))); + self::register("melon_stem", fn(BID $id) => new MelonStem($id, "Melon Stem", new Info(BreakInfo::instant()))); + self::register("monster_spawner", fn(BID $id) => new MonsterSpawner($id, "Monster Spawner", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))), TileMonsterSpawner::class); + self::register("mycelium", fn(BID $id) => new Mycelium($id, "Mycelium", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); $netherBrickBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("nether_bricks", new Opaque(new BID(Ids::NETHER_BRICKS), "Nether Bricks", $netherBrickBreakInfo)); - self::register("red_nether_bricks", new Opaque(new BID(Ids::RED_NETHER_BRICKS), "Red Nether Bricks", $netherBrickBreakInfo)); - self::register("nether_brick_fence", new Fence(new BID(Ids::NETHER_BRICK_FENCE), "Nether Brick Fence", $netherBrickBreakInfo)); - self::register("nether_brick_stairs", new Stair(new BID(Ids::NETHER_BRICK_STAIRS), "Nether Brick Stairs", $netherBrickBreakInfo)); - self::register("red_nether_brick_stairs", new Stair(new BID(Ids::RED_NETHER_BRICK_STAIRS), "Red Nether Brick Stairs", $netherBrickBreakInfo)); - self::register("chiseled_nether_bricks", new Opaque(new BID(Ids::CHISELED_NETHER_BRICKS), "Chiseled Nether Bricks", $netherBrickBreakInfo)); - self::register("cracked_nether_bricks", new Opaque(new BID(Ids::CRACKED_NETHER_BRICKS), "Cracked Nether Bricks", $netherBrickBreakInfo)); + self::register("nether_bricks", fn(BID $id) => new Opaque($id, "Nether Bricks", $netherBrickBreakInfo)); + self::register("red_nether_bricks", fn(BID $id) => new Opaque($id, "Red Nether Bricks", $netherBrickBreakInfo)); + self::register("nether_brick_fence", fn(BID $id) => new Fence($id, "Nether Brick Fence", $netherBrickBreakInfo)); + self::register("nether_brick_stairs", fn(BID $id) => new Stair($id, "Nether Brick Stairs", $netherBrickBreakInfo)); + self::register("red_nether_brick_stairs", fn(BID $id) => new Stair($id, "Red Nether Brick Stairs", $netherBrickBreakInfo)); + self::register("chiseled_nether_bricks", fn(BID $id) => new Opaque($id, "Chiseled Nether Bricks", $netherBrickBreakInfo)); + self::register("cracked_nether_bricks", fn(BID $id) => new Opaque($id, "Cracked Nether Bricks", $netherBrickBreakInfo)); - self::register("nether_portal", new NetherPortal(new BID(Ids::NETHER_PORTAL), "Nether Portal", new Info(BreakInfo::indestructible(0.0)))); - self::register("nether_reactor_core", new NetherReactor(new BID(Ids::NETHER_REACTOR_CORE), "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); - self::register("nether_wart_block", new Opaque(new BID(Ids::NETHER_WART_BLOCK), "Nether Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); - self::register("nether_wart", new NetherWartPlant(new BID(Ids::NETHER_WART), "Nether Wart", new Info(BreakInfo::instant()))); - self::register("netherrack", new Netherrack(new BID(Ids::NETHERRACK), "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD)))); - self::register("note_block", new Note(new BID(Ids::NOTE_BLOCK, TileNote::class), "Note Block", new Info(BreakInfo::axe(0.8)))); - self::register("obsidian", new Opaque(new BID(Ids::OBSIDIAN), "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */, ToolTier::DIAMOND, 6000.0)))); - self::register("packed_ice", new PackedIce(new BID(Ids::PACKED_ICE), "Packed Ice", new Info(BreakInfo::pickaxe(0.5)))); - self::register("podzol", new Podzol(new BID(Ids::PODZOL), "Podzol", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); - self::register("potatoes", new Potato(new BID(Ids::POTATOES), "Potato Block", new Info(BreakInfo::instant()))); - self::register("powered_rail", new PoweredRail(new BID(Ids::POWERED_RAIL), "Powered Rail", $railBreakInfo)); + self::register("nether_portal", fn(BID $id) => new NetherPortal($id, "Nether Portal", new Info(BreakInfo::indestructible(0.0)))); + self::register("nether_reactor_core", fn(BID $id) => new NetherReactor($id, "Nether Reactor Core", new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)))); + self::register("nether_wart_block", fn(BID $id) => new Opaque($id, "Nether Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); + self::register("nether_wart", fn(BID $id) => new NetherWartPlant($id, "Nether Wart", new Info(BreakInfo::instant()))); + self::register("netherrack", fn(BID $id) => new Netherrack($id, "Netherrack", new Info(BreakInfo::pickaxe(0.4, ToolTier::WOOD)))); + self::register("note_block", fn(BID $id) => new Note($id, "Note Block", new Info(BreakInfo::axe(0.8))), TileNote::class); + self::register("obsidian", fn(BID $id) => new Opaque($id, "Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in PC */, ToolTier::DIAMOND, 6000.0)))); + self::register("packed_ice", fn(BID $id) => new PackedIce($id, "Packed Ice", new Info(BreakInfo::pickaxe(0.5)))); + self::register("podzol", fn(BID $id) => new Podzol($id, "Podzol", new Info(BreakInfo::shovel(0.5), [Tags::DIRT]))); + self::register("potatoes", fn(BID $id) => new Potato($id, "Potato Block", new Info(BreakInfo::instant()))); + self::register("powered_rail", fn(BID $id) => new PoweredRail($id, "Powered Rail", $railBreakInfo)); $prismarineBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("prismarine", new Opaque(new BID(Ids::PRISMARINE), "Prismarine", $prismarineBreakInfo)); - self::register("dark_prismarine", new Opaque(new BID(Ids::DARK_PRISMARINE), "Dark Prismarine", $prismarineBreakInfo)); - self::register("prismarine_bricks", new Opaque(new BID(Ids::PRISMARINE_BRICKS), "Prismarine Bricks", $prismarineBreakInfo)); - self::register("prismarine_bricks_stairs", new Stair(new BID(Ids::PRISMARINE_BRICKS_STAIRS), "Prismarine Bricks Stairs", $prismarineBreakInfo)); - self::register("dark_prismarine_stairs", new Stair(new BID(Ids::DARK_PRISMARINE_STAIRS), "Dark Prismarine Stairs", $prismarineBreakInfo)); - self::register("prismarine_stairs", new Stair(new BID(Ids::PRISMARINE_STAIRS), "Prismarine Stairs", $prismarineBreakInfo)); + self::register("prismarine", fn(BID $id) => new Opaque($id, "Prismarine", $prismarineBreakInfo)); + self::register("dark_prismarine", fn(BID $id) => new Opaque($id, "Dark Prismarine", $prismarineBreakInfo)); + self::register("prismarine_bricks", fn(BID $id) => new Opaque($id, "Prismarine Bricks", $prismarineBreakInfo)); + self::register("prismarine_bricks_stairs", fn(BID $id) => new Stair($id, "Prismarine Bricks Stairs", $prismarineBreakInfo)); + self::register("dark_prismarine_stairs", fn(BID $id) => new Stair($id, "Dark Prismarine Stairs", $prismarineBreakInfo)); + self::register("prismarine_stairs", fn(BID $id) => new Stair($id, "Prismarine Stairs", $prismarineBreakInfo)); $pumpkinBreakInfo = new Info(BreakInfo::axe(1.0)); - self::register("pumpkin", new Pumpkin(new BID(Ids::PUMPKIN), "Pumpkin", $pumpkinBreakInfo)); - self::register("carved_pumpkin", new CarvedPumpkin(new BID(Ids::CARVED_PUMPKIN), "Carved Pumpkin", new Info(BreakInfo::axe(1.0), enchantmentTags: [EnchantmentTags::MASK]))); - self::register("lit_pumpkin", new LitPumpkin(new BID(Ids::LIT_PUMPKIN), "Jack o'Lantern", $pumpkinBreakInfo)); + self::register("pumpkin", fn(BID $id) => new Pumpkin($id, "Pumpkin", $pumpkinBreakInfo)); + self::register("carved_pumpkin", fn(BID $id) => new CarvedPumpkin($id, "Carved Pumpkin", new Info(BreakInfo::axe(1.0), enchantmentTags: [EnchantmentTags::MASK]))); + self::register("lit_pumpkin", fn(BID $id) => new LitPumpkin($id, "Jack o'Lantern", $pumpkinBreakInfo)); - self::register("pumpkin_stem", new PumpkinStem(new BID(Ids::PUMPKIN_STEM), "Pumpkin Stem", new Info(BreakInfo::instant()))); + self::register("pumpkin_stem", fn(BID $id) => new PumpkinStem($id, "Pumpkin Stem", new Info(BreakInfo::instant()))); $purpurBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("purpur", new Opaque(new BID(Ids::PURPUR), "Purpur Block", $purpurBreakInfo)); - self::register("purpur_pillar", new SimplePillar(new BID(Ids::PURPUR_PILLAR), "Purpur Pillar", $purpurBreakInfo)); - self::register("purpur_stairs", new Stair(new BID(Ids::PURPUR_STAIRS), "Purpur Stairs", $purpurBreakInfo)); + self::register("purpur", fn(BID $id) => new Opaque($id, "Purpur Block", $purpurBreakInfo)); + self::register("purpur_pillar", fn(BID $id) => new SimplePillar($id, "Purpur Pillar", $purpurBreakInfo)); + self::register("purpur_stairs", fn(BID $id) => new Stair($id, "Purpur Stairs", $purpurBreakInfo)); $quartzBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); - self::register("quartz", new Opaque(new BID(Ids::QUARTZ), "Quartz Block", $quartzBreakInfo)); - self::register("chiseled_quartz", new SimplePillar(new BID(Ids::CHISELED_QUARTZ), "Chiseled Quartz Block", $quartzBreakInfo)); - self::register("quartz_pillar", new SimplePillar(new BID(Ids::QUARTZ_PILLAR), "Quartz Pillar", $quartzBreakInfo)); - self::register("smooth_quartz", new Opaque(new BID(Ids::SMOOTH_QUARTZ), "Smooth Quartz Block", $quartzBreakInfo)); - self::register("quartz_bricks", new Opaque(new BID(Ids::QUARTZ_BRICKS), "Quartz Bricks", $quartzBreakInfo)); + self::register("quartz", fn(BID $id) => new Opaque($id, "Quartz Block", $quartzBreakInfo)); + self::register("chiseled_quartz", fn(BID $id) => new SimplePillar($id, "Chiseled Quartz Block", $quartzBreakInfo)); + self::register("quartz_pillar", fn(BID $id) => new SimplePillar($id, "Quartz Pillar", $quartzBreakInfo)); + self::register("smooth_quartz", fn(BID $id) => new Opaque($id, "Smooth Quartz Block", $quartzBreakInfo)); + self::register("quartz_bricks", fn(BID $id) => new Opaque($id, "Quartz Bricks", $quartzBreakInfo)); - self::register("quartz_stairs", new Stair(new BID(Ids::QUARTZ_STAIRS), "Quartz Stairs", $quartzBreakInfo)); - self::register("smooth_quartz_stairs", new Stair(new BID(Ids::SMOOTH_QUARTZ_STAIRS), "Smooth Quartz Stairs", $quartzBreakInfo)); + self::register("quartz_stairs", fn(BID $id) => new Stair($id, "Quartz Stairs", $quartzBreakInfo)); + self::register("smooth_quartz_stairs", fn(BID $id) => new Stair($id, "Smooth Quartz Stairs", $quartzBreakInfo)); - self::register("rail", new Rail(new BID(Ids::RAIL), "Rail", $railBreakInfo)); - self::register("red_mushroom", new RedMushroom(new BID(Ids::RED_MUSHROOM), "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); - self::register("redstone", new Redstone(new BID(Ids::REDSTONE), "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); - self::register("redstone_comparator", new RedstoneComparator(new BID(Ids::REDSTONE_COMPARATOR, TileComparator::class), "Redstone Comparator", new Info(BreakInfo::instant()))); - self::register("redstone_lamp", new RedstoneLamp(new BID(Ids::REDSTONE_LAMP), "Redstone Lamp", new Info(new BreakInfo(0.3)))); - self::register("redstone_repeater", new RedstoneRepeater(new BID(Ids::REDSTONE_REPEATER), "Redstone Repeater", new Info(BreakInfo::instant()))); - self::register("redstone_torch", new RedstoneTorch(new BID(Ids::REDSTONE_TORCH), "Redstone Torch", new Info(BreakInfo::instant()))); - self::register("redstone_wire", new RedstoneWire(new BID(Ids::REDSTONE_WIRE), "Redstone", new Info(BreakInfo::instant()))); - self::register("reserved6", new Reserved6(new BID(Ids::RESERVED6), "reserved6", new Info(BreakInfo::instant()))); + self::register("rail", fn(BID $id) => new Rail($id, "Rail", $railBreakInfo)); + self::register("red_mushroom", fn(BID $id) => new RedMushroom($id, "Red Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("redstone", fn(BID $id) => new Redstone($id, "Redstone Block", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 30.0)))); + self::register("redstone_comparator", fn(BID $id) => new RedstoneComparator($id, "Redstone Comparator", new Info(BreakInfo::instant())), TileComparator::class); + self::register("redstone_lamp", fn(BID $id) => new RedstoneLamp($id, "Redstone Lamp", new Info(new BreakInfo(0.3)))); + self::register("redstone_repeater", fn(BID $id) => new RedstoneRepeater($id, "Redstone Repeater", new Info(BreakInfo::instant()))); + self::register("redstone_torch", fn(BID $id) => new RedstoneTorch($id, "Redstone Torch", new Info(BreakInfo::instant()))); + self::register("redstone_wire", fn(BID $id) => new RedstoneWire($id, "Redstone", new Info(BreakInfo::instant()))); + self::register("reserved6", fn(BID $id) => new Reserved6($id, "reserved6", new Info(BreakInfo::instant()))); $sandTypeInfo = new Info(BreakInfo::shovel(0.5), [Tags::SAND]); - self::register("sand", new Sand(new BID(Ids::SAND), "Sand", $sandTypeInfo)); - self::register("red_sand", new Sand(new BID(Ids::RED_SAND), "Red Sand", $sandTypeInfo)); + self::register("sand", fn(BID $id) => new Sand($id, "Sand", $sandTypeInfo)); + self::register("red_sand", fn(BID $id) => new Sand($id, "Red Sand", $sandTypeInfo)); - self::register("sea_lantern", new SeaLantern(new BID(Ids::SEA_LANTERN), "Sea Lantern", new Info(new BreakInfo(0.3)))); - self::register("sea_pickle", new SeaPickle(new BID(Ids::SEA_PICKLE), "Sea Pickle", new Info(BreakInfo::instant()))); - self::register("mob_head", new MobHead(new BID(Ids::MOB_HEAD, TileMobHead::class), "Mob Head", new Info(new BreakInfo(1.0), enchantmentTags: [EnchantmentTags::MASK]))); - self::register("slime", new Slime(new BID(Ids::SLIME), "Slime Block", new Info(BreakInfo::instant()))); - self::register("snow", new Snow(new BID(Ids::SNOW), "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD)))); - self::register("snow_layer", new SnowLayer(new BID(Ids::SNOW_LAYER), "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD)))); - self::register("soul_sand", new SoulSand(new BID(Ids::SOUL_SAND), "Soul Sand", new Info(BreakInfo::shovel(0.5)))); - self::register("sponge", new Sponge(new BID(Ids::SPONGE), "Sponge", new Info(new BreakInfo(0.6, ToolType::HOE)))); + self::register("sea_lantern", fn(BID $id) => new SeaLantern($id, "Sea Lantern", new Info(new BreakInfo(0.3)))); + self::register("sea_pickle", fn(BID $id) => new SeaPickle($id, "Sea Pickle", new Info(BreakInfo::instant()))); + self::register("mob_head", fn(BID $id) => new MobHead($id, "Mob Head", new Info(new BreakInfo(1.0), enchantmentTags: [EnchantmentTags::MASK])), TileMobHead::class); + self::register("slime", fn(BID $id) => new Slime($id, "Slime Block", new Info(BreakInfo::instant()))); + self::register("snow", fn(BID $id) => new Snow($id, "Snow Block", new Info(BreakInfo::shovel(0.2, ToolTier::WOOD)))); + self::register("snow_layer", fn(BID $id) => new SnowLayer($id, "Snow Layer", new Info(BreakInfo::shovel(0.1, ToolTier::WOOD)))); + self::register("soul_sand", fn(BID $id) => new SoulSand($id, "Soul Sand", new Info(BreakInfo::shovel(0.5)))); + self::register("sponge", fn(BID $id) => new Sponge($id, "Sponge", new Info(new BreakInfo(0.6, ToolType::HOE)))); $shulkerBoxBreakInfo = new Info(BreakInfo::pickaxe(2)); - self::register("shulker_box", new ShulkerBox(new BID(Ids::SHULKER_BOX, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo)); + self::register("shulker_box", fn(BID $id) => new ShulkerBox($id, "Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); $stoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register( + $stone = self::register( "stone", - $stone = new class(new BID(Ids::STONE), "Stone", $stoneBreakInfo) extends Opaque{ + fn(BID $id) => new class($id, "Stone", $stoneBreakInfo) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLESTONE()->asItem()]; } @@ -1066,110 +1090,110 @@ final class VanillaBlocks{ } } ); - self::register("andesite", new Opaque(new BID(Ids::ANDESITE), "Andesite", $stoneBreakInfo)); - self::register("diorite", new Opaque(new BID(Ids::DIORITE), "Diorite", $stoneBreakInfo)); - self::register("granite", new Opaque(new BID(Ids::GRANITE), "Granite", $stoneBreakInfo)); - self::register("polished_andesite", new Opaque(new BID(Ids::POLISHED_ANDESITE), "Polished Andesite", $stoneBreakInfo)); - self::register("polished_diorite", new Opaque(new BID(Ids::POLISHED_DIORITE), "Polished Diorite", $stoneBreakInfo)); - self::register("polished_granite", new Opaque(new BID(Ids::POLISHED_GRANITE), "Polished Granite", $stoneBreakInfo)); + self::register("andesite", fn(BID $id) => new Opaque($id, "Andesite", $stoneBreakInfo)); + self::register("diorite", fn(BID $id) => new Opaque($id, "Diorite", $stoneBreakInfo)); + self::register("granite", fn(BID $id) => new Opaque($id, "Granite", $stoneBreakInfo)); + self::register("polished_andesite", fn(BID $id) => new Opaque($id, "Polished Andesite", $stoneBreakInfo)); + self::register("polished_diorite", fn(BID $id) => new Opaque($id, "Polished Diorite", $stoneBreakInfo)); + self::register("polished_granite", fn(BID $id) => new Opaque($id, "Polished Granite", $stoneBreakInfo)); - self::register("stone_bricks", $stoneBrick = new Opaque(new BID(Ids::STONE_BRICKS), "Stone Bricks", $stoneBreakInfo)); - self::register("mossy_stone_bricks", $mossyStoneBrick = new Opaque(new BID(Ids::MOSSY_STONE_BRICKS), "Mossy Stone Bricks", $stoneBreakInfo)); - self::register("cracked_stone_bricks", $crackedStoneBrick = new Opaque(new BID(Ids::CRACKED_STONE_BRICKS), "Cracked Stone Bricks", $stoneBreakInfo)); - self::register("chiseled_stone_bricks", $chiseledStoneBrick = new Opaque(new BID(Ids::CHISELED_STONE_BRICKS), "Chiseled Stone Bricks", $stoneBreakInfo)); + $stoneBrick = self::register("stone_bricks", fn(BID $id) => new Opaque($id, "Stone Bricks", $stoneBreakInfo)); + $mossyStoneBrick = self::register("mossy_stone_bricks", fn(BID $id) => new Opaque($id, "Mossy Stone Bricks", $stoneBreakInfo)); + $crackedStoneBrick = self::register("cracked_stone_bricks", fn(BID $id) => new Opaque($id, "Cracked Stone Bricks", $stoneBreakInfo)); + $chiseledStoneBrick = self::register("chiseled_stone_bricks", fn(BID $id) => new Opaque($id, "Chiseled Stone Bricks", $stoneBreakInfo)); $infestedStoneBreakInfo = new Info(BreakInfo::pickaxe(0.75)); - self::register("infested_stone", new InfestedStone(new BID(Ids::INFESTED_STONE), "Infested Stone", $infestedStoneBreakInfo, $stone)); - self::register("infested_stone_brick", new InfestedStone(new BID(Ids::INFESTED_STONE_BRICK), "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); - self::register("infested_cobblestone", new InfestedStone(new BID(Ids::INFESTED_COBBLESTONE), "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); - self::register("infested_mossy_stone_brick", new InfestedStone(new BID(Ids::INFESTED_MOSSY_STONE_BRICK), "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); - self::register("infested_cracked_stone_brick", new InfestedStone(new BID(Ids::INFESTED_CRACKED_STONE_BRICK), "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); - self::register("infested_chiseled_stone_brick", new InfestedStone(new BID(Ids::INFESTED_CHISELED_STONE_BRICK), "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); + self::register("infested_stone", fn(BID $id) => new InfestedStone($id, "Infested Stone", $infestedStoneBreakInfo, $stone)); + self::register("infested_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Stone Brick", $infestedStoneBreakInfo, $stoneBrick)); + self::register("infested_cobblestone", fn(BID $id) => new InfestedStone($id, "Infested Cobblestone", $infestedStoneBreakInfo, $cobblestone)); + self::register("infested_mossy_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Mossy Stone Brick", $infestedStoneBreakInfo, $mossyStoneBrick)); + self::register("infested_cracked_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Cracked Stone Brick", $infestedStoneBreakInfo, $crackedStoneBrick)); + self::register("infested_chiseled_stone_brick", fn(BID $id) => new InfestedStone($id, "Infested Chiseled Stone Brick", $infestedStoneBreakInfo, $chiseledStoneBrick)); - self::register("stone_stairs", new Stair(new BID(Ids::STONE_STAIRS), "Stone Stairs", $stoneBreakInfo)); - self::register("smooth_stone", new Opaque(new BID(Ids::SMOOTH_STONE), "Smooth Stone", $stoneBreakInfo)); - self::register("andesite_stairs", new Stair(new BID(Ids::ANDESITE_STAIRS), "Andesite Stairs", $stoneBreakInfo)); - self::register("diorite_stairs", new Stair(new BID(Ids::DIORITE_STAIRS), "Diorite Stairs", $stoneBreakInfo)); - self::register("granite_stairs", new Stair(new BID(Ids::GRANITE_STAIRS), "Granite Stairs", $stoneBreakInfo)); - self::register("polished_andesite_stairs", new Stair(new BID(Ids::POLISHED_ANDESITE_STAIRS), "Polished Andesite Stairs", $stoneBreakInfo)); - self::register("polished_diorite_stairs", new Stair(new BID(Ids::POLISHED_DIORITE_STAIRS), "Polished Diorite Stairs", $stoneBreakInfo)); - self::register("polished_granite_stairs", new Stair(new BID(Ids::POLISHED_GRANITE_STAIRS), "Polished Granite Stairs", $stoneBreakInfo)); - self::register("stone_brick_stairs", new Stair(new BID(Ids::STONE_BRICK_STAIRS), "Stone Brick Stairs", $stoneBreakInfo)); - self::register("mossy_stone_brick_stairs", new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS), "Mossy Stone Brick Stairs", $stoneBreakInfo)); - self::register("stone_button", new StoneButton(new BID(Ids::STONE_BUTTON), "Stone Button", new Info(BreakInfo::pickaxe(0.5)))); - self::register("stonecutter", new Stonecutter(new BID(Ids::STONECUTTER), "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); - self::register("stone_pressure_plate", new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE), "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("stone_stairs", fn(BID $id) => new Stair($id, "Stone Stairs", $stoneBreakInfo)); + self::register("smooth_stone", fn(BID $id) => new Opaque($id, "Smooth Stone", $stoneBreakInfo)); + self::register("andesite_stairs", fn(BID $id) => new Stair($id, "Andesite Stairs", $stoneBreakInfo)); + self::register("diorite_stairs", fn(BID $id) => new Stair($id, "Diorite Stairs", $stoneBreakInfo)); + self::register("granite_stairs", fn(BID $id) => new Stair($id, "Granite Stairs", $stoneBreakInfo)); + self::register("polished_andesite_stairs", fn(BID $id) => new Stair($id, "Polished Andesite Stairs", $stoneBreakInfo)); + self::register("polished_diorite_stairs", fn(BID $id) => new Stair($id, "Polished Diorite Stairs", $stoneBreakInfo)); + self::register("polished_granite_stairs", fn(BID $id) => new Stair($id, "Polished Granite Stairs", $stoneBreakInfo)); + self::register("stone_brick_stairs", fn(BID $id) => new Stair($id, "Stone Brick Stairs", $stoneBreakInfo)); + self::register("mossy_stone_brick_stairs", fn(BID $id) => new Stair($id, "Mossy Stone Brick Stairs", $stoneBreakInfo)); + self::register("stone_button", fn(BID $id) => new StoneButton($id, "Stone Button", new Info(BreakInfo::pickaxe(0.5)))); + self::register("stonecutter", fn(BID $id) => new Stonecutter($id, "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); + self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); //TODO: in the future this won't be the same for all the types $stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("brick_slab", new Slab(new BID(Ids::BRICK_SLAB), "Brick", $stoneSlabBreakInfo)); - self::register("cobblestone_slab", new Slab(new BID(Ids::COBBLESTONE_SLAB), "Cobblestone", $stoneSlabBreakInfo)); - self::register("fake_wooden_slab", new Slab(new BID(Ids::FAKE_WOODEN_SLAB), "Fake Wooden", $stoneSlabBreakInfo)); - self::register("nether_brick_slab", new Slab(new BID(Ids::NETHER_BRICK_SLAB), "Nether Brick", $stoneSlabBreakInfo)); - self::register("quartz_slab", new Slab(new BID(Ids::QUARTZ_SLAB), "Quartz", $stoneSlabBreakInfo)); - self::register("sandstone_slab", new Slab(new BID(Ids::SANDSTONE_SLAB), "Sandstone", $stoneSlabBreakInfo)); - self::register("smooth_stone_slab", new Slab(new BID(Ids::SMOOTH_STONE_SLAB), "Smooth Stone", $stoneSlabBreakInfo)); - self::register("stone_brick_slab", new Slab(new BID(Ids::STONE_BRICK_SLAB), "Stone Brick", $stoneSlabBreakInfo)); - self::register("dark_prismarine_slab", new Slab(new BID(Ids::DARK_PRISMARINE_SLAB), "Dark Prismarine", $stoneSlabBreakInfo)); - self::register("mossy_cobblestone_slab", new Slab(new BID(Ids::MOSSY_COBBLESTONE_SLAB), "Mossy Cobblestone", $stoneSlabBreakInfo)); - self::register("prismarine_slab", new Slab(new BID(Ids::PRISMARINE_SLAB), "Prismarine", $stoneSlabBreakInfo)); - self::register("prismarine_bricks_slab", new Slab(new BID(Ids::PRISMARINE_BRICKS_SLAB), "Prismarine Bricks", $stoneSlabBreakInfo)); - self::register("purpur_slab", new Slab(new BID(Ids::PURPUR_SLAB), "Purpur", $stoneSlabBreakInfo)); - self::register("red_nether_brick_slab", new Slab(new BID(Ids::RED_NETHER_BRICK_SLAB), "Red Nether Brick", $stoneSlabBreakInfo)); - self::register("red_sandstone_slab", new Slab(new BID(Ids::RED_SANDSTONE_SLAB), "Red Sandstone", $stoneSlabBreakInfo)); - self::register("smooth_sandstone_slab", new Slab(new BID(Ids::SMOOTH_SANDSTONE_SLAB), "Smooth Sandstone", $stoneSlabBreakInfo)); - self::register("andesite_slab", new Slab(new BID(Ids::ANDESITE_SLAB), "Andesite", $stoneSlabBreakInfo)); - self::register("diorite_slab", new Slab(new BID(Ids::DIORITE_SLAB), "Diorite", $stoneSlabBreakInfo)); - self::register("end_stone_brick_slab", new Slab(new BID(Ids::END_STONE_BRICK_SLAB), "End Stone Brick", $stoneSlabBreakInfo)); - self::register("granite_slab", new Slab(new BID(Ids::GRANITE_SLAB), "Granite", $stoneSlabBreakInfo)); - self::register("polished_andesite_slab", new Slab(new BID(Ids::POLISHED_ANDESITE_SLAB), "Polished Andesite", $stoneSlabBreakInfo)); - self::register("polished_diorite_slab", new Slab(new BID(Ids::POLISHED_DIORITE_SLAB), "Polished Diorite", $stoneSlabBreakInfo)); - self::register("polished_granite_slab", new Slab(new BID(Ids::POLISHED_GRANITE_SLAB), "Polished Granite", $stoneSlabBreakInfo)); - self::register("smooth_red_sandstone_slab", new Slab(new BID(Ids::SMOOTH_RED_SANDSTONE_SLAB), "Smooth Red Sandstone", $stoneSlabBreakInfo)); - self::register("cut_red_sandstone_slab", new Slab(new BID(Ids::CUT_RED_SANDSTONE_SLAB), "Cut Red Sandstone", $stoneSlabBreakInfo)); - self::register("cut_sandstone_slab", new Slab(new BID(Ids::CUT_SANDSTONE_SLAB), "Cut Sandstone", $stoneSlabBreakInfo)); - self::register("mossy_stone_brick_slab", new Slab(new BID(Ids::MOSSY_STONE_BRICK_SLAB), "Mossy Stone Brick", $stoneSlabBreakInfo)); - self::register("smooth_quartz_slab", new Slab(new BID(Ids::SMOOTH_QUARTZ_SLAB), "Smooth Quartz", $stoneSlabBreakInfo)); - self::register("stone_slab", new Slab(new BID(Ids::STONE_SLAB), "Stone", $stoneSlabBreakInfo)); + self::register("brick_slab", fn(BID $id) => new Slab($id, "Brick", $stoneSlabBreakInfo)); + self::register("cobblestone_slab", fn(BID $id) => new Slab($id, "Cobblestone", $stoneSlabBreakInfo)); + self::register("fake_wooden_slab", fn(BID $id) => new Slab($id, "Fake Wooden", $stoneSlabBreakInfo)); + self::register("nether_brick_slab", fn(BID $id) => new Slab($id, "Nether Brick", $stoneSlabBreakInfo)); + self::register("quartz_slab", fn(BID $id) => new Slab($id, "Quartz", $stoneSlabBreakInfo)); + self::register("sandstone_slab", fn(BID $id) => new Slab($id, "Sandstone", $stoneSlabBreakInfo)); + self::register("smooth_stone_slab", fn(BID $id) => new Slab($id, "Smooth Stone", $stoneSlabBreakInfo)); + self::register("stone_brick_slab", fn(BID $id) => new Slab($id, "Stone Brick", $stoneSlabBreakInfo)); + self::register("dark_prismarine_slab", fn(BID $id) => new Slab($id, "Dark Prismarine", $stoneSlabBreakInfo)); + self::register("mossy_cobblestone_slab", fn(BID $id) => new Slab($id, "Mossy Cobblestone", $stoneSlabBreakInfo)); + self::register("prismarine_slab", fn(BID $id) => new Slab($id, "Prismarine", $stoneSlabBreakInfo)); + self::register("prismarine_bricks_slab", fn(BID $id) => new Slab($id, "Prismarine Bricks", $stoneSlabBreakInfo)); + self::register("purpur_slab", fn(BID $id) => new Slab($id, "Purpur", $stoneSlabBreakInfo)); + self::register("red_nether_brick_slab", fn(BID $id) => new Slab($id, "Red Nether Brick", $stoneSlabBreakInfo)); + self::register("red_sandstone_slab", fn(BID $id) => new Slab($id, "Red Sandstone", $stoneSlabBreakInfo)); + self::register("smooth_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Sandstone", $stoneSlabBreakInfo)); + self::register("andesite_slab", fn(BID $id) => new Slab($id, "Andesite", $stoneSlabBreakInfo)); + self::register("diorite_slab", fn(BID $id) => new Slab($id, "Diorite", $stoneSlabBreakInfo)); + self::register("end_stone_brick_slab", fn(BID $id) => new Slab($id, "End Stone Brick", $stoneSlabBreakInfo)); + self::register("granite_slab", fn(BID $id) => new Slab($id, "Granite", $stoneSlabBreakInfo)); + self::register("polished_andesite_slab", fn(BID $id) => new Slab($id, "Polished Andesite", $stoneSlabBreakInfo)); + self::register("polished_diorite_slab", fn(BID $id) => new Slab($id, "Polished Diorite", $stoneSlabBreakInfo)); + self::register("polished_granite_slab", fn(BID $id) => new Slab($id, "Polished Granite", $stoneSlabBreakInfo)); + self::register("smooth_red_sandstone_slab", fn(BID $id) => new Slab($id, "Smooth Red Sandstone", $stoneSlabBreakInfo)); + self::register("cut_red_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Red Sandstone", $stoneSlabBreakInfo)); + self::register("cut_sandstone_slab", fn(BID $id) => new Slab($id, "Cut Sandstone", $stoneSlabBreakInfo)); + self::register("mossy_stone_brick_slab", fn(BID $id) => new Slab($id, "Mossy Stone Brick", $stoneSlabBreakInfo)); + self::register("smooth_quartz_slab", fn(BID $id) => new Slab($id, "Smooth Quartz", $stoneSlabBreakInfo)); + self::register("stone_slab", fn(BID $id) => new Slab($id, "Stone", $stoneSlabBreakInfo)); - self::register("legacy_stonecutter", new Opaque(new BID(Ids::LEGACY_STONECUTTER), "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); - self::register("sugarcane", new Sugarcane(new BID(Ids::SUGARCANE), "Sugarcane", new Info(BreakInfo::instant()))); - self::register("sweet_berry_bush", new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH), "Sweet Berry Bush", new Info(BreakInfo::instant()))); - self::register("tnt", new TNT(new BID(Ids::TNT), "TNT", new Info(BreakInfo::instant()))); - self::register("fern", new TallGrass(new BID(Ids::FERN), "Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); - self::register("tall_grass", new TallGrass(new BID(Ids::TALL_GRASS), "Tall Grass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("legacy_stonecutter", fn(BID $id) => new Opaque($id, "Legacy Stonecutter", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD)))); + self::register("sugarcane", fn(BID $id) => new Sugarcane($id, "Sugarcane", new Info(BreakInfo::instant()))); + self::register("sweet_berry_bush", fn(BID $id) => new SweetBerryBush($id, "Sweet Berry Bush", new Info(BreakInfo::instant()))); + self::register("tnt", fn(BID $id) => new TNT($id, "TNT", new Info(BreakInfo::instant()))); + self::register("fern", fn(BID $id) => new TallGrass($id, "Fern", new Info(BreakInfo::instant(ToolType::SHEARS, 1), [Tags::POTTABLE_PLANTS]))); + self::register("tall_grass", fn(BID $id) => new TallGrass($id, "Tall Grass", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("blue_torch", new Torch(new BID(Ids::BLUE_TORCH), "Blue Torch", new Info(BreakInfo::instant()))); - self::register("purple_torch", new Torch(new BID(Ids::PURPLE_TORCH), "Purple Torch", new Info(BreakInfo::instant()))); - self::register("red_torch", new Torch(new BID(Ids::RED_TORCH), "Red Torch", new Info(BreakInfo::instant()))); - self::register("green_torch", new Torch(new BID(Ids::GREEN_TORCH), "Green Torch", new Info(BreakInfo::instant()))); - self::register("torch", new Torch(new BID(Ids::TORCH), "Torch", new Info(BreakInfo::instant()))); + self::register("blue_torch", fn(BID $id) => new Torch($id, "Blue Torch", new Info(BreakInfo::instant()))); + self::register("purple_torch", fn(BID $id) => new Torch($id, "Purple Torch", new Info(BreakInfo::instant()))); + self::register("red_torch", fn(BID $id) => new Torch($id, "Red Torch", new Info(BreakInfo::instant()))); + self::register("green_torch", fn(BID $id) => new Torch($id, "Green Torch", new Info(BreakInfo::instant()))); + self::register("torch", fn(BID $id) => new Torch($id, "Torch", new Info(BreakInfo::instant()))); - self::register("trapped_chest", new TrappedChest(new BID(Ids::TRAPPED_CHEST, TileChest::class), "Trapped Chest", $chestBreakInfo)); - self::register("tripwire", new Tripwire(new BID(Ids::TRIPWIRE), "Tripwire", new Info(BreakInfo::instant()))); - self::register("tripwire_hook", new TripwireHook(new BID(Ids::TRIPWIRE_HOOK), "Tripwire Hook", new Info(BreakInfo::instant()))); - self::register("underwater_torch", new UnderwaterTorch(new BID(Ids::UNDERWATER_TORCH), "Underwater Torch", new Info(BreakInfo::instant()))); - self::register("vines", new Vine(new BID(Ids::VINES), "Vines", new Info(BreakInfo::axe(0.2)))); - self::register("water", new Water(new BID(Ids::WATER), "Water", new Info(BreakInfo::indestructible(500.0)))); - self::register("lily_pad", new WaterLily(new BID(Ids::LILY_PAD), "Lily Pad", new Info(BreakInfo::instant()))); + self::register("trapped_chest", fn(BID $id) => new TrappedChest($id, "Trapped Chest", $chestBreakInfo), TileChest::class); + self::register("tripwire", fn(BID $id) => new Tripwire($id, "Tripwire", new Info(BreakInfo::instant()))); + self::register("tripwire_hook", fn(BID $id) => new TripwireHook($id, "Tripwire Hook", new Info(BreakInfo::instant()))); + self::register("underwater_torch", fn(BID $id) => new UnderwaterTorch($id, "Underwater Torch", new Info(BreakInfo::instant()))); + self::register("vines", fn(BID $id) => new Vine($id, "Vines", new Info(BreakInfo::axe(0.2)))); + self::register("water", fn(BID $id) => new Water($id, "Water", new Info(BreakInfo::indestructible(500.0)))); + self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant()))); $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)); - self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy( - new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), + self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy( + $id, "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo, deactivationDelayTicks: 10, signalStrengthFactor: 0.1 )); - self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight( - new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), + self::register("weighted_pressure_plate_light", fn(BID $id) => new WeightedPressurePlateLight( + $id, "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo, deactivationDelayTicks: 10, signalStrengthFactor: 1.0 )); - self::register("wheat", new Wheat(new BID(Ids::WHEAT), "Wheat Block", new Info(BreakInfo::instant()))); + self::register("wheat", fn(BID $id) => new Wheat($id, "Wheat Block", new Info(BreakInfo::instant()))); $leavesBreakInfo = new Info(new class(0.2, ToolType::HOE) extends BreakInfo{ public function getBreakTime(Item $item) : float{ @@ -1183,39 +1207,39 @@ final class VanillaBlocks{ foreach(SaplingType::cases() as $saplingType){ $name = $saplingType->getDisplayName(); - self::register(strtolower($saplingType->name) . "_sapling", new Sapling(WoodLikeBlockIdHelper::getSaplingIdentifier($saplingType), $name . " Sapling", $saplingTypeInfo, $saplingType)); + self::register(strtolower($saplingType->name) . "_sapling", fn(BID $id) => new Sapling($id, $name . " Sapling", $saplingTypeInfo, $saplingType)); } foreach(LeavesType::cases() as $leavesType){ $name = $leavesType->getDisplayName(); - self::register(strtolower($leavesType->name) . "_leaves", new Leaves(WoodLikeBlockIdHelper::getLeavesIdentifier($leavesType), $name . " Leaves", $leavesBreakInfo, $leavesType)); + self::register(strtolower($leavesType->name) . "_leaves", fn(BID $id) => new Leaves($id, $name . " Leaves", $leavesBreakInfo, $leavesType)); } $sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD)); - self::register("red_sandstone_stairs", new Stair(new BID(Ids::RED_SANDSTONE_STAIRS), "Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone_stairs", new Stair(new BID(Ids::SMOOTH_RED_SANDSTONE_STAIRS), "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); - self::register("red_sandstone", new Opaque(new BID(Ids::RED_SANDSTONE), "Red Sandstone", $sandstoneBreakInfo)); - self::register("chiseled_red_sandstone", new Opaque(new BID(Ids::CHISELED_RED_SANDSTONE), "Chiseled Red Sandstone", $sandstoneBreakInfo)); - self::register("cut_red_sandstone", new Opaque(new BID(Ids::CUT_RED_SANDSTONE), "Cut Red Sandstone", $sandstoneBreakInfo)); - self::register("smooth_red_sandstone", new Opaque(new BID(Ids::SMOOTH_RED_SANDSTONE), "Smooth Red Sandstone", $sandstoneBreakInfo)); + self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Red Sandstone Stairs", $sandstoneBreakInfo)); + self::register("red_sandstone", fn(BID $id) => new Opaque($id, "Red Sandstone", $sandstoneBreakInfo)); + self::register("chiseled_red_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Red Sandstone", $sandstoneBreakInfo)); + self::register("cut_red_sandstone", fn(BID $id) => new Opaque($id, "Cut Red Sandstone", $sandstoneBreakInfo)); + self::register("smooth_red_sandstone", fn(BID $id) => new Opaque($id, "Smooth Red Sandstone", $sandstoneBreakInfo)); - self::register("sandstone_stairs", new Stair(new BID(Ids::SANDSTONE_STAIRS), "Sandstone Stairs", $sandstoneBreakInfo)); - self::register("smooth_sandstone_stairs", new Stair(new BID(Ids::SMOOTH_SANDSTONE_STAIRS), "Smooth Sandstone Stairs", $sandstoneBreakInfo)); - self::register("sandstone", new Opaque(new BID(Ids::SANDSTONE), "Sandstone", $sandstoneBreakInfo)); - self::register("chiseled_sandstone", new Opaque(new BID(Ids::CHISELED_SANDSTONE), "Chiseled Sandstone", $sandstoneBreakInfo)); - self::register("cut_sandstone", new Opaque(new BID(Ids::CUT_SANDSTONE), "Cut Sandstone", $sandstoneBreakInfo)); - self::register("smooth_sandstone", new Opaque(new BID(Ids::SMOOTH_SANDSTONE), "Smooth Sandstone", $sandstoneBreakInfo)); + self::register("sandstone_stairs", fn(BID $id) => new Stair($id, "Sandstone Stairs", $sandstoneBreakInfo)); + self::register("smooth_sandstone_stairs", fn(BID $id) => new Stair($id, "Smooth Sandstone Stairs", $sandstoneBreakInfo)); + self::register("sandstone", fn(BID $id) => new Opaque($id, "Sandstone", $sandstoneBreakInfo)); + self::register("chiseled_sandstone", fn(BID $id) => new Opaque($id, "Chiseled Sandstone", $sandstoneBreakInfo)); + self::register("cut_sandstone", fn(BID $id) => new Opaque($id, "Cut Sandstone", $sandstoneBreakInfo)); + self::register("smooth_sandstone", fn(BID $id) => new Opaque($id, "Smooth Sandstone", $sandstoneBreakInfo)); - self::register("glazed_terracotta", new GlazedTerracotta(new BID(Ids::GLAZED_TERRACOTTA), "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); - self::register("dyed_shulker_box", new DyedShulkerBox(new BID(Ids::DYED_SHULKER_BOX, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo)); - self::register("stained_glass", new StainedGlass(new BID(Ids::STAINED_GLASS), "Stained Glass", $glassBreakInfo)); - self::register("stained_glass_pane", new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE), "Stained Glass Pane", $glassBreakInfo)); - self::register("stained_clay", new StainedHardenedClay(new BID(Ids::STAINED_CLAY), "Stained Clay", $hardenedClayBreakInfo)); - self::register("stained_hardened_glass", new StainedHardenedGlass(new BID(Ids::STAINED_HARDENED_GLASS), "Stained Hardened Glass", $hardenedGlassBreakInfo)); - self::register("stained_hardened_glass_pane", new StainedHardenedGlassPane(new BID(Ids::STAINED_HARDENED_GLASS_PANE), "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); - self::register("carpet", new Carpet(new BID(Ids::CARPET), "Carpet", new Info(new BreakInfo(0.1)))); - self::register("concrete", new Concrete(new BID(Ids::CONCRETE), "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD)))); - self::register("concrete_powder", new ConcretePowder(new BID(Ids::CONCRETE_POWDER), "Concrete Powder", new Info(BreakInfo::shovel(0.5)))); - self::register("wool", new Wool(new BID(Ids::WOOL), "Wool", new Info(new class(0.8, ToolType::SHEARS) extends BreakInfo{ + self::register("glazed_terracotta", fn(BID $id) => new GlazedTerracotta($id, "Glazed Terracotta", new Info(BreakInfo::pickaxe(1.4, ToolTier::WOOD)))); + self::register("dyed_shulker_box", fn(BID $id) => new DyedShulkerBox($id, "Dyed Shulker Box", $shulkerBoxBreakInfo), TileShulkerBox::class); + self::register("stained_glass", fn(BID $id) => new StainedGlass($id, "Stained Glass", $glassBreakInfo)); + self::register("stained_glass_pane", fn(BID $id) => new StainedGlassPane($id, "Stained Glass Pane", $glassBreakInfo)); + self::register("stained_clay", fn(BID $id) => new StainedHardenedClay($id, "Stained Clay", $hardenedClayBreakInfo)); + self::register("stained_hardened_glass", fn(BID $id) => new StainedHardenedGlass($id, "Stained Hardened Glass", $hardenedGlassBreakInfo)); + self::register("stained_hardened_glass_pane", fn(BID $id) => new StainedHardenedGlassPane($id, "Stained Hardened Glass Pane", $hardenedGlassBreakInfo)); + self::register("carpet", fn(BID $id) => new Carpet($id, "Carpet", new Info(new BreakInfo(0.1)))); + self::register("concrete", fn(BID $id) => new Concrete($id, "Concrete", new Info(BreakInfo::pickaxe(1.8, ToolTier::WOOD)))); + self::register("concrete_powder", fn(BID $id) => new ConcretePowder($id, "Concrete Powder", new Info(BreakInfo::shovel(0.5)))); + self::register("wool", fn(BID $id) => new Wool($id, "Wool", new Info(new class(0.8, ToolType::SHEARS) extends BreakInfo{ public function getBreakTime(Item $item) : float{ $time = parent::getBreakTime($item); if($item->getBlockToolType() === ToolType::SHEARS){ @@ -1228,54 +1252,54 @@ final class VanillaBlocks{ //TODO: in the future these won't all have the same hardness; they only do now because of the old metadata crap $wallBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("cobblestone_wall", new Wall(new BID(Ids::COBBLESTONE_WALL), "Cobblestone Wall", $wallBreakInfo)); - self::register("andesite_wall", new Wall(new BID(Ids::ANDESITE_WALL), "Andesite Wall", $wallBreakInfo)); - self::register("brick_wall", new Wall(new BID(Ids::BRICK_WALL), "Brick Wall", $wallBreakInfo)); - self::register("diorite_wall", new Wall(new BID(Ids::DIORITE_WALL), "Diorite Wall", $wallBreakInfo)); - self::register("end_stone_brick_wall", new Wall(new BID(Ids::END_STONE_BRICK_WALL), "End Stone Brick Wall", $wallBreakInfo)); - self::register("granite_wall", new Wall(new BID(Ids::GRANITE_WALL), "Granite Wall", $wallBreakInfo)); - self::register("mossy_stone_brick_wall", new Wall(new BID(Ids::MOSSY_STONE_BRICK_WALL), "Mossy Stone Brick Wall", $wallBreakInfo)); - self::register("mossy_cobblestone_wall", new Wall(new BID(Ids::MOSSY_COBBLESTONE_WALL), "Mossy Cobblestone Wall", $wallBreakInfo)); - self::register("nether_brick_wall", new Wall(new BID(Ids::NETHER_BRICK_WALL), "Nether Brick Wall", $wallBreakInfo)); - self::register("prismarine_wall", new Wall(new BID(Ids::PRISMARINE_WALL), "Prismarine Wall", $wallBreakInfo)); - self::register("red_nether_brick_wall", new Wall(new BID(Ids::RED_NETHER_BRICK_WALL), "Red Nether Brick Wall", $wallBreakInfo)); - self::register("red_sandstone_wall", new Wall(new BID(Ids::RED_SANDSTONE_WALL), "Red Sandstone Wall", $wallBreakInfo)); - self::register("sandstone_wall", new Wall(new BID(Ids::SANDSTONE_WALL), "Sandstone Wall", $wallBreakInfo)); - self::register("stone_brick_wall", new Wall(new BID(Ids::STONE_BRICK_WALL), "Stone Brick Wall", $wallBreakInfo)); + self::register("cobblestone_wall", fn(BID $id) => new Wall($id, "Cobblestone Wall", $wallBreakInfo)); + self::register("andesite_wall", fn(BID $id) => new Wall($id, "Andesite Wall", $wallBreakInfo)); + self::register("brick_wall", fn(BID $id) => new Wall($id, "Brick Wall", $wallBreakInfo)); + self::register("diorite_wall", fn(BID $id) => new Wall($id, "Diorite Wall", $wallBreakInfo)); + self::register("end_stone_brick_wall", fn(BID $id) => new Wall($id, "End Stone Brick Wall", $wallBreakInfo)); + self::register("granite_wall", fn(BID $id) => new Wall($id, "Granite Wall", $wallBreakInfo)); + self::register("mossy_stone_brick_wall", fn(BID $id) => new Wall($id, "Mossy Stone Brick Wall", $wallBreakInfo)); + self::register("mossy_cobblestone_wall", fn(BID $id) => new Wall($id, "Mossy Cobblestone Wall", $wallBreakInfo)); + self::register("nether_brick_wall", fn(BID $id) => new Wall($id, "Nether Brick Wall", $wallBreakInfo)); + self::register("prismarine_wall", fn(BID $id) => new Wall($id, "Prismarine Wall", $wallBreakInfo)); + self::register("red_nether_brick_wall", fn(BID $id) => new Wall($id, "Red Nether Brick Wall", $wallBreakInfo)); + self::register("red_sandstone_wall", fn(BID $id) => new Wall($id, "Red Sandstone Wall", $wallBreakInfo)); + self::register("sandstone_wall", fn(BID $id) => new Wall($id, "Sandstone Wall", $wallBreakInfo)); + self::register("stone_brick_wall", fn(BID $id) => new Wall($id, "Stone Brick Wall", $wallBreakInfo)); self::registerElements(); $chemistryTableBreakInfo = new Info(BreakInfo::pickaxe(2.5, ToolTier::WOOD)); - self::register("compound_creator", new ChemistryTable(new BID(Ids::COMPOUND_CREATOR), "Compound Creator", $chemistryTableBreakInfo)); - self::register("element_constructor", new ChemistryTable(new BID(Ids::ELEMENT_CONSTRUCTOR), "Element Constructor", $chemistryTableBreakInfo)); - self::register("lab_table", new ChemistryTable(new BID(Ids::LAB_TABLE), "Lab Table", $chemistryTableBreakInfo)); - self::register("material_reducer", new ChemistryTable(new BID(Ids::MATERIAL_REDUCER), "Material Reducer", $chemistryTableBreakInfo)); + self::register("compound_creator", fn(BID $id) => new ChemistryTable($id, "Compound Creator", $chemistryTableBreakInfo)); + self::register("element_constructor", fn(BID $id) => new ChemistryTable($id, "Element Constructor", $chemistryTableBreakInfo)); + self::register("lab_table", fn(BID $id) => new ChemistryTable($id, "Lab Table", $chemistryTableBreakInfo)); + self::register("material_reducer", fn(BID $id) => new ChemistryTable($id, "Material Reducer", $chemistryTableBreakInfo)); - self::register("chemical_heat", new ChemicalHeat(new BID(Ids::CHEMICAL_HEAT), "Heat Block", $chemistryTableBreakInfo)); + self::register("chemical_heat", fn(BID $id) => new ChemicalHeat($id, "Heat Block", $chemistryTableBreakInfo)); self::registerMushroomBlocks(); - self::register("coral", new Coral( - new BID(Ids::CORAL), + self::register("coral", fn(BID $id) => new Coral( + $id, "Coral", new Info(BreakInfo::instant()), )); - self::register("coral_fan", new FloorCoralFan( - new BID(Ids::CORAL_FAN), + self::register("coral_fan", fn(BID $id) => new FloorCoralFan( + $id, "Coral Fan", new Info(BreakInfo::instant()), )); - self::register("wall_coral_fan", new WallCoralFan( - new BID(Ids::WALL_CORAL_FAN), + self::register("wall_coral_fan", fn(BID $id) => new WallCoralFan( + $id, "Wall Coral Fan", new Info(BreakInfo::instant()), )); - self::register("mangrove_roots", new MangroveRoots(new BID(Ids::MANGROVE_ROOTS), "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); - self::register("muddy_mangrove_roots", new SimplePillar(new BID(Ids::MUDDY_MANGROVE_ROOTS), "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); - self::register("froglight", new Froglight(new BID(Ids::FROGLIGHT), "Froglight", new Info(new BreakInfo(0.3)))); - self::register("sculk", new Sculk(new BID(Ids::SCULK), "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); - self::register("reinforced_deepslate", new class(new BID(Ids::REINFORCED_DEEPSLATE), "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ + self::register("mangrove_roots", fn(BID $id) => new MangroveRoots($id, "Mangrove Roots", new Info(BreakInfo::axe(0.7)))); + self::register("muddy_mangrove_roots", fn(BID $id) => new SimplePillar($id, "Muddy Mangrove Roots", new Info(BreakInfo::shovel(0.7), [Tags::MUD]))); + self::register("froglight", fn(BID $id) => new Froglight($id, "Froglight", new Info(new BreakInfo(0.3)))); + self::register("sculk", fn(BID $id) => new Sculk($id, "Sculk", new Info(new BreakInfo(0.6, ToolType::HOE)))); + self::register("reinforced_deepslate", fn(BID $id) => new class($id, "Reinforced Deepslate", new Info(new BreakInfo(55.0, ToolType::NONE, 0, 3600.0))) extends Opaque{ public function getDropsForCompatibleTool(Item $item) : array{ return []; } @@ -1308,300 +1332,311 @@ final class VanillaBlocks{ $name = $woodType->getDisplayName(); $idName = fn(string $suffix) => strtolower($woodType->name) . "_" . $suffix; - self::register($idName(mb_strtolower($woodType->getStandardLogSuffix() ?? "log", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getLogIdentifier($woodType), $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType)); - self::register($idName(mb_strtolower($woodType->getAllSidedLogSuffix() ?? "wood", 'US-ASCII')), new Wood(WoodLikeBlockIdHelper::getAllSidedLogIdentifier($woodType), $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType)); + self::register($idName(mb_strtolower($woodType->getStandardLogSuffix() ?? "log", 'US-ASCII')), fn(BID $id) => new Wood($id, $name . " " . ($woodType->getStandardLogSuffix() ?? "Log"), $logBreakInfo, $woodType)); + self::register($idName(mb_strtolower($woodType->getAllSidedLogSuffix() ?? "wood", 'US-ASCII')), fn(BID $id) => new Wood($id, $name . " " . ($woodType->getAllSidedLogSuffix() ?? "Wood"), $logBreakInfo, $woodType)); - self::register($idName("planks"), new Planks(WoodLikeBlockIdHelper::getPlanksIdentifier($woodType), $name . " Planks", $planksBreakInfo, $woodType)); - self::register($idName("fence"), new WoodenFence(WoodLikeBlockIdHelper::getFenceIdentifier($woodType), $name . " Fence", $planksBreakInfo, $woodType)); - self::register($idName("slab"), new WoodenSlab(WoodLikeBlockIdHelper::getSlabIdentifier($woodType), $name, $planksBreakInfo, $woodType)); + self::register($idName("planks"), fn(BID $id) => new Planks($id, $name . " Planks", $planksBreakInfo, $woodType)); + self::register($idName("fence"), fn(BID $id) => new WoodenFence($id, $name . " Fence", $planksBreakInfo, $woodType)); + self::register($idName("slab"), fn(BID $id) => new WoodenSlab($id, $name, $planksBreakInfo, $woodType)); - self::register($idName("fence_gate"), new FenceGate(WoodLikeBlockIdHelper::getFenceGateIdentifier($woodType), $name . " Fence Gate", $planksBreakInfo, $woodType)); - self::register($idName("stairs"), new WoodenStairs(WoodLikeBlockIdHelper::getStairsIdentifier($woodType), $name . " Stairs", $planksBreakInfo, $woodType)); - self::register($idName("door"), new WoodenDoor(WoodLikeBlockIdHelper::getDoorIdentifier($woodType), $name . " Door", $woodenDoorBreakInfo, $woodType)); + self::register($idName("fence_gate"), fn(BID $id) => new FenceGate($id, $name . " Fence Gate", $planksBreakInfo, $woodType)); + self::register($idName("stairs"), fn(BID $id) => new WoodenStairs($id, $name . " Stairs", $planksBreakInfo, $woodType)); + self::register($idName("door"), fn(BID $id) => new WoodenDoor($id, $name . " Door", $woodenDoorBreakInfo, $woodType)); - self::register($idName("button"), new WoodenButton(WoodLikeBlockIdHelper::getButtonIdentifier($woodType), $name . " Button", $woodenButtonBreakInfo, $woodType)); - self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20)); - self::register($idName("trapdoor"), new WoodenTrapdoor(WoodLikeBlockIdHelper::getTrapdoorIdentifier($woodType), $name . " Trapdoor", $woodenDoorBreakInfo, $woodType)); + self::register($idName("button"), fn(BID $id) => new WoodenButton($id, $name . " Button", $woodenButtonBreakInfo, $woodType)); + self::register($idName("pressure_plate"), fn(BID $id) => new WoodenPressurePlate($id, $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20)); + self::register($idName("trapdoor"), fn(BID $id) => new WoodenTrapdoor($id, $name . " Trapdoor", $woodenDoorBreakInfo, $woodType)); - [$floorSignId, $wallSignId, $signAsItem] = WoodLikeBlockIdHelper::getSignInfo($woodType); - self::register($idName("sign"), new FloorSign($floorSignId, $name . " Sign", $signBreakInfo, $woodType, $signAsItem)); - self::register($idName("wall_sign"), new WallSign($wallSignId, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem)); + $signAsItem = match($woodType){ + WoodType::OAK => VanillaItems::OAK_SIGN(...), + WoodType::SPRUCE => VanillaItems::SPRUCE_SIGN(...), + WoodType::BIRCH => VanillaItems::BIRCH_SIGN(...), + WoodType::JUNGLE => VanillaItems::JUNGLE_SIGN(...), + WoodType::ACACIA => VanillaItems::ACACIA_SIGN(...), + WoodType::DARK_OAK => VanillaItems::DARK_OAK_SIGN(...), + WoodType::MANGROVE => VanillaItems::MANGROVE_SIGN(...), + WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...), + WoodType::WARPED => VanillaItems::WARPED_SIGN(...), + WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...), + }; + self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem)); + self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem)); } } private static function registerMushroomBlocks() : void{ $mushroomBlockBreakInfo = new Info(BreakInfo::axe(0.2)); - self::register("brown_mushroom_block", new BrownMushroomBlock(new BID(Ids::BROWN_MUSHROOM_BLOCK), "Brown Mushroom Block", $mushroomBlockBreakInfo)); - self::register("red_mushroom_block", new RedMushroomBlock(new BID(Ids::RED_MUSHROOM_BLOCK), "Red Mushroom Block", $mushroomBlockBreakInfo)); + self::register("brown_mushroom_block", fn(BID $id) => new BrownMushroomBlock($id, "Brown Mushroom Block", $mushroomBlockBreakInfo)); + self::register("red_mushroom_block", fn(BID $id) => new RedMushroomBlock($id, "Red Mushroom Block", $mushroomBlockBreakInfo)); //finally, the stems - self::register("mushroom_stem", new MushroomStem(new BID(Ids::MUSHROOM_STEM), "Mushroom Stem", $mushroomBlockBreakInfo)); - self::register("all_sided_mushroom_stem", new MushroomStem(new BID(Ids::ALL_SIDED_MUSHROOM_STEM), "All Sided Mushroom Stem", $mushroomBlockBreakInfo)); + self::register("mushroom_stem", fn(BID $id) => new MushroomStem($id, "Mushroom Stem", $mushroomBlockBreakInfo)); + self::register("all_sided_mushroom_stem", fn(BID $id) => new MushroomStem($id, "All Sided Mushroom Stem", $mushroomBlockBreakInfo)); } private static function registerElements() : void{ $instaBreak = new Info(BreakInfo::instant()); - self::register("element_zero", new Opaque(new BID(Ids::ELEMENT_ZERO), "???", $instaBreak)); + self::register("element_zero", fn(BID $id) => new Opaque($id, "???", $instaBreak)); - $register = fn(string $name, int $id, string $displayName, string $symbol, int $atomicWeight, int $group) => - self::register("element_$name", new Element(new BID($id), $displayName, $instaBreak, $symbol, $atomicWeight, $group)); + $register = fn(string $name, string $displayName, string $symbol, int $atomicWeight, int $group) => + self::register("element_$name", fn(BID $id) => new Element($id, $displayName, $instaBreak, $symbol, $atomicWeight, $group)); - $register("hydrogen", Ids::ELEMENT_HYDROGEN, "Hydrogen", "h", 1, 5); - $register("helium", Ids::ELEMENT_HELIUM, "Helium", "he", 2, 7); - $register("lithium", Ids::ELEMENT_LITHIUM, "Lithium", "li", 3, 0); - $register("beryllium", Ids::ELEMENT_BERYLLIUM, "Beryllium", "be", 4, 1); - $register("boron", Ids::ELEMENT_BORON, "Boron", "b", 5, 4); - $register("carbon", Ids::ELEMENT_CARBON, "Carbon", "c", 6, 5); - $register("nitrogen", Ids::ELEMENT_NITROGEN, "Nitrogen", "n", 7, 5); - $register("oxygen", Ids::ELEMENT_OXYGEN, "Oxygen", "o", 8, 5); - $register("fluorine", Ids::ELEMENT_FLUORINE, "Fluorine", "f", 9, 6); - $register("neon", Ids::ELEMENT_NEON, "Neon", "ne", 10, 7); - $register("sodium", Ids::ELEMENT_SODIUM, "Sodium", "na", 11, 0); - $register("magnesium", Ids::ELEMENT_MAGNESIUM, "Magnesium", "mg", 12, 1); - $register("aluminum", Ids::ELEMENT_ALUMINUM, "Aluminum", "al", 13, 3); - $register("silicon", Ids::ELEMENT_SILICON, "Silicon", "si", 14, 4); - $register("phosphorus", Ids::ELEMENT_PHOSPHORUS, "Phosphorus", "p", 15, 5); - $register("sulfur", Ids::ELEMENT_SULFUR, "Sulfur", "s", 16, 5); - $register("chlorine", Ids::ELEMENT_CHLORINE, "Chlorine", "cl", 17, 6); - $register("argon", Ids::ELEMENT_ARGON, "Argon", "ar", 18, 7); - $register("potassium", Ids::ELEMENT_POTASSIUM, "Potassium", "k", 19, 0); - $register("calcium", Ids::ELEMENT_CALCIUM, "Calcium", "ca", 20, 1); - $register("scandium", Ids::ELEMENT_SCANDIUM, "Scandium", "sc", 21, 2); - $register("titanium", Ids::ELEMENT_TITANIUM, "Titanium", "ti", 22, 2); - $register("vanadium", Ids::ELEMENT_VANADIUM, "Vanadium", "v", 23, 2); - $register("chromium", Ids::ELEMENT_CHROMIUM, "Chromium", "cr", 24, 2); - $register("manganese", Ids::ELEMENT_MANGANESE, "Manganese", "mn", 25, 2); - $register("iron", Ids::ELEMENT_IRON, "Iron", "fe", 26, 2); - $register("cobalt", Ids::ELEMENT_COBALT, "Cobalt", "co", 27, 2); - $register("nickel", Ids::ELEMENT_NICKEL, "Nickel", "ni", 28, 2); - $register("copper", Ids::ELEMENT_COPPER, "Copper", "cu", 29, 2); - $register("zinc", Ids::ELEMENT_ZINC, "Zinc", "zn", 30, 2); - $register("gallium", Ids::ELEMENT_GALLIUM, "Gallium", "ga", 31, 3); - $register("germanium", Ids::ELEMENT_GERMANIUM, "Germanium", "ge", 32, 4); - $register("arsenic", Ids::ELEMENT_ARSENIC, "Arsenic", "as", 33, 4); - $register("selenium", Ids::ELEMENT_SELENIUM, "Selenium", "se", 34, 5); - $register("bromine", Ids::ELEMENT_BROMINE, "Bromine", "br", 35, 6); - $register("krypton", Ids::ELEMENT_KRYPTON, "Krypton", "kr", 36, 7); - $register("rubidium", Ids::ELEMENT_RUBIDIUM, "Rubidium", "rb", 37, 0); - $register("strontium", Ids::ELEMENT_STRONTIUM, "Strontium", "sr", 38, 1); - $register("yttrium", Ids::ELEMENT_YTTRIUM, "Yttrium", "y", 39, 2); - $register("zirconium", Ids::ELEMENT_ZIRCONIUM, "Zirconium", "zr", 40, 2); - $register("niobium", Ids::ELEMENT_NIOBIUM, "Niobium", "nb", 41, 2); - $register("molybdenum", Ids::ELEMENT_MOLYBDENUM, "Molybdenum", "mo", 42, 2); - $register("technetium", Ids::ELEMENT_TECHNETIUM, "Technetium", "tc", 43, 2); - $register("ruthenium", Ids::ELEMENT_RUTHENIUM, "Ruthenium", "ru", 44, 2); - $register("rhodium", Ids::ELEMENT_RHODIUM, "Rhodium", "rh", 45, 2); - $register("palladium", Ids::ELEMENT_PALLADIUM, "Palladium", "pd", 46, 2); - $register("silver", Ids::ELEMENT_SILVER, "Silver", "ag", 47, 2); - $register("cadmium", Ids::ELEMENT_CADMIUM, "Cadmium", "cd", 48, 2); - $register("indium", Ids::ELEMENT_INDIUM, "Indium", "in", 49, 3); - $register("tin", Ids::ELEMENT_TIN, "Tin", "sn", 50, 3); - $register("antimony", Ids::ELEMENT_ANTIMONY, "Antimony", "sb", 51, 4); - $register("tellurium", Ids::ELEMENT_TELLURIUM, "Tellurium", "te", 52, 4); - $register("iodine", Ids::ELEMENT_IODINE, "Iodine", "i", 53, 6); - $register("xenon", Ids::ELEMENT_XENON, "Xenon", "xe", 54, 7); - $register("cesium", Ids::ELEMENT_CESIUM, "Cesium", "cs", 55, 0); - $register("barium", Ids::ELEMENT_BARIUM, "Barium", "ba", 56, 1); - $register("lanthanum", Ids::ELEMENT_LANTHANUM, "Lanthanum", "la", 57, 8); - $register("cerium", Ids::ELEMENT_CERIUM, "Cerium", "ce", 58, 8); - $register("praseodymium", Ids::ELEMENT_PRASEODYMIUM, "Praseodymium", "pr", 59, 8); - $register("neodymium", Ids::ELEMENT_NEODYMIUM, "Neodymium", "nd", 60, 8); - $register("promethium", Ids::ELEMENT_PROMETHIUM, "Promethium", "pm", 61, 8); - $register("samarium", Ids::ELEMENT_SAMARIUM, "Samarium", "sm", 62, 8); - $register("europium", Ids::ELEMENT_EUROPIUM, "Europium", "eu", 63, 8); - $register("gadolinium", Ids::ELEMENT_GADOLINIUM, "Gadolinium", "gd", 64, 8); - $register("terbium", Ids::ELEMENT_TERBIUM, "Terbium", "tb", 65, 8); - $register("dysprosium", Ids::ELEMENT_DYSPROSIUM, "Dysprosium", "dy", 66, 8); - $register("holmium", Ids::ELEMENT_HOLMIUM, "Holmium", "ho", 67, 8); - $register("erbium", Ids::ELEMENT_ERBIUM, "Erbium", "er", 68, 8); - $register("thulium", Ids::ELEMENT_THULIUM, "Thulium", "tm", 69, 8); - $register("ytterbium", Ids::ELEMENT_YTTERBIUM, "Ytterbium", "yb", 70, 8); - $register("lutetium", Ids::ELEMENT_LUTETIUM, "Lutetium", "lu", 71, 8); - $register("hafnium", Ids::ELEMENT_HAFNIUM, "Hafnium", "hf", 72, 2); - $register("tantalum", Ids::ELEMENT_TANTALUM, "Tantalum", "ta", 73, 2); - $register("tungsten", Ids::ELEMENT_TUNGSTEN, "Tungsten", "w", 74, 2); - $register("rhenium", Ids::ELEMENT_RHENIUM, "Rhenium", "re", 75, 2); - $register("osmium", Ids::ELEMENT_OSMIUM, "Osmium", "os", 76, 2); - $register("iridium", Ids::ELEMENT_IRIDIUM, "Iridium", "ir", 77, 2); - $register("platinum", Ids::ELEMENT_PLATINUM, "Platinum", "pt", 78, 2); - $register("gold", Ids::ELEMENT_GOLD, "Gold", "au", 79, 2); - $register("mercury", Ids::ELEMENT_MERCURY, "Mercury", "hg", 80, 2); - $register("thallium", Ids::ELEMENT_THALLIUM, "Thallium", "tl", 81, 3); - $register("lead", Ids::ELEMENT_LEAD, "Lead", "pb", 82, 3); - $register("bismuth", Ids::ELEMENT_BISMUTH, "Bismuth", "bi", 83, 3); - $register("polonium", Ids::ELEMENT_POLONIUM, "Polonium", "po", 84, 4); - $register("astatine", Ids::ELEMENT_ASTATINE, "Astatine", "at", 85, 6); - $register("radon", Ids::ELEMENT_RADON, "Radon", "rn", 86, 7); - $register("francium", Ids::ELEMENT_FRANCIUM, "Francium", "fr", 87, 0); - $register("radium", Ids::ELEMENT_RADIUM, "Radium", "ra", 88, 1); - $register("actinium", Ids::ELEMENT_ACTINIUM, "Actinium", "ac", 89, 9); - $register("thorium", Ids::ELEMENT_THORIUM, "Thorium", "th", 90, 9); - $register("protactinium", Ids::ELEMENT_PROTACTINIUM, "Protactinium", "pa", 91, 9); - $register("uranium", Ids::ELEMENT_URANIUM, "Uranium", "u", 92, 9); - $register("neptunium", Ids::ELEMENT_NEPTUNIUM, "Neptunium", "np", 93, 9); - $register("plutonium", Ids::ELEMENT_PLUTONIUM, "Plutonium", "pu", 94, 9); - $register("americium", Ids::ELEMENT_AMERICIUM, "Americium", "am", 95, 9); - $register("curium", Ids::ELEMENT_CURIUM, "Curium", "cm", 96, 9); - $register("berkelium", Ids::ELEMENT_BERKELIUM, "Berkelium", "bk", 97, 9); - $register("californium", Ids::ELEMENT_CALIFORNIUM, "Californium", "cf", 98, 9); - $register("einsteinium", Ids::ELEMENT_EINSTEINIUM, "Einsteinium", "es", 99, 9); - $register("fermium", Ids::ELEMENT_FERMIUM, "Fermium", "fm", 100, 9); - $register("mendelevium", Ids::ELEMENT_MENDELEVIUM, "Mendelevium", "md", 101, 9); - $register("nobelium", Ids::ELEMENT_NOBELIUM, "Nobelium", "no", 102, 9); - $register("lawrencium", Ids::ELEMENT_LAWRENCIUM, "Lawrencium", "lr", 103, 9); - $register("rutherfordium", Ids::ELEMENT_RUTHERFORDIUM, "Rutherfordium", "rf", 104, 2); - $register("dubnium", Ids::ELEMENT_DUBNIUM, "Dubnium", "db", 105, 2); - $register("seaborgium", Ids::ELEMENT_SEABORGIUM, "Seaborgium", "sg", 106, 2); - $register("bohrium", Ids::ELEMENT_BOHRIUM, "Bohrium", "bh", 107, 2); - $register("hassium", Ids::ELEMENT_HASSIUM, "Hassium", "hs", 108, 2); - $register("meitnerium", Ids::ELEMENT_MEITNERIUM, "Meitnerium", "mt", 109, 2); - $register("darmstadtium", Ids::ELEMENT_DARMSTADTIUM, "Darmstadtium", "ds", 110, 2); - $register("roentgenium", Ids::ELEMENT_ROENTGENIUM, "Roentgenium", "rg", 111, 2); - $register("copernicium", Ids::ELEMENT_COPERNICIUM, "Copernicium", "cn", 112, 2); - $register("nihonium", Ids::ELEMENT_NIHONIUM, "Nihonium", "nh", 113, 3); - $register("flerovium", Ids::ELEMENT_FLEROVIUM, "Flerovium", "fl", 114, 3); - $register("moscovium", Ids::ELEMENT_MOSCOVIUM, "Moscovium", "mc", 115, 3); - $register("livermorium", Ids::ELEMENT_LIVERMORIUM, "Livermorium", "lv", 116, 3); - $register("tennessine", Ids::ELEMENT_TENNESSINE, "Tennessine", "ts", 117, 6); - $register("oganesson", Ids::ELEMENT_OGANESSON, "Oganesson", "og", 118, 7); + $register("hydrogen", "Hydrogen", "h", 1, 5); + $register("helium", "Helium", "he", 2, 7); + $register("lithium", "Lithium", "li", 3, 0); + $register("beryllium", "Beryllium", "be", 4, 1); + $register("boron", "Boron", "b", 5, 4); + $register("carbon", "Carbon", "c", 6, 5); + $register("nitrogen", "Nitrogen", "n", 7, 5); + $register("oxygen", "Oxygen", "o", 8, 5); + $register("fluorine", "Fluorine", "f", 9, 6); + $register("neon", "Neon", "ne", 10, 7); + $register("sodium", "Sodium", "na", 11, 0); + $register("magnesium", "Magnesium", "mg", 12, 1); + $register("aluminum", "Aluminum", "al", 13, 3); + $register("silicon", "Silicon", "si", 14, 4); + $register("phosphorus", "Phosphorus", "p", 15, 5); + $register("sulfur", "Sulfur", "s", 16, 5); + $register("chlorine", "Chlorine", "cl", 17, 6); + $register("argon", "Argon", "ar", 18, 7); + $register("potassium", "Potassium", "k", 19, 0); + $register("calcium", "Calcium", "ca", 20, 1); + $register("scandium", "Scandium", "sc", 21, 2); + $register("titanium", "Titanium", "ti", 22, 2); + $register("vanadium", "Vanadium", "v", 23, 2); + $register("chromium", "Chromium", "cr", 24, 2); + $register("manganese", "Manganese", "mn", 25, 2); + $register("iron", "Iron", "fe", 26, 2); + $register("cobalt", "Cobalt", "co", 27, 2); + $register("nickel", "Nickel", "ni", 28, 2); + $register("copper", "Copper", "cu", 29, 2); + $register("zinc", "Zinc", "zn", 30, 2); + $register("gallium", "Gallium", "ga", 31, 3); + $register("germanium", "Germanium", "ge", 32, 4); + $register("arsenic", "Arsenic", "as", 33, 4); + $register("selenium", "Selenium", "se", 34, 5); + $register("bromine", "Bromine", "br", 35, 6); + $register("krypton", "Krypton", "kr", 36, 7); + $register("rubidium", "Rubidium", "rb", 37, 0); + $register("strontium", "Strontium", "sr", 38, 1); + $register("yttrium", "Yttrium", "y", 39, 2); + $register("zirconium", "Zirconium", "zr", 40, 2); + $register("niobium", "Niobium", "nb", 41, 2); + $register("molybdenum", "Molybdenum", "mo", 42, 2); + $register("technetium", "Technetium", "tc", 43, 2); + $register("ruthenium", "Ruthenium", "ru", 44, 2); + $register("rhodium", "Rhodium", "rh", 45, 2); + $register("palladium", "Palladium", "pd", 46, 2); + $register("silver", "Silver", "ag", 47, 2); + $register("cadmium", "Cadmium", "cd", 48, 2); + $register("indium", "Indium", "in", 49, 3); + $register("tin", "Tin", "sn", 50, 3); + $register("antimony", "Antimony", "sb", 51, 4); + $register("tellurium", "Tellurium", "te", 52, 4); + $register("iodine", "Iodine", "i", 53, 6); + $register("xenon", "Xenon", "xe", 54, 7); + $register("cesium", "Cesium", "cs", 55, 0); + $register("barium", "Barium", "ba", 56, 1); + $register("lanthanum", "Lanthanum", "la", 57, 8); + $register("cerium", "Cerium", "ce", 58, 8); + $register("praseodymium", "Praseodymium", "pr", 59, 8); + $register("neodymium", "Neodymium", "nd", 60, 8); + $register("promethium", "Promethium", "pm", 61, 8); + $register("samarium", "Samarium", "sm", 62, 8); + $register("europium", "Europium", "eu", 63, 8); + $register("gadolinium", "Gadolinium", "gd", 64, 8); + $register("terbium", "Terbium", "tb", 65, 8); + $register("dysprosium", "Dysprosium", "dy", 66, 8); + $register("holmium", "Holmium", "ho", 67, 8); + $register("erbium", "Erbium", "er", 68, 8); + $register("thulium", "Thulium", "tm", 69, 8); + $register("ytterbium", "Ytterbium", "yb", 70, 8); + $register("lutetium", "Lutetium", "lu", 71, 8); + $register("hafnium", "Hafnium", "hf", 72, 2); + $register("tantalum", "Tantalum", "ta", 73, 2); + $register("tungsten", "Tungsten", "w", 74, 2); + $register("rhenium", "Rhenium", "re", 75, 2); + $register("osmium", "Osmium", "os", 76, 2); + $register("iridium", "Iridium", "ir", 77, 2); + $register("platinum", "Platinum", "pt", 78, 2); + $register("gold", "Gold", "au", 79, 2); + $register("mercury", "Mercury", "hg", 80, 2); + $register("thallium", "Thallium", "tl", 81, 3); + $register("lead", "Lead", "pb", 82, 3); + $register("bismuth", "Bismuth", "bi", 83, 3); + $register("polonium", "Polonium", "po", 84, 4); + $register("astatine", "Astatine", "at", 85, 6); + $register("radon", "Radon", "rn", 86, 7); + $register("francium", "Francium", "fr", 87, 0); + $register("radium", "Radium", "ra", 88, 1); + $register("actinium", "Actinium", "ac", 89, 9); + $register("thorium", "Thorium", "th", 90, 9); + $register("protactinium", "Protactinium", "pa", 91, 9); + $register("uranium", "Uranium", "u", 92, 9); + $register("neptunium", "Neptunium", "np", 93, 9); + $register("plutonium", "Plutonium", "pu", 94, 9); + $register("americium", "Americium", "am", 95, 9); + $register("curium", "Curium", "cm", 96, 9); + $register("berkelium", "Berkelium", "bk", 97, 9); + $register("californium", "Californium", "cf", 98, 9); + $register("einsteinium", "Einsteinium", "es", 99, 9); + $register("fermium", "Fermium", "fm", 100, 9); + $register("mendelevium", "Mendelevium", "md", 101, 9); + $register("nobelium", "Nobelium", "no", 102, 9); + $register("lawrencium", "Lawrencium", "lr", 103, 9); + $register("rutherfordium", "Rutherfordium", "rf", 104, 2); + $register("dubnium", "Dubnium", "db", 105, 2); + $register("seaborgium", "Seaborgium", "sg", 106, 2); + $register("bohrium", "Bohrium", "bh", 107, 2); + $register("hassium", "Hassium", "hs", 108, 2); + $register("meitnerium", "Meitnerium", "mt", 109, 2); + $register("darmstadtium", "Darmstadtium", "ds", 110, 2); + $register("roentgenium", "Roentgenium", "rg", 111, 2); + $register("copernicium", "Copernicium", "cn", 112, 2); + $register("nihonium", "Nihonium", "nh", 113, 3); + $register("flerovium", "Flerovium", "fl", 114, 3); + $register("moscovium", "Moscovium", "mc", 115, 3); + $register("livermorium", "Livermorium", "lv", 116, 3); + $register("tennessine", "Tennessine", "ts", 117, 6); + $register("oganesson", "Oganesson", "og", 118, 7); } private static function registerOres() : void{ $stoneOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(3.0, $toolTier)); - self::register("coal_ore", new CoalOre(new BID(Ids::COAL_ORE), "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD))); - self::register("copper_ore", new CopperOre(new BID(Ids::COPPER_ORE), "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("diamond_ore", new DiamondOre(new BID(Ids::DIAMOND_ORE), "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("emerald_ore", new EmeraldOre(new BID(Ids::EMERALD_ORE), "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("gold_ore", new GoldOre(new BID(Ids::GOLD_ORE), "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON))); - self::register("iron_ore", new IronOre(new BID(Ids::IRON_ORE), "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("lapis_lazuli_ore", new LapisOre(new BID(Ids::LAPIS_LAZULI_ORE), "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); - self::register("redstone_ore", new RedstoneOre(new BID(Ids::REDSTONE_ORE), "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("coal_ore", fn(BID $id) => new CoalOre($id, "Coal Ore", $stoneOreBreakInfo(ToolTier::WOOD))); + self::register("copper_ore", fn(BID $id) => new CopperOre($id, "Copper Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("diamond_ore", fn(BID $id) => new DiamondOre($id, "Diamond Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("emerald_ore", fn(BID $id) => new EmeraldOre($id, "Emerald Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("gold_ore", fn(BID $id) => new GoldOre($id, "Gold Ore", $stoneOreBreakInfo(ToolTier::IRON))); + self::register("iron_ore", fn(BID $id) => new IronOre($id, "Iron Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Lapis Lazuli Ore", $stoneOreBreakInfo(ToolTier::STONE))); + self::register("redstone_ore", fn(BID $id) => new RedstoneOre($id, "Redstone Ore", $stoneOreBreakInfo(ToolTier::IRON))); $deepslateOreBreakInfo = fn(ToolTier $toolTier) => new Info(BreakInfo::pickaxe(4.5, $toolTier)); - self::register("deepslate_coal_ore", new CoalOre(new BID(Ids::DEEPSLATE_COAL_ORE), "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); - self::register("deepslate_copper_ore", new CopperOre(new BID(Ids::DEEPSLATE_COPPER_ORE), "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_diamond_ore", new DiamondOre(new BID(Ids::DEEPSLATE_DIAMOND_ORE), "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_emerald_ore", new EmeraldOre(new BID(Ids::DEEPSLATE_EMERALD_ORE), "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_gold_ore", new GoldOre(new BID(Ids::DEEPSLATE_GOLD_ORE), "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON))); - self::register("deepslate_iron_ore", new IronOre(new BID(Ids::DEEPSLATE_IRON_ORE), "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_lapis_lazuli_ore", new LapisOre(new BID(Ids::DEEPSLATE_LAPIS_LAZULI_ORE), "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE))); - self::register("deepslate_redstone_ore", new RedstoneOre(new BID(Ids::DEEPSLATE_REDSTONE_ORE), "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_coal_ore", fn(BID $id) => new CoalOre($id, "Deepslate Coal Ore", $deepslateOreBreakInfo(ToolTier::WOOD))); + self::register("deepslate_copper_ore", fn(BID $id) => new CopperOre($id, "Deepslate Copper Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_diamond_ore", fn(BID $id) => new DiamondOre($id, "Deepslate Diamond Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_emerald_ore", fn(BID $id) => new EmeraldOre($id, "Deepslate Emerald Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_gold_ore", fn(BID $id) => new GoldOre($id, "Deepslate Gold Ore", $deepslateOreBreakInfo(ToolTier::IRON))); + self::register("deepslate_iron_ore", fn(BID $id) => new IronOre($id, "Deepslate Iron Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_lapis_lazuli_ore", fn(BID $id) => new LapisOre($id, "Deepslate Lapis Lazuli Ore", $deepslateOreBreakInfo(ToolTier::STONE))); + self::register("deepslate_redstone_ore", fn(BID $id) => new RedstoneOre($id, "Deepslate Redstone Ore", $deepslateOreBreakInfo(ToolTier::IRON))); $netherrackOreBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::WOOD)); - self::register("nether_quartz_ore", new NetherQuartzOre(new BID(Ids::NETHER_QUARTZ_ORE), "Nether Quartz Ore", $netherrackOreBreakInfo)); - self::register("nether_gold_ore", new NetherGoldOre(new BID(Ids::NETHER_GOLD_ORE), "Nether Gold Ore", $netherrackOreBreakInfo)); + self::register("nether_quartz_ore", fn(BID $id) => new NetherQuartzOre($id, "Nether Quartz Ore", $netherrackOreBreakInfo)); + self::register("nether_gold_ore", fn(BID $id) => new NetherGoldOre($id, "Nether Gold Ore", $netherrackOreBreakInfo)); } private static function registerCraftingTables() : void{ //TODO: this is the same for all wooden crafting blocks $craftingBlockBreakInfo = new Info(BreakInfo::axe(2.5)); - self::register("cartography_table", new CartographyTable(new BID(Ids::CARTOGRAPHY_TABLE), "Cartography Table", $craftingBlockBreakInfo)); - self::register("crafting_table", new CraftingTable(new BID(Ids::CRAFTING_TABLE), "Crafting Table", $craftingBlockBreakInfo)); - self::register("fletching_table", new FletchingTable(new BID(Ids::FLETCHING_TABLE), "Fletching Table", $craftingBlockBreakInfo)); - self::register("loom", new Loom(new BID(Ids::LOOM), "Loom", $craftingBlockBreakInfo)); - self::register("smithing_table", new SmithingTable(new BID(Ids::SMITHING_TABLE), "Smithing Table", $craftingBlockBreakInfo)); + self::register("cartography_table", fn(BID $id) => new CartographyTable($id, "Cartography Table", $craftingBlockBreakInfo)); + self::register("crafting_table", fn(BID $id) => new CraftingTable($id, "Crafting Table", $craftingBlockBreakInfo)); + self::register("fletching_table", fn(BID $id) => new FletchingTable($id, "Fletching Table", $craftingBlockBreakInfo)); + self::register("loom", fn(BID $id) => new Loom($id, "Loom", $craftingBlockBreakInfo)); + self::register("smithing_table", fn(BID $id) => new SmithingTable($id, "Smithing Table", $craftingBlockBreakInfo)); } private static function registerChorusBlocks() : void{ $chorusBlockBreakInfo = new Info(BreakInfo::axe(0.4)); - self::register("chorus_plant", new ChorusPlant(new BID(Ids::CHORUS_PLANT), "Chorus Plant", $chorusBlockBreakInfo)); - self::register("chorus_flower", new ChorusFlower(new BID(Ids::CHORUS_FLOWER), "Chorus Flower", $chorusBlockBreakInfo)); + self::register("chorus_plant", fn(BID $id) => new ChorusPlant($id, "Chorus Plant", $chorusBlockBreakInfo)); + self::register("chorus_flower", fn(BID $id) => new ChorusFlower($id, "Chorus Flower", $chorusBlockBreakInfo)); } private static function registerBlocksR13() : void{ - self::register("light", new Light(new BID(Ids::LIGHT), "Light Block", new Info(BreakInfo::indestructible()))); - self::register("wither_rose", new WitherRose(new BID(Ids::WITHER_ROSE), "Wither Rose", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); + self::register("light", fn(BID $id) => new Light($id, "Light Block", new Info(BreakInfo::indestructible()))); + self::register("wither_rose", fn(BID $id) => new WitherRose($id, "Wither Rose", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); } private static function registerBlocksR14() : void{ - self::register("honeycomb", new Opaque(new BID(Ids::HONEYCOMB), "Honeycomb Block", new Info(new BreakInfo(0.6)))); + self::register("honeycomb", fn(BID $id) => new Opaque($id, "Honeycomb Block", new Info(new BreakInfo(0.6)))); } private static function registerBlocksR16() : void{ //for some reason, slabs have weird hardness like the legacy ones $slabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("ancient_debris", new class(new BID(Ids::ANCIENT_DEBRIS), "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ + self::register("ancient_debris", fn(BID $id) => new class($id, "Ancient Debris", new Info(BreakInfo::pickaxe(30, ToolTier::DIAMOND, 3600.0))) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); $netheriteBreakInfo = new Info(BreakInfo::pickaxe(50, ToolTier::DIAMOND, 3600.0)); - self::register("netherite", new class(new BID(Ids::NETHERITE), "Netherite Block", $netheriteBreakInfo) extends Opaque{ + self::register("netherite", fn(BID $id) => new class($id, "Netherite Block", $netheriteBreakInfo) extends Opaque{ public function isFireProofAsItem() : bool{ return true; } }); $basaltBreakInfo = new Info(BreakInfo::pickaxe(1.25, ToolTier::WOOD, 21.0)); - self::register("basalt", new SimplePillar(new BID(Ids::BASALT), "Basalt", $basaltBreakInfo)); - self::register("polished_basalt", new SimplePillar(new BID(Ids::POLISHED_BASALT), "Polished Basalt", $basaltBreakInfo)); - self::register("smooth_basalt", new Opaque(new BID(Ids::SMOOTH_BASALT), "Smooth Basalt", $basaltBreakInfo)); + self::register("basalt", fn(BID $id) => new SimplePillar($id, "Basalt", $basaltBreakInfo)); + self::register("polished_basalt", fn(BID $id) => new SimplePillar($id, "Polished Basalt", $basaltBreakInfo)); + self::register("smooth_basalt", fn(BID $id) => new Opaque($id, "Smooth Basalt", $basaltBreakInfo)); $blackstoneBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("blackstone", new Opaque(new BID(Ids::BLACKSTONE), "Blackstone", $blackstoneBreakInfo)); - self::register("blackstone_slab", new Slab(new BID(Ids::BLACKSTONE_SLAB), "Blackstone", $slabBreakInfo)); - self::register("blackstone_stairs", new Stair(new BID(Ids::BLACKSTONE_STAIRS), "Blackstone Stairs", $blackstoneBreakInfo)); - self::register("blackstone_wall", new Wall(new BID(Ids::BLACKSTONE_WALL), "Blackstone Wall", $blackstoneBreakInfo)); + self::register("blackstone", fn(BID $id) => new Opaque($id, "Blackstone", $blackstoneBreakInfo)); + self::register("blackstone_slab", fn(BID $id) => new Slab($id, "Blackstone", $slabBreakInfo)); + self::register("blackstone_stairs", fn(BID $id) => new Stair($id, "Blackstone Stairs", $blackstoneBreakInfo)); + self::register("blackstone_wall", fn(BID $id) => new Wall($id, "Blackstone Wall", $blackstoneBreakInfo)); - self::register("gilded_blackstone", new GildedBlackstone(new BID(Ids::GILDED_BLACKSTONE), "Gilded Blackstone", $blackstoneBreakInfo)); + self::register("gilded_blackstone", fn(BID $id) => new GildedBlackstone($id, "Gilded Blackstone", $blackstoneBreakInfo)); //TODO: polished blackstone ought to have 2.0 hardness (as per java) but it's 1.5 in Bedrock (probably parity bug) $prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone", new Opaque(new BID(Ids::POLISHED_BLACKSTONE), $prefix(""), $blackstoneBreakInfo)); - self::register("polished_blackstone_button", new StoneButton(new BID(Ids::POLISHED_BLACKSTONE_BUTTON), $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); - self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)), 20)); - self::register("polished_blackstone_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_SLAB), $prefix(""), $slabBreakInfo)); - self::register("polished_blackstone_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_WALL), $prefix("Wall"), $blackstoneBreakInfo)); - self::register("chiseled_polished_blackstone", new Opaque(new BID(Ids::CHISELED_POLISHED_BLACKSTONE), "Chiseled Polished Blackstone", $blackstoneBreakInfo)); + self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $blackstoneBreakInfo)); + self::register("polished_blackstone_button", fn(BID $id) => new StoneButton($id, $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); + self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)), 20)); + self::register("polished_blackstone_slab", fn(BID $id) => new Slab($id, $prefix(""), $slabBreakInfo)); + self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); + self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("chiseled_polished_blackstone", fn(BID $id) => new Opaque($id, "Chiseled Polished Blackstone", $blackstoneBreakInfo)); $prefix = fn(string $thing) => "Polished Blackstone Brick" . ($thing !== "" ? " $thing" : ""); - self::register("polished_blackstone_bricks", new Opaque(new BID(Ids::POLISHED_BLACKSTONE_BRICKS), "Polished Blackstone Bricks", $blackstoneBreakInfo)); - self::register("polished_blackstone_brick_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_BRICK_SLAB), "Polished Blackstone Brick", $slabBreakInfo)); - self::register("polished_blackstone_brick_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_BRICK_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo)); - self::register("polished_blackstone_brick_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_BRICK_WALL), $prefix("Wall"), $blackstoneBreakInfo)); - self::register("cracked_polished_blackstone_bricks", new Opaque(new BID(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS), "Cracked Polished Blackstone Bricks", $blackstoneBreakInfo)); + self::register("polished_blackstone_bricks", fn(BID $id) => new Opaque($id, "Polished Blackstone Bricks", $blackstoneBreakInfo)); + self::register("polished_blackstone_brick_slab", fn(BID $id) => new Slab($id, "Polished Blackstone Brick", $slabBreakInfo)); + self::register("polished_blackstone_brick_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); + self::register("polished_blackstone_brick_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); + self::register("cracked_polished_blackstone_bricks", fn(BID $id) => new Opaque($id, "Cracked Polished Blackstone Bricks", $blackstoneBreakInfo)); - self::register("soul_torch", new Torch(new BID(Ids::SOUL_TORCH), "Soul Torch", new Info(BreakInfo::instant()))); - self::register("soul_fire", new SoulFire(new BID(Ids::SOUL_FIRE), "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); + self::register("soul_torch", fn(BID $id) => new Torch($id, "Soul Torch", new Info(BreakInfo::instant()))); + self::register("soul_fire", fn(BID $id) => new SoulFire($id, "Soul Fire", new Info(BreakInfo::instant(), [Tags::FIRE]))); //TODO: soul soul ought to have 0.5 hardness (as per java) but it's 1.0 in Bedrock (probably parity bug) - self::register("soul_soil", new Opaque(new BID(Ids::SOUL_SOIL), "Soul Soil", new Info(BreakInfo::shovel(1.0)))); + self::register("soul_soil", fn(BID $id) => new Opaque($id, "Soul Soil", new Info(BreakInfo::shovel(1.0)))); - self::register("shroomlight", new class(new BID(Ids::SHROOMLIGHT), "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ + self::register("shroomlight", fn(BID $id) => new class($id, "Shroomlight", new Info(new BreakInfo(1.0, ToolType::HOE))) extends Opaque{ public function getLightLevel() : int{ return 15; } }); - self::register("warped_wart_block", new Opaque(new BID(Ids::WARPED_WART_BLOCK), "Warped Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); - self::register("crying_obsidian", new class(new BID(Ids::CRYING_OBSIDIAN), "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND, 6000.0))) extends Opaque{ + self::register("warped_wart_block", fn(BID $id) => new Opaque($id, "Warped Wart Block", new Info(new BreakInfo(1.0, ToolType::HOE)))); + self::register("crying_obsidian", fn(BID $id) => new class($id, "Crying Obsidian", new Info(BreakInfo::pickaxe(35.0 /* 50 in Java */, ToolTier::DIAMOND, 6000.0))) extends Opaque{ public function getLightLevel() : int{ return 10;} }); - self::register("twisting_vines", new NetherVines(new BID(Ids::TWISTING_VINES), "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP)); - self::register("weeping_vines", new NetherVines(new BID(Ids::WEEPING_VINES), "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN)); + self::register("twisting_vines", fn(BID $id) => new NetherVines($id, "Twisting Vines", new Info(BreakInfo::instant()), Facing::UP)); + self::register("weeping_vines", fn(BID $id) => new NetherVines($id, "Weeping Vines", new Info(BreakInfo::instant()), Facing::DOWN)); $netherRootsInfo = new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]); - self::register("crimson_roots", new NetherRoots(new BID(Ids::CRIMSON_ROOTS), "Crimson Roots", $netherRootsInfo)); - self::register("warped_roots", new NetherRoots(new BID(Ids::WARPED_ROOTS), "Warped Roots", $netherRootsInfo)); + self::register("crimson_roots", fn(BID $id) => new NetherRoots($id, "Crimson Roots", $netherRootsInfo)); + self::register("warped_roots", fn(BID $id) => new NetherRoots($id, "Warped Roots", $netherRootsInfo)); - self::register("chain", new Chain(new BID(Ids::CHAIN), "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); + self::register("chain", fn(BID $id) => new Chain($id, "Chain", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); } private static function registerBlocksR17() : void{ //in java this can be acquired using any tool - seems to be a parity issue in bedrock $amethystInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD)); - self::register("amethyst", new class(new BID(Ids::AMETHYST), "Amethyst", $amethystInfo) extends Opaque{ + self::register("amethyst", fn(BID $id) => new class($id, "Amethyst", $amethystInfo) extends Opaque{ use AmethystTrait; }); - self::register("budding_amethyst", new BuddingAmethyst(new BID(Ids::BUDDING_AMETHYST), "Budding Amethyst", $amethystInfo)); - self::register("amethyst_cluster", new AmethystCluster(new BID(Ids::AMETHYST_CLUSTER), "Amethyst Cluster", $amethystInfo)); + self::register("budding_amethyst", fn(BID $id) => new BuddingAmethyst($id, "Budding Amethyst", $amethystInfo)); + self::register("amethyst_cluster", fn(BID $id) => new AmethystCluster($id, "Amethyst Cluster", $amethystInfo)); - self::register("calcite", new Opaque(new BID(Ids::CALCITE), "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD)))); + self::register("calcite", fn(BID $id) => new Opaque($id, "Calcite", new Info(BreakInfo::pickaxe(0.75, ToolTier::WOOD)))); - self::register("raw_copper", new Opaque(new BID(Ids::RAW_COPPER), "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); - self::register("raw_gold", new Opaque(new BID(Ids::RAW_GOLD), "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); - self::register("raw_iron", new Opaque(new BID(Ids::RAW_IRON), "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); + self::register("raw_copper", fn(BID $id) => new Opaque($id, "Raw Copper Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); + self::register("raw_gold", fn(BID $id) => new Opaque($id, "Raw Gold Block", new Info(BreakInfo::pickaxe(5, ToolTier::IRON, 30.0)))); + self::register("raw_iron", fn(BID $id) => new Opaque($id, "Raw Iron Block", new Info(BreakInfo::pickaxe(5, ToolTier::STONE, 30.0)))); $deepslateBreakInfo = new Info(BreakInfo::pickaxe(3, ToolTier::WOOD, 18.0)); - self::register("deepslate", new class(new BID(Ids::DEEPSLATE), "Deepslate", $deepslateBreakInfo) extends SimplePillar{ + self::register("deepslate", fn(BID $id) => new class($id, "Deepslate", $deepslateBreakInfo) extends SimplePillar{ public function getDropsForCompatibleTool(Item $item) : array{ return [VanillaBlocks::COBBLED_DEEPSLATE()->asItem()]; } @@ -1612,113 +1647,113 @@ final class VanillaBlocks{ }); //TODO: parity issue here - in Java this has a hardness of 3.0, but in bedrock it's 3.5 - self::register("chiseled_deepslate", new Opaque(new BID(Ids::CHISELED_DEEPSLATE), "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); + self::register("chiseled_deepslate", fn(BID $id) => new Opaque($id, "Chiseled Deepslate", new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)))); $deepslateBrickBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("deepslate_bricks", new Opaque(new BID(Ids::DEEPSLATE_BRICKS), "Deepslate Bricks", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_slab", new Slab(new BID(Ids::DEEPSLATE_BRICK_SLAB), "Deepslate Brick", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_stairs", new Stair(new BID(Ids::DEEPSLATE_BRICK_STAIRS), "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); - self::register("deepslate_brick_wall", new Wall(new BID(Ids::DEEPSLATE_BRICK_WALL), "Deepslate Brick Wall", $deepslateBrickBreakInfo)); - self::register("cracked_deepslate_bricks", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_BRICKS), "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); + self::register("deepslate_bricks", fn(BID $id) => new Opaque($id, "Deepslate Bricks", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_slab", fn(BID $id) => new Slab($id, "Deepslate Brick", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_stairs", fn(BID $id) => new Stair($id, "Deepslate Brick Stairs", $deepslateBrickBreakInfo)); + self::register("deepslate_brick_wall", fn(BID $id) => new Wall($id, "Deepslate Brick Wall", $deepslateBrickBreakInfo)); + self::register("cracked_deepslate_bricks", fn(BID $id) => new Opaque($id, "Cracked Deepslate Bricks", $deepslateBrickBreakInfo)); $deepslateTilesBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("deepslate_tiles", new Opaque(new BID(Ids::DEEPSLATE_TILES), "Deepslate Tiles", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_slab", new Slab(new BID(Ids::DEEPSLATE_TILE_SLAB), "Deepslate Tile", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_stairs", new Stair(new BID(Ids::DEEPSLATE_TILE_STAIRS), "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); - self::register("deepslate_tile_wall", new Wall(new BID(Ids::DEEPSLATE_TILE_WALL), "Deepslate Tile Wall", $deepslateTilesBreakInfo)); - self::register("cracked_deepslate_tiles", new Opaque(new BID(Ids::CRACKED_DEEPSLATE_TILES), "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); + self::register("deepslate_tiles", fn(BID $id) => new Opaque($id, "Deepslate Tiles", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_slab", fn(BID $id) => new Slab($id, "Deepslate Tile", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_stairs", fn(BID $id) => new Stair($id, "Deepslate Tile Stairs", $deepslateTilesBreakInfo)); + self::register("deepslate_tile_wall", fn(BID $id) => new Wall($id, "Deepslate Tile Wall", $deepslateTilesBreakInfo)); + self::register("cracked_deepslate_tiles", fn(BID $id) => new Opaque($id, "Cracked Deepslate Tiles", $deepslateTilesBreakInfo)); $cobbledDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("cobbled_deepslate", new Opaque(new BID(Ids::COBBLED_DEEPSLATE), "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_slab", new Slab(new BID(Ids::COBBLED_DEEPSLATE_SLAB), "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_stairs", new Stair(new BID(Ids::COBBLED_DEEPSLATE_STAIRS), "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); - self::register("cobbled_deepslate_wall", new Wall(new BID(Ids::COBBLED_DEEPSLATE_WALL), "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate", fn(BID $id) => new Opaque($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_slab", fn(BID $id) => new Slab($id, "Cobbled Deepslate", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_stairs", fn(BID $id) => new Stair($id, "Cobbled Deepslate Stairs", $cobbledDeepslateBreakInfo)); + self::register("cobbled_deepslate_wall", fn(BID $id) => new Wall($id, "Cobbled Deepslate Wall", $cobbledDeepslateBreakInfo)); $polishedDeepslateBreakInfo = new Info(BreakInfo::pickaxe(3.5, ToolTier::WOOD, 18.0)); - self::register("polished_deepslate", new Opaque(new BID(Ids::POLISHED_DEEPSLATE), "Polished Deepslate", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_slab", new Slab(new BID(Ids::POLISHED_DEEPSLATE_SLAB), "Polished Deepslate", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_stairs", new Stair(new BID(Ids::POLISHED_DEEPSLATE_STAIRS), "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); - self::register("polished_deepslate_wall", new Wall(new BID(Ids::POLISHED_DEEPSLATE_WALL), "Polished Deepslate Wall", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate", fn(BID $id) => new Opaque($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_slab", fn(BID $id) => new Slab($id, "Polished Deepslate", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_stairs", fn(BID $id) => new Stair($id, "Polished Deepslate Stairs", $polishedDeepslateBreakInfo)); + self::register("polished_deepslate_wall", fn(BID $id) => new Wall($id, "Polished Deepslate Wall", $polishedDeepslateBreakInfo)); - self::register("tinted_glass", new TintedGlass(new BID(Ids::TINTED_GLASS), "Tinted Glass", new Info(new BreakInfo(0.3)))); + self::register("tinted_glass", fn(BID $id) => new TintedGlass($id, "Tinted Glass", new Info(new BreakInfo(0.3)))); //blast resistance should be 30 if we were matched with java :( $copperBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 18.0)); - self::register("lightning_rod", new LightningRod(new BID(Ids::LIGHTNING_ROD), "Lightning Rod", $copperBreakInfo)); + self::register("lightning_rod", fn(BID $id) => new LightningRod($id, "Lightning Rod", $copperBreakInfo)); - self::register("copper", new Copper(new BID(Ids::COPPER), "Copper Block", $copperBreakInfo)); - self::register("chiseled_copper", new Copper(new BID(Ids::CHISELED_COPPER), "Chiseled Copper", $copperBreakInfo)); - self::register("copper_grate", new CopperGrate(new BID(Ids::COPPER_GRATE), "Copper Grate", $copperBreakInfo)); - self::register("cut_copper", new Copper(new BID(Ids::CUT_COPPER), "Cut Copper Block", $copperBreakInfo)); - self::register("cut_copper_slab", new CopperSlab(new BID(Ids::CUT_COPPER_SLAB), "Cut Copper Slab", $copperBreakInfo)); - self::register("cut_copper_stairs", new CopperStairs(new BID(Ids::CUT_COPPER_STAIRS), "Cut Copper Stairs", $copperBreakInfo)); - self::register("copper_bulb", new CopperBulb(new BID(Ids::COPPER_BULB), "Copper Bulb", $copperBreakInfo)); + self::register("copper", fn(BID $id) => new Copper($id, "Copper Block", $copperBreakInfo)); + self::register("chiseled_copper", fn(BID $id) => new Copper($id, "Chiseled Copper", $copperBreakInfo)); + self::register("copper_grate", fn(BID $id) => new CopperGrate($id, "Copper Grate", $copperBreakInfo)); + self::register("cut_copper", fn(BID $id) => new Copper($id, "Cut Copper Block", $copperBreakInfo)); + self::register("cut_copper_slab", fn(BID $id) => new CopperSlab($id, "Cut Copper Slab", $copperBreakInfo)); + self::register("cut_copper_stairs", fn(BID $id) => new CopperStairs($id, "Cut Copper Stairs", $copperBreakInfo)); + self::register("copper_bulb", fn(BID $id) => new CopperBulb($id, "Copper Bulb", $copperBreakInfo)); $copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); - self::register("copper_door", new CopperDoor(new BID(Ids::COPPER_DOOR), "Copper Door", $copperDoorBreakInfo)); - self::register("copper_trapdoor", new CopperTrapdoor(new BID(Ids::COPPER_TRAPDOOR), "Copper Trapdoor", $copperDoorBreakInfo)); + self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", $copperDoorBreakInfo)); + self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", $copperDoorBreakInfo)); $candleBreakInfo = new Info(new BreakInfo(0.1)); - self::register("candle", new Candle(new BID(Ids::CANDLE), "Candle", $candleBreakInfo)); - self::register("dyed_candle", new DyedCandle(new BID(Ids::DYED_CANDLE), "Dyed Candle", $candleBreakInfo)); + self::register("candle", fn(BID $id) => new Candle($id, "Candle", $candleBreakInfo)); + self::register("dyed_candle", fn(BID $id) => new DyedCandle($id, "Dyed Candle", $candleBreakInfo)); //TODO: duplicated break info :( $cakeBreakInfo = new Info(new BreakInfo(0.5)); - self::register("cake_with_candle", new CakeWithCandle(new BID(Ids::CAKE_WITH_CANDLE), "Cake With Candle", $cakeBreakInfo)); - self::register("cake_with_dyed_candle", new CakeWithDyedCandle(new BID(Ids::CAKE_WITH_DYED_CANDLE), "Cake With Dyed Candle", $cakeBreakInfo)); + self::register("cake_with_candle", fn(BID $id) => new CakeWithCandle($id, "Cake With Candle", $cakeBreakInfo)); + self::register("cake_with_dyed_candle", fn(BID $id) => new CakeWithDyedCandle($id, "Cake With Dyed Candle", $cakeBreakInfo)); - self::register("hanging_roots", new HangingRoots(new BID(Ids::HANGING_ROOTS), "Hanging Roots", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); + self::register("hanging_roots", fn(BID $id) => new HangingRoots($id, "Hanging Roots", new Info(BreakInfo::instant(ToolType::SHEARS, 1)))); - self::register("cave_vines", new CaveVines(new BID(Ids::CAVE_VINES), "Cave Vines", new Info(BreakInfo::instant()))); + self::register("cave_vines", fn(BID $id) => new CaveVines($id, "Cave Vines", new Info(BreakInfo::instant()))); - self::register("small_dripleaf", new SmallDripleaf(new BID(Ids::SMALL_DRIPLEAF), "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); - self::register("big_dripleaf_head", new BigDripleafHead(new BID(Ids::BIG_DRIPLEAF_HEAD), "Big Dripleaf", new Info(BreakInfo::instant()))); - self::register("big_dripleaf_stem", new BigDripleafStem(new BID(Ids::BIG_DRIPLEAF_STEM), "Big Dripleaf Stem", new Info(BreakInfo::instant()))); + self::register("small_dripleaf", fn(BID $id) => new SmallDripleaf($id, "Small Dripleaf", new Info(BreakInfo::instant(ToolType::SHEARS, toolHarvestLevel: 1)))); + self::register("big_dripleaf_head", fn(BID $id) => new BigDripleafHead($id, "Big Dripleaf", new Info(BreakInfo::instant()))); + self::register("big_dripleaf_stem", fn(BID $id) => new BigDripleafStem($id, "Big Dripleaf Stem", new Info(BreakInfo::instant()))); } private static function registerBlocksR18() : void{ - self::register("spore_blossom", new SporeBlossom(new BID(Ids::SPORE_BLOSSOM), "Spore Blossom", new Info(BreakInfo::instant()))); + self::register("spore_blossom", fn(BID $id) => new SporeBlossom($id, "Spore Blossom", new Info(BreakInfo::instant()))); } private static function registerMudBlocks() : void{ - self::register("mud", new Opaque(new BID(Ids::MUD), "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); - self::register("packed_mud", new Opaque(new BID(Ids::PACKED_MUD), "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); + self::register("mud", fn(BID $id) => new Opaque($id, "Mud", new Info(BreakInfo::shovel(0.5), [Tags::MUD]))); + self::register("packed_mud", fn(BID $id) => new Opaque($id, "Packed Mud", new Info(BreakInfo::pickaxe(1.0, null, 15.0)))); $mudBricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); - self::register("mud_bricks", new Opaque(new BID(Ids::MUD_BRICKS), "Mud Bricks", $mudBricksBreakInfo)); - self::register("mud_brick_slab", new Slab(new BID(Ids::MUD_BRICK_SLAB), "Mud Brick", $mudBricksBreakInfo)); - self::register("mud_brick_stairs", new Stair(new BID(Ids::MUD_BRICK_STAIRS), "Mud Brick Stairs", $mudBricksBreakInfo)); - self::register("mud_brick_wall", new Wall(new BID(Ids::MUD_BRICK_WALL), "Mud Brick Wall", $mudBricksBreakInfo)); + self::register("mud_bricks", fn(BID $id) => new Opaque($id, "Mud Bricks", $mudBricksBreakInfo)); + self::register("mud_brick_slab", fn(BID $id) => new Slab($id, "Mud Brick", $mudBricksBreakInfo)); + self::register("mud_brick_stairs", fn(BID $id) => new Stair($id, "Mud Brick Stairs", $mudBricksBreakInfo)); + self::register("mud_brick_wall", fn(BID $id) => new Wall($id, "Mud Brick Wall", $mudBricksBreakInfo)); } private static function registerTuffBlocks() : void{ $tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); - self::register("tuff", new Opaque(new BID(Ids::TUFF), "Tuff", $tuffBreakInfo)); - self::register("tuff_slab", new Slab(new BID(Ids::TUFF_SLAB), "Tuff", $tuffBreakInfo)); - self::register("tuff_stairs", new Stair(new BID(Ids::TUFF_STAIRS), "Tuff Stairs", $tuffBreakInfo)); - self::register("tuff_wall", new Wall(new BID(Ids::TUFF_WALL), "Tuff Wall", $tuffBreakInfo)); - self::register("chiseled_tuff", new Opaque(new BID(Ids::CHISELED_TUFF), "Chiseled Tuff", $tuffBreakInfo)); + self::register("tuff", fn(BID $id) => new Opaque($id, "Tuff", $tuffBreakInfo)); + self::register("tuff_slab", fn(BID $id) => new Slab($id, "Tuff", $tuffBreakInfo)); + self::register("tuff_stairs", fn(BID $id) => new Stair($id, "Tuff Stairs", $tuffBreakInfo)); + self::register("tuff_wall", fn(BID $id) => new Wall($id, "Tuff Wall", $tuffBreakInfo)); + self::register("chiseled_tuff", fn(BID $id) => new Opaque($id, "Chiseled Tuff", $tuffBreakInfo)); - self::register("tuff_bricks", new Opaque(new BID(Ids::TUFF_BRICKS), "Tuff Bricks", $tuffBreakInfo)); - self::register("tuff_brick_slab", new Slab(new BID(Ids::TUFF_BRICK_SLAB), "Tuff Brick", $tuffBreakInfo)); - self::register("tuff_brick_stairs", new Stair(new BID(Ids::TUFF_BRICK_STAIRS), "Tuff Brick Stairs", $tuffBreakInfo)); - self::register("tuff_brick_wall", new Wall(new BID(Ids::TUFF_BRICK_WALL), "Tuff Brick Wall", $tuffBreakInfo)); - self::register("chiseled_tuff_bricks", new Opaque(new BID(Ids::CHISELED_TUFF_BRICKS), "Chiseled Tuff Bricks", $tuffBreakInfo)); + self::register("tuff_bricks", fn(BID $id) => new Opaque($id, "Tuff Bricks", $tuffBreakInfo)); + self::register("tuff_brick_slab", fn(BID $id) => new Slab($id, "Tuff Brick", $tuffBreakInfo)); + self::register("tuff_brick_stairs", fn(BID $id) => new Stair($id, "Tuff Brick Stairs", $tuffBreakInfo)); + self::register("tuff_brick_wall", fn(BID $id) => new Wall($id, "Tuff Brick Wall", $tuffBreakInfo)); + self::register("chiseled_tuff_bricks", fn(BID $id) => new Opaque($id, "Chiseled Tuff Bricks", $tuffBreakInfo)); - self::register("polished_tuff", new Opaque(new BID(Ids::POLISHED_TUFF), "Polished Tuff", $tuffBreakInfo)); - self::register("polished_tuff_slab", new Slab(new BID(Ids::POLISHED_TUFF_SLAB), "Polished Tuff", $tuffBreakInfo)); - self::register("polished_tuff_stairs", new Stair(new BID(Ids::POLISHED_TUFF_STAIRS), "Polished Tuff Stairs", $tuffBreakInfo)); - self::register("polished_tuff_wall", new Wall(new BID(Ids::POLISHED_TUFF_WALL), "Polished Tuff Wall", $tuffBreakInfo)); + self::register("polished_tuff", fn(BID $id) => new Opaque($id, "Polished Tuff", $tuffBreakInfo)); + self::register("polished_tuff_slab", fn(BID $id) => new Slab($id, "Polished Tuff", $tuffBreakInfo)); + self::register("polished_tuff_stairs", fn(BID $id) => new Stair($id, "Polished Tuff Stairs", $tuffBreakInfo)); + self::register("polished_tuff_wall", fn(BID $id) => new Wall($id, "Polished Tuff Wall", $tuffBreakInfo)); } private static function registerCauldronBlocks() : void{ $cauldronBreakInfo = new Info(BreakInfo::pickaxe(2, ToolTier::WOOD)); - self::register("cauldron", new Cauldron(new BID(Ids::CAULDRON, TileCauldron::class), "Cauldron", $cauldronBreakInfo)); - self::register("water_cauldron", new WaterCauldron(new BID(Ids::WATER_CAULDRON, TileCauldron::class), "Water Cauldron", $cauldronBreakInfo)); - self::register("lava_cauldron", new LavaCauldron(new BID(Ids::LAVA_CAULDRON, TileCauldron::class), "Lava Cauldron", $cauldronBreakInfo)); - self::register("potion_cauldron", new PotionCauldron(new BID(Ids::POTION_CAULDRON, TileCauldron::class), "Potion Cauldron", $cauldronBreakInfo)); + self::register("cauldron", fn(BID $id) => new Cauldron($id, "Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("water_cauldron", fn(BID $id) => new WaterCauldron($id, "Water Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("lava_cauldron", fn(BID $id) => new LavaCauldron($id, "Lava Cauldron", $cauldronBreakInfo), TileCauldron::class); + self::register("potion_cauldron", fn(BID $id) => new PotionCauldron($id, "Potion Cauldron", $cauldronBreakInfo), TileCauldron::class); } } diff --git a/src/block/WoodLikeBlockIdHelper.php b/src/block/WoodLikeBlockIdHelper.php deleted file mode 100644 index 88fdff3a6..000000000 --- a/src/block/WoodLikeBlockIdHelper.php +++ /dev/null @@ -1,263 +0,0 @@ - Ids::OAK_PLANKS, - WoodType::SPRUCE => Ids::SPRUCE_PLANKS, - WoodType::BIRCH => Ids::BIRCH_PLANKS, - WoodType::JUNGLE => Ids::JUNGLE_PLANKS, - WoodType::ACACIA => Ids::ACACIA_PLANKS, - WoodType::DARK_OAK => Ids::DARK_OAK_PLANKS, - WoodType::MANGROVE => Ids::MANGROVE_PLANKS, - WoodType::CRIMSON => Ids::CRIMSON_PLANKS, - WoodType::WARPED => Ids::WARPED_PLANKS, - WoodType::CHERRY => Ids::CHERRY_PLANKS, - }); - } - - public static function getFenceIdentifier(WoodType $type) : BID{ - return new BID(match($type){ - WoodType::OAK => Ids::OAK_FENCE, - WoodType::SPRUCE => Ids::SPRUCE_FENCE, - WoodType::BIRCH => Ids::BIRCH_FENCE, - WoodType::JUNGLE => Ids::JUNGLE_FENCE, - WoodType::ACACIA => Ids::ACACIA_FENCE, - WoodType::DARK_OAK => Ids::DARK_OAK_FENCE, - WoodType::MANGROVE => Ids::MANGROVE_FENCE, - WoodType::CRIMSON => Ids::CRIMSON_FENCE, - WoodType::WARPED => Ids::WARPED_FENCE, - WoodType::CHERRY => Ids::CHERRY_FENCE, - }); - } - - public static function getSlabIdentifier(WoodType $type) : BID{ - return new BID(match($type){ - WoodType::OAK => Ids::OAK_SLAB, - WoodType::SPRUCE => Ids::SPRUCE_SLAB, - WoodType::BIRCH => Ids::BIRCH_SLAB, - WoodType::JUNGLE => Ids::JUNGLE_SLAB, - WoodType::ACACIA => Ids::ACACIA_SLAB, - WoodType::DARK_OAK => Ids::DARK_OAK_SLAB, - WoodType::MANGROVE => Ids::MANGROVE_SLAB, - WoodType::CRIMSON => Ids::CRIMSON_SLAB, - WoodType::WARPED => Ids::WARPED_SLAB, - WoodType::CHERRY => Ids::CHERRY_SLAB, - }); - } - - public static function getLogIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_LOG, - WoodType::SPRUCE => Ids::SPRUCE_LOG, - WoodType::BIRCH => Ids::BIRCH_LOG, - WoodType::JUNGLE => Ids::JUNGLE_LOG, - WoodType::ACACIA => Ids::ACACIA_LOG, - WoodType::DARK_OAK => Ids::DARK_OAK_LOG, - WoodType::MANGROVE => Ids::MANGROVE_LOG, - WoodType::CRIMSON => Ids::CRIMSON_STEM, - WoodType::WARPED => Ids::WARPED_STEM, - WoodType::CHERRY => Ids::CHERRY_LOG, - }); - } - - public static function getAllSidedLogIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_WOOD, - WoodType::SPRUCE => Ids::SPRUCE_WOOD, - WoodType::BIRCH => Ids::BIRCH_WOOD, - WoodType::JUNGLE => Ids::JUNGLE_WOOD, - WoodType::ACACIA => Ids::ACACIA_WOOD, - WoodType::DARK_OAK => Ids::DARK_OAK_WOOD, - WoodType::MANGROVE => Ids::MANGROVE_WOOD, - WoodType::CRIMSON => Ids::CRIMSON_HYPHAE, - WoodType::WARPED => Ids::WARPED_HYPHAE, - WoodType::CHERRY => Ids::CHERRY_WOOD, - }); - } - - public static function getLeavesIdentifier(LeavesType $leavesType) : BID{ - return new BID(match($leavesType){ - LeavesType::OAK => Ids::OAK_LEAVES, - LeavesType::SPRUCE => Ids::SPRUCE_LEAVES, - LeavesType::BIRCH => Ids::BIRCH_LEAVES, - LeavesType::JUNGLE => Ids::JUNGLE_LEAVES, - LeavesType::ACACIA => Ids::ACACIA_LEAVES, - LeavesType::DARK_OAK => Ids::DARK_OAK_LEAVES, - LeavesType::MANGROVE => Ids::MANGROVE_LEAVES, - LeavesType::AZALEA => Ids::AZALEA_LEAVES, - LeavesType::FLOWERING_AZALEA => Ids::FLOWERING_AZALEA_LEAVES, - LeavesType::CHERRY => Ids::CHERRY_LEAVES, - }); - } - - public static function getSaplingIdentifier(SaplingType $treeType) : BID{ - return new BID(match($treeType){ - SaplingType::OAK => Ids::OAK_SAPLING, - SaplingType::SPRUCE => Ids::SPRUCE_SAPLING, - SaplingType::BIRCH => Ids::BIRCH_SAPLING, - SaplingType::JUNGLE => Ids::JUNGLE_SAPLING, - SaplingType::ACACIA => Ids::ACACIA_SAPLING, - SaplingType::DARK_OAK => Ids::DARK_OAK_SAPLING, - }); - } - - /** - * @return BID[]|\Closure[] - * @phpstan-return array{BID, BID, \Closure() : \pocketmine\item\Item} - */ - public static function getSignInfo(WoodType $treeType) : array{ - $make = fn(int $floorId, int $wallId, \Closure $getItem) => [ - new BID($floorId, TileSign::class), - new BID($wallId, TileSign::class), - $getItem - ]; - return match($treeType){ - WoodType::OAK => $make(Ids::OAK_SIGN, Ids::OAK_WALL_SIGN, fn() => VanillaItems::OAK_SIGN()), - WoodType::SPRUCE => $make(Ids::SPRUCE_SIGN, Ids::SPRUCE_WALL_SIGN, fn() => VanillaItems::SPRUCE_SIGN()), - WoodType::BIRCH => $make(Ids::BIRCH_SIGN, Ids::BIRCH_WALL_SIGN, fn() => VanillaItems::BIRCH_SIGN()), - WoodType::JUNGLE => $make(Ids::JUNGLE_SIGN, Ids::JUNGLE_WALL_SIGN, fn() => VanillaItems::JUNGLE_SIGN()), - WoodType::ACACIA => $make(Ids::ACACIA_SIGN, Ids::ACACIA_WALL_SIGN, fn() => VanillaItems::ACACIA_SIGN()), - WoodType::DARK_OAK => $make(Ids::DARK_OAK_SIGN, Ids::DARK_OAK_WALL_SIGN, fn() => VanillaItems::DARK_OAK_SIGN()), - WoodType::MANGROVE => $make(Ids::MANGROVE_SIGN, Ids::MANGROVE_WALL_SIGN, fn() => VanillaItems::MANGROVE_SIGN()), - WoodType::CRIMSON => $make(Ids::CRIMSON_SIGN, Ids::CRIMSON_WALL_SIGN, fn() => VanillaItems::CRIMSON_SIGN()), - WoodType::WARPED => $make(Ids::WARPED_SIGN, Ids::WARPED_WALL_SIGN, fn() => VanillaItems::WARPED_SIGN()), - WoodType::CHERRY => $make(Ids::CHERRY_SIGN, Ids::CHERRY_WALL_SIGN, fn() => VanillaItems::CHERRY_SIGN()), - }; - } - - public static function getTrapdoorIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_TRAPDOOR, - WoodType::SPRUCE => Ids::SPRUCE_TRAPDOOR, - WoodType::BIRCH => Ids::BIRCH_TRAPDOOR, - WoodType::JUNGLE => Ids::JUNGLE_TRAPDOOR, - WoodType::ACACIA => Ids::ACACIA_TRAPDOOR, - WoodType::DARK_OAK => Ids::DARK_OAK_TRAPDOOR, - WoodType::MANGROVE => Ids::MANGROVE_TRAPDOOR, - WoodType::CRIMSON => Ids::CRIMSON_TRAPDOOR, - WoodType::WARPED => Ids::WARPED_TRAPDOOR, - WoodType::CHERRY => Ids::CHERRY_TRAPDOOR, - }); - } - - public static function getButtonIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_BUTTON, - WoodType::SPRUCE => Ids::SPRUCE_BUTTON, - WoodType::BIRCH => Ids::BIRCH_BUTTON, - WoodType::JUNGLE => Ids::JUNGLE_BUTTON, - WoodType::ACACIA => Ids::ACACIA_BUTTON, - WoodType::DARK_OAK => Ids::DARK_OAK_BUTTON, - WoodType::MANGROVE => Ids::MANGROVE_BUTTON, - WoodType::CRIMSON => Ids::CRIMSON_BUTTON, - WoodType::WARPED => Ids::WARPED_BUTTON, - WoodType::CHERRY => Ids::CHERRY_BUTTON, - }); - } - - public static function getPressurePlateIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_PRESSURE_PLATE, - WoodType::SPRUCE => Ids::SPRUCE_PRESSURE_PLATE, - WoodType::BIRCH => Ids::BIRCH_PRESSURE_PLATE, - WoodType::JUNGLE => Ids::JUNGLE_PRESSURE_PLATE, - WoodType::ACACIA => Ids::ACACIA_PRESSURE_PLATE, - WoodType::DARK_OAK => Ids::DARK_OAK_PRESSURE_PLATE, - WoodType::MANGROVE => Ids::MANGROVE_PRESSURE_PLATE, - WoodType::CRIMSON => Ids::CRIMSON_PRESSURE_PLATE, - WoodType::WARPED => Ids::WARPED_PRESSURE_PLATE, - WoodType::CHERRY => Ids::CHERRY_PRESSURE_PLATE, - }); - } - - public static function getDoorIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_DOOR, - WoodType::SPRUCE => Ids::SPRUCE_DOOR, - WoodType::BIRCH => Ids::BIRCH_DOOR, - WoodType::JUNGLE => Ids::JUNGLE_DOOR, - WoodType::ACACIA => Ids::ACACIA_DOOR, - WoodType::DARK_OAK => Ids::DARK_OAK_DOOR, - WoodType::MANGROVE => Ids::MANGROVE_DOOR, - WoodType::CRIMSON => Ids::CRIMSON_DOOR, - WoodType::WARPED => Ids::WARPED_DOOR, - WoodType::CHERRY => Ids::CHERRY_DOOR, - }); - } - - public static function getFenceGateIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_FENCE_GATE, - WoodType::SPRUCE => Ids::SPRUCE_FENCE_GATE, - WoodType::BIRCH => Ids::BIRCH_FENCE_GATE, - WoodType::JUNGLE => Ids::JUNGLE_FENCE_GATE, - WoodType::ACACIA => Ids::ACACIA_FENCE_GATE, - WoodType::DARK_OAK => Ids::DARK_OAK_FENCE_GATE, - WoodType::MANGROVE => Ids::MANGROVE_FENCE_GATE, - WoodType::CRIMSON => Ids::CRIMSON_FENCE_GATE, - WoodType::WARPED => Ids::WARPED_FENCE_GATE, - WoodType::CHERRY => Ids::CHERRY_FENCE_GATE, - }); - } - - public static function getStairsIdentifier(WoodType $treeType) : BID{ - return new BID(match($treeType){ - WoodType::OAK => Ids::OAK_STAIRS, - WoodType::SPRUCE => Ids::SPRUCE_STAIRS, - WoodType::BIRCH => Ids::BIRCH_STAIRS, - WoodType::JUNGLE => Ids::JUNGLE_STAIRS, - WoodType::ACACIA => Ids::ACACIA_STAIRS, - WoodType::DARK_OAK => Ids::DARK_OAK_STAIRS, - WoodType::MANGROVE => Ids::MANGROVE_STAIRS, - WoodType::CRIMSON => Ids::CRIMSON_STAIRS, - WoodType::WARPED => Ids::WARPED_STAIRS, - WoodType::CHERRY => Ids::CHERRY_STAIRS, - }); - } -} diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index c5ab59447..a3366b85e 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -33,11 +33,12 @@ use pocketmine\entity\Zombie; use pocketmine\inventory\ArmorInventory; use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags; use pocketmine\item\ItemIdentifier as IID; -use pocketmine\item\ItemTypeIds as Ids; use pocketmine\item\VanillaArmorMaterials as ArmorMaterials; use pocketmine\math\Vector3; use pocketmine\utils\CloningRegistryTrait; use pocketmine\world\World; +use function is_int; +use function mb_strtoupper; use function strtolower; /** @@ -340,8 +341,29 @@ final class VanillaItems{ //NOOP } - protected static function register(string $name, Item $item) : void{ + /** + * @phpstan-template TItem of Item + * @phpstan-param \Closure(IID) : TItem $createItem + * @phpstan-return TItem + */ + protected static function register(string $name, \Closure $createItem) : Item{ + //this sketchy hack allows us to avoid manually writing the constants inline + //since type IDs are generated from this class anyway, I'm OK with this hack + //nonetheless, we should try to get rid of it in a future major version (e.g by using string type IDs) + $reflect = new \ReflectionClass(ItemTypeIds::class); + $typeId = $reflect->getConstant(mb_strtoupper($name)); + if(!is_int($typeId)){ + //this allows registering new stuff without adding new type ID constants + //this reduces the number of mandatory steps to test new features in local development + \GlobalLogger::get()->error(self::class . ": No constant type ID found for $name, generating a new one"); + $typeId = ItemTypeIds::newId(); + } + + $item = $createItem(new IID($typeId)); + self::_registryRegister($name, $item); + + return $item; } /** @@ -361,243 +383,237 @@ final class VanillaItems{ self::registerTierToolItems(); self::registerSmithingTemplates(); - self::register("air", Blocks::AIR()->asItem()->setCount(0)); + //this doesn't use the regular register() because it doesn't have an item typeID + //in the future we'll probably want to dissociate this from the air block and make a proper null item + self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0)); - self::register("acacia_sign", new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); - self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard")); - self::register("apple", new Apple(new IID(Ids::APPLE), "Apple")); - self::register("arrow", new Arrow(new IID(Ids::ARROW), "Arrow")); - self::register("baked_potato", new BakedPotato(new IID(Ids::BAKED_POTATO), "Baked Potato")); - self::register("bamboo", new Bamboo(new IID(Ids::BAMBOO), "Bamboo")); - self::register("banner", new Banner(new IID(Ids::BANNER), Blocks::BANNER(), Blocks::WALL_BANNER())); - self::register("beetroot", new Beetroot(new IID(Ids::BEETROOT), "Beetroot")); - self::register("beetroot_seeds", new BeetrootSeeds(new IID(Ids::BEETROOT_SEEDS), "Beetroot Seeds")); - self::register("beetroot_soup", new BeetrootSoup(new IID(Ids::BEETROOT_SOUP), "Beetroot Soup")); - self::register("birch_sign", new ItemBlockWallOrFloor(new IID(Ids::BIRCH_SIGN), Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); - self::register("blaze_powder", new Item(new IID(Ids::BLAZE_POWDER), "Blaze Powder")); - self::register("blaze_rod", new BlazeRod(new IID(Ids::BLAZE_ROD), "Blaze Rod")); - self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach")); - self::register("bone", new Item(new IID(Ids::BONE), "Bone")); - self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal")); - self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL])); - self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW])); - self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl")); - self::register("bread", new Bread(new IID(Ids::BREAD), "Bread")); - self::register("brick", new Item(new IID(Ids::BRICK), "Brick")); - self::register("bucket", new Bucket(new IID(Ids::BUCKET), "Bucket")); - self::register("carrot", new Carrot(new IID(Ids::CARROT), "Carrot")); - self::register("charcoal", new Coal(new IID(Ids::CHARCOAL), "Charcoal")); - self::register("cherry_sign", new ItemBlockWallOrFloor(new IID(Ids::CHERRY_SIGN), Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); - self::register("chemical_aluminium_oxide", new Item(new IID(Ids::CHEMICAL_ALUMINIUM_OXIDE), "Aluminium Oxide")); - self::register("chemical_ammonia", new Item(new IID(Ids::CHEMICAL_AMMONIA), "Ammonia")); - self::register("chemical_barium_sulphate", new Item(new IID(Ids::CHEMICAL_BARIUM_SULPHATE), "Barium Sulphate")); - self::register("chemical_benzene", new Item(new IID(Ids::CHEMICAL_BENZENE), "Benzene")); - self::register("chemical_boron_trioxide", new Item(new IID(Ids::CHEMICAL_BORON_TRIOXIDE), "Boron Trioxide")); - self::register("chemical_calcium_bromide", new Item(new IID(Ids::CHEMICAL_CALCIUM_BROMIDE), "Calcium Bromide")); - self::register("chemical_calcium_chloride", new Item(new IID(Ids::CHEMICAL_CALCIUM_CHLORIDE), "Calcium Chloride")); - self::register("chemical_cerium_chloride", new Item(new IID(Ids::CHEMICAL_CERIUM_CHLORIDE), "Cerium Chloride")); - self::register("chemical_charcoal", new Item(new IID(Ids::CHEMICAL_CHARCOAL), "Charcoal")); - self::register("chemical_crude_oil", new Item(new IID(Ids::CHEMICAL_CRUDE_OIL), "Crude Oil")); - self::register("chemical_glue", new Item(new IID(Ids::CHEMICAL_GLUE), "Glue")); - self::register("chemical_hydrogen_peroxide", new Item(new IID(Ids::CHEMICAL_HYDROGEN_PEROXIDE), "Hydrogen Peroxide")); - self::register("chemical_hypochlorite", new Item(new IID(Ids::CHEMICAL_HYPOCHLORITE), "Hypochlorite")); - self::register("chemical_ink", new Item(new IID(Ids::CHEMICAL_INK), "Ink")); - self::register("chemical_iron_sulphide", new Item(new IID(Ids::CHEMICAL_IRON_SULPHIDE), "Iron Sulphide")); - self::register("chemical_latex", new Item(new IID(Ids::CHEMICAL_LATEX), "Latex")); - self::register("chemical_lithium_hydride", new Item(new IID(Ids::CHEMICAL_LITHIUM_HYDRIDE), "Lithium Hydride")); - self::register("chemical_luminol", new Item(new IID(Ids::CHEMICAL_LUMINOL), "Luminol")); - self::register("chemical_magnesium_nitrate", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_NITRATE), "Magnesium Nitrate")); - self::register("chemical_magnesium_oxide", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_OXIDE), "Magnesium Oxide")); - self::register("chemical_magnesium_salts", new Item(new IID(Ids::CHEMICAL_MAGNESIUM_SALTS), "Magnesium Salts")); - self::register("chemical_mercuric_chloride", new Item(new IID(Ids::CHEMICAL_MERCURIC_CHLORIDE), "Mercuric Chloride")); - self::register("chemical_polyethylene", new Item(new IID(Ids::CHEMICAL_POLYETHYLENE), "Polyethylene")); - self::register("chemical_potassium_chloride", new Item(new IID(Ids::CHEMICAL_POTASSIUM_CHLORIDE), "Potassium Chloride")); - self::register("chemical_potassium_iodide", new Item(new IID(Ids::CHEMICAL_POTASSIUM_IODIDE), "Potassium Iodide")); - self::register("chemical_rubbish", new Item(new IID(Ids::CHEMICAL_RUBBISH), "Rubbish")); - self::register("chemical_salt", new Item(new IID(Ids::CHEMICAL_SALT), "Salt")); - self::register("chemical_soap", new Item(new IID(Ids::CHEMICAL_SOAP), "Soap")); - self::register("chemical_sodium_acetate", new Item(new IID(Ids::CHEMICAL_SODIUM_ACETATE), "Sodium Acetate")); - self::register("chemical_sodium_fluoride", new Item(new IID(Ids::CHEMICAL_SODIUM_FLUORIDE), "Sodium Fluoride")); - self::register("chemical_sodium_hydride", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDRIDE), "Sodium Hydride")); - self::register("chemical_sodium_hydroxide", new Item(new IID(Ids::CHEMICAL_SODIUM_HYDROXIDE), "Sodium Hydroxide")); - self::register("chemical_sodium_hypochlorite", new Item(new IID(Ids::CHEMICAL_SODIUM_HYPOCHLORITE), "Sodium Hypochlorite")); - self::register("chemical_sodium_oxide", new Item(new IID(Ids::CHEMICAL_SODIUM_OXIDE), "Sodium Oxide")); - self::register("chemical_sugar", new Item(new IID(Ids::CHEMICAL_SUGAR), "Sugar")); - self::register("chemical_sulphate", new Item(new IID(Ids::CHEMICAL_SULPHATE), "Sulphate")); - self::register("chemical_tungsten_chloride", new Item(new IID(Ids::CHEMICAL_TUNGSTEN_CHLORIDE), "Tungsten Chloride")); - self::register("chemical_water", new Item(new IID(Ids::CHEMICAL_WATER), "Water")); - self::register("chorus_fruit", new ChorusFruit(new IID(Ids::CHORUS_FRUIT), "Chorus Fruit")); - self::register("clay", new Item(new IID(Ids::CLAY), "Clay")); - self::register("clock", new Clock(new IID(Ids::CLOCK), "Clock")); - self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish")); - self::register("coal", new Coal(new IID(Ids::COAL), "Coal")); - self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans")); - self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS])); - self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken")); - self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish")); - self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton")); - self::register("cooked_porkchop", new CookedPorkchop(new IID(Ids::COOKED_PORKCHOP), "Cooked Porkchop")); - self::register("cooked_rabbit", new CookedRabbit(new IID(Ids::COOKED_RABBIT), "Cooked Rabbit")); - self::register("cooked_salmon", new CookedSalmon(new IID(Ids::COOKED_SALMON), "Cooked Salmon")); - self::register("cookie", new Cookie(new IID(Ids::COOKIE), "Cookie")); - self::register("copper_ingot", new Item(new IID(Ids::COPPER_INGOT), "Copper Ingot")); - self::register("coral_fan", new CoralFan(new IID(Ids::CORAL_FAN))); - self::register("crimson_sign", new ItemBlockWallOrFloor(new IID(Ids::CRIMSON_SIGN), Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); - self::register("dark_oak_sign", new ItemBlockWallOrFloor(new IID(Ids::DARK_OAK_SIGN), Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); - self::register("diamond", new Item(new IID(Ids::DIAMOND), "Diamond")); - self::register("disc_fragment_5", new Item(new IID(Ids::DISC_FRAGMENT_5), "Disc Fragment (5)")); - self::register("dragon_breath", new Item(new IID(Ids::DRAGON_BREATH), "Dragon's Breath")); - self::register("dried_kelp", new DriedKelp(new IID(Ids::DRIED_KELP), "Dried Kelp")); + self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); + self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard")); + self::register("apple", fn(IID $id) => new Apple($id, "Apple")); + self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow")); + self::register("baked_potato", fn(IID $id) => new BakedPotato($id, "Baked Potato")); + self::register("bamboo", fn(IID $id) => new Bamboo($id, "Bamboo")); + self::register("banner", fn(IID $id) => new Banner($id, Blocks::BANNER(), Blocks::WALL_BANNER())); + self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot")); + self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds")); + self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup")); + self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); + self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder")); + self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod")); + self::register("bleach", fn(IID $id) => new Item($id, "Bleach")); + self::register("bone", fn(IID $id) => new Item($id, "Bone")); + self::register("bone_meal", fn(IID $id) => new Fertilizer($id, "Bone Meal")); + self::register("book", fn(IID $id) => new Book($id, "Book", [EnchantmentTags::ALL])); + self::register("bow", fn(IID $id) => new Bow($id, "Bow", [EnchantmentTags::BOW])); + self::register("bowl", fn(IID $id) => new Bowl($id, "Bowl")); + self::register("bread", fn(IID $id) => new Bread($id, "Bread")); + self::register("brick", fn(IID $id) => new Item($id, "Brick")); + self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket")); + self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot")); + self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal")); + self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); + self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide")); + self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia")); + self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate")); + self::register("chemical_benzene", fn(IID $id) => new Item($id, "Benzene")); + self::register("chemical_boron_trioxide", fn(IID $id) => new Item($id, "Boron Trioxide")); + self::register("chemical_calcium_bromide", fn(IID $id) => new Item($id, "Calcium Bromide")); + self::register("chemical_calcium_chloride", fn(IID $id) => new Item($id, "Calcium Chloride")); + self::register("chemical_cerium_chloride", fn(IID $id) => new Item($id, "Cerium Chloride")); + self::register("chemical_charcoal", fn(IID $id) => new Item($id, "Charcoal")); + self::register("chemical_crude_oil", fn(IID $id) => new Item($id, "Crude Oil")); + self::register("chemical_glue", fn(IID $id) => new Item($id, "Glue")); + self::register("chemical_hydrogen_peroxide", fn(IID $id) => new Item($id, "Hydrogen Peroxide")); + self::register("chemical_hypochlorite", fn(IID $id) => new Item($id, "Hypochlorite")); + self::register("chemical_ink", fn(IID $id) => new Item($id, "Ink")); + self::register("chemical_iron_sulphide", fn(IID $id) => new Item($id, "Iron Sulphide")); + self::register("chemical_latex", fn(IID $id) => new Item($id, "Latex")); + self::register("chemical_lithium_hydride", fn(IID $id) => new Item($id, "Lithium Hydride")); + self::register("chemical_luminol", fn(IID $id) => new Item($id, "Luminol")); + self::register("chemical_magnesium_nitrate", fn(IID $id) => new Item($id, "Magnesium Nitrate")); + self::register("chemical_magnesium_oxide", fn(IID $id) => new Item($id, "Magnesium Oxide")); + self::register("chemical_magnesium_salts", fn(IID $id) => new Item($id, "Magnesium Salts")); + self::register("chemical_mercuric_chloride", fn(IID $id) => new Item($id, "Mercuric Chloride")); + self::register("chemical_polyethylene", fn(IID $id) => new Item($id, "Polyethylene")); + self::register("chemical_potassium_chloride", fn(IID $id) => new Item($id, "Potassium Chloride")); + self::register("chemical_potassium_iodide", fn(IID $id) => new Item($id, "Potassium Iodide")); + self::register("chemical_rubbish", fn(IID $id) => new Item($id, "Rubbish")); + self::register("chemical_salt", fn(IID $id) => new Item($id, "Salt")); + self::register("chemical_soap", fn(IID $id) => new Item($id, "Soap")); + self::register("chemical_sodium_acetate", fn(IID $id) => new Item($id, "Sodium Acetate")); + self::register("chemical_sodium_fluoride", fn(IID $id) => new Item($id, "Sodium Fluoride")); + self::register("chemical_sodium_hydride", fn(IID $id) => new Item($id, "Sodium Hydride")); + self::register("chemical_sodium_hydroxide", fn(IID $id) => new Item($id, "Sodium Hydroxide")); + self::register("chemical_sodium_hypochlorite", fn(IID $id) => new Item($id, "Sodium Hypochlorite")); + self::register("chemical_sodium_oxide", fn(IID $id) => new Item($id, "Sodium Oxide")); + self::register("chemical_sugar", fn(IID $id) => new Item($id, "Sugar")); + self::register("chemical_sulphate", fn(IID $id) => new Item($id, "Sulphate")); + self::register("chemical_tungsten_chloride", fn(IID $id) => new Item($id, "Tungsten Chloride")); + self::register("chemical_water", fn(IID $id) => new Item($id, "Water")); + self::register("chorus_fruit", fn(IID $id) => new ChorusFruit($id, "Chorus Fruit")); + self::register("clay", fn(IID $id) => new Item($id, "Clay")); + self::register("clock", fn(IID $id) => new Clock($id, "Clock")); + self::register("clownfish", fn(IID $id) => new Clownfish($id, "Clownfish")); + self::register("coal", fn(IID $id) => new Coal($id, "Coal")); + self::register("cocoa_beans", fn(IID $id) => new CocoaBeans($id, "Cocoa Beans")); + self::register("compass", fn(IID $id) => new Compass($id, "Compass", [EnchantmentTags::COMPASS])); + self::register("cooked_chicken", fn(IID $id) => new CookedChicken($id, "Cooked Chicken")); + self::register("cooked_fish", fn(IID $id) => new CookedFish($id, "Cooked Fish")); + self::register("cooked_mutton", fn(IID $id) => new CookedMutton($id, "Cooked Mutton")); + self::register("cooked_porkchop", fn(IID $id) => new CookedPorkchop($id, "Cooked Porkchop")); + self::register("cooked_rabbit", fn(IID $id) => new CookedRabbit($id, "Cooked Rabbit")); + self::register("cooked_salmon", fn(IID $id) => new CookedSalmon($id, "Cooked Salmon")); + self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie")); + self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot")); + self::register("coral_fan", fn(IID $id) => new CoralFan($id)); + self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); + self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); + self::register("diamond", fn(IID $id) => new Item($id, "Diamond")); + self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)")); + self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath")); + self::register("dried_kelp", fn(IID $id) => new DriedKelp($id, "Dried Kelp")); //TODO: add interface to dye-colour objects - self::register("dye", new Dye(new IID(Ids::DYE), "Dye")); - self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard")); - self::register("egg", new Egg(new IID(Ids::EGG), "Egg")); - self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald")); - self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book", [EnchantmentTags::ALL])); - self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple")); - self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl")); - self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting")); - self::register("feather", new Item(new IID(Ids::FEATHER), "Feather")); - self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye")); - self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge")); - self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD])); - self::register("flint", new Item(new IID(Ids::FLINT), "Flint")); - self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); - self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear")); - self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle")); - self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon")); - self::register("glow_berries", new GlowBerries(new IID(Ids::GLOW_BERRIES), "Glow Berries")); - self::register("glow_ink_sac", new Item(new IID(Ids::GLOW_INK_SAC), "Glow Ink Sac")); - self::register("glowstone_dust", new Item(new IID(Ids::GLOWSTONE_DUST), "Glowstone Dust")); - self::register("goat_horn", new GoatHorn(new IID(Ids::GOAT_HORN), "Goat Horn")); - self::register("gold_ingot", new Item(new IID(Ids::GOLD_INGOT), "Gold Ingot")); - self::register("gold_nugget", new Item(new IID(Ids::GOLD_NUGGET), "Gold Nugget")); - self::register("golden_apple", new GoldenApple(new IID(Ids::GOLDEN_APPLE), "Golden Apple")); - self::register("golden_carrot", new GoldenCarrot(new IID(Ids::GOLDEN_CARROT), "Golden Carrot")); - self::register("gunpowder", new Item(new IID(Ids::GUNPOWDER), "Gunpowder")); - self::register("heart_of_the_sea", new Item(new IID(Ids::HEART_OF_THE_SEA), "Heart of the Sea")); - self::register("honey_bottle", new HoneyBottle(new IID(Ids::HONEY_BOTTLE), "Honey Bottle")); - self::register("honeycomb", new Item(new IID(Ids::HONEYCOMB), "Honeycomb")); - self::register("ink_sac", new Item(new IID(Ids::INK_SAC), "Ink Sac")); - self::register("iron_ingot", new Item(new IID(Ids::IRON_INGOT), "Iron Ingot")); - self::register("iron_nugget", new Item(new IID(Ids::IRON_NUGGET), "Iron Nugget")); - self::register("jungle_sign", new ItemBlockWallOrFloor(new IID(Ids::JUNGLE_SIGN), Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); - self::register("lapis_lazuli", new Item(new IID(Ids::LAPIS_LAZULI), "Lapis Lazuli")); - self::register("lava_bucket", new LiquidBucket(new IID(Ids::LAVA_BUCKET), "Lava Bucket", Blocks::LAVA())); - self::register("leather", new Item(new IID(Ids::LEATHER), "Leather")); - self::register("magma_cream", new Item(new IID(Ids::MAGMA_CREAM), "Magma Cream")); - self::register("mangrove_sign", new ItemBlockWallOrFloor(new IID(Ids::MANGROVE_SIGN), Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); - self::register("medicine", new Medicine(new IID(Ids::MEDICINE), "Medicine")); - self::register("melon", new Melon(new IID(Ids::MELON), "Melon")); - self::register("melon_seeds", new MelonSeeds(new IID(Ids::MELON_SEEDS), "Melon Seeds")); - self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket")); - self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart")); - self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew")); - self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag")); - self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell")); - self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick")); - self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz")); - self::register("nether_star", new Item(new IID(Ids::NETHER_STAR), "Nether Star")); - self::register("netherite_ingot", new class(new IID(Ids::NETHERITE_INGOT), "Netherite Ingot") extends Item{ + self::register("dye", fn(IID $id) => new Dye($id, "Dye")); + self::register("echo_shard", fn(IID $id) => new Item($id, "Echo Shard")); + self::register("egg", fn(IID $id) => new Egg($id, "Egg")); + self::register("emerald", fn(IID $id) => new Item($id, "Emerald")); + self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL])); + self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple")); + self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl")); + self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting")); + self::register("feather", fn(IID $id) => new Item($id, "Feather")); + self::register("fermented_spider_eye", fn(IID $id) => new Item($id, "Fermented Spider Eye")); + self::register("fire_charge", fn(IID $id) => new FireCharge($id, "Fire Charge")); + self::register("fishing_rod", fn(IID $id) => new FishingRod($id, "Fishing Rod", [EnchantmentTags::FISHING_ROD])); + self::register("flint", fn(IID $id) => new Item($id, "Flint")); + self::register("flint_and_steel", fn(IID $id) => new FlintSteel($id, "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL])); + self::register("ghast_tear", fn(IID $id) => new Item($id, "Ghast Tear")); + self::register("glass_bottle", fn(IID $id) => new GlassBottle($id, "Glass Bottle")); + self::register("glistering_melon", fn(IID $id) => new Item($id, "Glistering Melon")); + self::register("glow_berries", fn(IID $id) => new GlowBerries($id, "Glow Berries")); + self::register("glow_ink_sac", fn(IID $id) => new Item($id, "Glow Ink Sac")); + self::register("glowstone_dust", fn(IID $id) => new Item($id, "Glowstone Dust")); + self::register("goat_horn", fn(IID $id) => new GoatHorn($id, "Goat Horn")); + self::register("gold_ingot", fn(IID $id) => new Item($id, "Gold Ingot")); + self::register("gold_nugget", fn(IID $id) => new Item($id, "Gold Nugget")); + self::register("golden_apple", fn(IID $id) => new GoldenApple($id, "Golden Apple")); + self::register("golden_carrot", fn(IID $id) => new GoldenCarrot($id, "Golden Carrot")); + self::register("gunpowder", fn(IID $id) => new Item($id, "Gunpowder")); + self::register("heart_of_the_sea", fn(IID $id) => new Item($id, "Heart of the Sea")); + self::register("honey_bottle", fn(IID $id) => new HoneyBottle($id, "Honey Bottle")); + self::register("honeycomb", fn(IID $id) => new Item($id, "Honeycomb")); + self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac")); + self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot")); + self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget")); + self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); + self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); + self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); + self::register("leather", fn(IID $id) => new Item($id, "Leather")); + self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); + self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); + self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); + self::register("melon", fn(IID $id) => new Melon($id, "Melon")); + self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds")); + self::register("milk_bucket", fn(IID $id) => new MilkBucket($id, "Milk Bucket")); + self::register("minecart", fn(IID $id) => new Minecart($id, "Minecart")); + self::register("mushroom_stew", fn(IID $id) => new MushroomStew($id, "Mushroom Stew")); + self::register("name_tag", fn(IID $id) => new NameTag($id, "Name Tag")); + self::register("nautilus_shell", fn(IID $id) => new Item($id, "Nautilus Shell")); + self::register("nether_brick", fn(IID $id) => new Item($id, "Nether Brick")); + self::register("nether_quartz", fn(IID $id) => new Item($id, "Nether Quartz")); + self::register("nether_star", fn(IID $id) => new Item($id, "Nether Star")); + self::register("netherite_ingot", fn(IID $id) => new class($id, "Netherite Ingot") extends Item{ public function isFireProof() : bool{ return true; } }); - self::register("netherite_scrap", new class(new IID(Ids::NETHERITE_SCRAP), "Netherite Scrap") extends Item{ + self::register("netherite_scrap", fn(IID $id) => new class($id, "Netherite Scrap") extends Item{ public function isFireProof() : bool{ return true; } }); - self::register("oak_sign", new ItemBlockWallOrFloor(new IID(Ids::OAK_SIGN), Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); - self::register("painting", new PaintingItem(new IID(Ids::PAINTING), "Painting")); - self::register("paper", new Item(new IID(Ids::PAPER), "Paper")); - self::register("phantom_membrane", new Item(new IID(Ids::PHANTOM_MEMBRANE), "Phantom Membrane")); - self::register("pitcher_pod", new PitcherPod(new IID(Ids::PITCHER_POD), "Pitcher Pod")); - self::register("poisonous_potato", new PoisonousPotato(new IID(Ids::POISONOUS_POTATO), "Poisonous Potato")); - self::register("popped_chorus_fruit", new Item(new IID(Ids::POPPED_CHORUS_FRUIT), "Popped Chorus Fruit")); - self::register("potato", new Potato(new IID(Ids::POTATO), "Potato")); - self::register("potion", new Potion(new IID(Ids::POTION), "Potion")); - self::register("prismarine_crystals", new Item(new IID(Ids::PRISMARINE_CRYSTALS), "Prismarine Crystals")); - self::register("prismarine_shard", new Item(new IID(Ids::PRISMARINE_SHARD), "Prismarine Shard")); - self::register("pufferfish", new Pufferfish(new IID(Ids::PUFFERFISH), "Pufferfish")); - self::register("pumpkin_pie", new PumpkinPie(new IID(Ids::PUMPKIN_PIE), "Pumpkin Pie")); - self::register("pumpkin_seeds", new PumpkinSeeds(new IID(Ids::PUMPKIN_SEEDS), "Pumpkin Seeds")); - self::register("rabbit_foot", new Item(new IID(Ids::RABBIT_FOOT), "Rabbit's Foot")); - self::register("rabbit_hide", new Item(new IID(Ids::RABBIT_HIDE), "Rabbit Hide")); - self::register("rabbit_stew", new RabbitStew(new IID(Ids::RABBIT_STEW), "Rabbit Stew")); - self::register("raw_beef", new RawBeef(new IID(Ids::RAW_BEEF), "Raw Beef")); - self::register("raw_chicken", new RawChicken(new IID(Ids::RAW_CHICKEN), "Raw Chicken")); - self::register("raw_copper", new Item(new IID(Ids::RAW_COPPER), "Raw Copper")); - self::register("raw_fish", new RawFish(new IID(Ids::RAW_FISH), "Raw Fish")); - self::register("raw_gold", new Item(new IID(Ids::RAW_GOLD), "Raw Gold")); - self::register("raw_iron", new Item(new IID(Ids::RAW_IRON), "Raw Iron")); - self::register("raw_mutton", new RawMutton(new IID(Ids::RAW_MUTTON), "Raw Mutton")); - self::register("raw_porkchop", new RawPorkchop(new IID(Ids::RAW_PORKCHOP), "Raw Porkchop")); - self::register("raw_rabbit", new RawRabbit(new IID(Ids::RAW_RABBIT), "Raw Rabbit")); - self::register("raw_salmon", new RawSalmon(new IID(Ids::RAW_SALMON), "Raw Salmon")); - self::register("record_11", new Record(new IID(Ids::RECORD_11), RecordType::DISK_11, "Record 11")); - self::register("record_13", new Record(new IID(Ids::RECORD_13), RecordType::DISK_13, "Record 13")); - self::register("record_5", new Record(new IID(Ids::RECORD_5), RecordType::DISK_5, "Record 5")); - self::register("record_blocks", new Record(new IID(Ids::RECORD_BLOCKS), RecordType::DISK_BLOCKS, "Record Blocks")); - self::register("record_cat", new Record(new IID(Ids::RECORD_CAT), RecordType::DISK_CAT, "Record Cat")); - self::register("record_chirp", new Record(new IID(Ids::RECORD_CHIRP), RecordType::DISK_CHIRP, "Record Chirp")); - self::register("record_far", new Record(new IID(Ids::RECORD_FAR), RecordType::DISK_FAR, "Record Far")); - self::register("record_mall", new Record(new IID(Ids::RECORD_MALL), RecordType::DISK_MALL, "Record Mall")); - self::register("record_mellohi", new Record(new IID(Ids::RECORD_MELLOHI), RecordType::DISK_MELLOHI, "Record Mellohi")); - self::register("record_otherside", new Record(new IID(Ids::RECORD_OTHERSIDE), RecordType::DISK_OTHERSIDE, "Record Otherside")); - self::register("record_pigstep", new Record(new IID(Ids::RECORD_PIGSTEP), RecordType::DISK_PIGSTEP, "Record Pigstep")); - self::register("record_stal", new Record(new IID(Ids::RECORD_STAL), RecordType::DISK_STAL, "Record Stal")); - self::register("record_strad", new Record(new IID(Ids::RECORD_STRAD), RecordType::DISK_STRAD, "Record Strad")); - self::register("record_wait", new Record(new IID(Ids::RECORD_WAIT), RecordType::DISK_WAIT, "Record Wait")); - self::register("record_ward", new Record(new IID(Ids::RECORD_WARD), RecordType::DISK_WARD, "Record Ward")); - self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone")); - self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh")); - self::register("scute", new Item(new IID(Ids::SCUTE), "Scute")); - self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS])); - self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell")); - self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball")); - self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball")); - self::register("spider_eye", new SpiderEye(new IID(Ids::SPIDER_EYE), "Spider Eye")); - self::register("splash_potion", new SplashPotion(new IID(Ids::SPLASH_POTION), "Splash Potion")); - self::register("spruce_sign", new ItemBlockWallOrFloor(new IID(Ids::SPRUCE_SIGN), Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); - self::register("spyglass", new Spyglass(new IID(Ids::SPYGLASS), "Spyglass")); - self::register("steak", new Steak(new IID(Ids::STEAK), "Steak")); - self::register("stick", new Stick(new IID(Ids::STICK), "Stick")); - self::register("string", new StringItem(new IID(Ids::STRING), "String")); - self::register("sugar", new Item(new IID(Ids::SUGAR), "Sugar")); - self::register("suspicious_stew", new SuspiciousStew(new IID(Ids::SUSPICIOUS_STEW), "Suspicious Stew")); - self::register("sweet_berries", new SweetBerries(new IID(Ids::SWEET_BERRIES), "Sweet Berries")); - self::register("torchflower_seeds", new TorchflowerSeeds(new IID(Ids::TORCHFLOWER_SEEDS), "Torchflower Seeds")); - self::register("totem", new Totem(new IID(Ids::TOTEM), "Totem of Undying")); - self::register("warped_sign", new ItemBlockWallOrFloor(new IID(Ids::WARPED_SIGN), Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); - self::register("water_bucket", new LiquidBucket(new IID(Ids::WATER_BUCKET), "Water Bucket", Blocks::WATER())); - self::register("wheat", new Item(new IID(Ids::WHEAT), "Wheat")); - self::register("wheat_seeds", new WheatSeeds(new IID(Ids::WHEAT_SEEDS), "Wheat Seeds")); - self::register("writable_book", new WritableBook(new IID(Ids::WRITABLE_BOOK), "Book & Quill")); - self::register("written_book", new WrittenBook(new IID(Ids::WRITTEN_BOOK), "Written Book")); + self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); + self::register("paper", fn(IID $id) => new Item($id, "Paper")); + self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane")); + self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod")); + self::register("poisonous_potato", fn(IID $id) => new PoisonousPotato($id, "Poisonous Potato")); + self::register("popped_chorus_fruit", fn(IID $id) => new Item($id, "Popped Chorus Fruit")); + self::register("potato", fn(IID $id) => new Potato($id, "Potato")); + self::register("potion", fn(IID $id) => new Potion($id, "Potion")); + self::register("prismarine_crystals", fn(IID $id) => new Item($id, "Prismarine Crystals")); + self::register("prismarine_shard", fn(IID $id) => new Item($id, "Prismarine Shard")); + self::register("pufferfish", fn(IID $id) => new Pufferfish($id, "Pufferfish")); + self::register("pumpkin_pie", fn(IID $id) => new PumpkinPie($id, "Pumpkin Pie")); + self::register("pumpkin_seeds", fn(IID $id) => new PumpkinSeeds($id, "Pumpkin Seeds")); + self::register("rabbit_foot", fn(IID $id) => new Item($id, "Rabbit's Foot")); + self::register("rabbit_hide", fn(IID $id) => new Item($id, "Rabbit Hide")); + self::register("rabbit_stew", fn(IID $id) => new RabbitStew($id, "Rabbit Stew")); + self::register("raw_beef", fn(IID $id) => new RawBeef($id, "Raw Beef")); + self::register("raw_chicken", fn(IID $id) => new RawChicken($id, "Raw Chicken")); + self::register("raw_copper", fn(IID $id) => new Item($id, "Raw Copper")); + self::register("raw_fish", fn(IID $id) => new RawFish($id, "Raw Fish")); + self::register("raw_gold", fn(IID $id) => new Item($id, "Raw Gold")); + self::register("raw_iron", fn(IID $id) => new Item($id, "Raw Iron")); + self::register("raw_mutton", fn(IID $id) => new RawMutton($id, "Raw Mutton")); + self::register("raw_porkchop", fn(IID $id) => new RawPorkchop($id, "Raw Porkchop")); + self::register("raw_rabbit", fn(IID $id) => new RawRabbit($id, "Raw Rabbit")); + self::register("raw_salmon", fn(IID $id) => new RawSalmon($id, "Raw Salmon")); + self::register("record_11", fn(IID $id) => new Record($id, RecordType::DISK_11, "Record 11")); + self::register("record_13", fn(IID $id) => new Record($id, RecordType::DISK_13, "Record 13")); + self::register("record_5", fn(IID $id) => new Record($id, RecordType::DISK_5, "Record 5")); + self::register("record_blocks", fn(IID $id) => new Record($id, RecordType::DISK_BLOCKS, "Record Blocks")); + self::register("record_cat", fn(IID $id) => new Record($id, RecordType::DISK_CAT, "Record Cat")); + self::register("record_chirp", fn(IID $id) => new Record($id, RecordType::DISK_CHIRP, "Record Chirp")); + self::register("record_far", fn(IID $id) => new Record($id, RecordType::DISK_FAR, "Record Far")); + self::register("record_mall", fn(IID $id) => new Record($id, RecordType::DISK_MALL, "Record Mall")); + self::register("record_mellohi", fn(IID $id) => new Record($id, RecordType::DISK_MELLOHI, "Record Mellohi")); + self::register("record_otherside", fn(IID $id) => new Record($id, RecordType::DISK_OTHERSIDE, "Record Otherside")); + self::register("record_pigstep", fn(IID $id) => new Record($id, RecordType::DISK_PIGSTEP, "Record Pigstep")); + self::register("record_stal", fn(IID $id) => new Record($id, RecordType::DISK_STAL, "Record Stal")); + self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad")); + self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait")); + self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward")); + self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone")); + self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh")); + self::register("scute", fn(IID $id) => new Item($id, "Scute")); + self::register("shears", fn(IID $id) => new Shears($id, "Shears", [EnchantmentTags::SHEARS])); + self::register("shulker_shell", fn(IID $id) => new Item($id, "Shulker Shell")); + self::register("slimeball", fn(IID $id) => new Item($id, "Slimeball")); + self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball")); + self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye")); + self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion")); + self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); + self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass")); + self::register("steak", fn(IID $id) => new Steak($id, "Steak")); + self::register("stick", fn(IID $id) => new Stick($id, "Stick")); + self::register("string", fn(IID $id) => new StringItem($id, "String")); + self::register("sugar", fn(IID $id) => new Item($id, "Sugar")); + self::register("suspicious_stew", fn(IID $id) => new SuspiciousStew($id, "Suspicious Stew")); + self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries")); + self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds")); + self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying")); + self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); + self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER())); + self::register("wheat", fn(IID $id) => new Item($id, "Wheat")); + self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds")); + self::register("writable_book", fn(IID $id) => new WritableBook($id, "Book & Quill")); + self::register("written_book", fn(IID $id) => new WrittenBook($id, "Written Book")); foreach(BoatType::cases() as $type){ //boat type is static, because different types of wood may have different properties - self::register(strtolower($type->name) . "_boat", new Boat(new IID(match($type){ - BoatType::OAK => Ids::OAK_BOAT, - BoatType::SPRUCE => Ids::SPRUCE_BOAT, - BoatType::BIRCH => Ids::BIRCH_BOAT, - BoatType::JUNGLE => Ids::JUNGLE_BOAT, - BoatType::ACACIA => Ids::ACACIA_BOAT, - BoatType::DARK_OAK => Ids::DARK_OAK_BOAT, - BoatType::MANGROVE => Ids::MANGROVE_BOAT, - }), $type->getDisplayName() . " Boat", $type)); + self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $type)); } } private static function registerSpawnEggs() : void{ - self::register("zombie_spawn_egg", new class(new IID(Ids::ZOMBIE_SPAWN_EGG), "Zombie Spawn Egg") extends SpawnEgg{ + self::register("zombie_spawn_egg", fn(IID $id) => new class($id, "Zombie Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch)); } }); - self::register("squid_spawn_egg", new class(new IID(Ids::SQUID_SPAWN_EGG), "Squid Spawn Egg") extends SpawnEgg{ + self::register("squid_spawn_egg", fn(IID $id) => new class($id, "Squid Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Squid(Location::fromObject($pos, $world, $yaw, $pitch)); } }); - self::register("villager_spawn_egg", new class(new IID(Ids::VILLAGER_SPAWN_EGG), "Villager Spawn Egg") extends SpawnEgg{ + self::register("villager_spawn_egg", fn(IID $id) => new class($id, "Villager Spawn Egg") extends SpawnEgg{ protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{ return new Villager(Location::fromObject($pos, $world, $yaw, $pitch)); } @@ -605,87 +621,87 @@ final class VanillaItems{ } private static function registerTierToolItems() : void{ - self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE])); - self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE])); - self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE])); - self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE])); - self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE])); - self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE])); - self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE])); - self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE])); - self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE])); - self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE])); - self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE])); - self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE])); - self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE])); - self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE])); - self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE])); - self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE])); - self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE])); - self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE])); - self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL])); - self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL])); - self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL])); - self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL])); - self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL])); - self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL])); - self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD])); - self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD])); - self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD])); - self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD])); - self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD])); - self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD])); + self::register("diamond_axe", fn(IID $id) => new Axe($id, "Diamond Axe", ToolTier::DIAMOND, [EnchantmentTags::AXE])); + self::register("golden_axe", fn(IID $id) => new Axe($id, "Golden Axe", ToolTier::GOLD, [EnchantmentTags::AXE])); + self::register("iron_axe", fn(IID $id) => new Axe($id, "Iron Axe", ToolTier::IRON, [EnchantmentTags::AXE])); + self::register("netherite_axe", fn(IID $id) => new Axe($id, "Netherite Axe", ToolTier::NETHERITE, [EnchantmentTags::AXE])); + self::register("stone_axe", fn(IID $id) => new Axe($id, "Stone Axe", ToolTier::STONE, [EnchantmentTags::AXE])); + self::register("wooden_axe", fn(IID $id) => new Axe($id, "Wooden Axe", ToolTier::WOOD, [EnchantmentTags::AXE])); + self::register("diamond_hoe", fn(IID $id) => new Hoe($id, "Diamond Hoe", ToolTier::DIAMOND, [EnchantmentTags::HOE])); + self::register("golden_hoe", fn(IID $id) => new Hoe($id, "Golden Hoe", ToolTier::GOLD, [EnchantmentTags::HOE])); + self::register("iron_hoe", fn(IID $id) => new Hoe($id, "Iron Hoe", ToolTier::IRON, [EnchantmentTags::HOE])); + self::register("netherite_hoe", fn(IID $id) => new Hoe($id, "Netherite Hoe", ToolTier::NETHERITE, [EnchantmentTags::HOE])); + self::register("stone_hoe", fn(IID $id) => new Hoe($id, "Stone Hoe", ToolTier::STONE, [EnchantmentTags::HOE])); + self::register("wooden_hoe", fn(IID $id) => new Hoe($id, "Wooden Hoe", ToolTier::WOOD, [EnchantmentTags::HOE])); + self::register("diamond_pickaxe", fn(IID $id) => new Pickaxe($id, "Diamond Pickaxe", ToolTier::DIAMOND, [EnchantmentTags::PICKAXE])); + self::register("golden_pickaxe", fn(IID $id) => new Pickaxe($id, "Golden Pickaxe", ToolTier::GOLD, [EnchantmentTags::PICKAXE])); + self::register("iron_pickaxe", fn(IID $id) => new Pickaxe($id, "Iron Pickaxe", ToolTier::IRON, [EnchantmentTags::PICKAXE])); + self::register("netherite_pickaxe", fn(IID $id) => new Pickaxe($id, "Netherite Pickaxe", ToolTier::NETHERITE, [EnchantmentTags::PICKAXE])); + self::register("stone_pickaxe", fn(IID $id) => new Pickaxe($id, "Stone Pickaxe", ToolTier::STONE, [EnchantmentTags::PICKAXE])); + self::register("wooden_pickaxe", fn(IID $id) => new Pickaxe($id, "Wooden Pickaxe", ToolTier::WOOD, [EnchantmentTags::PICKAXE])); + self::register("diamond_shovel", fn(IID $id) => new Shovel($id, "Diamond Shovel", ToolTier::DIAMOND, [EnchantmentTags::SHOVEL])); + self::register("golden_shovel", fn(IID $id) => new Shovel($id, "Golden Shovel", ToolTier::GOLD, [EnchantmentTags::SHOVEL])); + self::register("iron_shovel", fn(IID $id) => new Shovel($id, "Iron Shovel", ToolTier::IRON, [EnchantmentTags::SHOVEL])); + self::register("netherite_shovel", fn(IID $id) => new Shovel($id, "Netherite Shovel", ToolTier::NETHERITE, [EnchantmentTags::SHOVEL])); + self::register("stone_shovel", fn(IID $id) => new Shovel($id, "Stone Shovel", ToolTier::STONE, [EnchantmentTags::SHOVEL])); + self::register("wooden_shovel", fn(IID $id) => new Shovel($id, "Wooden Shovel", ToolTier::WOOD, [EnchantmentTags::SHOVEL])); + self::register("diamond_sword", fn(IID $id) => new Sword($id, "Diamond Sword", ToolTier::DIAMOND, [EnchantmentTags::SWORD])); + self::register("golden_sword", fn(IID $id) => new Sword($id, "Golden Sword", ToolTier::GOLD, [EnchantmentTags::SWORD])); + self::register("iron_sword", fn(IID $id) => new Sword($id, "Iron Sword", ToolTier::IRON, [EnchantmentTags::SWORD])); + self::register("netherite_sword", fn(IID $id) => new Sword($id, "Netherite Sword", ToolTier::NETHERITE, [EnchantmentTags::SWORD])); + self::register("stone_sword", fn(IID $id) => new Sword($id, "Stone Sword", ToolTier::STONE, [EnchantmentTags::SWORD])); + self::register("wooden_sword", fn(IID $id) => new Sword($id, "Wooden Sword", ToolTier::WOOD, [EnchantmentTags::SWORD])); } private static function registerArmorItems() : void{ - self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS])); - self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS])); - self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS])); - self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS])); - self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS])); - self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS])); + self::register("chainmail_boots", fn(IID $id) => new Armor($id, "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS])); + self::register("diamond_boots", fn(IID $id) => new Armor($id, "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS])); + self::register("golden_boots", fn(IID $id) => new Armor($id, "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS])); + self::register("iron_boots", fn(IID $id) => new Armor($id, "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS])); + self::register("leather_boots", fn(IID $id) => new Armor($id, "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS])); + self::register("netherite_boots", fn(IID $id) => new Armor($id, "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS])); - self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE])); - self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE])); - self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE])); - self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE])); - self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE])); - self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE])); + self::register("chainmail_chestplate", fn(IID $id) => new Armor($id, "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE])); + self::register("diamond_chestplate", fn(IID $id) => new Armor($id, "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE])); + self::register("golden_chestplate", fn(IID $id) => new Armor($id, "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE])); + self::register("iron_chestplate", fn(IID $id) => new Armor($id, "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE])); + self::register("leather_tunic", fn(IID $id) => new Armor($id, "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE])); + self::register("netherite_chestplate", fn(IID $id) => new Armor($id, "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE])); - self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET])); - self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET])); - self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET])); - self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET])); - self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET])); - self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET])); - self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET])); + self::register("chainmail_helmet", fn(IID $id) => new Armor($id, "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET])); + self::register("diamond_helmet", fn(IID $id) => new Armor($id, "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET])); + self::register("golden_helmet", fn(IID $id) => new Armor($id, "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET])); + self::register("iron_helmet", fn(IID $id) => new Armor($id, "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET])); + self::register("leather_cap", fn(IID $id) => new Armor($id, "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET])); + self::register("netherite_helmet", fn(IID $id) => new Armor($id, "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET])); + self::register("turtle_helmet", fn(IID $id) => new TurtleHelmet($id, "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET])); - self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS])); - self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS])); - self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS])); - self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS])); - self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS])); - self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS])); + self::register("chainmail_leggings", fn(IID $id) => new Armor($id, "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS])); + self::register("diamond_leggings", fn(IID $id) => new Armor($id, "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS])); + self::register("golden_leggings", fn(IID $id) => new Armor($id, "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS])); + self::register("iron_leggings", fn(IID $id) => new Armor($id, "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS])); + self::register("leather_pants", fn(IID $id) => new Armor($id, "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS])); + self::register("netherite_leggings", fn(IID $id) => new Armor($id, "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS])); } private static function registerSmithingTemplates() : void{ - self::register("netherite_upgrade_smithing_template", new Item(new IID(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE), "Netherite Upgrade Smithing Template")); - self::register("coast_armor_trim_smithing_template", new Item(new IID(Ids::COAST_ARMOR_TRIM_SMITHING_TEMPLATE), "Coast Armor Trim Smithing Template")); - self::register("dune_armor_trim_smithing_template", new Item(new IID(Ids::DUNE_ARMOR_TRIM_SMITHING_TEMPLATE), "Dune Armor Trim Smithing Template")); - self::register("eye_armor_trim_smithing_template", new Item(new IID(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE), "Eye Armor Trim Smithing Template")); - self::register("host_armor_trim_smithing_template", new Item(new IID(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE), "Host Armor Trim Smithing Template")); - self::register("raiser_armor_trim_smithing_template", new Item(new IID(Ids::RAISER_ARMOR_TRIM_SMITHING_TEMPLATE), "Raiser Armor Trim Smithing Template")); - self::register("rib_armor_trim_smithing_template", new Item(new IID(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE), "Rib Armor Trim Smithing Template")); - self::register("sentry_armor_trim_smithing_template", new Item(new IID(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE), "Sentry Armor Trim Smithing Template")); - self::register("shaper_armor_trim_smithing_template", new Item(new IID(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE), "Shaper Armor Trim Smithing Template")); - self::register("silence_armor_trim_smithing_template", new Item(new IID(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE), "Silence Armor Trim Smithing Template")); - self::register("snout_armor_trim_smithing_template", new Item(new IID(Ids::SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE), "Snout Armor Trim Smithing Template")); - self::register("spire_armor_trim_smithing_template", new Item(new IID(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE), "Spire Armor Trim Smithing Template")); - self::register("tide_armor_trim_smithing_template", new Item(new IID(Ids::TIDE_ARMOR_TRIM_SMITHING_TEMPLATE), "Tide Armor Trim Smithing Template")); - self::register("vex_armor_trim_smithing_template", new Item(new IID(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE), "Vex Armor Trim Smithing Template")); - self::register("ward_armor_trim_smithing_template", new Item(new IID(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE), "Ward Armor Trim Smithing Template")); - self::register("wayfinder_armor_trim_smithing_template", new Item(new IID(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE), "Wayfinder Armor Trim Smithing Template")); - self::register("wild_armor_trim_smithing_template", new Item(new IID(Ids::WILD_ARMOR_TRIM_SMITHING_TEMPLATE), "Wild Armor Trim Smithing Template")); + self::register("netherite_upgrade_smithing_template", fn(IID $id) => new Item($id, "Netherite Upgrade Smithing Template")); + self::register("coast_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Coast Armor Trim Smithing Template")); + self::register("dune_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Dune Armor Trim Smithing Template")); + self::register("eye_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Eye Armor Trim Smithing Template")); + self::register("host_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Host Armor Trim Smithing Template")); + self::register("raiser_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Raiser Armor Trim Smithing Template")); + self::register("rib_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Rib Armor Trim Smithing Template")); + self::register("sentry_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Sentry Armor Trim Smithing Template")); + self::register("shaper_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Shaper Armor Trim Smithing Template")); + self::register("silence_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Silence Armor Trim Smithing Template")); + self::register("snout_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Snout Armor Trim Smithing Template")); + self::register("spire_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Spire Armor Trim Smithing Template")); + self::register("tide_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Tide Armor Trim Smithing Template")); + self::register("vex_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Vex Armor Trim Smithing Template")); + self::register("ward_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Ward Armor Trim Smithing Template")); + self::register("wayfinder_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wayfinder Armor Trim Smithing Template")); + self::register("wild_armor_trim_smithing_template", fn(IID $id) => new Item($id, "Wild Armor Trim Smithing Template")); } } diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 0fc3defda..e9de04a39 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -20,6 +20,56 @@ parameters: count: 1 path: ../../../src/block/DoubleTallGrass.php + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:ACACIA_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:BIRCH_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CHERRY_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:CRIMSON_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:DARK_OAK_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:JUNGLE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:MANGROVE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:OAK_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:WARPED_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: "#^Call to function assert\\(\\) with false and 'unknown hit type' will always evaluate to false\\.$#" count: 1 diff --git a/tests/phpunit/block/BlockTypeIdsTest.php b/tests/phpunit/block/BlockTypeIdsTest.php index 32c8d4902..ce21a89ab 100644 --- a/tests/phpunit/block/BlockTypeIdsTest.php +++ b/tests/phpunit/block/BlockTypeIdsTest.php @@ -51,6 +51,7 @@ class BlockTypeIdsTest extends TestCase{ foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $block){ $expected = $block->getTypeId(); $actual = $reflect->getConstant($name); + self::assertNotFalse($actual, "VanillaBlocks::$name() does not have a BlockTypeIds constant"); self::assertSame($expected, $actual, "VanillaBlocks::$name() does not match BlockTypeIds::$name"); } } diff --git a/tests/phpunit/item/ItemTypeIdsTest.php b/tests/phpunit/item/ItemTypeIdsTest.php index 7ac8485fc..7336780b3 100644 --- a/tests/phpunit/item/ItemTypeIdsTest.php +++ b/tests/phpunit/item/ItemTypeIdsTest.php @@ -54,6 +54,7 @@ class ItemTypeIdsTest extends TestCase{ } $expected = $item->getTypeId(); $actual = $reflect->getConstant($name); + self::assertNotFalse($actual, "VanillaItems::$name() does not have an ItemTypeIds constant"); self::assertSame($expected, $actual, "VanillaItems::$name() type ID does not match ItemTypeIds::$name"); } } From 2d9cee3d6232b12991a1ec1f9bf1cf8e8f7e72b3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Nov 2024 23:14:23 +0000 Subject: [PATCH 075/290] Update Language dependency --- composer.json | 2 +- composer.lock | 14 +++---- src/lang/KnownTranslationFactory.php | 63 ++++++++++++++++++++++++++++ src/lang/KnownTranslationKeys.php | 13 ++++++ 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index ed5bb582f..9747cb567 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.19.0", + "pocketmine/locale-data": "~2.21.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", diff --git a/composer.lock b/composer.lock index eb1061ff5..c1a0b0073 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b2fbf6e7a9d650341dc71fa4dd124681", + "content-hash": "476374fb3d22e26a97c1dea8c6736faf", "packages": [ { "name": "adhocore/json-comment", @@ -420,16 +420,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.19.6", + "version": "2.21.1", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e" + "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/93e473e20e7f4515ecf45c5ef0f9155b9247a86e", - "reference": "93e473e20e7f4515ecf45c5ef0f9155b9247a86e", + "url": "https://api.github.com/repos/pmmp/Language/zipball/fdba0f764d6281f64e5968dca94fdab96bf4e167", + "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167", "shasum": "" }, "type": "library", @@ -437,9 +437,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.19.6" + "source": "https://github.com/pmmp/Language/tree/2.21.1" }, - "time": "2023-08-08T16:53:23+00:00" + "time": "2024-11-14T23:11:22+00:00" }, { "name": "pocketmine/log", diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index ea8c2952e..8153a80d6 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -603,6 +603,31 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::COMMANDS_WHITELIST_USAGE, []); } + public static function commands_xp_failure_widthdrawXp() : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_FAILURE_WIDTHDRAWXP, []); + } + + public static function commands_xp_success(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS, [ + 0 => $param0, + 1 => $param1, + ]); + } + + public static function commands_xp_success_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_LEVELS, [ + 0 => $param0, + 1 => $param1, + ]); + } + + public static function commands_xp_success_negative_levels(Translatable|string $param0, Translatable|string $param1) : Translatable{ + return new Translatable(KnownTranslationKeys::COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS, [ + 0 => $param0, + 1 => $param1, + ]); + } + public static function death_attack_anvil(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_ANVIL, [ 0 => $param0, @@ -667,6 +692,12 @@ final class KnownTranslationFactory{ ]); } + public static function death_attack_flyIntoWall(Translatable|string $param0) : Translatable{ + return new Translatable(KnownTranslationKeys::DEATH_ATTACK_FLYINTOWALL, [ + 0 => $param0, + ]); + } + public static function death_attack_generic(Translatable|string $param0) : Translatable{ return new Translatable(KnownTranslationKeys::DEATH_ATTACK_GENERIC, [ 0 => $param0, @@ -1025,6 +1056,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_CHIRP_DESC, []); } + public static function item_record_creator_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_DESC, []); + } + + public static function item_record_creator_music_box_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_CREATOR_MUSIC_BOX_DESC, []); + } + public static function item_record_far_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_FAR_DESC, []); } @@ -1045,6 +1084,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_PIGSTEP_DESC, []); } + public static function item_record_precipice_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_PRECIPICE_DESC, []); + } + + public static function item_record_relic_desc() : Translatable{ + return new Translatable(KnownTranslationKeys::ITEM_RECORD_RELIC_DESC, []); + } + public static function item_record_stal_desc() : Translatable{ return new Translatable(KnownTranslationKeys::ITEM_RECORD_STAL_DESC, []); } @@ -1536,6 +1583,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_WHITELIST_DESCRIPTION, []); } + public static function pocketmine_command_xp_description() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_DESCRIPTION, []); + } + + public static function pocketmine_command_xp_usage() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_XP_USAGE, []); + } + public static function pocketmine_crash_archive(Translatable|string $param0, Translatable|string $param1) : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_CRASH_ARCHIVE, [ 0 => $param0, @@ -2056,6 +2111,14 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE, []); } + public static function pocketmine_permission_command_xp_other() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_OTHER, []); + } + + public static function pocketmine_permission_command_xp_self() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_COMMAND_XP_SELF, []); + } + public static function pocketmine_permission_group_console() : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_PERMISSION_GROUP_CONSOLE, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index c83452730..4805d0c56 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -137,6 +137,10 @@ final class KnownTranslationKeys{ public const COMMANDS_WHITELIST_REMOVE_SUCCESS = "commands.whitelist.remove.success"; public const COMMANDS_WHITELIST_REMOVE_USAGE = "commands.whitelist.remove.usage"; public const COMMANDS_WHITELIST_USAGE = "commands.whitelist.usage"; + public const COMMANDS_XP_FAILURE_WIDTHDRAWXP = "commands.xp.failure.widthdrawXp"; + public const COMMANDS_XP_SUCCESS = "commands.xp.success"; + public const COMMANDS_XP_SUCCESS_LEVELS = "commands.xp.success.levels"; + public const COMMANDS_XP_SUCCESS_NEGATIVE_LEVELS = "commands.xp.success.negative.levels"; public const DEATH_ATTACK_ANVIL = "death.attack.anvil"; public const DEATH_ATTACK_ARROW = "death.attack.arrow"; public const DEATH_ATTACK_ARROW_ITEM = "death.attack.arrow.item"; @@ -147,6 +151,7 @@ final class KnownTranslationKeys{ public const DEATH_ATTACK_FALL = "death.attack.fall"; public const DEATH_ATTACK_FALLINGBLOCK = "death.attack.fallingBlock"; public const DEATH_ATTACK_FIREWORKS = "death.attack.fireworks"; + public const DEATH_ATTACK_FLYINTOWALL = "death.attack.flyIntoWall"; public const DEATH_ATTACK_GENERIC = "death.attack.generic"; public const DEATH_ATTACK_INFIRE = "death.attack.inFire"; public const DEATH_ATTACK_INWALL = "death.attack.inWall"; @@ -227,11 +232,15 @@ final class KnownTranslationKeys{ public const ITEM_RECORD_BLOCKS_DESC = "item.record_blocks.desc"; public const ITEM_RECORD_CAT_DESC = "item.record_cat.desc"; public const ITEM_RECORD_CHIRP_DESC = "item.record_chirp.desc"; + public const ITEM_RECORD_CREATOR_DESC = "item.record_creator.desc"; + public const ITEM_RECORD_CREATOR_MUSIC_BOX_DESC = "item.record_creator_music_box.desc"; public const ITEM_RECORD_FAR_DESC = "item.record_far.desc"; public const ITEM_RECORD_MALL_DESC = "item.record_mall.desc"; public const ITEM_RECORD_MELLOHI_DESC = "item.record_mellohi.desc"; public const ITEM_RECORD_OTHERSIDE_DESC = "item.record_otherside.desc"; public const ITEM_RECORD_PIGSTEP_DESC = "item.record_pigstep.desc"; + public const ITEM_RECORD_PRECIPICE_DESC = "item.record_precipice.desc"; + public const ITEM_RECORD_RELIC_DESC = "item.record_relic.desc"; public const ITEM_RECORD_STAL_DESC = "item.record_stal.desc"; public const ITEM_RECORD_STRAD_DESC = "item.record_strad.desc"; public const ITEM_RECORD_WAIT_DESC = "item.record_wait.desc"; @@ -336,6 +345,8 @@ final class KnownTranslationKeys{ public const POCKETMINE_COMMAND_VERSION_SERVERSOFTWAREVERSION = "pocketmine.command.version.serverSoftwareVersion"; public const POCKETMINE_COMMAND_VERSION_USAGE = "pocketmine.command.version.usage"; public const POCKETMINE_COMMAND_WHITELIST_DESCRIPTION = "pocketmine.command.whitelist.description"; + public const POCKETMINE_COMMAND_XP_DESCRIPTION = "pocketmine.command.xp.description"; + public const POCKETMINE_COMMAND_XP_USAGE = "pocketmine.command.xp.usage"; public const POCKETMINE_CRASH_ARCHIVE = "pocketmine.crash.archive"; public const POCKETMINE_CRASH_CREATE = "pocketmine.crash.create"; public const POCKETMINE_CRASH_ERROR = "pocketmine.crash.error"; @@ -449,6 +460,8 @@ final class KnownTranslationKeys{ public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_LIST = "pocketmine.permission.command.whitelist.list"; public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_RELOAD = "pocketmine.permission.command.whitelist.reload"; public const POCKETMINE_PERMISSION_COMMAND_WHITELIST_REMOVE = "pocketmine.permission.command.whitelist.remove"; + public const POCKETMINE_PERMISSION_COMMAND_XP_OTHER = "pocketmine.permission.command.xp.other"; + public const POCKETMINE_PERMISSION_COMMAND_XP_SELF = "pocketmine.permission.command.xp.self"; public const POCKETMINE_PERMISSION_GROUP_CONSOLE = "pocketmine.permission.group.console"; public const POCKETMINE_PERMISSION_GROUP_OPERATOR = "pocketmine.permission.group.operator"; public const POCKETMINE_PERMISSION_GROUP_USER = "pocketmine.permission.group.user"; From b5469dede222e33cbfa3371eace28d0f9058f7b8 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Fri, 15 Nov 2024 03:10:43 +0300 Subject: [PATCH 076/290] Flowable blocks now can't be placed inside liquid (#5392) --- src/block/Flowable.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/block/Flowable.php b/src/block/Flowable.php index 795fe2756..0328bcd74 100644 --- a/src/block/Flowable.php +++ b/src/block/Flowable.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\SupportType; use pocketmine\math\AxisAlignedBB; +use pocketmine\math\Vector3; /** * "Flowable" blocks are destroyed if water flows into the same space as the block. These blocks usually don't have any @@ -40,6 +41,11 @@ abstract class Flowable extends Transparent{ return false; } + public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{ + return (!$this->canBeFlowedInto() || !$blockReplace instanceof Liquid) && + parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); + } + /** * @return AxisAlignedBB[] */ From 0b0c425805a493478388222e6d961084cc593803 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:47:26 +0300 Subject: [PATCH 077/290] Extract glow lichen multi face logic into traits (#6396) This will be useful for future block additions --- src/block/GlowLichen.php | 95 ++--------------------- src/block/utils/MultiAnyFacingTrait.php | 72 ++++++++++++++++++ src/block/utils/MultiAnySupportTrait.php | 96 ++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 88 deletions(-) create mode 100644 src/block/utils/MultiAnyFacingTrait.php create mode 100644 src/block/utils/MultiAnySupportTrait.php diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index de66ccad7..d30e25395 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -24,60 +24,20 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; +use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; -use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Fertilizer; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\world\BlockTransaction; use pocketmine\world\World; -use function array_key_first; use function count; use function shuffle; class GlowLichen extends Transparent{ - - /** @var int[] */ - protected array $faces = []; - - protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->facingFlags($this->faces); - } - - /** @return int[] */ - public function getFaces() : array{ return $this->faces; } - - public function hasFace(int $face) : bool{ - return isset($this->faces[$face]); - } - - /** - * @param int[] $faces - * @return $this - */ - public function setFaces(array $faces) : self{ - $uniqueFaces = []; - foreach($faces as $face){ - Facing::validate($face); - $uniqueFaces[$face] = $face; - } - $this->faces = $uniqueFaces; - return $this; - } - - /** @return $this */ - public function setFace(int $face, bool $value) : self{ - Facing::validate($face); - if($value){ - $this->faces[$face] = $face; - }else{ - unset($this->faces[$face]); - } - return $this; - } + use MultiAnySupportTrait; public function getLightLevel() : int{ return 7; @@ -102,39 +62,11 @@ class GlowLichen extends Transparent{ return true; } - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - $this->faces = $blockReplace instanceof GlowLichen ? $blockReplace->faces : []; - $availableFaces = $this->getAvailableFaces(); - - if(count($availableFaces) === 0){ - return false; - } - - $opposite = Facing::opposite($face); - $placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces); - $this->faces[$placedFace] = $placedFace; - - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function onNearbyBlockChange() : void{ - $changed = false; - - foreach($this->faces as $face){ - if($this->getAdjacentSupportType($face) !== SupportType::FULL){ - unset($this->faces[$face]); - $changed = true; - } - } - - if($changed){ - $world = $this->position->getWorld(); - if(count($this->faces) === 0){ - $world->useBreakOn($this->position); - }else{ - $world->setBlock($this->position, $this); - } - } + /** + * @return int[] + */ + protected function getInitialPlaceFaces(Block $blockReplace) : array{ + return $blockReplace instanceof GlowLichen ? $blockReplace->faces : []; } private function getSpreadBlock(Block $replace, int $spreadFace) : ?Block{ @@ -261,17 +193,4 @@ class GlowLichen extends Transparent{ public function getFlammability() : int{ return 100; } - - /** - * @return array $faces - */ - private function getAvailableFaces() : array{ - $faces = []; - foreach(Facing::ALL as $face){ - if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){ - $faces[$face] = $face; - } - } - return $faces; - } } diff --git a/src/block/utils/MultiAnyFacingTrait.php b/src/block/utils/MultiAnyFacingTrait.php new file mode 100644 index 000000000..66f26d980 --- /dev/null +++ b/src/block/utils/MultiAnyFacingTrait.php @@ -0,0 +1,72 @@ +facingFlags($this->faces); + } + + /** @return int[] */ + public function getFaces() : array{ return $this->faces; } + + public function hasFace(int $face) : bool{ + return isset($this->faces[$face]); + } + + /** + * @param int[] $faces + * @return $this + */ + public function setFaces(array $faces) : self{ + $uniqueFaces = []; + foreach($faces as $face){ + Facing::validate($face); + $uniqueFaces[$face] = $face; + } + $this->faces = $uniqueFaces; + return $this; + } + + /** @return $this */ + public function setFace(int $face, bool $value) : self{ + Facing::validate($face); + if($value){ + $this->faces[$face] = $face; + }else{ + unset($this->faces[$face]); + } + return $this; + } +} diff --git a/src/block/utils/MultiAnySupportTrait.php b/src/block/utils/MultiAnySupportTrait.php new file mode 100644 index 000000000..ae1da7bef --- /dev/null +++ b/src/block/utils/MultiAnySupportTrait.php @@ -0,0 +1,96 @@ +faces = $this->getInitialPlaceFaces($blockReplace); + $availableFaces = $this->getAvailableFaces(); + + if(count($availableFaces) === 0){ + return false; + } + + $opposite = Facing::opposite($face); + $placedFace = isset($availableFaces[$opposite]) ? $opposite : array_key_first($availableFaces); + $this->faces[$placedFace] = $placedFace; + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onNearbyBlockChange() : void{ + $changed = false; + + foreach($this->faces as $face){ + if($this->getAdjacentSupportType($face) !== SupportType::FULL){ + unset($this->faces[$face]); + $changed = true; + } + } + + if($changed){ + $world = $this->position->getWorld(); + if(count($this->faces) === 0){ + $world->useBreakOn($this->position); + }else{ + $world->setBlock($this->position, $this); + } + } + } + + /** + * @return array $faces + */ + private function getAvailableFaces() : array{ + $faces = []; + foreach(Facing::ALL as $face){ + if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){ + $faces[$face] = $face; + } + } + return $faces; + } +} From a75d4687ce7e75e9cbf432c1762b5c7d82b66ac4 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:09:55 +0300 Subject: [PATCH 078/290] Implemented vanilla /xp command (#6429) --- src/command/SimpleCommandMap.php | 4 +- src/command/defaults/XpCommand.php | 89 +++++++++++++++++++++++ src/permission/DefaultPermissionNames.php | 2 + src/permission/DefaultPermissions.php | 2 + 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/command/defaults/XpCommand.php diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php index 1268e715b..a6a7e303b 100644 --- a/src/command/SimpleCommandMap.php +++ b/src/command/SimpleCommandMap.php @@ -64,6 +64,7 @@ use pocketmine\command\defaults\TransferServerCommand; use pocketmine\command\defaults\VanillaCommand; use pocketmine\command\defaults\VersionCommand; use pocketmine\command\defaults\WhitelistCommand; +use pocketmine\command\defaults\XpCommand; use pocketmine\command\utils\CommandStringHelper; use pocketmine\command\utils\InvalidCommandSyntaxException; use pocketmine\lang\KnownTranslationFactory; @@ -128,7 +129,8 @@ class SimpleCommandMap implements CommandMap{ new TitleCommand(), new TransferServerCommand(), new VersionCommand(), - new WhitelistCommand() + new WhitelistCommand(), + new XpCommand(), ]); } diff --git a/src/command/defaults/XpCommand.php b/src/command/defaults/XpCommand.php new file mode 100644 index 000000000..cb0352365 --- /dev/null +++ b/src/command/defaults/XpCommand.php @@ -0,0 +1,89 @@ +setPermissions([ + DefaultPermissionNames::COMMAND_XP_SELF, + DefaultPermissionNames::COMMAND_XP_OTHER + ]); + } + + public function execute(CommandSender $sender, string $commandLabel, array $args){ + if(count($args) < 1){ + throw new InvalidCommandSyntaxException(); + } + + $player = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER); + if($player === null){ + return true; + } + + $xpManager = $player->getXpManager(); + if(str_ends_with($args[0], "L")){ + $xpLevelAttr = $player->getAttributeMap()->get(Attribute::EXPERIENCE_LEVEL) ?? throw new AssumptionFailedError(); + $maxXpLevel = (int) $xpLevelAttr->getMaxValue(); + $currentXpLevel = $xpManager->getXpLevel(); + $xpLevels = $this->getInteger($sender, substr($args[0], 0, -1), -$currentXpLevel, $maxXpLevel - $currentXpLevel); + if($xpLevels >= 0){ + $xpManager->addXpLevels($xpLevels, false); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success_levels((string) $xpLevels, $player->getName())); + }else{ + $xpLevels = abs($xpLevels); + $xpManager->subtractXpLevels($xpLevels); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success_negative_levels((string) $xpLevels, $player->getName())); + } + }else{ + $xp = $this->getInteger($sender, $args[0], max: Limits::INT32_MAX); + if($xp < 0){ + $sender->sendMessage(KnownTranslationFactory::commands_xp_failure_widthdrawXp()->prefix(TextFormat::RED)); + }else{ + $xpManager->addXp($xp, false); + $sender->sendMessage(KnownTranslationFactory::commands_xp_success((string) $xp, $player->getName())); + } + } + + return true; + } +} diff --git a/src/permission/DefaultPermissionNames.php b/src/permission/DefaultPermissionNames.php index fab532e28..a916e813c 100644 --- a/src/permission/DefaultPermissionNames.php +++ b/src/permission/DefaultPermissionNames.php @@ -84,6 +84,8 @@ final class DefaultPermissionNames{ public const COMMAND_WHITELIST_LIST = "pocketmine.command.whitelist.list"; public const COMMAND_WHITELIST_RELOAD = "pocketmine.command.whitelist.reload"; public const COMMAND_WHITELIST_REMOVE = "pocketmine.command.whitelist.remove"; + public const COMMAND_XP_OTHER = "pocketmine.command.xp.other"; + public const COMMAND_XP_SELF = "pocketmine.command.xp.self"; public const GROUP_CONSOLE = "pocketmine.group.console"; public const GROUP_OPERATOR = "pocketmine.group.operator"; public const GROUP_USER = "pocketmine.group.user"; diff --git a/src/permission/DefaultPermissions.php b/src/permission/DefaultPermissions.php index c72765af6..1030e9ff8 100644 --- a/src/permission/DefaultPermissions.php +++ b/src/permission/DefaultPermissions.php @@ -112,5 +112,7 @@ abstract class DefaultPermissions{ self::registerPermission(new Permission(Names::COMMAND_WHITELIST_LIST, l10n::pocketmine_permission_command_whitelist_list()), [$operatorRoot]); self::registerPermission(new Permission(Names::COMMAND_WHITELIST_RELOAD, l10n::pocketmine_permission_command_whitelist_reload()), [$operatorRoot]); self::registerPermission(new Permission(Names::COMMAND_WHITELIST_REMOVE, l10n::pocketmine_permission_command_whitelist_remove()), [$operatorRoot]); + self::registerPermission(new Permission(Names::COMMAND_XP_OTHER, l10n::pocketmine_permission_command_xp_other()), [$operatorRoot]); + self::registerPermission(new Permission(Names::COMMAND_XP_SELF, l10n::pocketmine_permission_command_xp_self()), [$operatorRoot]); } } From 8474eaf5f105ed93bae462cdcb370768098ee4bf Mon Sep 17 00:00:00 2001 From: bonbionseker <167460154+bonbionseker@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:27:27 +0300 Subject: [PATCH 079/290] Adjust Sugar Cane to break when there is no water (#6486) --- src/block/Sugarcane.php | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 97b4aee9c..2da2dc9b9 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -36,7 +36,9 @@ use pocketmine\world\Position; class Sugarcane extends Flowable{ use AgeableTrait; - use StaticSupportTrait; + use StaticSupportTrait { + onNearbyBlockChange as onSupportBlockChange; + } public const MAX_AGE = 15; @@ -97,7 +99,13 @@ class Sugarcane extends Flowable{ } public function onRandomTick() : void{ - if(!$this->getSide(Facing::DOWN)->hasSameTypeId($this)){ + $down = $this->getSide(Facing::DOWN); + if(!$down->hasSameTypeId($this)){ + if(!$this->hasNearbyWater($down)){ + $this->position->getWorld()->useBreakOn($this->position, createParticles: true); + return; + } + if($this->age === self::MAX_AGE){ $this->grow($this->position); }else{ @@ -123,4 +131,23 @@ class Sugarcane extends Flowable{ return false; } + + private function hasNearbyWater(Block $down) : bool{ + foreach($down->getHorizontalSides() as $sideBlock){ + $blockId = $sideBlock->getTypeId(); + if($blockId === BlockTypeIds::WATER || $blockId === BlockTypeIds::FROSTED_ICE){ + return true; + } + } + return false; + } + + public function onNearbyBlockChange() : void{ + $down = $this->getSide(Facing::DOWN); + if(!$down->hasSameTypeId($this) && !$this->hasNearbyWater($down)){ + $this->position->getWorld()->useBreakOn($this->position, createParticles: true); + }else{ + $this->onSupportBlockChange(); + } + } } From 8a693f2a4c4b2fcd29788584cbe5b6af9a9470fd Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 15 Nov 2024 20:08:54 +0000 Subject: [PATCH 080/290] team-pr-auto-approve: Use RestrictedActions auto approver --- .github/workflows/team-pr-auto-approve.yml | 31 +++++++++------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index a582be328..ad2812383 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -13,30 +13,25 @@ on: - reopened - ready_for_review -permissions: - pull-requests: write - jobs: approve: name: Auto approve runs-on: ubuntu-latest steps: - - name: Check if PR author has write access - id: check-permission - uses: actions-cool/check-user-permission@v2 + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} - require: write - username: ${{ github.event.pull_request.user.login }} - #technically this would be fine for dependabot but generally bots don't count as team members - check-bot: true + app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} + private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} + owner: ${{ github.repository_owner }} + repositories: RestrictedActions - #TODO: Some way to avoid unnecessary repeated reviews would be nice here - - - name: Approve PR if authorized - if: steps.check-permission.outputs.require-result == 'true' && steps.check-permission.outputs.check-result == 'false' - uses: juliangruber/approve-pull-request-action@v2 + - name: Dispatch restricted action + uses: peter-evans/repository-dispatch@v3 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - number: ${{ github.event.pull_request.number }} + token: ${{ steps.generate-token.outputs.token }} + repository: ${{ github.repository_owner }}/RestrictedActions + event-type: auto_approve_collaborator_pr + client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}" }' From 4331f69b9c6a3366b3a8b7335bfea8139c0d88ef Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 15 Nov 2024 20:44:51 +0000 Subject: [PATCH 081/290] Update team-pr-auto-approve.yml --- .github/workflows/team-pr-auto-approve.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index ad2812383..8f40b16e3 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -14,8 +14,8 @@ on: - ready_for_review jobs: - approve: - name: Auto approve + dispatch: + name: Request approval runs-on: ubuntu-latest steps: From 4b630cb72615e6a4f02cbaca7778fd0877abcba9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 15 Nov 2024 21:14:21 +0000 Subject: [PATCH 082/290] start.sh: print warnings on unusual exit codes from the server process (#6497) --- start.sh | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/start.sh b/start.sh index 4d4787114..8f458b4e6 100755 --- a/start.sh +++ b/start.sh @@ -44,6 +44,27 @@ fi LOOPS=0 +handle_exit_code() { + local exitcode=$1 + if [ "$exitcode" -eq 134 ] || [ "$exitcode" -eq 139 ]; then #SIGABRT/SIGSEGV + echo "" + echo "ERROR: The server process was killed due to a critical error (code $exitcode) which could indicate a problem with PHP." + echo "Updating your PHP binary is recommended." + echo "If this keeps happening, please open a bug report." + echo "" + elif [ "$exitcode" -eq 143 ]; then #SIGKILL, maybe user intervention + echo "" + echo "WARNING: Server was forcibly killed!" + echo "If you didn't kill the server manually, this probably means the server used too much memory and was killed by the system's OOM Killer." + echo "Please ensure your system has enough available RAM." + echo "" + elif [ "$exitcode" -ne 0 ] && [ "$exitcode" -ne 137 ]; then #normal exit / SIGTERM + echo "" + echo "WARNING: Server did not shut down correctly! (code $exitcode)" + echo "" + fi +} + set +e if [ "$DO_LOOP" == "yes" ]; then @@ -52,11 +73,15 @@ if [ "$DO_LOOP" == "yes" ]; then echo "Restarted $LOOPS times" fi "$PHP_BINARY" "$POCKETMINE_FILE" "$@" + handle_exit_code $? echo "To escape the loop, press CTRL+C now. Otherwise, wait 5 seconds for the server to restart." echo "" sleep 5 ((LOOPS++)) done else - exec "$PHP_BINARY" "$POCKETMINE_FILE" "$@" + "$PHP_BINARY" "$POCKETMINE_FILE" "$@" + exitcode=$? + handle_exit_code $exitcode + exit $exitcode fi From ff695a5f97182226d9c7d28ac317128672fc9fce Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 15 Nov 2024 21:19:54 +0000 Subject: [PATCH 083/290] PlayerInteractEvent: added APIs to independently control reaction of item and block (#4683) This allows, for example, banning the usage of spawn eggs, without preventing opening of doors, without the need for item ID whitelists. It also allows customizing the behaviour of item and block interactions when sneaking - it's now possible to force spawn eggs to work when sneaking, or force containers to open. Finally, this also allows preventing any interaction at all without preventing block placement (by setting both to false). Since cancelling the event will typically prevent placement too (which might not be desired). Side note: Blocks are now always synced when right-clicking on a block. This differs from the previous behaviour, where the blocks were only synced when the action "failed". However, since this change introduces a situation where the action may succeed but have different results than the client expects, it's best to just always sync blocks in this situation. Fixes #3267 --- src/event/player/PlayerInteractEvent.php | 27 +++++++++++++++++++ .../mcpe/handler/InGamePacketHandler.php | 19 +++++++------ src/world/World.php | 14 +++++++--- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/event/player/PlayerInteractEvent.php b/src/event/player/PlayerInteractEvent.php index 6119d5e99..46daf7081 100644 --- a/src/event/player/PlayerInteractEvent.php +++ b/src/event/player/PlayerInteractEvent.php @@ -42,6 +42,9 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{ protected Vector3 $touchVector; + protected bool $useItem = true; + protected bool $useBlock = true; + public function __construct( Player $player, protected Item $item, @@ -73,4 +76,28 @@ class PlayerInteractEvent extends PlayerEvent implements Cancellable{ public function getFace() : int{ return $this->blockFace; } + + /** + * Returns whether the item may react to the interaction. If disabled, items such as spawn eggs will not activate. + * This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking. + */ + public function useItem() : bool{ return $this->useItem; } + + /** + * Sets whether the used item may react to the interaction. If false, items such as spawn eggs will not activate. + * This does NOT prevent blocks from being placed - it makes the item behave as if the player is sneaking. + */ + public function setUseItem(bool $useItem) : void{ $this->useItem = $useItem; } + + /** + * Returns whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not + * respond, containers will not open, etc. + */ + public function useBlock() : bool{ return $this->useBlock; } + + /** + * Sets whether the block may react to the interaction. If false, doors, fence gates and trapdoors will not + * respond, containers will not open, etc. + */ + public function setUseBlock(bool $useBlock) : void{ $this->useBlock = $useBlock; } } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index c92db3133..9e0039db4 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -493,15 +493,18 @@ class InGamePacketHandler extends PacketHandler{ $blockPos = $data->getBlockPosition(); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); - if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){ - $this->onFailedBlockAction($vBlockPos, $data->getFace()); - } + $this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos); + //always sync this in case plugins caused a different result than the client expected + //we *could* try to enhance detection of plugin-altered behaviour, but this would require propagating + //more information up the stack. For now I think this is good enough. + //if only the client would tell us what blocks it thinks changed... + $this->syncBlocksNearby($vBlockPos, $data->getFace()); return true; case UseItemTransactionData::ACTION_BREAK_BLOCK: $blockPos = $data->getBlockPosition(); $vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ()); if(!$this->player->breakBlock($vBlockPos)){ - $this->onFailedBlockAction($vBlockPos, null); + $this->syncBlocksNearby($vBlockPos, null); } return true; case UseItemTransactionData::ACTION_CLICK_AIR: @@ -529,9 +532,9 @@ class InGamePacketHandler extends PacketHandler{ } /** - * Internal function used to execute rollbacks when an action fails on a block. + * Syncs blocks nearby to ensure that the client and server agree on the world's blocks after a block interaction. */ - private function onFailedBlockAction(Vector3 $blockPos, ?int $face) : void{ + private function syncBlocksNearby(Vector3 $blockPos, ?int $face) : void{ if($blockPos->distanceSquared($this->player->getLocation()) < 10000){ $blocks = $blockPos->sidesArray(); if($face !== null){ @@ -682,7 +685,7 @@ class InGamePacketHandler extends PacketHandler{ case PlayerAction::START_BREAK: self::validateFacing($face); if(!$this->player->attackBlock($pos, $face)){ - $this->onFailedBlockAction($pos, $face); + $this->syncBlocksNearby($pos, $face); } break; @@ -998,7 +1001,7 @@ class InGamePacketHandler extends PacketHandler{ $lectern = $world->getBlockAt($pos->getX(), $pos->getY(), $pos->getZ()); if($lectern instanceof Lectern && $this->player->canInteract($lectern->getPosition(), 15)){ if(!$lectern->onPageTurn($packet->page)){ - $this->onFailedBlockAction($lectern->getPosition(), null); + $this->syncBlocksNearby($lectern->getPosition(), null); } return true; } diff --git a/src/world/World.php b/src/world/World.php index fbfa05b71..702410191 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2173,19 +2173,25 @@ class World implements ChunkManager{ if($player !== null){ $ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK); + if($player->isSneaking()){ + $ev->setUseItem(false); + $ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand + } if($player->isSpectator()){ $ev->cancel(); //set it to cancelled so plugins can bypass this } $ev->call(); if(!$ev->isCancelled()){ - if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ + if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ return true; } - $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); - if($result !== ItemUseResult::NONE){ - return $result === ItemUseResult::SUCCESS; + if($ev->useItem()){ + $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); + if($result !== ItemUseResult::NONE){ + return $result === ItemUseResult::SUCCESS; + } } }else{ return false; From d3add78d3e5d949a26af5c63eb3e0e8f8c0f2ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Le=C3=B3n?= <58715544+JavierLeon9966@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:27:10 -0300 Subject: [PATCH 084/290] Add support for basic entity picking via middle-click (#5397) Support for more advanced stuff like NBT copying wasn't added in this PR, as the NBT used by PM is currently an inconsistent mess and doesn't play nice with vanilla. In the interests of avoiding this mess propagating, it's been left for another time. Adds PlayerEntityPickEvent a la PlayerBlockPickEvent and Entity->getPickedItem(). --- src/entity/Entity.php | 8 +++ src/entity/Squid.php | 5 ++ src/entity/Villager.php | 6 ++ src/entity/Zombie.php | 5 ++ src/entity/object/FallingBlock.php | 5 ++ src/entity/object/Painting.php | 5 ++ src/entity/object/PrimedTNT.php | 6 ++ src/event/player/PlayerEntityPickEvent.php | 53 +++++++++++++++ .../mcpe/handler/InGamePacketHandler.php | 2 +- src/player/Player.php | 66 ++++++++++++++----- 10 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/event/player/PlayerEntityPickEvent.php diff --git a/src/entity/Entity.php b/src/entity/Entity.php index c55a8716c..68f5cbdd3 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityMotionEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntityTeleportEvent; +use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector2; @@ -1560,6 +1561,13 @@ abstract class Entity{ $this->hasSpawned = []; } + /** + * Returns the item that players will equip when middle-clicking on this entity. + */ + public function getPickedItem() : ?Item{ + return null; + } + /** * Flags the entity to be removed from the world on the next tick. */ diff --git a/src/entity/Squid.php b/src/entity/Squid.php index 75c50061b..a0f3fef48 100644 --- a/src/entity/Squid.php +++ b/src/entity/Squid.php @@ -26,6 +26,7 @@ namespace pocketmine\entity; use pocketmine\entity\animation\SquidInkCloudAnimation; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; @@ -124,4 +125,8 @@ class Squid extends WaterAnimal{ VanillaItems::INK_SAC()->setCount(mt_rand(1, 3)) ]; } + + public function getPickedItem() : ?Item{ + return VanillaItems::SQUID_SPAWN_EGG(); + } } diff --git a/src/entity/Villager.php b/src/entity/Villager.php index 376401a5d..fee65b014 100644 --- a/src/entity/Villager.php +++ b/src/entity/Villager.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\entity; +use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; @@ -87,6 +89,10 @@ class Villager extends Living implements Ageable{ return $this->baby; } + public function getPickedItem() : ?Item{ + return VanillaItems::VILLAGER_SPAWN_EGG(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); $properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); diff --git a/src/entity/Zombie.php b/src/entity/Zombie.php index 18fc2207e..159a2dd25 100644 --- a/src/entity/Zombie.php +++ b/src/entity/Zombie.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\entity; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use function mt_rand; @@ -65,4 +66,8 @@ class Zombie extends Living{ //TODO: check for equipment and whether it's a baby return 5; } + + public function getPickedItem() : ?Item{ + return VanillaItems::ZOMBIE_SPAWN_EGG(); + } } diff --git a/src/entity/object/FallingBlock.php b/src/entity/object/FallingBlock.php index 9d8af8934..66d4049f8 100644 --- a/src/entity/object/FallingBlock.php +++ b/src/entity/object/FallingBlock.php @@ -35,6 +35,7 @@ use pocketmine\entity\Location; use pocketmine\event\entity\EntityBlockChangeEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; @@ -194,6 +195,10 @@ class FallingBlock extends Entity{ return $nbt; } + public function getPickedItem() : ?Item{ + return $this->block->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); diff --git a/src/entity/object/Painting.php b/src/entity/object/Painting.php index f6449883c..641d04051 100644 --- a/src/entity/object/Painting.php +++ b/src/entity/object/Painting.php @@ -28,6 +28,7 @@ use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Location; use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; @@ -165,6 +166,10 @@ class Painting extends Entity{ )); } + public function getPickedItem() : ?Item{ + return VanillaItems::PAINTING(); + } + /** * Returns the painting motive (which image is displayed on the painting) */ diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index ec621adfb..af3c97922 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace pocketmine\entity\object; +use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Explosive; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityPreExplodeEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; @@ -127,6 +129,10 @@ class PrimedTNT extends Entity implements Explosive{ } } + public function getPickedItem() : ?Item{ + return VanillaBlocks::TNT()->setWorksUnderwater($this->worksUnderwater)->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); diff --git a/src/event/player/PlayerEntityPickEvent.php b/src/event/player/PlayerEntityPickEvent.php new file mode 100644 index 000000000..3c742d6c4 --- /dev/null +++ b/src/event/player/PlayerEntityPickEvent.php @@ -0,0 +1,53 @@ +player = $player; + } + + public function getEntity() : Entity{ + return $this->entityClicked; + } + + public function getResultItem() : Item{ + return $this->resultItem; + } +} diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 9e0039db4..e74eb87c6 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -671,7 +671,7 @@ class InGamePacketHandler extends PacketHandler{ } public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{ - return false; //TODO + return $this->player->pickEntity($packet->actorUniqueId); } public function handlePlayerAction(PlayerActionPacket $packet) : bool{ diff --git a/src/player/Player.php b/src/player/Player.php index 192e26a5f..999c07a93 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -57,6 +57,7 @@ use pocketmine\event\player\PlayerDisplayNameChangeEvent; use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; +use pocketmine\event\player\PlayerEntityPickEvent; use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerInteractEvent; @@ -1709,29 +1710,58 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $ev->call(); if(!$ev->isCancelled()){ - if($existingSlot !== -1){ - if($existingSlot < $this->inventory->getHotbarSize()){ - $this->inventory->setHeldItemIndex($existingSlot); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); - } - }else{ - $firstEmpty = $this->inventory->firstEmpty(); - if($firstEmpty === -1){ //full inventory - $this->inventory->setItemInHand($item); - }elseif($firstEmpty < $this->inventory->getHotbarSize()){ - $this->inventory->setItem($firstEmpty, $item); - $this->inventory->setHeldItemIndex($firstEmpty); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); - $this->inventory->setItemInHand($item); - } - } + $this->equipOrAddPickedItem($existingSlot, $item); } return true; } + public function pickEntity(int $entityId) : bool{ + $entity = $this->getWorld()->getEntity($entityId); + if($entity === null){ + return true; + } + + $item = $entity->getPickedItem(); + if($item === null){ + return true; + } + + $ev = new PlayerEntityPickEvent($this, $entity, $item); + $existingSlot = $this->inventory->first($item); + if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){ + $ev->cancel(); + } + $ev->call(); + + if(!$ev->isCancelled()){ + $this->equipOrAddPickedItem($existingSlot, $item); + } + + return true; + } + + private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{ + if($existingSlot !== -1){ + if($existingSlot < $this->inventory->getHotbarSize()){ + $this->inventory->setHeldItemIndex($existingSlot); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); + } + }else{ + $firstEmpty = $this->inventory->firstEmpty(); + if($firstEmpty === -1){ //full inventory + $this->inventory->setItemInHand($item); + }elseif($firstEmpty < $this->inventory->getHotbarSize()){ + $this->inventory->setItem($firstEmpty, $item); + $this->inventory->setHeldItemIndex($firstEmpty); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); + $this->inventory->setItemInHand($item); + } + } + } + /** * Performs a left-click (attack) action on the block. * From 48a908ee8caf3d41df694def05912dc5867504d9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 16 Nov 2024 01:36:51 +0000 Subject: [PATCH 085/290] maybe making this specific to gameplay wasn't a good idea --- .github/PULL_REQUEST_TEMPLATE.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c27ea7a47..82fd81a1d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -24,11 +24,9 @@ -## In-Game Testing +## Tests From e77f2c5198ebe58b06f3f857674b0c8588c7f250 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:57:57 +0300 Subject: [PATCH 086/290] Implemented End Crystal (#4715) Co-authored-by: Dylan T. --- .../ItemSerializerDeserializerRegistrar.php | 1 + src/entity/EntityFactory.php | 5 + src/entity/object/EndCrystal.php | 140 ++++++++++++++++++ src/entity/projectile/Projectile.php | 3 +- src/item/EndCrystal.php | 59 ++++++++ src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 1 + src/item/VanillaItems.php | 2 + 8 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/entity/object/EndCrystal.php create mode 100644 src/item/EndCrystal.php diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 661a4ed7d..7803cea5c 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -232,6 +232,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::EMERALD, Items::EMERALD()); $this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK()); $this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE()); + $this->map1to1Item(Ids::END_CRYSTAL, Items::END_CRYSTAL()); $this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); $this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index d8d189cff..3d53233ab 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -32,6 +32,7 @@ use pocketmine\data\bedrock\PotionTypeIdMap; use pocketmine\data\bedrock\PotionTypeIds; use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\EntityDataHelper as Helper; +use pocketmine\entity\object\EndCrystal; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\ItemEntity; @@ -92,6 +93,10 @@ final class EntityFactory{ return new Egg(Helper::parseLocation($nbt, $world), null, $nbt); }, ['Egg', 'minecraft:egg']); + $this->register(EndCrystal::class, function(World $world, CompoundTag $nbt) : EndCrystal{ + return new EndCrystal(Helper::parseLocation($nbt, $world), $nbt); + }, ['EnderCrystal', 'minecraft:ender_crystal']); + $this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{ return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt); }, ['ThrownEnderpearl', 'minecraft:ender_pearl']); diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php new file mode 100644 index 000000000..2c4a4f7e5 --- /dev/null +++ b/src/entity/object/EndCrystal.php @@ -0,0 +1,140 @@ +showBase; + } + + public function setShowBase(bool $showBase) : void{ + $this->showBase = $showBase; + $this->networkPropertiesDirty = true; + } + + public function getBeamTarget() : ?Vector3{ + return $this->beamTarget; + } + + public function setBeamTarget(?Vector3 $beamTarget) : void{ + $this->beamTarget = $beamTarget; + $this->networkPropertiesDirty = true; + } + + public function attack(EntityDamageEvent $source) : void{ + parent::attack($source); + if( + $source->getCause() !== EntityDamageEvent::CAUSE_VOID && + !$this->isFlaggedForDespawn() && + !$source->isCancelled() + ){ + $this->flagForDespawn(); + $this->explode(); + } + } + + protected function initEntity(CompoundTag $nbt) : void{ + parent::initEntity($nbt); + + $this->setMaxHealth(1); + $this->setHealth(1); + + $this->setShowBase($nbt->getByte(self::TAG_SHOWBASE, 0) === 1); + + if( + ($beamXTag = $nbt->getTag(self::TAG_BLOCKTARGET_X)) instanceof IntTag && + ($beamYTag = $nbt->getTag(self::TAG_BLOCKTARGET_Y)) instanceof IntTag && + ($beamZTag = $nbt->getTag(self::TAG_BLOCKTARGET_Z)) instanceof IntTag + ){ + $this->setBeamTarget(new Vector3($beamXTag->getValue(), $beamYTag->getValue(), $beamZTag->getValue())); + } + } + + public function saveNBT() : CompoundTag{ + $nbt = parent::saveNBT(); + + $nbt->setByte(self::TAG_SHOWBASE, $this->showBase ? 1 : 0); + if($this->beamTarget !== null){ + $nbt->setInt(self::TAG_BLOCKTARGET_X, $this->beamTarget->getFloorX()); + $nbt->setInt(self::TAG_BLOCKTARGET_Y, $this->beamTarget->getFloorY()); + $nbt->setInt(self::TAG_BLOCKTARGET_Z, $this->beamTarget->getFloorZ()); + } + return $nbt; + } + + public function explode() : void{ + $ev = new EntityPreExplodeEvent($this, 6); + $ev->call(); + if(!$ev->isCancelled()){ + $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this); + if($ev->isBlockBreaking()){ + $explosion->explodeA(); + } + $explosion->explodeB(); + } + } + + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ + parent::syncNetworkData($properties); + + $properties->setGenericFlag(EntityMetadataFlags::SHOWBASE, $this->showBase); + $properties->setBlockPos(EntityMetadataProperties::BLOCK_TARGET, BlockPosition::fromVector3($this->beamTarget ?? Vector3::zero())); + } +} diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 55950b6a6..0abc274b5 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -28,6 +28,7 @@ use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\entity\Location; +use pocketmine\entity\object\EndCrystal; use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; @@ -96,7 +97,7 @@ abstract class Projectile extends Entity{ } public function canCollideWith(Entity $entity) : bool{ - return $entity instanceof Living && !$this->onGround; + return ($entity instanceof Living || $entity instanceof EndCrystal) && !$this->onGround; } public function canBeCollidedWith() : bool{ diff --git a/src/item/EndCrystal.php b/src/item/EndCrystal.php new file mode 100644 index 000000000..320d657e6 --- /dev/null +++ b/src/item/EndCrystal.php @@ -0,0 +1,59 @@ +getTypeId() === BlockTypeIds::OBSIDIAN || $blockClicked->getTypeId() === BlockTypeIds::BEDROCK){ + $pos = $blockClicked->getPosition(); + $world = $pos->getWorld(); + $bb = AxisAlignedBB::one() + ->offset($pos->getX(), $pos->getY(), $pos->getZ()) + ->extend(Facing::UP, 1); + if( + count($world->getNearbyEntities($bb)) === 0 && + $blockClicked->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR && + $blockClicked->getSide(Facing::UP, 2)->getTypeId() === BlockTypeIds::AIR + ){ + $crystal = new EntityEndCrystal(Location::fromObject($pos->add(0.5, 1, 0.5), $world)); + $crystal->spawnToAll(); + + $this->pop(); + return ItemUseResult::SUCCESS; + } + } + return ItemUseResult::NONE; + } +} diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index 60bae0dd3..96ba2a867 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -325,8 +325,9 @@ final class ItemTypeIds{ public const PITCHER_POD = 20286; public const NAME_TAG = 20287; public const GOAT_HORN = 20288; + public const END_CRYSTAL = 20289; - public const FIRST_UNUSED_ITEM_ID = 20289; + public const FIRST_UNUSED_ITEM_ID = 20290; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index e03db28bc..4dae231ac 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1330,6 +1330,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK()); $result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE()); + $result->register("end_crystal", fn() => Items::END_CRYSTAL()); $result->register("ender_pearl", fn() => Items::ENDER_PEARL()); $result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index a3366b85e..5899b6357 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -158,6 +158,7 @@ use function strtolower; * @method static EnchantedBook ENCHANTED_BOOK() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static EnderPearl ENDER_PEARL() + * @method static EndCrystal END_CRYSTAL() * @method static ExperienceBottle EXPERIENCE_BOTTLE() * @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static Item FEATHER() @@ -479,6 +480,7 @@ final class VanillaItems{ self::register("emerald", fn(IID $id) => new Item($id, "Emerald")); self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL])); self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple")); + self::register("end_crystal", fn(IID $id) => new EndCrystal($id, "End Crystal")); self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl")); self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting")); self::register("feather", fn(IID $id) => new Item($id, "Feather")); From 1555fa17e719f01c67ddf2fa75a4a795e49fa695 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sat, 16 Nov 2024 22:06:03 +0300 Subject: [PATCH 087/290] Added ability to pick end crystal item (#6509) --- src/entity/object/EndCrystal.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php index 2c4a4f7e5..afaeb6769 100644 --- a/src/entity/object/EndCrystal.php +++ b/src/entity/object/EndCrystal.php @@ -28,6 +28,8 @@ use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Explosive; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityPreExplodeEvent; +use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; @@ -60,6 +62,10 @@ class EndCrystal extends Entity implements Explosive{ return true; } + public function getPickedItem() : ?Item{ + return VanillaItems::END_CRYSTAL(); + } + public function showBase() : bool{ return $this->showBase; } From b2aa6396c3cc2cafdd815eacc360e1ad89599899 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 16 Nov 2024 23:15:07 +0000 Subject: [PATCH 088/290] auto-approve: don't request approvals for draft PRs --- .github/workflows/team-pr-auto-approve.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index 8f40b16e3..405aafd8b 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -17,6 +17,7 @@ jobs: dispatch: name: Request approval runs-on: ubuntu-latest + if: '! github.event.pull_request.draft' steps: - name: Generate access token From a6534ecbbbcf369264567d27e5ed70f7f5be9816 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 17 Nov 2024 01:55:46 +0000 Subject: [PATCH 089/290] Fixed merge error --- src/entity/object/EndCrystal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php index afaeb6769..937a4023f 100644 --- a/src/entity/object/EndCrystal.php +++ b/src/entity/object/EndCrystal.php @@ -47,7 +47,7 @@ class EndCrystal extends Entity implements Explosive{ private const TAG_BLOCKTARGET_Y = "BlockTargetY"; //TAG_Int private const TAG_BLOCKTARGET_Z = "BlockTargetZ"; //TAG_Int - public static function getNetworkTypeId() : string{ return EntityIds::ENDER_CRYSTAL; } + public function getNetworkTypeId() : string{ return EntityIds::ENDER_CRYSTAL; } protected bool $showBase = false; protected ?Vector3 $beamTarget = null; From faf1e26bacbd993c7715a2ac11efd6b393e70d19 Mon Sep 17 00:00:00 2001 From: Akmal Fairuz <35138228+AkmalFairuz@users.noreply.github.com> Date: Tue, 19 Nov 2024 06:54:22 +0700 Subject: [PATCH 090/290] Fix: implicitly nullable parameter declarations deprecated (#6522) --- src/world/World.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/world/World.php b/src/world/World.php index fbfa05b71..187f7ab8b 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2037,7 +2037,7 @@ class World implements ChunkManager{ * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) * @phpstan-param-out Item $item */ - public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false, array &$returnedItems = []) : bool{ + public function useBreakOn(Vector3 $vector, ?Item &$item = null, ?Player $player = null, bool $createParticles = false, array &$returnedItems = []) : bool{ $vector = $vector->floor(); $chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE; From e710b3750f5ca8547198ecb3ebfc9169ee6b5688 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 19 Nov 2024 18:05:21 +0100 Subject: [PATCH 091/290] Adjust pretty name of closures on PHP 8.4 (#6351) related to https://github.com/php/php-src/pull/13550 see analog symfony change: https://github.com/symfony/symfony/pull/54614 --- src/utils/Utils.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 46d673216..ef3f2d249 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -80,7 +80,7 @@ use function preg_match_all; use function preg_replace; use function shell_exec; use function spl_object_id; -use function str_ends_with; +use function str_contains; use function str_pad; use function str_split; use function str_starts_with; @@ -121,7 +121,7 @@ final class Utils{ */ public static function getNiceClosureName(\Closure $closure) : string{ $func = new \ReflectionFunction($closure); - if(!str_ends_with($func->getName(), '{closure}')){ + if(!str_contains($func->getName(), '{closure')){ //closure wraps a named function, can be done with reflection or fromCallable() //isClosure() is useless here because it just tells us if $func is reflecting a Closure object From bf7a53b00ff4a7f0ac47a405ec32b40afac37de8 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 19 Nov 2024 17:47:46 +0000 Subject: [PATCH 092/290] Update api-change-request.md [ci skip] --- .github/ISSUE_TEMPLATE/api-change-request.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/api-change-request.md b/.github/ISSUE_TEMPLATE/api-change-request.md index 615ab12ef..e3d24ea0f 100644 --- a/.github/ISSUE_TEMPLATE/api-change-request.md +++ b/.github/ISSUE_TEMPLATE/api-change-request.md @@ -7,13 +7,13 @@ assignees: '' --- - -## Description + +## Problem description - -## Justification + +## Proposed solution -## Alternative methods +## Alternative solutions that don't require API changes From 9195c8867046de1a4a64f027b56be63826289abc Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 20 Nov 2024 14:56:52 +0000 Subject: [PATCH 093/290] ConsoleReader: Use proc_open()'s socket support to send commands back to the main server process (#5273) Support for this was introduced in PHP 8.0, though not mentioned in any changelog: php/php-src#5777 This simplifies the subprocess handling considerably. However, there is a potential for problems if PHP generates any E_* errors, since these get written to STDOUT as well. To avoid error messages being treated as a command, a hash is attached to each IPC message, seeded with an incrementing counter. This prevents error messages causing command replays or unintended commands. Unfortunately, PHP doesn't support binding pipes other than stdin/stdout/stderr on Windows for the child process, so we have to use stdout for this. In the future, if it becomes possible, a dedicated pipe for the purpose should be introduced. We'd need something like php://fd/ to work on Windows. --- src/console/ConsoleReaderChildProcess.php | 30 +++--- .../ConsoleReaderChildProcessDaemon.php | 54 ++++++----- .../ConsoleReaderChildProcessUtils.php | 71 ++++++++++++++ .../ConsoleReaderChildProcessUtilsTest.php | 92 +++++++++++++++++++ 4 files changed, 209 insertions(+), 38 deletions(-) create mode 100644 src/console/ConsoleReaderChildProcessUtils.php create mode 100644 tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php diff --git a/src/console/ConsoleReaderChildProcess.php b/src/console/ConsoleReaderChildProcess.php index 0d5a30fdc..c0c4f0048 100644 --- a/src/console/ConsoleReaderChildProcess.php +++ b/src/console/ConsoleReaderChildProcess.php @@ -29,23 +29,21 @@ use pocketmine\utils\Process; use function cli_set_process_title; use function count; use function dirname; -use function feof; use function fwrite; -use function stream_socket_client; +use function is_numeric; +use const PHP_EOL; +use const STDOUT; + +if(count($argv) !== 2 || !is_numeric($argv[1])){ + echo "Usage: " . $argv[0] . " " . PHP_EOL; + exit(1); +} + +$commandTokenSeed = (int) $argv[1]; require dirname(__DIR__, 2) . '/vendor/autoload.php'; -if(count($argv) !== 2){ - die("Please provide a server to connect to"); -} - @cli_set_process_title('PocketMine-MP Console Reader'); -$errCode = null; -$errMessage = null; -$socket = stream_socket_client($argv[1], $errCode, $errMessage, 15.0); -if($socket === false){ - throw new \RuntimeException("Failed to connect to server process ($errCode): $errMessage"); -} /** @phpstan-var ThreadSafeArray $channel */ $channel = new ThreadSafeArray(); @@ -75,15 +73,15 @@ $thread = new class($channel) extends NativeThread{ }; $thread->start(NativeThread::INHERIT_NONE); -while(!feof($socket)){ +while(true){ $line = $channel->synchronized(function() use ($channel) : ?string{ if(count($channel) === 0){ $channel->wait(1_000_000); } - $line = $channel->shift(); - return $line; + return $channel->shift(); }); - if(@fwrite($socket, ($line ?? "") . "\n") === false){ + $message = $line !== null ? ConsoleReaderChildProcessUtils::createMessage($line, $commandTokenSeed) : ""; + if(@fwrite(STDOUT, $message . "\n") === false){ //Always send even if there's no line, to check if the parent is alive //If the parent process was terminated forcibly, it won't close the connection properly, so feof() will return //false even though the connection is actually broken. However, fwrite() will fail. diff --git a/src/console/ConsoleReaderChildProcessDaemon.php b/src/console/ConsoleReaderChildProcessDaemon.php index 138559f06..f7300b7a5 100644 --- a/src/console/ConsoleReaderChildProcessDaemon.php +++ b/src/console/ConsoleReaderChildProcessDaemon.php @@ -29,19 +29,16 @@ use Symfony\Component\Filesystem\Path; use function base64_encode; use function fgets; use function fopen; +use function mt_rand; use function preg_replace; use function proc_close; use function proc_open; use function proc_terminate; +use function rtrim; use function sprintf; use function stream_select; -use function stream_socket_accept; -use function stream_socket_get_name; -use function stream_socket_server; -use function stream_socket_shutdown; use function trim; use const PHP_BINARY; -use const STREAM_SHUT_RDWR; /** * This pile of shit exists because PHP on Windows is broken, and can't handle stream_select() on stdin or pipes @@ -58,44 +55,44 @@ use const STREAM_SHUT_RDWR; * communication. */ final class ConsoleReaderChildProcessDaemon{ + public const TOKEN_DELIMITER = ":"; + public const TOKEN_HASH_ALGO = "xxh3"; + private \PrefixedLogger $logger; /** @var resource */ private $subprocess; /** @var resource */ private $socket; + private int $commandTokenSeed; public function __construct( \Logger $logger ){ $this->logger = new \PrefixedLogger($logger, "Console Reader Daemon"); + $this->commandTokenSeed = mt_rand(); $this->prepareSubprocess(); } private function prepareSubprocess() : void{ - $server = stream_socket_server("tcp://127.0.0.1:0"); - if($server === false){ - throw new \RuntimeException("Failed to open console reader socket server"); - } - $address = Utils::assumeNotFalse(stream_socket_get_name($server, false), "stream_socket_get_name() shouldn't return false here"); - //Windows sucks, and likes to corrupt UTF-8 file paths when they travel to the subprocess, so we base64 encode //the path to avoid the problem. This is an abysmally shitty hack, but here we are :( $sub = Utils::assumeNotFalse(proc_open( - [PHP_BINARY, '-dopcache.enable_cli=0', '-r', sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), $address], [ + PHP_BINARY, + '-dopcache.enable_cli=0', + '-r', + sprintf('require base64_decode("%s", true);', base64_encode(Path::join(__DIR__, 'ConsoleReaderChildProcess.php'))), + (string) $this->commandTokenSeed + ], + [ + 1 => ['socket'], 2 => fopen("php://stderr", "w"), ], $pipes ), "Something has gone horribly wrong"); - $client = stream_socket_accept($server, 15); - if($client === false){ - throw new AssumptionFailedError("stream_socket_accept() returned false"); - } - stream_socket_shutdown($server, STREAM_SHUT_RDWR); - $this->subprocess = $sub; - $this->socket = $client; + $this->socket = $pipes[1]; } private function shutdownSubprocess() : void{ @@ -104,7 +101,6 @@ final class ConsoleReaderChildProcessDaemon{ //the first place). proc_terminate($this->subprocess); proc_close($this->subprocess); - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); } public function readLine() : ?string{ @@ -112,13 +108,27 @@ final class ConsoleReaderChildProcessDaemon{ $w = null; $e = null; if(stream_select($r, $w, $e, 0, 0) === 1){ - $command = fgets($this->socket); - if($command === false){ + $line = fgets($this->socket); + if($line === false){ $this->logger->debug("Lost connection to subprocess, restarting (maybe the child process was killed from outside?)"); $this->shutdownSubprocess(); $this->prepareSubprocess(); return null; } + $line = rtrim($line, "\n"); + + if($line === ""){ + //keepalive + return null; + } + + $command = ConsoleReaderChildProcessUtils::parseMessage($line, $this->commandTokenSeed); + if($command === null){ + //this is not a command - it may be some kind of error output from the subprocess + //write it directly to the console + $this->logger->warning("Unexpected output from child process: $line"); + return null; + } $command = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", trim($command)) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); $command = preg_replace('/[[:cntrl:]]/', '', $command) ?? throw new AssumptionFailedError("This regex is assumed to be valid"); diff --git a/src/console/ConsoleReaderChildProcessUtils.php b/src/console/ConsoleReaderChildProcessUtils.php new file mode 100644 index 000000000..661e9b0f7 --- /dev/null +++ b/src/console/ConsoleReaderChildProcessUtils.php @@ -0,0 +1,71 @@ + $counter]); + $counter++; + return $line . self::TOKEN_DELIMITER . $token; + } + + /** + * Extracts a command from an IPC message from the console reader subprocess. + * Returns the user's input command, or null if this isn't a user input. + */ + public static function parseMessage(string $message, int &$counter) : ?string{ + $delimiterPos = strrpos($message, self::TOKEN_DELIMITER); + if($delimiterPos !== false){ + $left = substr($message, 0, $delimiterPos); + $right = substr($message, $delimiterPos + strlen(self::TOKEN_DELIMITER)); + $expectedToken = hash(self::TOKEN_HASH_ALGO, $left, options: ['seed' => $counter]); + + if($expectedToken === $right){ + $counter++; + return $left; + } + } + + return null; + } +} diff --git a/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php new file mode 100644 index 000000000..31ae2e27a --- /dev/null +++ b/tests/phpunit/console/ConsoleReaderChildProcessUtilsTest.php @@ -0,0 +1,92 @@ + + */ + public static function commandStringProvider() : \Generator{ + yield ["stop"]; + yield ["pocketmine:status"]; + yield [str_repeat("s", 1000)]; + yield ["time set day"]; + yield ["give \"Steve\" golden_apple"]; + } + + /** + * @dataProvider commandStringProvider + */ + public function testCreateParseSymmetry(string $input) : void{ + $counterCreate = $counterParse = mt_rand(); + $message = ConsoleReaderChildProcessUtils::createMessage($input, $counterCreate); + $parsedInput = ConsoleReaderChildProcessUtils::parseMessage($message, $counterParse); + + self::assertSame($input, $parsedInput); + } + + public function testCreateMessage() : void{ + $counter = 0; + + ConsoleReaderChildProcessUtils::createMessage("", $counter); + self::assertSame(1, $counter, "createMessage should always bump the counter"); + } + + /** + * @phpstan-return \Generator + */ + public static function parseMessageProvider() : \Generator{ + $counter = 0; + yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), true]; + + yield ["", false]; //keepalive message, doesn't bump counter + + $counter = 1; + yield [ConsoleReaderChildProcessUtils::createMessage("", $counter), false]; //mismatched counter + + $counter = 0; + yield ["a" . ConsoleReaderChildProcessUtils::TOKEN_DELIMITER . "b", false]; //message with delimiter but not a valid IPC message + } + + /** + * @dataProvider parseMessageProvider + */ + public static function testParseMessage(string $message, bool $valid) : void{ + $counter = $oldCounter = 0; + + $input = ConsoleReaderChildProcessUtils::parseMessage($message, $counter); + if(!$valid){ + self::assertNull($input, "Result should be null on invalid message"); + self::assertSame($oldCounter, $counter, "Counter shouldn't be bumped on invalid message"); + }else{ + self::assertNotNull($input, "This was a valid message, expected a result"); + self::assertSame($oldCounter + 1, $counter, "Counter should be bumped on valid message parse"); + } + } +} From 49bdaee930f552b387c39992217519eff381447d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Nov 2024 15:38:44 +0000 Subject: [PATCH 094/290] Move event handler inheritance test to PHPUnit using mock objects this is dodgy and we shouldn't rely on it long term. relates to #6524 --- tests/phpunit/event/EventTest.php | 65 ++++++++++++++ .../event/TestChildEvent.php} | 4 +- .../event/TestGrandchildEvent.php} | 4 +- .../event/TestParentEvent.php} | 4 +- .../src/EventHandlerInheritanceTest.php | 88 ------------------- tests/plugins/TesterPlugin/src/Main.php | 2 +- 6 files changed, 72 insertions(+), 95 deletions(-) create mode 100644 tests/phpunit/event/EventTest.php rename tests/{plugins/TesterPlugin/src/event/GrandchildEvent.php => phpunit/event/TestChildEvent.php} (90%) rename tests/{plugins/TesterPlugin/src/event/ParentEvent.php => phpunit/event/TestGrandchildEvent.php} (89%) rename tests/{plugins/TesterPlugin/src/event/ChildEvent.php => phpunit/event/TestParentEvent.php} (91%) delete mode 100644 tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php diff --git a/tests/phpunit/event/EventTest.php b/tests/phpunit/event/EventTest.php new file mode 100644 index 000000000..259daaf5d --- /dev/null +++ b/tests/phpunit/event/EventTest.php @@ -0,0 +1,65 @@ +createMock(Server::class); + $mockPlugin = self::createStub(Plugin::class); + $mockPlugin->method('isEnabled')->willReturn(true); + + $pluginManager = new PluginManager($mockServer, null); + + $expectedOrder = [ + TestGrandchildEvent::class, + TestChildEvent::class, + TestParentEvent::class + ]; + $actualOrder = []; + + foreach($expectedOrder as $class){ + $pluginManager->registerEvent( + $class, + function(TestParentEvent $event) use (&$actualOrder, $class) : void{ + $actualOrder[] = $class; + }, + EventPriority::NORMAL, + $mockPlugin + ); + } + + $event = new TestGrandchildEvent(); + $event->call(); + + self::assertSame($expectedOrder, $actualOrder, "Expected event handlers to be called from most specific to least specific"); + } +} diff --git a/tests/plugins/TesterPlugin/src/event/GrandchildEvent.php b/tests/phpunit/event/TestChildEvent.php similarity index 90% rename from tests/plugins/TesterPlugin/src/event/GrandchildEvent.php rename to tests/phpunit/event/TestChildEvent.php index 40c37c567..2b79f882f 100644 --- a/tests/plugins/TesterPlugin/src/event/GrandchildEvent.php +++ b/tests/phpunit/event/TestChildEvent.php @@ -21,8 +21,8 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event; -class GrandchildEvent extends ChildEvent{ +class TestChildEvent extends TestParentEvent{ } diff --git a/tests/plugins/TesterPlugin/src/event/ParentEvent.php b/tests/phpunit/event/TestGrandchildEvent.php similarity index 89% rename from tests/plugins/TesterPlugin/src/event/ParentEvent.php rename to tests/phpunit/event/TestGrandchildEvent.php index 68f7df630..7685f3b0b 100644 --- a/tests/plugins/TesterPlugin/src/event/ParentEvent.php +++ b/tests/phpunit/event/TestGrandchildEvent.php @@ -21,8 +21,8 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event; -class ParentEvent extends \pocketmine\event\Event{ +class TestGrandchildEvent extends TestChildEvent{ } diff --git a/tests/plugins/TesterPlugin/src/event/ChildEvent.php b/tests/phpunit/event/TestParentEvent.php similarity index 91% rename from tests/plugins/TesterPlugin/src/event/ChildEvent.php rename to tests/phpunit/event/TestParentEvent.php index b71d2627f..c770f6372 100644 --- a/tests/plugins/TesterPlugin/src/event/ChildEvent.php +++ b/tests/phpunit/event/TestParentEvent.php @@ -21,8 +21,8 @@ declare(strict_types=1); -namespace pmmp\TesterPlugin\event; +namespace pocketmine\event; -class ChildEvent extends ParentEvent{ +class TestParentEvent extends Event{ } diff --git a/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php b/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php deleted file mode 100644 index efe20f8d8..000000000 --- a/tests/plugins/TesterPlugin/src/EventHandlerInheritanceTest.php +++ /dev/null @@ -1,88 +0,0 @@ -getPlugin(); - $plugin->getServer()->getPluginManager()->registerEvent( - ParentEvent::class, - function(ParentEvent $event) : void{ - $this->callOrder[] = ParentEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - $plugin->getServer()->getPluginManager()->registerEvent( - ChildEvent::class, - function(ChildEvent $event) : void{ - $this->callOrder[] = ChildEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - $plugin->getServer()->getPluginManager()->registerEvent( - GrandchildEvent::class, - function(GrandchildEvent $event) : void{ - $this->callOrder[] = GrandchildEvent::class; - }, - EventPriority::NORMAL, - $plugin - ); - - $event = new GrandchildEvent(); - $event->call(); - - if($this->callOrder === self::EXPECTED_ORDER){ - $this->setResult(Test::RESULT_OK); - }else{ - $plugin->getLogger()->error("Expected order: " . implode(", ", self::EXPECTED_ORDER) . ", got: " . implode(", ", $this->callOrder)); - $this->setResult(Test::RESULT_FAILED); - } - } -} diff --git a/tests/plugins/TesterPlugin/src/Main.php b/tests/plugins/TesterPlugin/src/Main.php index 26d3441f4..08b59dbac 100644 --- a/tests/plugins/TesterPlugin/src/Main.php +++ b/tests/plugins/TesterPlugin/src/Main.php @@ -57,7 +57,7 @@ class Main extends PluginBase implements Listener{ }), 10); $this->waitingTests = [ - new EventHandlerInheritanceTest($this), + //Add test objects here ]; } From 844ba0ff9fa69785ba8a583dd1cc7af5100f9871 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Nov 2024 15:40:34 +0000 Subject: [PATCH 095/290] Move event test fixtures to a subdirectory --- tests/phpunit/event/EventTest.php | 3 +++ tests/phpunit/event/HandlerListManagerTest.php | 6 ++++++ .../event/{ => fixtures}/TestAbstractAllowHandleEvent.php | 4 +++- tests/phpunit/event/{ => fixtures}/TestAbstractEvent.php | 4 +++- tests/phpunit/event/{ => fixtures}/TestChildEvent.php | 2 +- tests/phpunit/event/{ => fixtures}/TestConcreteEvent.php | 4 +++- .../{ => fixtures}/TestConcreteExtendsAbstractEvent.php | 2 +- .../{ => fixtures}/TestConcreteExtendsAllowHandleEvent.php | 2 +- .../{ => fixtures}/TestConcreteExtendsConcreteEvent.php | 2 +- tests/phpunit/event/{ => fixtures}/TestGrandchildEvent.php | 2 +- tests/phpunit/event/{ => fixtures}/TestParentEvent.php | 4 +++- 11 files changed, 26 insertions(+), 9 deletions(-) rename tests/phpunit/event/{ => fixtures}/TestAbstractAllowHandleEvent.php (92%) rename tests/phpunit/event/{ => fixtures}/TestAbstractEvent.php (92%) rename tests/phpunit/event/{ => fixtures}/TestChildEvent.php (95%) rename tests/phpunit/event/{ => fixtures}/TestConcreteEvent.php (92%) rename tests/phpunit/event/{ => fixtures}/TestConcreteExtendsAbstractEvent.php (95%) rename tests/phpunit/event/{ => fixtures}/TestConcreteExtendsAllowHandleEvent.php (95%) rename tests/phpunit/event/{ => fixtures}/TestConcreteExtendsConcreteEvent.php (95%) rename tests/phpunit/event/{ => fixtures}/TestGrandchildEvent.php (95%) rename tests/phpunit/event/{ => fixtures}/TestParentEvent.php (92%) diff --git a/tests/phpunit/event/EventTest.php b/tests/phpunit/event/EventTest.php index 259daaf5d..5d5ace54d 100644 --- a/tests/phpunit/event/EventTest.php +++ b/tests/phpunit/event/EventTest.php @@ -24,6 +24,9 @@ declare(strict_types=1); namespace pocketmine\event; use PHPUnit\Framework\TestCase; +use pocketmine\event\fixtures\TestChildEvent; +use pocketmine\event\fixtures\TestGrandchildEvent; +use pocketmine\event\fixtures\TestParentEvent; use pocketmine\plugin\Plugin; use pocketmine\plugin\PluginManager; use pocketmine\Server; diff --git a/tests/phpunit/event/HandlerListManagerTest.php b/tests/phpunit/event/HandlerListManagerTest.php index edff36639..c61043dab 100644 --- a/tests/phpunit/event/HandlerListManagerTest.php +++ b/tests/phpunit/event/HandlerListManagerTest.php @@ -24,6 +24,12 @@ declare(strict_types=1); namespace pocketmine\event; use PHPUnit\Framework\TestCase; +use pocketmine\event\fixtures\TestAbstractAllowHandleEvent; +use pocketmine\event\fixtures\TestAbstractEvent; +use pocketmine\event\fixtures\TestConcreteEvent; +use pocketmine\event\fixtures\TestConcreteExtendsAbstractEvent; +use pocketmine\event\fixtures\TestConcreteExtendsAllowHandleEvent; +use pocketmine\event\fixtures\TestConcreteExtendsConcreteEvent; class HandlerListManagerTest extends TestCase{ diff --git a/tests/phpunit/event/TestAbstractAllowHandleEvent.php b/tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php similarity index 92% rename from tests/phpunit/event/TestAbstractAllowHandleEvent.php rename to tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php index 1bac06bbb..383169809 100644 --- a/tests/phpunit/event/TestAbstractAllowHandleEvent.php +++ b/tests/phpunit/event/fixtures/TestAbstractAllowHandleEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; /** * @allowHandle diff --git a/tests/phpunit/event/TestAbstractEvent.php b/tests/phpunit/event/fixtures/TestAbstractEvent.php similarity index 92% rename from tests/phpunit/event/TestAbstractEvent.php rename to tests/phpunit/event/fixtures/TestAbstractEvent.php index 92a95363e..b48d8f526 100644 --- a/tests/phpunit/event/TestAbstractEvent.php +++ b/tests/phpunit/event/fixtures/TestAbstractEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; abstract class TestAbstractEvent extends Event{ diff --git a/tests/phpunit/event/TestChildEvent.php b/tests/phpunit/event/fixtures/TestChildEvent.php similarity index 95% rename from tests/phpunit/event/TestChildEvent.php rename to tests/phpunit/event/fixtures/TestChildEvent.php index 2b79f882f..569a2c069 100644 --- a/tests/phpunit/event/TestChildEvent.php +++ b/tests/phpunit/event/fixtures/TestChildEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestChildEvent extends TestParentEvent{ diff --git a/tests/phpunit/event/TestConcreteEvent.php b/tests/phpunit/event/fixtures/TestConcreteEvent.php similarity index 92% rename from tests/phpunit/event/TestConcreteEvent.php rename to tests/phpunit/event/fixtures/TestConcreteEvent.php index 8b159df91..cf744eb2c 100644 --- a/tests/phpunit/event/TestConcreteEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; class TestConcreteEvent extends Event{ diff --git a/tests/phpunit/event/TestConcreteExtendsAbstractEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsAbstractEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php index 3f0fa572f..54ec3dbb1 100644 --- a/tests/phpunit/event/TestConcreteExtendsAbstractEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsAbstractEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsAbstractEvent extends TestAbstractEvent{ diff --git a/tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php index e41465121..362ce693b 100644 --- a/tests/phpunit/event/TestConcreteExtendsAllowHandleEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsAllowHandleEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsAllowHandleEvent extends TestAbstractAllowHandleEvent{ diff --git a/tests/phpunit/event/TestConcreteExtendsConcreteEvent.php b/tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php similarity index 95% rename from tests/phpunit/event/TestConcreteExtendsConcreteEvent.php rename to tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php index cc9558935..3fd3a6cf0 100644 --- a/tests/phpunit/event/TestConcreteExtendsConcreteEvent.php +++ b/tests/phpunit/event/fixtures/TestConcreteExtendsConcreteEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestConcreteExtendsConcreteEvent extends TestConcreteEvent{ diff --git a/tests/phpunit/event/TestGrandchildEvent.php b/tests/phpunit/event/fixtures/TestGrandchildEvent.php similarity index 95% rename from tests/phpunit/event/TestGrandchildEvent.php rename to tests/phpunit/event/fixtures/TestGrandchildEvent.php index 7685f3b0b..bfe50f9f3 100644 --- a/tests/phpunit/event/TestGrandchildEvent.php +++ b/tests/phpunit/event/fixtures/TestGrandchildEvent.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; class TestGrandchildEvent extends TestChildEvent{ diff --git a/tests/phpunit/event/TestParentEvent.php b/tests/phpunit/event/fixtures/TestParentEvent.php similarity index 92% rename from tests/phpunit/event/TestParentEvent.php rename to tests/phpunit/event/fixtures/TestParentEvent.php index c770f6372..c20442272 100644 --- a/tests/phpunit/event/TestParentEvent.php +++ b/tests/phpunit/event/fixtures/TestParentEvent.php @@ -21,7 +21,9 @@ declare(strict_types=1); -namespace pocketmine\event; +namespace pocketmine\event\fixtures; + +use pocketmine\event\Event; class TestParentEvent extends Event{ From 1e3a858de6758c37e8a25c83258f439c348edde8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Nov 2024 16:42:50 +0000 Subject: [PATCH 096/290] Add setUp and tearDown for event unit tests --- tests/phpunit/event/EventTest.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/event/EventTest.php b/tests/phpunit/event/EventTest.php index 5d5ace54d..7410cc3bb 100644 --- a/tests/phpunit/event/EventTest.php +++ b/tests/phpunit/event/EventTest.php @@ -33,15 +33,26 @@ use pocketmine\Server; final class EventTest extends TestCase{ - public function testHandlerInheritance() : void{ + private Plugin $mockPlugin; + private PluginManager $pluginManager; + + protected function setUp() : void{ + HandlerListManager::global()->unregisterAll(); + //TODO: this is a really bad hack and could break any time if PluginManager decides to access its Server field //we really need to make it possible to register events without a Plugin or Server context $mockServer = $this->createMock(Server::class); - $mockPlugin = self::createStub(Plugin::class); - $mockPlugin->method('isEnabled')->willReturn(true); + $this->mockPlugin = self::createStub(Plugin::class); + $this->mockPlugin->method('isEnabled')->willReturn(true); - $pluginManager = new PluginManager($mockServer, null); + $this->pluginManager = new PluginManager($mockServer, null); + } + public static function tearDownAfterClass() : void{ + HandlerListManager::global()->unregisterAll(); + } + + public function testHandlerInheritance() : void{ $expectedOrder = [ TestGrandchildEvent::class, TestChildEvent::class, @@ -50,13 +61,13 @@ final class EventTest extends TestCase{ $actualOrder = []; foreach($expectedOrder as $class){ - $pluginManager->registerEvent( + $this->pluginManager->registerEvent( $class, function(TestParentEvent $event) use (&$actualOrder, $class) : void{ $actualOrder[] = $class; }, EventPriority::NORMAL, - $mockPlugin + $this->mockPlugin ); } From 7c2ed7d8845e7d2ce0dd62c77cdac5dd53306b19 Mon Sep 17 00:00:00 2001 From: GameParrot <85067619+GameParrot@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:22:10 -0500 Subject: [PATCH 097/290] Fix insta break check (#6528) --- src/player/Player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/player/Player.php b/src/player/Player.php index 192e26a5f..d203fe6a9 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1764,7 +1764,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return true; } - if(!$this->isCreative() && !$block->getBreakInfo()->breaksInstantly()){ + if(!$this->isCreative() && !$target->getBreakInfo()->breaksInstantly()){ $this->blockBreakHandler = new SurvivalBlockBreakHandler($this, $pos, $target, $face, 16); } From 0070426e97fa1a188e47f552e996cfb3fecc17b7 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 23 Nov 2024 19:07:24 +0000 Subject: [PATCH 098/290] auto-approve: only re-review if previous review was dismissed this avoids unnecessary spam when someone clicks "Update branch" on the PR. --- .github/workflows/team-pr-auto-approve.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index 405aafd8b..f14581213 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -9,9 +9,10 @@ on: pull_request_target: types: - opened - - synchronize - reopened - ready_for_review + pull_request_review: + types: dismissed jobs: dispatch: @@ -35,4 +36,4 @@ jobs: token: ${{ steps.generate-token.outputs.token }} repository: ${{ github.repository_owner }}/RestrictedActions event-type: auto_approve_collaborator_pr - client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}" }' + client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}", "reviewer_id": "${{ github.event.review.user.id || 0 }}" }' From 5b72f202bfb1d6351d60b8e611245896cb3dff25 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 23 Nov 2024 20:13:46 +0000 Subject: [PATCH 099/290] actions: automatically remove waiting label from PRs on synchronize there are probably other conditions where we'd want to remove this, but this will do for now. --- .github/workflows/pr-remove-waiting-label.yml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/pr-remove-waiting-label.yml diff --git a/.github/workflows/pr-remove-waiting-label.yml b/.github/workflows/pr-remove-waiting-label.yml new file mode 100644 index 000000000..0e411fe1a --- /dev/null +++ b/.github/workflows/pr-remove-waiting-label.yml @@ -0,0 +1,24 @@ +name: Remove waiting label from PRs + +on: + pull_request_target: + types: synchronize + +jobs: + delabel: + name: Remove label + runs-on: ubuntu-latest + + steps: + - name: Remove label + uses: actions/github-script@v7 + with: + github-token: ${{ github.token }} + script: | + const [owner, repo] = context.payload.repository.full_name.split('/'); + await github.rest.issues.removeLabel({ + owner: owner, + repo: repo, + issue_number: context.payload.number, + name: "Status: Waiting on Author", + }); From 7460e12b6affaf097a63edf35a77bb0cbee7799f Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 23 Nov 2024 21:48:30 +0000 Subject: [PATCH 100/290] pr-remove-waiting-label: suppress failure on 404 errors this usually means the label wasn't on the PR in the first place --- .github/workflows/pr-remove-waiting-label.yml | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-remove-waiting-label.yml b/.github/workflows/pr-remove-waiting-label.yml index 0e411fe1a..eb46043bd 100644 --- a/.github/workflows/pr-remove-waiting-label.yml +++ b/.github/workflows/pr-remove-waiting-label.yml @@ -16,9 +16,18 @@ jobs: github-token: ${{ github.token }} script: | const [owner, repo] = context.payload.repository.full_name.split('/'); - await github.rest.issues.removeLabel({ - owner: owner, - repo: repo, - issue_number: context.payload.number, - name: "Status: Waiting on Author", - }); + try { + await github.rest.issues.removeLabel({ + owner: owner, + repo: repo, + issue_number: context.payload.number, + name: "Status: Waiting on Author", + }); + } catch (error) { + if (error.status === 404) { + //probably label wasn't set on the issue + console.log('Failed to remove label (probably label isn\'t on the PR): ' + error.message); + } else { + throw error; + } + } From 8338ebaffdc1cb1ed14960ec27df16e14acab709 Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Sun, 24 Nov 2024 15:14:34 +0100 Subject: [PATCH 101/290] Add generic types for TaskHandler (#6030) --- src/scheduler/Task.php | 7 ++++ src/scheduler/TaskHandler.php | 9 +++++ src/scheduler/TaskScheduler.php | 44 ++++++++++++++++++++-- src/timings/Timings.php | 5 +++ tests/phpstan/configs/actual-problems.neon | 2 +- 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/scheduler/Task.php b/src/scheduler/Task.php index bde405a63..db42b477a 100644 --- a/src/scheduler/Task.php +++ b/src/scheduler/Task.php @@ -26,8 +26,12 @@ namespace pocketmine\scheduler; use pocketmine\utils\Utils; abstract class Task{ + /** @phpstan-var TaskHandler|null */ private ?TaskHandler $taskHandler = null; + /** + * @phpstan-return TaskHandler|null + */ final public function getHandler() : ?TaskHandler{ return $this->taskHandler; } @@ -36,6 +40,9 @@ abstract class Task{ return Utils::getNiceClassName($this); } + /** + * @phpstan-param TaskHandler|null $taskHandler + */ final public function setHandler(?TaskHandler $taskHandler) : void{ if($this->taskHandler === null || $taskHandler === null){ $this->taskHandler = $taskHandler; diff --git a/src/scheduler/TaskHandler.php b/src/scheduler/TaskHandler.php index b90c992d9..985344b64 100644 --- a/src/scheduler/TaskHandler.php +++ b/src/scheduler/TaskHandler.php @@ -26,6 +26,9 @@ namespace pocketmine\scheduler; use pocketmine\timings\Timings; use pocketmine\timings\TimingsHandler; +/** + * @template TTask of Task + */ class TaskHandler{ protected int $nextRun; @@ -36,6 +39,9 @@ class TaskHandler{ private string $taskName; private string $ownerName; + /** + * @phpstan-param TTask $task + */ public function __construct( protected Task $task, protected int $delay = -1, @@ -66,6 +72,9 @@ class TaskHandler{ $this->nextRun = $ticks; } + /** + * @phpstan-return TTask + */ public function getTask() : Task{ return $this->task; } diff --git a/src/scheduler/TaskScheduler.php b/src/scheduler/TaskScheduler.php index f2b7c4846..3e2b089c6 100644 --- a/src/scheduler/TaskScheduler.php +++ b/src/scheduler/TaskScheduler.php @@ -33,12 +33,12 @@ use pocketmine\utils\ReversePriorityQueue; class TaskScheduler{ private bool $enabled = true; - /** @phpstan-var ReversePriorityQueue */ + /** @phpstan-var ReversePriorityQueue> */ protected ReversePriorityQueue $queue; /** * @var ObjectSet|TaskHandler[] - * @phpstan-var ObjectSet + * @phpstan-var ObjectSet> */ protected ObjectSet $tasks; @@ -51,18 +51,42 @@ class TaskScheduler{ $this->tasks = new ObjectSet(); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleTask(Task $task) : TaskHandler{ return $this->addTask($task, -1, -1); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleDelayedTask(Task $task, int $delay) : TaskHandler{ return $this->addTask($task, $delay, -1); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleRepeatingTask(Task $task, int $period) : TaskHandler{ return $this->addTask($task, -1, $period); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ public function scheduleDelayedRepeatingTask(Task $task, int $delay, int $period) : TaskHandler{ return $this->addTask($task, $delay, $period); } @@ -77,10 +101,19 @@ class TaskScheduler{ } } + /** + * @phpstan-param TaskHandler $task + */ public function isQueued(TaskHandler $task) : bool{ return $this->tasks->contains($task); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TTask $task + * + * @phpstan-return TaskHandler + */ private function addTask(Task $task, int $delay, int $period) : TaskHandler{ if(!$this->enabled){ throw new \LogicException("Tried to schedule task to disabled scheduler"); @@ -99,6 +132,11 @@ class TaskScheduler{ return $this->handle(new TaskHandler($task, $delay, $period, $this->owner)); } + /** + * @phpstan-template TTask of Task + * @phpstan-param TaskHandler $handler + * @phpstan-return TaskHandler + */ private function handle(TaskHandler $handler) : TaskHandler{ if($handler->isDelayed()){ $nextRun = $this->currentTick + $handler->getDelay(); @@ -128,7 +166,7 @@ class TaskScheduler{ } $this->currentTick = $currentTick; while($this->isReady($this->currentTick)){ - /** @var TaskHandler $task */ + /** @phpstan-var TaskHandler $task */ $task = $this->queue->extract(); if($task->isCancelled()){ $this->tasks->remove($task); diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 563af69bf..77f8efee6 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -30,6 +30,7 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\player\Player; use pocketmine\scheduler\AsyncTask; +use pocketmine\scheduler\Task; use pocketmine\scheduler\TaskHandler; use function get_class; use function str_starts_with; @@ -192,6 +193,10 @@ abstract class Timings{ } + /** + * @template TTask of Task + * @phpstan-param TaskHandler $task + */ public static function getScheduledTaskTimings(TaskHandler $task, int $period) : TimingsHandler{ self::init(); $name = "Task: " . $task->getTaskName(); diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index e778cf004..f37d23878 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -801,7 +801,7 @@ parameters: path: ../../../src/scheduler/BulkCurlTask.php - - message: "#^Cannot call method getNextRun\\(\\) on array\\\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\.$#" + message: "#^Cannot call method getNextRun\\(\\) on array\\\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\\\.$#" count: 1 path: ../../../src/scheduler/TaskScheduler.php From a5f607138c491ef55431f5d20baf31d03be59769 Mon Sep 17 00:00:00 2001 From: zSALLAZAR <59490940+zSALLAZAR@users.noreply.github.com> Date: Sun, 24 Nov 2024 16:01:26 +0100 Subject: [PATCH 102/290] Implement Ice Bomb (#5452) Co-authored-by: Dylan K. Taylor --- .../ItemSerializerDeserializerRegistrar.php | 1 + src/entity/EntityFactory.php | 5 ++ src/entity/projectile/IceBomb.php | 86 +++++++++++++++++++ src/item/IceBomb.php | 48 +++++++++++ src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 1 + src/item/VanillaItems.php | 2 + src/world/sound/IceBombHitSound.php | 34 ++++++++ 8 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/entity/projectile/IceBomb.php create mode 100644 src/item/IceBomb.php create mode 100644 src/world/sound/IceBombHitSound.php diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 7803cea5c..8240bf063 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -266,6 +266,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::HONEY_BOTTLE, Items::HONEY_BOTTLE()); $this->map1to1Item(Ids::HONEYCOMB, Items::HONEYCOMB()); $this->map1to1Item(Ids::HOST_ARMOR_TRIM_SMITHING_TEMPLATE, Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE()); + $this->map1to1Item(Ids::ICE_BOMB, Items::ICE_BOMB()); $this->map1to1Item(Ids::INK_SAC, Items::INK_SAC()); $this->map1to1Item(Ids::IRON_AXE, Items::IRON_AXE()); $this->map1to1Item(Ids::IRON_BOOTS, Items::IRON_BOOTS()); diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 3d53233ab..03d9c03e6 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -43,6 +43,7 @@ use pocketmine\entity\projectile\Arrow; use pocketmine\entity\projectile\Egg; use pocketmine\entity\projectile\EnderPearl; use pocketmine\entity\projectile\ExperienceBottle; +use pocketmine\entity\projectile\IceBomb; use pocketmine\entity\projectile\Snowball; use pocketmine\entity\projectile\SplashPotion; use pocketmine\item\Item; @@ -120,6 +121,10 @@ final class EntityFactory{ return new FallingBlock(Helper::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(RuntimeBlockStateRegistry::getInstance(), $nbt), $nbt); }, ['FallingSand', 'minecraft:falling_block']); + $this->register(IceBomb::class, function(World $world, CompoundTag $nbt) : IceBomb{ + return new IceBomb(Helper::parseLocation($nbt, $world), null, $nbt); + }, ['minecraft:ice_bomb']); + $this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{ $itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM); if($itemTag === null){ diff --git a/src/entity/projectile/IceBomb.php b/src/entity/projectile/IceBomb.php new file mode 100644 index 000000000..69a5d46a2 --- /dev/null +++ b/src/entity/projectile/IceBomb.php @@ -0,0 +1,86 @@ +getTypeId() === BlockTypeIds::WATER){ + $pos = $block->getPosition(); + + return AxisAlignedBB::one()->offset($pos->x, $pos->y, $pos->z)->calculateIntercept($start, $end); + } + + return parent::calculateInterceptWithBlock($block, $start, $end); + } + + protected function onHit(ProjectileHitEvent $event) : void{ + $world = $this->getWorld(); + $pos = $this->location; + + $world->addSound($pos, new IceBombHitSound()); + $itemBreakParticle = new ItemBreakParticle(VanillaItems::ICE_BOMB()); + for($i = 0; $i < 6; ++$i){ + $world->addParticle($pos, $itemBreakParticle); + } + } + + protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{ + parent::onHitBlock($blockHit, $hitResult); + + $pos = $blockHit->getPosition(); + $world = $pos->getWorld(); + $posX = $pos->getFloorX(); + $posY = $pos->getFloorY(); + $posZ = $pos->getFloorZ(); + + $ice = VanillaBlocks::ICE(); + for($x = $posX - 1; $x <= $posX + 1; $x++){ + for($y = $posY - 1; $y <= $posY + 1; $y++){ + for($z = $posZ - 1; $z <= $posZ + 1; $z++){ + if($world->getBlockAt($x, $y, $z)->getTypeId() === BlockTypeIds::WATER){ + $world->setBlockAt($x, $y, $z, $ice); + } + } + } + } + } +} diff --git a/src/item/IceBomb.php b/src/item/IceBomb.php new file mode 100644 index 000000000..fbc9f24a2 --- /dev/null +++ b/src/item/IceBomb.php @@ -0,0 +1,48 @@ +register("honey_bottle", fn() => Items::HONEY_BOTTLE()); $result->register("host_armor_trim_smithing_template", fn() => Items::HOST_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("honeycomb", fn() => Items::HONEYCOMB()); + $result->register("ice_bomb", fn() => Items::ICE_BOMB()); $result->register("ink_sac", fn() => Items::INK_SAC()); $result->register("iron_axe", fn() => Items::IRON_AXE()); $result->register("iron_boots", fn() => Items::IRON_BOOTS()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 5899b6357..dcf59daf6 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -192,6 +192,7 @@ use function strtolower; * @method static Item HONEYCOMB() * @method static HoneyBottle HONEY_BOTTLE() * @method static Item HOST_ARMOR_TRIM_SMITHING_TEMPLATE() + * @method static IceBomb ICE_BOMB() * @method static Item INK_SAC() * @method static Axe IRON_AXE() * @method static Armor IRON_BOOTS() @@ -504,6 +505,7 @@ final class VanillaItems{ self::register("heart_of_the_sea", fn(IID $id) => new Item($id, "Heart of the Sea")); self::register("honey_bottle", fn(IID $id) => new HoneyBottle($id, "Honey Bottle")); self::register("honeycomb", fn(IID $id) => new Item($id, "Honeycomb")); + self::register("ice_bomb", fn(IID $id) => new IceBomb($id, "Ice Bomb")); self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac")); self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot")); self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget")); diff --git a/src/world/sound/IceBombHitSound.php b/src/world/sound/IceBombHitSound.php new file mode 100644 index 000000000..7dcdeebf6 --- /dev/null +++ b/src/world/sound/IceBombHitSound.php @@ -0,0 +1,34 @@ + Date: Sun, 24 Nov 2024 23:49:21 +0000 Subject: [PATCH 103/290] Candle: fix extinguish logic closes #5983 --- src/block/CakeWithCandle.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 4479faee1..380d080c5 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -52,6 +52,9 @@ class CakeWithCandle extends BaseCake{ } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($this->lit && $face !== Facing::UP){ + return true; + } if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){ return true; } From aef4fa71747b1773541bc7a294c98b8a40565127 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Nov 2024 23:50:30 +0000 Subject: [PATCH 104/290] Remove unused variable --- src/network/mcpe/InventoryManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index e4c303121..70c427aa1 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -592,7 +592,6 @@ class InventoryManager{ $info = $this->trackItemStack($entry, $slot, $itemStack, null); $contents[] = new ItemStackWrapper($info->getStackId(), $itemStack); } - $clearSlotWrapper = new ItemStackWrapper(0, ItemStack::null()); if($entry->complexSlotMap !== null){ foreach($contents as $slotId => $info){ $packetSlot = $entry->complexSlotMap->mapCoreToNet($slotId) ?? null; From d72941c36c76813150803e2137bc2bbd22c3c4fb Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 24 Nov 2024 23:56:44 +0000 Subject: [PATCH 105/290] Update IceBomb.php --- src/entity/projectile/IceBomb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/projectile/IceBomb.php b/src/entity/projectile/IceBomb.php index 69a5d46a2..5a3525c74 100644 --- a/src/entity/projectile/IceBomb.php +++ b/src/entity/projectile/IceBomb.php @@ -36,7 +36,7 @@ use pocketmine\world\particle\ItemBreakParticle; use pocketmine\world\sound\IceBombHitSound; class IceBomb extends Throwable{ - public static function getNetworkTypeId() : string{ return EntityIds::ICE_BOMB; } + public function getNetworkTypeId() : string{ return EntityIds::ICE_BOMB; } public function getResultDamage() : int{ return -1; From 5325ecee373bbba5fb9e4a42ffbe38adb8655e6c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Nov 2024 14:30:58 +0000 Subject: [PATCH 106/290] Deal with a whole lot of PHPStan suppressed key casting errors closes #6534 --- build/generate-biome-ids.php | 2 +- ...enerate-pocketmine-yml-property-consts.php | 2 +- build/generate-registry-annotations.php | 4 ++- src/MemoryManager.php | 10 +++---- src/Server.php | 13 +++++++-- src/block/RuntimeBlockStateRegistry.php | 1 + src/block/inventory/EnchantInventory.php | 5 +++- src/block/utils/SignText.php | 7 ++++- src/command/Command.php | 13 +++++++-- src/command/SimpleCommandMap.php | 11 ++++++-- src/crafting/CraftingManager.php | 6 ++-- .../CraftingManagerFromDataHelper.php | 2 +- src/crafting/CraftingRecipe.php | 2 ++ src/crafting/ShapedRecipe.php | 28 ++++++++++++++----- src/crafting/ShapelessRecipe.php | 17 +++++++---- src/crash/CrashDumpData.php | 10 +++++-- src/data/bedrock/ItemTagToIdMap.php | 2 +- src/data/bedrock/LegacyToStringIdMap.php | 3 +- .../item/upgrade/R12ItemIdToBlockIdMap.php | 2 +- src/entity/Entity.php | 5 +++- src/event/HandlerList.php | 5 +++- src/inventory/BaseInventory.php | 7 +++-- src/inventory/CreativeInventory.php | 6 +++- .../transaction/CraftingTransaction.php | 19 +++++++++++-- .../transaction/InventoryTransaction.php | 23 +++++++++------ src/item/ItemEnchantmentHandlingTrait.php | 6 +++- src/item/LegacyStringToItemParser.php | 3 +- src/lang/Language.php | 4 +-- src/lang/Translatable.php | 4 ++- src/network/NetworkSessionManager.php | 10 +++++-- src/network/mcpe/InventoryManager.php | 1 + src/network/mcpe/NetworkSession.php | 2 +- .../mcpe/StandardPacketBroadcaster.php | 1 - src/network/mcpe/cache/ChunkCache.php | 5 +++- .../convert/BlockStateDictionaryEntry.php | 5 +++- .../ItemTypeDictionaryFromDataHelper.php | 3 +- .../mcpe/handler/InGamePacketHandler.php | 2 +- .../mcpe/handler/ItemStackRequestExecutor.php | 3 +- .../mcpe/handler/LoginPacketHandler.php | 2 +- src/permission/BanList.php | 8 ++++-- src/permission/PermissionAttachment.php | 10 +++++-- src/permission/PermissionManager.php | 13 +++++++-- src/plugin/ApiVersion.php | 4 +-- src/plugin/PluginDescription.php | 5 ++-- src/plugin/PluginGraylist.php | 3 +- src/plugin/PluginManager.php | 13 +++++++-- src/promise/Promise.php | 3 +- src/resourcepacks/ResourcePackManager.php | 3 +- src/utils/Utils.php | 15 +++++++++- src/world/BlockTransaction.php | 5 +++- src/world/WorldManager.php | 6 +++- src/world/format/SubChunk.php | 10 ++++--- src/world/format/io/BaseWorldProvider.php | 2 ++ src/world/format/io/ChunkData.php | 15 ++++++++-- .../format/io/region/RegionGarbageMap.php | 10 +++---- src/world/format/io/region/RegionLoader.php | 8 ++++-- .../format/io/region/RegionWorldProvider.php | 5 +++- src/world/generator/PopulationTask.php | 6 ++-- tests/phpstan/configs/phpstan-bugs.neon | 5 ++++ .../rules/UnsafeForeachArrayOfStringRule.php | 26 ++++++++++++++--- tests/phpunit/block/BlockTest.php | 2 +- tests/phpunit/item/ItemTest.php | 1 - .../utils/CloningRegistryTraitTest.php | 2 +- tests/phpunit/utils/TestCloningRegistry.php | 6 +++- tools/compact-regions.php | 6 ++-- tools/generate-bedrock-data-from-packets.php | 14 +++++----- 66 files changed, 338 insertions(+), 124 deletions(-) diff --git a/build/generate-biome-ids.php b/build/generate-biome-ids.php index f36591fe4..56f871f43 100644 --- a/build/generate-biome-ids.php +++ b/build/generate-biome-ids.php @@ -122,7 +122,7 @@ if(!is_array($ids)){ throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object"); } $cleanedIds = []; -foreach($ids as $name => $id){ +foreach(Utils::promoteKeys($ids) as $name => $id){ if(!is_string($name) || !is_int($id)){ throw new \RuntimeException("Invalid biome ID map, expected string => int map"); } diff --git a/build/generate-pocketmine-yml-property-consts.php b/build/generate-pocketmine-yml-property-consts.php index dcc574f8a..f90f3fc66 100644 --- a/build/generate-pocketmine-yml-property-consts.php +++ b/build/generate-pocketmine-yml-property-consts.php @@ -42,7 +42,7 @@ $constants = []; * @phpstan-param-out array $constants */ function collectProperties(string $prefix, array $properties, array &$constants) : void{ - foreach($properties as $propertyName => $property){ + foreach(Utils::promoteKeys($properties) as $propertyName => $property){ $fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName; $constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName)); diff --git a/build/generate-registry-annotations.php b/build/generate-registry-annotations.php index bcdaee4eb..35d089395 100644 --- a/build/generate-registry-annotations.php +++ b/build/generate-registry-annotations.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\build\update_registry_annotations; +use pocketmine\utils\Utils; use function basename; use function class_exists; use function count; @@ -48,6 +49,7 @@ if(count($argv) !== 2){ /** * @param object[] $members + * @phpstan-param array $members */ function generateMethodAnnotations(string $namespaceName, array $members) : string{ $selfName = basename(__FILE__); @@ -60,7 +62,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri static $lineTmpl = " * @method static %2\$s %s()"; $memberLines = []; - foreach($members as $name => $member){ + foreach(Utils::stringifyKeys($members) as $name => $member){ $reflect = new \ReflectionClass($member); while($reflect !== false && $reflect->isAnonymous()){ $reflect = $reflect->getParentClass(); diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 96e3bf49c..4308167d3 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -332,7 +332,7 @@ class MemoryManager{ continue; } $methodStatics = []; - foreach($method->getStaticVariables() as $name => $variable){ + foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){ $methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); } if(count($methodStatics) > 0){ @@ -360,7 +360,7 @@ class MemoryManager{ '_SESSION' => true ]; - foreach($GLOBALS as $varName => $value){ + foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){ if(isset($ignoredGlobals[$varName])){ continue; } @@ -376,7 +376,7 @@ class MemoryManager{ $reflect = new \ReflectionFunction($function); $vars = []; - foreach($reflect->getStaticVariables() as $varName => $variable){ + foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){ $vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); } if(count($vars) > 0){ @@ -416,7 +416,7 @@ class MemoryManager{ $info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize); } - foreach($reflect->getStaticVariables() as $name => $variable){ + foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){ $info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); } }else{ @@ -499,7 +499,7 @@ class MemoryManager{ } $data = []; $numeric = 0; - foreach($from as $key => $value){ + foreach(Utils::promoteKeys($from) as $key => $value){ $data[$numeric] = [ "k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), "v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), diff --git a/src/Server.php b/src/Server.php index a34349bb5..074088068 100644 --- a/src/Server.php +++ b/src/Server.php @@ -736,12 +736,15 @@ class Server{ /** * @return string[][] + * @phpstan-return array> */ public function getCommandAliases() : array{ $section = $this->configGroup->getProperty(Yml::ALIASES); $result = []; if(is_array($section)){ - foreach($section as $key => $value){ + foreach(Utils::promoteKeys($section) as $key => $value){ + //TODO: more validation needed here + //key might not be a string, value might not be list $commands = []; if(is_array($value)){ $commands = $value; @@ -749,7 +752,7 @@ class Server{ $commands[] = (string) $value; } - $result[$key] = $commands; + $result[(string) $key] = $commands; } } @@ -1094,7 +1097,11 @@ class Server{ $anyWorldFailedToLoad = false; - foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){ + foreach(Utils::promoteKeys((array) $this->configGroup->getProperty(Yml::WORLDS, [])) as $name => $options){ + if(!is_string($name)){ + //TODO: this probably should be an error + continue; + } if($options === null){ $options = []; }elseif(!is_array($options)){ diff --git a/src/block/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php index 78be658bc..a8ece7722 100644 --- a/src/block/RuntimeBlockStateRegistry.php +++ b/src/block/RuntimeBlockStateRegistry.php @@ -132,6 +132,7 @@ class RuntimeBlockStateRegistry{ /** * @return Block[] + * @phpstan-return array */ public function getAllKnownStates() : array{ return $this->fullList; diff --git a/src/block/inventory/EnchantInventory.php b/src/block/inventory/EnchantInventory.php index b726dbedf..6dffbad32 100644 --- a/src/block/inventory/EnchantInventory.php +++ b/src/block/inventory/EnchantInventory.php @@ -39,7 +39,10 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor public const SLOT_INPUT = 0; public const SLOT_LAPIS = 1; - /** @var EnchantingOption[] $options */ + /** + * @var EnchantingOption[] $options + * @phpstan-var list + */ private array $options = []; public function __construct(Position $holder){ diff --git a/src/block/utils/SignText.php b/src/block/utils/SignText.php index 51285d267..a7e8759b8 100644 --- a/src/block/utils/SignText.php +++ b/src/block/utils/SignText.php @@ -36,13 +36,17 @@ use function str_contains; class SignText{ public const LINE_COUNT = 4; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var array{0: string, 1: string, 2: string, 3: string} + */ private array $lines; private Color $baseColor; private bool $glowing; /** * @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order + * @phpstan-param array{0?: string, 1?: string, 2?: string, 3?: string}|null $lines * * @throws \InvalidArgumentException if the array size is greater than 4 * @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array @@ -82,6 +86,7 @@ class SignText{ * Returns an array of lines currently on the sign. * * @return string[] + * @phpstan-return array{0: string, 1: string, 2: string, 3: string} */ public function getLines() : array{ return $this->lines; diff --git a/src/command/Command.php b/src/command/Command.php index 02bae60d9..4c2c6815b 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -44,10 +44,16 @@ abstract class Command{ private string $nextLabel; private string $label; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $aliases = []; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $activeAliases = []; private ?CommandMap $commandMap = null; @@ -62,6 +68,7 @@ abstract class Command{ /** * @param string[] $aliases + * @phpstan-param list $aliases */ public function __construct(string $name, Translatable|string $description = "", Translatable|string|null $usageMessage = null, array $aliases = []){ $this->name = $name; @@ -182,6 +189,7 @@ abstract class Command{ /** * @return string[] + * @phpstan-return list */ public function getAliases() : array{ return $this->activeAliases; @@ -201,6 +209,7 @@ abstract class Command{ /** * @param string[] $aliases + * @phpstan-param list $aliases */ public function setAliases(array $aliases) : void{ $this->aliases = $aliases; diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php index 1268e715b..06367994a 100644 --- a/src/command/SimpleCommandMap.php +++ b/src/command/SimpleCommandMap.php @@ -70,6 +70,7 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\Server; use pocketmine\timings\Timings; use pocketmine\utils\TextFormat; +use pocketmine\utils\Utils; use function array_shift; use function count; use function implode; @@ -80,7 +81,10 @@ use function trim; class SimpleCommandMap implements CommandMap{ - /** @var Command[] */ + /** + * @var Command[] + * @phpstan-var array + */ protected array $knownCommands = []; public function __construct(private Server $server){ @@ -169,7 +173,7 @@ class SimpleCommandMap implements CommandMap{ } public function unregister(Command $command) : bool{ - foreach($this->knownCommands as $lbl => $cmd){ + foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){ if($cmd === $command){ unset($this->knownCommands[$lbl]); } @@ -237,6 +241,7 @@ class SimpleCommandMap implements CommandMap{ /** * @return Command[] + * @phpstan-return array */ public function getCommands() : array{ return $this->knownCommands; @@ -245,7 +250,7 @@ class SimpleCommandMap implements CommandMap{ public function registerServerAliases() : void{ $values = $this->server->getCommandAliases(); - foreach($values as $alias => $commandStrings){ + foreach(Utils::stringifyKeys($values) as $alias => $commandStrings){ if(str_contains($alias, ":")){ $this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias))); continue; diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php index c7c0b10c6..ff2be6926 100644 --- a/src/crafting/CraftingManager.php +++ b/src/crafting/CraftingManager.php @@ -110,14 +110,15 @@ class CraftingManager{ /** * @param Item[] $items + * @phpstan-param list $items * * @return Item[] + * @phpstan-return list */ private static function pack(array $items) : array{ - /** @var Item[] $result */ $result = []; - foreach($items as $i => $item){ + foreach($items as $item){ foreach($result as $otherItem){ if($item->canStackWith($otherItem)){ $otherItem->setCount($otherItem->getCount() + $item->getCount()); @@ -134,6 +135,7 @@ class CraftingManager{ /** * @param Item[] $outputs + * @phpstan-param list $outputs */ private static function hashOutputs(array $outputs) : string{ $outputs = self::pack($outputs); diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index 616c2a4bd..3df6bfb62 100644 --- a/src/crafting/CraftingManagerFromDataHelper.php +++ b/src/crafting/CraftingManagerFromDataHelper.php @@ -193,7 +193,7 @@ final class CraftingManagerFromDataHelper{ */ private static function loadJsonObjectListIntoModel(\JsonMapper $mapper, string $modelClass, array $data) : array{ $result = []; - foreach($data as $i => $item){ + foreach(Utils::promoteKeys($data) as $i => $item){ if(!is_object($item)){ throw new SavedDataLoadingException("Invalid entry at index $i: expected object, got " . get_debug_type($item)); } diff --git a/src/crafting/CraftingRecipe.php b/src/crafting/CraftingRecipe.php index 02e6fce04..d7342aae3 100644 --- a/src/crafting/CraftingRecipe.php +++ b/src/crafting/CraftingRecipe.php @@ -30,6 +30,7 @@ interface CraftingRecipe{ * Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count. * * @return RecipeIngredient[] + * @phpstan-return list */ public function getIngredientList() : array; @@ -37,6 +38,7 @@ interface CraftingRecipe{ * Returns a list of results this recipe will produce when the inputs in the given crafting grid are consumed. * * @return Item[] + * @phpstan-return list */ public function getResultsFor(CraftingGrid $grid) : array; diff --git a/src/crafting/ShapedRecipe.php b/src/crafting/ShapedRecipe.php index f13ba3ae7..4c40eb0f5 100644 --- a/src/crafting/ShapedRecipe.php +++ b/src/crafting/ShapedRecipe.php @@ -32,11 +32,20 @@ use function str_contains; use function strlen; class ShapedRecipe implements CraftingRecipe{ - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $shape = []; - /** @var RecipeIngredient[] char => RecipeIngredient map */ + /** + * @var RecipeIngredient[] char => RecipeIngredient map + * @phpstan-var array + */ private array $ingredientList = []; - /** @var Item[] */ + /** + * @var Item[] + * @phpstan-var list + */ private array $results = []; private int $height; @@ -56,6 +65,10 @@ class ShapedRecipe implements CraftingRecipe{ * @param Item[] $results List of items that this recipe produces when crafted. * * Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns. + * + * @phpstan-param list $shape + * @phpstan-param array $ingredients + * @phpstan-param list $results */ public function __construct(array $shape, array $ingredients, array $results){ $this->height = count($shape); @@ -84,7 +97,7 @@ class ShapedRecipe implements CraftingRecipe{ $this->shape = $shape; - foreach($ingredients as $char => $i){ + foreach(Utils::stringifyKeys($ingredients) as $char => $i){ if(!str_contains(implode($this->shape), $char)){ throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape"); } @@ -105,6 +118,7 @@ class ShapedRecipe implements CraftingRecipe{ /** * @return Item[] + * @phpstan-return list */ public function getResults() : array{ return Utils::cloneObjectArray($this->results); @@ -112,6 +126,7 @@ class ShapedRecipe implements CraftingRecipe{ /** * @return Item[] + * @phpstan-return list */ public function getResultsFor(CraftingGrid $grid) : array{ return $this->getResults(); @@ -119,6 +134,7 @@ class ShapedRecipe implements CraftingRecipe{ /** * @return (RecipeIngredient|null)[][] + * @phpstan-return list> */ public function getIngredientMap() : array{ $ingredients = []; @@ -132,9 +148,6 @@ class ShapedRecipe implements CraftingRecipe{ return $ingredients; } - /** - * @return RecipeIngredient[] - */ public function getIngredientList() : array{ $ingredients = []; @@ -157,6 +170,7 @@ class ShapedRecipe implements CraftingRecipe{ /** * Returns an array of strings containing characters representing the recipe's shape. * @return string[] + * @phpstan-return list */ public function getShape() : array{ return $this->shape; diff --git a/src/crafting/ShapelessRecipe.php b/src/crafting/ShapelessRecipe.php index a9e1f023c..7a4a22fda 100644 --- a/src/crafting/ShapelessRecipe.php +++ b/src/crafting/ShapelessRecipe.php @@ -28,15 +28,24 @@ use pocketmine\utils\Utils; use function count; class ShapelessRecipe implements CraftingRecipe{ - /** @var RecipeIngredient[] */ + /** + * @var RecipeIngredient[] + * @phpstan-var list + */ private array $ingredients = []; - /** @var Item[] */ + /** + * @var Item[] + * @phpstan-var list + */ private array $results; private ShapelessRecipeType $type; /** * @param RecipeIngredient[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array. * @param Item[] $results List of result items created by this recipe. + * + * @phpstan-param list $ingredients + * @phpstan-param list $results */ public function __construct(array $ingredients, array $results, ShapelessRecipeType $type){ $this->type = $type; @@ -50,6 +59,7 @@ class ShapelessRecipe implements CraftingRecipe{ /** * @return Item[] + * @phpstan-return list */ public function getResults() : array{ return Utils::cloneObjectArray($this->results); @@ -63,9 +73,6 @@ class ShapelessRecipe implements CraftingRecipe{ return $this->type; } - /** - * @return RecipeIngredient[] - */ public function getIngredientList() : array{ return $this->ingredients; } diff --git a/src/crash/CrashDumpData.php b/src/crash/CrashDumpData.php index b71e6f405..f8a20abd5 100644 --- a/src/crash/CrashDumpData.php +++ b/src/crash/CrashDumpData.php @@ -43,7 +43,10 @@ final class CrashDumpData implements \JsonSerializable{ public string $plugin = ""; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var array + */ public array $code = []; /** @var string[] */ @@ -55,7 +58,10 @@ final class CrashDumpData implements \JsonSerializable{ */ public array $plugins = []; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ public array $parameters = []; public string $serverDotProperties = ""; diff --git a/src/data/bedrock/ItemTagToIdMap.php b/src/data/bedrock/ItemTagToIdMap.php index 84f2ff451..af9cb3ce5 100644 --- a/src/data/bedrock/ItemTagToIdMap.php +++ b/src/data/bedrock/ItemTagToIdMap.php @@ -48,7 +48,7 @@ final class ItemTagToIdMap{ throw new AssumptionFailedError("Invalid item tag map, expected array"); } $cleanMap = []; - foreach($map as $tagName => $ids){ + foreach(Utils::promoteKeys($map) as $tagName => $ids){ if(!is_string($tagName)){ throw new AssumptionFailedError("Invalid item tag name $tagName, expected string as key"); } diff --git a/src/data/bedrock/LegacyToStringIdMap.php b/src/data/bedrock/LegacyToStringIdMap.php index 2b5375db2..1a92f2b6b 100644 --- a/src/data/bedrock/LegacyToStringIdMap.php +++ b/src/data/bedrock/LegacyToStringIdMap.php @@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; +use pocketmine\utils\Utils; use function is_array; use function is_int; use function is_string; @@ -43,7 +44,7 @@ abstract class LegacyToStringIdMap{ if(!is_array($stringToLegacyId)){ throw new AssumptionFailedError("Invalid format of ID map"); } - foreach($stringToLegacyId as $stringId => $legacyId){ + foreach(Utils::promoteKeys($stringToLegacyId) as $stringId => $legacyId){ if(!is_string($stringId) || !is_int($legacyId)){ throw new AssumptionFailedError("ID map should have string keys and int values"); } diff --git a/src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php b/src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php index 2aac8de64..19cfe6c78 100644 --- a/src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php +++ b/src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php @@ -56,7 +56,7 @@ final class R12ItemIdToBlockIdMap{ } $builtMap = []; - foreach($map as $itemId => $blockId){ + foreach(Utils::promoteKeys($map) as $itemId => $blockId){ if(!is_string($itemId)){ throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected string as key"); } diff --git a/src/entity/Entity.php b/src/entity/Entity.php index c55a8716c..3d791ebca 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -102,7 +102,10 @@ abstract class Entity{ return self::$entityCount++; } - /** @var Player[] */ + /** + * @var Player[] + * @phpstan-var array + */ protected array $hasSpawned = []; protected int $id; diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 2072cd522..e7d229a3f 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -30,7 +30,10 @@ use function spl_object_id; use const SORT_NUMERIC; class HandlerList{ - /** @var RegisteredListener[][] */ + /** + * @var RegisteredListener[][] + * @phpstan-var array> + */ private array $handlerSlots = []; /** @var RegisteredListenerCache[] */ diff --git a/src/inventory/BaseInventory.php b/src/inventory/BaseInventory.php index 522c827a4..0d5d1ffe6 100644 --- a/src/inventory/BaseInventory.php +++ b/src/inventory/BaseInventory.php @@ -41,7 +41,10 @@ use function spl_object_id; */ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ protected int $maxStackSize = Inventory::MAX_STACK; - /** @var Player[] */ + /** + * @var Player[] + * @phpstan-var array + */ protected array $viewers = []; /** * @var InventoryListener[]|ObjectSet @@ -286,8 +289,6 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{ } public function removeItem(Item ...$slots) : array{ - /** @var Item[] $searchItems */ - /** @var Item[] $slots */ $searchItems = []; foreach($slots as $slot){ if(!$slot->isNull()){ diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 7502a6785..57e5cbb4e 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -36,7 +36,10 @@ final class CreativeInventory{ use SingletonTrait; use DestructorCallbackTrait; - /** @var Item[] */ + /** + * @var Item[] + * @phpstan-var array + */ private array $creative = []; /** @phpstan-var ObjectSet<\Closure() : void> */ @@ -69,6 +72,7 @@ final class CreativeInventory{ /** * @return Item[] + * @phpstan-return array */ public function getAll() : array{ return Utils::cloneObjectArray($this->creative); diff --git a/src/inventory/transaction/CraftingTransaction.php b/src/inventory/transaction/CraftingTransaction.php index 2ae231b6e..9e3c50552 100644 --- a/src/inventory/transaction/CraftingTransaction.php +++ b/src/inventory/transaction/CraftingTransaction.php @@ -57,9 +57,15 @@ class CraftingTransaction extends InventoryTransaction{ protected ?CraftingRecipe $recipe = null; protected ?int $repetitions = null; - /** @var Item[] */ + /** + * @var Item[] + * @phpstan-var list + */ protected array $inputs = []; - /** @var Item[] */ + /** + * @var Item[] + * @phpstan-var list + */ protected array $outputs = []; private CraftingManager $craftingManager; @@ -74,6 +80,9 @@ class CraftingTransaction extends InventoryTransaction{ /** * @param Item[] $providedItems * @return Item[] + * + * @phpstan-param list $providedItems + * @phpstan-return list */ private static function packItems(array $providedItems) : array{ $packedProvidedItems = []; @@ -94,6 +103,9 @@ class CraftingTransaction extends InventoryTransaction{ /** * @param Item[] $providedItems * @param RecipeIngredient[] $recipeIngredients + * + * @phpstan-param list $providedItems + * @phpstan-param list $recipeIngredients */ public static function matchIngredients(array $providedItems, array $recipeIngredients, int $expectedIterations) : void{ if(count($recipeIngredients) === 0){ @@ -172,6 +184,9 @@ class CraftingTransaction extends InventoryTransaction{ * @param Item[] $txItems * @param Item[] $recipeItems * + * @phpstan-param list $txItems + * @phpstan-param list $recipeItems + * * @throws TransactionValidationException */ protected function matchOutputs(array $txItems, array $recipeItems) : int{ diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index 2ca00f910..b3465a8df 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -29,6 +29,7 @@ use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\item\Item; use pocketmine\player\Player; +use pocketmine\utils\Utils; use function array_keys; use function array_values; use function assert; @@ -57,10 +58,16 @@ use function spl_object_id; class InventoryTransaction{ protected bool $hasExecuted = false; - /** @var Inventory[] */ + /** + * @var Inventory[] + * @phpstan-var array + */ protected array $inventories = []; - /** @var InventoryAction[] */ + /** + * @var InventoryAction[] + * @phpstan-var array + */ protected array $actions = []; /** @@ -81,6 +88,7 @@ class InventoryTransaction{ /** * @return Inventory[] + * @phpstan-return array */ public function getInventories() : array{ return $this->inventories; @@ -93,6 +101,7 @@ class InventoryTransaction{ * significance and should not be relied on. * * @return InventoryAction[] + * @phpstan-return array */ public function getActions() : array{ return $this->actions; @@ -133,8 +142,8 @@ class InventoryTransaction{ /** * @param Item[] $needItems * @param Item[] $haveItems - * @phpstan-param-out Item[] $needItems - * @phpstan-param-out Item[] $haveItems + * @phpstan-param-out list $needItems + * @phpstan-param-out list $haveItems * * @throws TransactionValidationException */ @@ -188,11 +197,8 @@ class InventoryTransaction{ * wrong order), so this method also tries to chain them into order. */ protected function squashDuplicateSlotChanges() : void{ - /** @var SlotChangeAction[][] $slotChanges */ $slotChanges = []; - /** @var Inventory[] $inventories */ $inventories = []; - /** @var int[] $slots */ $slots = []; foreach($this->actions as $key => $action){ @@ -203,7 +209,7 @@ class InventoryTransaction{ } } - foreach($slotChanges as $hash => $list){ + foreach(Utils::stringifyKeys($slotChanges) as $hash => $list){ if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot continue; } @@ -233,6 +239,7 @@ class InventoryTransaction{ /** * @param SlotChangeAction[] $possibleActions + * @phpstan-param list $possibleActions */ protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{ assert(count($possibleActions) > 0); diff --git a/src/item/ItemEnchantmentHandlingTrait.php b/src/item/ItemEnchantmentHandlingTrait.php index d8302e7b3..10da7dd76 100644 --- a/src/item/ItemEnchantmentHandlingTrait.php +++ b/src/item/ItemEnchantmentHandlingTrait.php @@ -33,7 +33,10 @@ use function spl_object_id; * The primary purpose of this trait is providing scope isolation for the methods it contains. */ trait ItemEnchantmentHandlingTrait{ - /** @var EnchantmentInstance[] */ + /** + * @var EnchantmentInstance[] + * @phpstan-var array + */ protected array $enchantments = []; public function hasEnchantments() : bool{ @@ -79,6 +82,7 @@ trait ItemEnchantmentHandlingTrait{ /** * @return EnchantmentInstance[] + * @phpstan-return array */ public function getEnchantments() : array{ return $this->enchantments; diff --git a/src/item/LegacyStringToItemParser.php b/src/item/LegacyStringToItemParser.php index a52b6c716..6969190d5 100644 --- a/src/item/LegacyStringToItemParser.php +++ b/src/item/LegacyStringToItemParser.php @@ -29,6 +29,7 @@ use pocketmine\data\bedrock\item\upgrade\ItemDataUpgrader; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; +use pocketmine\utils\Utils; use pocketmine\world\format\io\GlobalItemDataHandlers; use Symfony\Component\Filesystem\Path; use function explode; @@ -67,7 +68,7 @@ final class LegacyStringToItemParser{ $mappings = json_decode($mappingsRaw, true); if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array"); - foreach($mappings as $name => $id){ + foreach(Utils::promoteKeys($mappings) as $name => $id){ if(!is_string($id)) throw new AssumptionFailedError("Invalid mappings format, expected string values"); $result->addMapping((string) $name, $id); } diff --git a/src/lang/Language.php b/src/lang/Language.php index a871c820a..29f28917d 100644 --- a/src/lang/Language.php +++ b/src/lang/Language.php @@ -147,7 +147,7 @@ class Language{ $baseText = $this->parseTranslation($str, $onlyPrefix); } - foreach($params as $i => $p){ + foreach(Utils::promoteKeys($params) as $i => $p){ $replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p; $baseText = str_replace("{%$i}", $replacement, $baseText); } @@ -161,7 +161,7 @@ class Language{ $baseText = $this->parseTranslation($c->getText()); } - foreach($c->getParameters() as $i => $p){ + foreach(Utils::promoteKeys($c->getParameters()) as $i => $p){ $replacement = $p instanceof Translatable ? $this->translate($p) : $p; $baseText = str_replace("{%$i}", $replacement, $baseText); } diff --git a/src/lang/Translatable.php b/src/lang/Translatable.php index 827a11657..8dee8f477 100644 --- a/src/lang/Translatable.php +++ b/src/lang/Translatable.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\lang; +use pocketmine\utils\Utils; + final class Translatable{ /** @var string[]|Translatable[] $params */ protected array $params = []; @@ -34,7 +36,7 @@ final class Translatable{ protected string $text, array $params = [] ){ - foreach($params as $k => $param){ + foreach(Utils::promoteKeys($params) as $k => $param){ if(!($param instanceof Translatable)){ $this->params[$k] = (string) $param; }else{ diff --git a/src/network/NetworkSessionManager.php b/src/network/NetworkSessionManager.php index d8ff7fe03..aecbc646d 100644 --- a/src/network/NetworkSessionManager.php +++ b/src/network/NetworkSessionManager.php @@ -30,10 +30,16 @@ use function spl_object_id; class NetworkSessionManager{ - /** @var NetworkSession[] */ + /** + * @var NetworkSession[] + * @phpstan-var array + */ private array $sessions = []; - /** @var NetworkSession[] */ + /** + * @var NetworkSession[] + * @phpstan-var array + */ private array $pendingLoginSessions = []; /** diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 70c427aa1..7df8c734b 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -695,6 +695,7 @@ class InventoryManager{ /** * @param EnchantingOption[] $options + * @phpstan-param list $options */ public function syncEnchantingTableOptions(array $options) : void{ $protocolOptions = []; diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 0eee71e0e..78b11e27e 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -1092,7 +1092,7 @@ class NetworkSession{ public function syncAvailableCommands() : void{ $commandData = []; - foreach($this->server->getCommandMap()->getCommands() as $name => $command){ + foreach($this->server->getCommandMap()->getCommands() as $command){ if(isset($commandData[$command->getLabel()]) || $command->getLabel() === "help" || !$command->testPermissionSilent($this->player)){ continue; } diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 32afdeeb7..7a91b397b 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -53,7 +53,6 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ $compressors = []; - /** @var NetworkSession[][] $targetsByCompressor */ $targetsByCompressor = []; foreach($recipients as $recipient){ //TODO: different compressors might be compatible, it might not be necessary to split them up by object diff --git a/src/network/mcpe/cache/ChunkCache.php b/src/network/mcpe/cache/ChunkCache.php index 6d8020085..12e769776 100644 --- a/src/network/mcpe/cache/ChunkCache.php +++ b/src/network/mcpe/cache/ChunkCache.php @@ -78,7 +78,10 @@ class ChunkCache implements ChunkListener{ } } - /** @var CompressBatchPromise[] */ + /** + * @var CompressBatchPromise[] + * @phpstan-var array + */ private array $caches = []; private int $hits = 0; diff --git a/src/network/mcpe/convert/BlockStateDictionaryEntry.php b/src/network/mcpe/convert/BlockStateDictionaryEntry.php index 8c6244da5..28cc3960d 100644 --- a/src/network/mcpe/convert/BlockStateDictionaryEntry.php +++ b/src/network/mcpe/convert/BlockStateDictionaryEntry.php @@ -28,6 +28,7 @@ use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\Tag; use pocketmine\nbt\TreeRoot; +use pocketmine\utils\Utils; use function count; use function ksort; use const SORT_STRING; @@ -43,6 +44,7 @@ final class BlockStateDictionaryEntry{ /** * @param Tag[] $stateProperties + * @phpstan-param array $stateProperties */ public function __construct( private string $stateName, @@ -79,6 +81,7 @@ final class BlockStateDictionaryEntry{ /** * @param Tag[] $properties + * @phpstan-param array $properties */ public static function encodeStateProperties(array $properties) : string{ if(count($properties) === 0){ @@ -87,7 +90,7 @@ final class BlockStateDictionaryEntry{ //TODO: make a more efficient encoding - NBT will do for now, but it's not very compact ksort($properties, SORT_STRING); $tag = new CompoundTag(); - foreach($properties as $k => $v){ + foreach(Utils::stringifyKeys($properties) as $k => $v){ $tag->setTag($k, $v); } return (new LittleEndianNbtSerializer())->write(new TreeRoot($tag)); diff --git a/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php b/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php index 5d06758ef..d962063d3 100644 --- a/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php +++ b/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php @@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\convert; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Utils; use function is_array; use function is_bool; use function is_int; @@ -41,7 +42,7 @@ final class ItemTypeDictionaryFromDataHelper{ } $params = []; - foreach($table as $name => $entry){ + foreach(Utils::promoteKeys($table) as $name => $entry){ if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){ throw new AssumptionFailedError("Invalid item list format"); } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index c92db3133..cc1f3eab4 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -251,7 +251,7 @@ class InGamePacketHandler extends PacketHandler{ if(count($blockActions) > 100){ throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket"); } - foreach($blockActions as $k => $blockAction){ + foreach(Utils::promoteKeys($blockActions) as $k => $blockAction){ $actionHandled = false; if($blockAction instanceof PlayerBlockActionStopBreak){ $actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), new BlockPosition(0, 0, 0), Facing::DOWN); diff --git a/src/network/mcpe/handler/ItemStackRequestExecutor.php b/src/network/mcpe/handler/ItemStackRequestExecutor.php index 54a192590..6db8f1e12 100644 --- a/src/network/mcpe/handler/ItemStackRequestExecutor.php +++ b/src/network/mcpe/handler/ItemStackRequestExecutor.php @@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResp use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset; use pocketmine\player\Player; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Utils; use function array_key_first; use function count; use function spl_object_id; @@ -370,7 +371,7 @@ class ItemStackRequestExecutor{ * @throws ItemStackRequestProcessException */ public function generateInventoryTransaction() : InventoryTransaction{ - foreach($this->request->getActions() as $k => $action){ + foreach(Utils::promoteKeys($this->request->getActions()) as $k => $action){ try{ $this->processItemStackRequestAction($action); }catch(ItemStackRequestProcessException $e){ diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index 2e3a51519..c15753dad 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -150,7 +150,7 @@ class LoginPacketHandler extends PacketHandler{ protected function fetchAuthData(JwtChain $chain) : AuthenticationData{ /** @var AuthenticationData|null $extraData */ $extraData = null; - foreach($chain->chain as $k => $jwt){ + foreach($chain->chain as $jwt){ //validate every chain element try{ [, $claims, ] = JwtUtils::parse($jwt); diff --git a/src/permission/BanList.php b/src/permission/BanList.php index b09cd98c9..36826ed66 100644 --- a/src/permission/BanList.php +++ b/src/permission/BanList.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\permission; +use pocketmine\utils\Utils; use function fclose; use function fgets; use function fopen; @@ -32,7 +33,10 @@ use function strtolower; use function trim; class BanList{ - /** @var BanEntry[] */ + /** + * @var BanEntry[] + * @phpstan-var array + */ private array $list = []; private bool $enabled = true; @@ -101,7 +105,7 @@ class BanList{ } public function removeExpired() : void{ - foreach($this->list as $name => $entry){ + foreach(Utils::promoteKeys($this->list) as $name => $entry){ if($entry->hasExpired()){ unset($this->list[$name]); } diff --git a/src/permission/PermissionAttachment.php b/src/permission/PermissionAttachment.php index b01fba307..79c66ba8a 100644 --- a/src/permission/PermissionAttachment.php +++ b/src/permission/PermissionAttachment.php @@ -25,10 +25,14 @@ namespace pocketmine\permission; use pocketmine\plugin\Plugin; use pocketmine\plugin\PluginException; +use pocketmine\utils\Utils; use function spl_object_id; class PermissionAttachment{ - /** @var bool[] */ + /** + * @var bool[] + * @phpstan-var array + */ private array $permissions = []; /** @@ -60,6 +64,7 @@ class PermissionAttachment{ /** * @return bool[] + * @phpstan-return array */ public function getPermissions() : array{ return $this->permissions; @@ -78,9 +83,10 @@ class PermissionAttachment{ /** * @param bool[] $permissions + * @phpstan-param array $permissions */ public function setPermissions(array $permissions) : void{ - foreach($permissions as $key => $value){ + foreach(Utils::stringifyKeys($permissions) as $key => $value){ $this->permissions[$key] = $value; } $this->recalculatePermissibles(); diff --git a/src/permission/PermissionManager.php b/src/permission/PermissionManager.php index 1291ba86b..f2b02e8e5 100644 --- a/src/permission/PermissionManager.php +++ b/src/permission/PermissionManager.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\permission; +use pocketmine\utils\Utils; use function count; use function spl_object_id; @@ -37,9 +38,15 @@ class PermissionManager{ return self::$instance; } - /** @var Permission[] */ + /** + * @var Permission[] + * @phpstan-var array + */ protected array $permissions = []; - /** @var PermissibleInternal[][] */ + /** + * @var PermissibleInternal[][] + * @phpstan-var array> + */ protected array $permSubs = []; public function getPermission(string $name) : ?Permission{ @@ -82,7 +89,7 @@ class PermissionManager{ } public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{ - foreach($this->permSubs as $permission => $subs){ + foreach(Utils::promoteKeys($this->permSubs) as $permission => $subs){ if(count($subs) === 1 && isset($subs[spl_object_id($permissible)])){ unset($this->permSubs[$permission]); }else{ diff --git a/src/plugin/ApiVersion.php b/src/plugin/ApiVersion.php index bcf7e59a0..d95b66576 100644 --- a/src/plugin/ApiVersion.php +++ b/src/plugin/ApiVersion.php @@ -70,7 +70,6 @@ final class ApiVersion{ * @return string[] */ public static function checkAmbiguousVersions(array $versions) : array{ - /** @var VersionString[][] $indexedVersions */ $indexedVersions = []; foreach($versions as $str){ @@ -85,9 +84,8 @@ final class ApiVersion{ } } - /** @var VersionString[] $result */ $result = []; - foreach($indexedVersions as $major => $list){ + foreach($indexedVersions as $list){ if(count($list) > 1){ array_push($result, ...$list); } diff --git a/src/plugin/PluginDescription.php b/src/plugin/PluginDescription.php index 72f0add7f..35ae2ba32 100644 --- a/src/plugin/PluginDescription.php +++ b/src/plugin/PluginDescription.php @@ -26,6 +26,7 @@ namespace pocketmine\plugin; use pocketmine\permission\Permission; use pocketmine\permission\PermissionParser; use pocketmine\permission\PermissionParserException; +use pocketmine\utils\Utils; use function array_map; use function array_values; use function get_debug_type; @@ -151,7 +152,7 @@ class PluginDescription{ $this->compatibleOperatingSystems = array_map("\strval", (array) ($plugin[self::KEY_OS] ?? [])); if(isset($plugin[self::KEY_COMMANDS]) && is_array($plugin[self::KEY_COMMANDS])){ - foreach($plugin[self::KEY_COMMANDS] as $commandName => $commandData){ + foreach(Utils::promoteKeys($plugin[self::KEY_COMMANDS]) as $commandName => $commandData){ if(!is_string($commandName)){ throw new PluginDescriptionParseException("Invalid Plugin commands, key must be the name of the command"); } @@ -177,7 +178,7 @@ class PluginDescription{ if(isset($plugin[self::KEY_EXTENSIONS])){ $extensions = (array) $plugin[self::KEY_EXTENSIONS]; $isLinear = $extensions === array_values($extensions); - foreach($extensions as $k => $v){ + foreach(Utils::promoteKeys($extensions) as $k => $v){ if($isLinear){ $k = $v; $v = "*"; diff --git a/src/plugin/PluginGraylist.php b/src/plugin/PluginGraylist.php index 84aa3f792..ff9d71832 100644 --- a/src/plugin/PluginGraylist.php +++ b/src/plugin/PluginGraylist.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\plugin; +use pocketmine\utils\Utils; use function array_flip; use function is_array; use function is_float; @@ -77,7 +78,7 @@ class PluginGraylist{ if(!is_array($array["plugins"])){ throw new \InvalidArgumentException("\"plugins\" must be an array"); } - foreach($array["plugins"] as $k => $v){ + foreach(Utils::promoteKeys($array["plugins"]) as $k => $v){ if(!is_string($v) && !is_int($v) && !is_float($v)){ throw new \InvalidArgumentException("\"plugins\" contains invalid element at position $k"); } diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 198e4e893..f84698aa0 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -69,10 +69,16 @@ use function strtolower; * Manages all the plugins */ class PluginManager{ - /** @var Plugin[] */ + /** + * @var Plugin[] + * @phpstan-var array + */ protected array $plugins = []; - /** @var Plugin[] */ + /** + * @var Plugin[] + * @phpstan-var array + */ protected array $enabledPlugins = []; /** @var array> */ @@ -114,6 +120,7 @@ class PluginManager{ /** * @return Plugin[] + * @phpstan-return array */ public function getPlugins() : array{ return $this->plugins; @@ -526,7 +533,7 @@ class PluginManager{ } public function tickSchedulers(int $currentTick) : void{ - foreach($this->enabledPlugins as $pluginName => $p){ + foreach(Utils::promoteKeys($this->enabledPlugins) as $pluginName => $p){ if(isset($this->enabledPlugins[$pluginName])){ //the plugin may have been disabled as a result of updating other plugins' schedulers, and therefore //removed from enabledPlugins; however, foreach will still see it due to copy-on-write diff --git a/src/promise/Promise.php b/src/promise/Promise.php index 0def7e605..cd8fcc4f1 100644 --- a/src/promise/Promise.php +++ b/src/promise/Promise.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\promise; +use pocketmine\utils\Utils; use function count; use function spl_object_id; @@ -83,7 +84,7 @@ final class Promise{ $toResolve = count($promises); $continue = true; - foreach($promises as $key => $promise){ + foreach(Utils::promoteKeys($promises) as $key => $promise){ $promise->onCompletion( function(mixed $value) use ($resolver, $key, $toResolve, &$values) : void{ $values[$key] = $value; diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 2df6750de..baf16ca20 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -25,6 +25,7 @@ namespace pocketmine\resourcepacks; use pocketmine\utils\Config; use pocketmine\utils\Filesystem; +use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function array_keys; use function copy; @@ -87,7 +88,7 @@ class ResourcePackManager{ throw new \InvalidArgumentException("\"resource_stack\" key should contain a list of pack names"); } - foreach($resourceStack as $pos => $pack){ + foreach(Utils::promoteKeys($resourceStack) as $pos => $pack){ if(!is_string($pack) && !is_int($pack) && !is_float($pack)){ $logger->critical("Found invalid entry in resource pack list at offset $pos of type " . gettype($pack)); continue; diff --git a/src/utils/Utils.php b/src/utils/Utils.php index ef3f2d249..551e6ae44 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -445,6 +445,7 @@ final class Utils{ * @phpstan-param list> $trace * * @return string[] + * @phpstan-return list */ public static function printableTrace(array $trace, int $maxStringLength = 80) : array{ $messages = []; @@ -456,7 +457,7 @@ final class Utils{ }else{ $args = $trace[$i]["params"]; } - /** @var mixed[] $args */ + /** @phpstan-var array $args */ $paramsList = []; $offset = 0; @@ -608,6 +609,18 @@ final class Utils{ } } + /** + * Gets rid of PHPStan BenevolentUnionType on array keys, so that wrong type errors get reported properly + * Use this if you don't care what the key type is and just want proper PHPStan error reporting + * + * @phpstan-template TValueType + * @phpstan-param array $array + * @phpstan-return array + */ + public static function promoteKeys(array $array) : array{ + return $array; + } + public static function checkUTF8(string $string) : void{ if(!mb_check_encoding($string, 'UTF-8')){ throw new \InvalidArgumentException("Text must be valid UTF-8"); diff --git a/src/world/BlockTransaction.php b/src/world/BlockTransaction.php index 5f7e9f9fa..46cbc7903 100644 --- a/src/world/BlockTransaction.php +++ b/src/world/BlockTransaction.php @@ -28,7 +28,10 @@ use pocketmine\math\Vector3; use pocketmine\utils\Utils; class BlockTransaction{ - /** @var Block[][][] */ + /** + * @var Block[][][] + * @phpstan-var array>> + */ private array $blocks = []; /** diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php index ff603a2df..7ff4ed6e0 100644 --- a/src/world/WorldManager.php +++ b/src/world/WorldManager.php @@ -56,7 +56,10 @@ use function trim; class WorldManager{ public const TICKS_PER_AUTOSAVE = 300 * Server::TARGET_TICKS_PER_SECOND; - /** @var World[] */ + /** + * @var World[] + * @phpstan-var array + */ private array $worlds = []; private ?World $defaultWorld = null; @@ -76,6 +79,7 @@ class WorldManager{ /** * @return World[] + * @phpstan-return array */ public function getWorlds() : array{ return $this->worlds; diff --git a/src/world/format/SubChunk.php b/src/world/format/SubChunk.php index 3f7e943e3..d8546e7e9 100644 --- a/src/world/format/SubChunk.php +++ b/src/world/format/SubChunk.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\world\format; use function array_map; -use function array_values; use function count; class SubChunk{ @@ -36,6 +35,7 @@ class SubChunk{ * SubChunk constructor. * * @param PalettedBlockArray[] $blockLayers + * @phpstan-param list $blockLayers */ public function __construct( private int $emptyBlockId, @@ -85,6 +85,7 @@ class SubChunk{ /** * @return PalettedBlockArray[] + * @phpstan-return list */ public function getBlockLayers() : array{ return $this->blockLayers; @@ -129,17 +130,18 @@ class SubChunk{ } public function collectGarbage() : void{ - foreach($this->blockLayers as $k => $layer){ + $cleanedLayers = []; + foreach($this->blockLayers as $layer){ $layer->collectGarbage(); foreach($layer->getPalette() as $p){ if($p !== $this->emptyBlockId){ + $cleanedLayers[] = $layer; continue 2; } } - unset($this->blockLayers[$k]); } - $this->blockLayers = array_values($this->blockLayers); + $this->blockLayers = $cleanedLayers; $this->biomes->collectGarbage(); if($this->skyLight !== null && $this->skyLight->isUniform(0)){ diff --git a/src/world/format/io/BaseWorldProvider.php b/src/world/format/io/BaseWorldProvider.php index f863fdf74..79f6875a4 100644 --- a/src/world/format/io/BaseWorldProvider.php +++ b/src/world/format/io/BaseWorldProvider.php @@ -65,6 +65,8 @@ abstract class BaseWorldProvider implements WorldProvider{ abstract protected function loadLevelData() : WorldData; private function translatePalette(PalettedBlockArray $blockArray, \Logger $logger) : PalettedBlockArray{ + //TODO: missing type info in stubs + /** @phpstan-var list $palette */ $palette = $blockArray->getPalette(); $newPalette = []; diff --git a/src/world/format/io/ChunkData.php b/src/world/format/io/ChunkData.php index 458e00196..235ac07bf 100644 --- a/src/world/format/io/ChunkData.php +++ b/src/world/format/io/ChunkData.php @@ -32,6 +32,10 @@ final class ChunkData{ * @param SubChunk[] $subChunks * @param CompoundTag[] $entityNBT * @param CompoundTag[] $tileNBT + * + * @phpstan-param array $subChunks + * @phpstan-param list $entityNBT + * @phpstan-param list $tileNBT */ public function __construct( private array $subChunks, @@ -42,14 +46,21 @@ final class ChunkData{ /** * @return SubChunk[] + * @phpstan-return array */ public function getSubChunks() : array{ return $this->subChunks; } public function isPopulated() : bool{ return $this->populated; } - /** @return CompoundTag[] */ + /** + * @return CompoundTag[] + * @phpstan-return list + */ public function getEntityNBT() : array{ return $this->entityNBT; } - /** @return CompoundTag[] */ + /** + * @return CompoundTag[] + * @phpstan-return list + */ public function getTileNBT() : array{ return $this->tileNBT; } } diff --git a/src/world/format/io/region/RegionGarbageMap.php b/src/world/format/io/region/RegionGarbageMap.php index d1e950452..9e4e232ee 100644 --- a/src/world/format/io/region/RegionGarbageMap.php +++ b/src/world/format/io/region/RegionGarbageMap.php @@ -31,7 +31,10 @@ use const SORT_NUMERIC; final class RegionGarbageMap{ - /** @var RegionLocationTableEntry[] */ + /** + * @var RegionLocationTableEntry[] + * @phpstan-var array + */ private array $entries = []; private bool $clean = false; @@ -48,7 +51,6 @@ final class RegionGarbageMap{ * @param RegionLocationTableEntry[]|null[] $locationTable */ public static function buildFromLocationTable(array $locationTable) : self{ - /** @var RegionLocationTableEntry[] $usedMap */ $usedMap = []; foreach($locationTable as $entry){ if($entry === null){ @@ -62,12 +64,10 @@ final class RegionGarbageMap{ ksort($usedMap, SORT_NUMERIC); - /** @var RegionLocationTableEntry[] $garbageMap */ $garbageMap = []; - /** @var RegionLocationTableEntry|null $prevEntry */ $prevEntry = null; - foreach($usedMap as $firstSector => $entry){ + foreach($usedMap as $entry){ $prevEndPlusOne = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR); $currentStart = $entry->getFirstSector(); if($prevEndPlusOne < $currentStart){ diff --git a/src/world/format/io/region/RegionLoader.php b/src/world/format/io/region/RegionLoader.php index f040b8a47..b65f5346c 100644 --- a/src/world/format/io/region/RegionLoader.php +++ b/src/world/format/io/region/RegionLoader.php @@ -67,7 +67,10 @@ class RegionLoader{ /** @var resource */ protected $filePointer; protected int $nextSector = self::FIRST_SECTOR; - /** @var RegionLocationTableEntry[]|null[] */ + /** + * @var RegionLocationTableEntry[]|null[] + * @phpstan-var list + */ protected array $locationTable = []; protected RegionGarbageMap $garbageTable; public int $lastUsed; @@ -327,7 +330,6 @@ class RegionLoader{ * @throws CorruptedRegionException */ private function checkLocationTableValidity() : void{ - /** @var int[] $usedOffsets */ $usedOffsets = []; $fileSize = filesize($this->filePath); @@ -355,7 +357,7 @@ class RegionLoader{ } ksort($usedOffsets, SORT_NUMERIC); $prevLocationIndex = null; - foreach($usedOffsets as $startOffset => $locationTableIndex){ + foreach($usedOffsets as $locationTableIndex){ if($this->locationTable[$locationTableIndex] === null){ continue; } diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index 1feca61be..75fcfd083 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -72,7 +72,10 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ return false; } - /** @var RegionLoader[] */ + /** + * @var RegionLoader[] + * @phpstan-var array + */ protected array $regions = []; protected function loadLevelData() : WorldData{ diff --git a/src/world/generator/PopulationTask.php b/src/world/generator/PopulationTask.php index 8479a79fa..bad134324 100644 --- a/src/world/generator/PopulationTask.php +++ b/src/world/generator/PopulationTask.php @@ -76,7 +76,10 @@ class PopulationTask extends AsyncTask{ $chunk = $this->chunk !== null ? FastChunkSerializer::deserializeTerrain($this->chunk) : null; - /** @var string[] $serialChunks */ + /** + * @var string[] $serialChunks + * @phpstan-var array $serialChunks + */ $serialChunks = igbinary_unserialize($this->adjacentChunks); $chunks = array_map( function(?string $serialized) : ?Chunk{ @@ -92,7 +95,6 @@ class PopulationTask extends AsyncTask{ self::setOrGenerateChunk($manager, $generator, $this->chunkX, $this->chunkZ, $chunk); - /** @var Chunk[] $resultChunks */ $resultChunks = []; //this is just to keep phpstan's type inference happy foreach($chunks as $relativeChunkHash => $c){ World::getXZ($relativeChunkHash, $relativeX, $relativeZ); diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 0fc3defda..02c94d2d6 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -60,3 +60,8 @@ parameters: count: 2 path: ../../phpunit/promise/PromiseTest.php + - + message: "#^Strict comparison using \\=\\=\\= between 0 and 0 will always evaluate to true\\.$#" + count: 1 + path: ../rules/UnsafeForeachArrayOfStringRule.php + diff --git a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php b/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php index e42d32927..745cf2109 100644 --- a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php +++ b/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php @@ -28,6 +28,7 @@ use PhpParser\Node\Stmt\Foreach_; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\BenevolentUnionType; use PHPStan\Type\ClassStringType; use PHPStan\Type\IntegerType; use PHPStan\Type\StringType; @@ -62,8 +63,17 @@ final class UnsafeForeachArrayOfStringRule implements Rule{ $hasCastableKeyTypes = false; $expectsIntKeyTypes = false; - TypeTraverser::map($iterableType->getIterableKeyType(), function(Type $type, callable $traverse) use (&$hasCastableKeyTypes, &$expectsIntKeyTypes) : Type{ - if($type instanceof IntegerType){ + $implicitType = false; + $benevolentUnionDepth = 0; + TypeTraverser::map($iterableType->getIterableKeyType(), function(Type $type, callable $traverse) use (&$hasCastableKeyTypes, &$expectsIntKeyTypes, &$benevolentUnionDepth, &$implicitType) : Type{ + if($type instanceof BenevolentUnionType){ + $implicitType = true; + $benevolentUnionDepth++; + $result = $traverse($type); + $benevolentUnionDepth--; + return $result; + } + if($type instanceof IntegerType && $benevolentUnionDepth === 0){ $expectsIntKeyTypes = true; return $type; } @@ -78,12 +88,20 @@ final class UnsafeForeachArrayOfStringRule implements Rule{ return $type; }); if($hasCastableKeyTypes && !$expectsIntKeyTypes){ - $func = Utils::stringifyKeys(...); + $tip = $implicitType ? + sprintf( + "Declare a key type using @phpstan-var or @phpstan-param, or use %s() to promote the key type to get proper error reporting", + Utils::getNiceClosureName(Utils::promoteKeys(...)) + ) : + sprintf( + "Use %s() to get a \Generator that will force the keys to string", + Utils::getNiceClosureName(Utils::stringifyKeys(...)), + ); return [ RuleErrorBuilder::message(sprintf( "Unsafe foreach on array with key type %s (they might be casted to int).", $iterableType->getIterableKeyType()->describe(VerbosityLevel::value()) - ))->tip(sprintf("Use %s() for a safe Generator-based iterator.", Utils::getNiceClosureName($func)))->build() + ))->tip($tip)->build() ]; } return []; diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php index 6ade2bffe..841917787 100644 --- a/tests/phpunit/block/BlockTest.php +++ b/tests/phpunit/block/BlockTest.php @@ -135,7 +135,7 @@ class BlockTest extends TestCase{ } $errors = []; - foreach($expected as $typeName => $numStates){ + foreach(Utils::promoteKeys($expected) as $typeName => $numStates){ if(!is_string($typeName) || !is_int($numStates)){ throw new AssumptionFailedError("Old table should be array"); } diff --git a/tests/phpunit/item/ItemTest.php b/tests/phpunit/item/ItemTest.php index 05cb48b30..36ca5c5ff 100644 --- a/tests/phpunit/item/ItemTest.php +++ b/tests/phpunit/item/ItemTest.php @@ -89,7 +89,6 @@ class ItemTest extends TestCase{ } public function testGetEnchantments() : void{ - /** @var EnchantmentInstance[] $enchantments */ $enchantments = [ new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 5), new EnchantmentInstance(VanillaEnchantments::SHARPNESS(), 1) diff --git a/tests/phpunit/utils/CloningRegistryTraitTest.php b/tests/phpunit/utils/CloningRegistryTraitTest.php index 7f8298ff9..e3b53ecb5 100644 --- a/tests/phpunit/utils/CloningRegistryTraitTest.php +++ b/tests/phpunit/utils/CloningRegistryTraitTest.php @@ -47,7 +47,7 @@ final class CloningRegistryTraitTest extends TestCase{ public function testGetAllClone() : void{ $list1 = TestCloningRegistry::getAll(); $list2 = TestCloningRegistry::getAll(); - foreach($list1 as $k => $member){ + foreach(Utils::promoteKeys($list1) as $k => $member){ self::assertNotSame($member, $list2[$k], "VanillaBlocks ought to clone its members"); } } diff --git a/tests/phpunit/utils/TestCloningRegistry.php b/tests/phpunit/utils/TestCloningRegistry.php index ade94d461..d65b8abaa 100644 --- a/tests/phpunit/utils/TestCloningRegistry.php +++ b/tests/phpunit/utils/TestCloningRegistry.php @@ -37,9 +37,13 @@ final class TestCloningRegistry{ /** * @return \stdClass[] + * @phpstan-return array */ public static function getAll() : array{ - /** @var \stdClass[] $result */ + /** + * @var \stdClass[] $result + * @phpstan-var array $result + */ $result = self::_registryGetAll(); return $result; } diff --git a/tools/compact-regions.php b/tools/compact-regions.php index 6959c82fe..04ac3f0c9 100644 --- a/tools/compact-regions.php +++ b/tools/compact-regions.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\tools\compact_regions; +use pocketmine\utils\Utils; use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\region\CorruptedRegionException; use pocketmine\world\format\io\region\RegionLoader; @@ -59,6 +60,7 @@ const SUPPORTED_EXTENSIONS = [ /** * @param int[] $files * @phpstan-param array $files + * @phpstan-param-out array $files */ function find_regions_recursive(string $dir, array &$files) : void{ $dirFiles = scandir($dir, SCANDIR_SORT_NONE); @@ -112,7 +114,7 @@ function main(array $argv) : int{ $corruptedFiles = []; $doneCount = 0; $totalCount = count($files); - foreach($files as $file => $size){ + foreach(Utils::stringifyKeys($files) as $file => $size){ try{ $oldRegion = RegionLoader::loadExisting($file); }catch(CorruptedRegionException $e){ @@ -162,7 +164,7 @@ function main(array $argv) : int{ clearstatcache(); $newSize = 0; - foreach($files as $file => $oldSize){ + foreach(Utils::stringifyKeys($files) as $file => $oldSize){ $newSize += file_exists($file) ? filesize($file) : 0; } $diff = $currentSize - $newSize; diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 0cb5ac366..2c20e6099 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -198,12 +198,12 @@ class ParserPacketHandler extends PacketHandler{ $result = (array) ($object instanceof \JsonSerializable ? $object->jsonSerialize() : $object); ksort($result, SORT_STRING); - foreach($result as $property => $value){ + foreach(Utils::promoteKeys($result) as $property => $value){ if(is_object($value)){ $result[$property] = self::objectToOrderedArray($value); }elseif(is_array($value)){ $array = []; - foreach($value as $k => $v){ + foreach(Utils::promoteKeys($value) as $k => $v){ if(is_object($v)){ $array[$k] = self::objectToOrderedArray($v); }else{ @@ -224,7 +224,7 @@ class ParserPacketHandler extends PacketHandler{ } if(is_array($object)){ $result = []; - foreach($object as $k => $v){ + foreach(Utils::promoteKeys($object) as $k => $v){ $result[$k] = self::sort($v); } return $result; @@ -247,7 +247,7 @@ class ParserPacketHandler extends PacketHandler{ ksort($table, SORT_STRING); file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n"); - foreach($packet->levelSettings->experiments->getExperiments() as $name => $experiment){ + foreach(Utils::promoteKeys($packet->levelSettings->experiments->getExperiments()) as $name => $experiment){ echo "Experiment \"$name\" is " . ($experiment ? "" : "not ") . "active\n"; } return true; @@ -317,8 +317,8 @@ class ParserPacketHandler extends PacketHandler{ $char = ord("A"); $outputsByKey = []; - foreach($entry->getInput() as $x => $row){ - foreach($row as $y => $ingredient){ + foreach(Utils::promoteKeys($entry->getInput()) as $x => $row){ + foreach(Utils::promoteKeys($row) as $y => $ingredient){ if($ingredient->getDescriptor() === null){ $shape[$x][$y] = " "; }else{ @@ -337,7 +337,7 @@ class ParserPacketHandler extends PacketHandler{ } $unlockingIngredients = $entry->getUnlockingRequirement()->getUnlockingIngredients(); return new ShapedRecipeData( - array_map(fn(array $array) => implode('', $array), $shape), + array_map(fn(array $array) => implode('', array_values($array)), array_values($shape)), $outputsByKey, array_map(fn(ItemStack $output) => $this->itemStackToJson($output), $entry->getOutput()), $entry->getBlockName(), From a9787f0d993ab2f9e6bac29649f732fe933099ce Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Nov 2024 14:32:17 +0000 Subject: [PATCH 107/290] Fix PHPStan error --- src/utils/Utils.php | 2 +- tests/phpstan/configs/actual-problems.neon | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 551e6ae44..38f523ad4 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -403,7 +403,7 @@ final class Utils{ } /** - * @param mixed[] $trace + * @param mixed[][] $trace * @return string[] */ public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{ diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index e778cf004..54028f0dd 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -840,11 +840,6 @@ parameters: count: 1 path: ../../../src/utils/Utils.php - - - message: "#^Parameter \\#1 \\$trace of static method pocketmine\\\\utils\\\\Utils\\:\\:printableTrace\\(\\) expects array\\\\>, array given\\.$#" - count: 1 - path: ../../../src/utils/Utils.php - - message: "#^Parameter \\#2 \\$file of class pocketmine\\\\thread\\\\ThreadCrashInfoFrame constructor expects string\\|null, mixed given\\.$#" count: 1 From 8cdc7d7ee16482d630e49af2765dc958062b8693 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 25 Nov 2024 20:43:59 +0000 Subject: [PATCH 108/290] auto-approve: drop pull_request_review trigger this doesn't work for PRs from forks, since fork PRs don't have access to repo secrets. we'll need some more advanced mechanism to avoid redundant reviews, but that's a job for another time. --- .github/workflows/team-pr-auto-approve.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/team-pr-auto-approve.yml b/.github/workflows/team-pr-auto-approve.yml index f14581213..0c2fdd81c 100644 --- a/.github/workflows/team-pr-auto-approve.yml +++ b/.github/workflows/team-pr-auto-approve.yml @@ -11,8 +11,7 @@ on: - opened - reopened - ready_for_review - pull_request_review: - types: dismissed + - synchronize jobs: dispatch: @@ -36,4 +35,4 @@ jobs: token: ${{ steps.generate-token.outputs.token }} repository: ${{ github.repository_owner }}/RestrictedActions event-type: auto_approve_collaborator_pr - client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}", "reviewer_id": "${{ github.event.review.user.id || 0 }}" }' + client-payload: '{"repo": "${{ github.repository }}", "pull_request_id": "${{ github.event.pull_request.number }}", "reviewer_id": "0" }' From 52fe2cb97fa0cee5e4d72fa8e4fd298b4697385f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Nov 2024 21:29:26 +0000 Subject: [PATCH 109/290] PermissionManager: deprecate permission subscription system this is no longer used by the core, and as far as I can tell no plugin uses it either. it was used in the past for chat broadcast channels, but not anymore. --- src/permission/PermissionManager.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/permission/PermissionManager.php b/src/permission/PermissionManager.php index f2b02e8e5..c9e37f5e9 100644 --- a/src/permission/PermissionManager.php +++ b/src/permission/PermissionManager.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\permission; +use pocketmine\Server; use pocketmine\utils\Utils; use function count; use function spl_object_id; @@ -71,6 +72,10 @@ class PermissionManager{ } } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::subscribeToBroadcastChannel() + */ public function subscribeToPermission(string $permission, PermissibleInternal $permissible) : void{ if(!isset($this->permSubs[$permission])){ $this->permSubs[$permission] = []; @@ -78,6 +83,10 @@ class PermissionManager{ $this->permSubs[$permission][spl_object_id($permissible)] = $permissible; } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::unsubscribeFromBroadcastChannel() + */ public function unsubscribeFromPermission(string $permission, PermissibleInternal $permissible) : void{ if(isset($this->permSubs[$permission][spl_object_id($permissible)])){ if(count($this->permSubs[$permission]) === 1){ @@ -88,6 +97,10 @@ class PermissionManager{ } } + /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::unsubscribeFromAllBroadcastChannels() + */ public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{ foreach(Utils::promoteKeys($this->permSubs) as $permission => $subs){ if(count($subs) === 1 && isset($subs[spl_object_id($permissible)])){ @@ -99,6 +112,8 @@ class PermissionManager{ } /** + * @deprecated Superseded by server chat broadcast channels + * @see Server::getBroadcastChannelSubscribers() * @return PermissibleInternal[] */ public function getPermissionSubscriptions(string $permission) : array{ From 905a10e980cb6552bab0bad59cc593852409033d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Nov 2024 21:39:35 +0000 Subject: [PATCH 110/290] Deprecate InventoryAction->onAddToTransaction() this never made any sense --- src/inventory/transaction/InventoryTransaction.php | 13 +++---------- .../transaction/action/InventoryAction.php | 1 + .../transaction/action/SlotChangeAction.php | 8 -------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index b3465a8df..47290e401 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -111,6 +111,9 @@ class InventoryTransaction{ if(!isset($this->actions[$hash = spl_object_id($action)])){ $this->actions[$hash] = $action; $action->onAddToTransaction($this); + if($action instanceof SlotChangeAction && !isset($this->inventories[$inventoryId = spl_object_id($action->getInventory())])){ + $this->inventories[$inventoryId] = $action->getInventory(); + } }else{ throw new \InvalidArgumentException("Tried to add the same action to a transaction twice"); } @@ -129,16 +132,6 @@ class InventoryTransaction{ $this->actions = $actions; } - /** - * @internal This method should not be used by plugins, it's used to add tracked inventories for InventoryActions - * involving inventories. - */ - public function addInventory(Inventory $inventory) : void{ - if(!isset($this->inventories[$hash = spl_object_id($inventory)])){ - $this->inventories[$hash] = $inventory; - } - } - /** * @param Item[] $needItems * @param Item[] $haveItems diff --git a/src/inventory/transaction/action/InventoryAction.php b/src/inventory/transaction/action/InventoryAction.php index fff3d22b8..2f0db083c 100644 --- a/src/inventory/transaction/action/InventoryAction.php +++ b/src/inventory/transaction/action/InventoryAction.php @@ -60,6 +60,7 @@ abstract class InventoryAction{ /** * Called when the action is added to the specified InventoryTransaction. + * @deprecated */ public function onAddToTransaction(InventoryTransaction $transaction) : void{ diff --git a/src/inventory/transaction/action/SlotChangeAction.php b/src/inventory/transaction/action/SlotChangeAction.php index 68c3dba1b..3c9b8e5cf 100644 --- a/src/inventory/transaction/action/SlotChangeAction.php +++ b/src/inventory/transaction/action/SlotChangeAction.php @@ -25,7 +25,6 @@ namespace pocketmine\inventory\transaction\action; use pocketmine\inventory\Inventory; use pocketmine\inventory\SlotValidatedInventory; -use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\Item; use pocketmine\player\Player; @@ -85,13 +84,6 @@ class SlotChangeAction extends InventoryAction{ } } - /** - * Adds this action's target inventory to the transaction's inventory list. - */ - public function onAddToTransaction(InventoryTransaction $transaction) : void{ - $transaction->addInventory($this->inventory); - } - /** * Sets the item into the target inventory. */ From 269effcecf2398046c22b399773f63c1d4fca1fe Mon Sep 17 00:00:00 2001 From: Akmal Fairuz <35138228+AkmalFairuz@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:33:29 +0700 Subject: [PATCH 111/290] Introduce Utils::getRandomFloat() (#6532) Drop-in replacement for lcg_value() for PHP 8.4 --- src/block/Anvil.php | 4 ++-- src/block/Farmland.php | 4 ++-- src/block/ItemFrame.php | 6 +++--- src/block/Liquid.php | 4 ++-- src/entity/Entity.php | 3 +-- src/entity/Living.php | 6 +++--- src/item/Armor.php | 4 ++-- src/item/Durable.php | 4 ++-- src/item/RottenFlesh.php | 4 ++-- src/item/SpawnEgg.php | 4 ++-- src/utils/Utils.php | 10 ++++++++++ src/world/World.php | 10 +++++----- 12 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 4b4afef61..0a1a47070 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -35,10 +35,10 @@ use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\AnvilFallSound; use pocketmine\world\sound\Sound; -use function lcg_value; use function round; class Anvil extends Transparent implements Fallable{ @@ -97,7 +97,7 @@ class Anvil extends Transparent implements Fallable{ } public function onHitGround(FallingBlock $blockEntity) : bool{ - if(lcg_value() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){ + if(Utils::getRandomFloat() < 0.05 + (round($blockEntity->getFallDistance()) - 1) * 0.05){ if($this->damage !== self::VERY_DAMAGED){ $this->damage = $this->damage + 1; }else{ diff --git a/src/block/Farmland.php b/src/block/Farmland.php index a17a220f0..b7a2500a8 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -31,8 +31,8 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; +use pocketmine\utils\Utils; use function intdiv; -use function lcg_value; class Farmland extends Transparent{ public const MAX_WETNESS = 7; @@ -148,7 +148,7 @@ class Farmland extends Transparent{ } public function onEntityLand(Entity $entity) : ?float{ - if($entity instanceof Living && lcg_value() < $entity->getFallDistance() - 0.5){ + if($entity instanceof Living && Utils::getRandomFloat() < $entity->getFallDistance() - 0.5){ $ev = new EntityTrampleFarmlandEvent($entity, $this); $ev->call(); if(!$ev->isCancelled()){ diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index b5b6093c4..c03806a3b 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -31,13 +31,13 @@ use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\BlockTransaction; use pocketmine\world\sound\ItemFrameAddItemSound; use pocketmine\world\sound\ItemFrameRemoveItemSound; use pocketmine\world\sound\ItemFrameRotateItemSound; use function is_infinite; use function is_nan; -use function lcg_value; class ItemFrame extends Flowable{ use AnyFacingTrait; @@ -154,7 +154,7 @@ class ItemFrame extends Flowable{ return false; } $world = $this->position->getWorld(); - if(lcg_value() <= $this->itemDropChance){ + if(Utils::getRandomFloat() <= $this->itemDropChance){ $world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem); $world->addSound($this->position, new ItemFrameRemoveItemSound()); } @@ -185,7 +185,7 @@ class ItemFrame extends Flowable{ public function getDropsForCompatibleTool(Item $item) : array{ $drops = parent::getDropsForCompatibleTool($item); - if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){ + if($this->framedItem !== null && Utils::getRandomFloat() <= $this->itemDropChance){ $drops[] = clone $this->framedItem; } diff --git a/src/block/Liquid.php b/src/block/Liquid.php index 6404cf908..a37019d65 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -33,9 +33,9 @@ use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\utils\Utils; use pocketmine\world\sound\FizzSound; use pocketmine\world\sound\Sound; -use function lcg_value; abstract class Liquid extends Transparent{ public const MAX_DECAY = 7; @@ -368,7 +368,7 @@ abstract class Liquid extends Transparent{ protected function liquidCollide(Block $cause, Block $result) : bool{ if(BlockEventHelper::form($this, $result, $cause)){ - $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (lcg_value() - lcg_value()) * 0.8)); + $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new FizzSound(2.6 + (Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.8)); } return true; } diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 0b690b0d9..9bd0de9ea 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -75,7 +75,6 @@ use function deg2rad; use function floor; use function fmod; use function get_class; -use function lcg_value; use function sin; use function spl_object_id; use const M_PI_2; @@ -910,7 +909,7 @@ abstract class Entity{ return false; } - $force = lcg_value() * 0.2 + 0.1; + $force = Utils::getRandomFloat() * 0.2 + 0.1; $this->motion = match($direction){ Facing::WEST => $this->motion->withComponents(-$force, null, null), diff --git a/src/entity/Living.php b/src/entity/Living.php index 81f46424f..1f27a5cac 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -58,6 +58,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\player\Player; use pocketmine\timings\Timings; use pocketmine\utils\Binary; +use pocketmine\utils\Utils; use pocketmine\world\sound\BurpSound; use pocketmine\world\sound\EntityLandSound; use pocketmine\world\sound\EntityLongFallSound; @@ -69,7 +70,6 @@ use function ceil; use function count; use function floor; use function ksort; -use function lcg_value; use function max; use function min; use function mt_getrandmax; @@ -490,7 +490,7 @@ abstract class Living extends Entity{ $helmet = $this->armorInventory->getHelmet(); if($helmet instanceof Armor){ $finalDamage = $source->getFinalDamage(); - $this->damageItem($helmet, (int) round($finalDamage * 4 + lcg_value() * $finalDamage * 2)); + $this->damageItem($helmet, (int) round($finalDamage * 4 + Utils::getRandomFloat() * $finalDamage * 2)); $this->armorInventory->setHelmet($helmet); } } @@ -697,7 +697,7 @@ abstract class Living extends Entity{ $this->setBreathing(false); if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(VanillaEnchantments::RESPIRATION())) <= 0 || - lcg_value() <= (1 / ($respirationLevel + 1)) + Utils::getRandomFloat() <= (1 / ($respirationLevel + 1)) ){ $ticks -= $tickDiff; if($ticks <= -20){ diff --git a/src/item/Armor.php b/src/item/Armor.php index 417c57f75..63a8003ad 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -33,7 +33,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\player\Player; use pocketmine\utils\Binary; -use function lcg_value; +use pocketmine\utils\Utils; use function mt_rand; class Armor extends Durable{ @@ -129,7 +129,7 @@ class Armor extends Durable{ $chance = 1 / ($unbreakingLevel + 1); for($i = 0; $i < $amount; ++$i){ - if(mt_rand(1, 100) > 60 && lcg_value() > $chance){ //unbreaking only applies to armor 40% of the time at best + if(mt_rand(1, 100) > 60 && Utils::getRandomFloat() > $chance){ //unbreaking only applies to armor 40% of the time at best $negated++; } } diff --git a/src/item/Durable.php b/src/item/Durable.php index f110f6ea5..069a01202 100644 --- a/src/item/Durable.php +++ b/src/item/Durable.php @@ -25,7 +25,7 @@ namespace pocketmine\item; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\nbt\tag\CompoundTag; -use function lcg_value; +use pocketmine\utils\Utils; use function min; abstract class Durable extends Item{ @@ -87,7 +87,7 @@ abstract class Durable extends Item{ $chance = 1 / ($unbreakingLevel + 1); for($i = 0; $i < $amount; ++$i){ - if(lcg_value() > $chance){ + if(Utils::getRandomFloat() > $chance){ $negated++; } } diff --git a/src/item/RottenFlesh.php b/src/item/RottenFlesh.php index 4cecc67fc..2ea3ee955 100644 --- a/src/item/RottenFlesh.php +++ b/src/item/RottenFlesh.php @@ -25,7 +25,7 @@ namespace pocketmine\item; use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\VanillaEffects; -use function lcg_value; +use pocketmine\utils\Utils; class RottenFlesh extends Food{ @@ -38,7 +38,7 @@ class RottenFlesh extends Food{ } public function getAdditionalEffects() : array{ - if(lcg_value() <= 0.8){ + if(Utils::getRandomFloat() <= 0.8){ return [ new EffectInstance(VanillaEffects::HUNGER(), 600) ]; diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index 51dcceebd..ab4f0e149 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -27,15 +27,15 @@ use pocketmine\block\Block; use pocketmine\entity\Entity; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\utils\Utils; use pocketmine\world\World; -use function lcg_value; abstract class SpawnEgg extends Item{ abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity; public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ - $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), lcg_value() * 360, 0); + $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), Utils::getRandomFloat() * 360, 0); if($this->hasCustomName()){ $entity->setNameTag($this->getCustomName()); diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 38f523ad4..c8be174d6 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -67,6 +67,8 @@ use function is_nan; use function is_object; use function is_string; use function mb_check_encoding; +use function mt_getrandmax; +use function mt_rand; use function ob_end_clean; use function ob_get_contents; use function ob_start; @@ -688,4 +690,12 @@ final class Utils{ //jit not available return null; } + + /** + * Returns a random float between 0.0 and 1.0 + * Drop-in replacement for lcg_value() + */ + public static function getRandomFloat() : float{ + return mt_rand() / mt_getrandmax(); + } } diff --git a/src/world/World.php b/src/world/World.php index a8e624dd5..ff65377c0 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -83,6 +83,7 @@ use pocketmine\ServerConfigGroup; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Limits; use pocketmine\utils\ReversePriorityQueue; +use pocketmine\utils\Utils; use pocketmine\world\biome\Biome; use pocketmine\world\biome\BiomeRegistry; use pocketmine\world\format\Chunk; @@ -120,7 +121,6 @@ use function get_class; use function gettype; use function is_a; use function is_object; -use function lcg_value; use function max; use function microtime; use function min; @@ -1998,10 +1998,10 @@ class World implements ChunkManager{ return null; } - $itemEntity = new ItemEntity(Location::fromObject($source, $this, lcg_value() * 360, 0), $item); + $itemEntity = new ItemEntity(Location::fromObject($source, $this, Utils::getRandomFloat() * 360, 0), $item); $itemEntity->setPickupDelay($delay); - $itemEntity->setMotion($motion ?? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1)); + $itemEntity->setMotion($motion ?? new Vector3(Utils::getRandomFloat() * 0.2 - 0.1, 0.2, Utils::getRandomFloat() * 0.2 - 0.1)); $itemEntity->spawnToAll(); return $itemEntity; @@ -2018,9 +2018,9 @@ class World implements ChunkManager{ $orbs = []; foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){ - $orb = new ExperienceOrb(Location::fromObject($pos, $this, lcg_value() * 360, 0), $split); + $orb = new ExperienceOrb(Location::fromObject($pos, $this, Utils::getRandomFloat() * 360, 0), $split); - $orb->setMotion(new Vector3((lcg_value() * 0.2 - 0.1) * 2, lcg_value() * 0.4, (lcg_value() * 0.2 - 0.1) * 2)); + $orb->setMotion(new Vector3((Utils::getRandomFloat() * 0.2 - 0.1) * 2, Utils::getRandomFloat() * 0.4, (Utils::getRandomFloat() * 0.2 - 0.1) * 2)); $orb->spawnToAll(); $orbs[] = $orb; From daacc8eddb6555e7a9015ebabda6a1fe75a92b82 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 27 Nov 2024 17:54:59 +0000 Subject: [PATCH 112/290] Updated setup-php-action to 3.2.0 --- .github/workflows/main-php-matrix.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main-php-matrix.yml b/.github/workflows/main-php-matrix.yml index 6d71a0e70..e26f7c318 100644 --- a/.github/workflows/main-php-matrix.yml +++ b/.github/workflows/main-php-matrix.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: pmmp/setup-php-action@3.1.0 + uses: pmmp/setup-php-action@3.2.0 with: php-version: ${{ inputs.php }} install-path: "./bin" @@ -62,7 +62,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: pmmp/setup-php-action@3.1.0 + uses: pmmp/setup-php-action@3.2.0 with: php-version: ${{ inputs.php }} install-path: "./bin" @@ -96,7 +96,7 @@ jobs: submodules: true - name: Setup PHP - uses: pmmp/setup-php-action@3.1.0 + uses: pmmp/setup-php-action@3.2.0 with: php-version: ${{ inputs.php }} install-path: "./bin" @@ -128,7 +128,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: pmmp/setup-php-action@3.1.0 + uses: pmmp/setup-php-action@3.2.0 with: php-version: ${{ inputs.php }} install-path: "./bin" From 0543bf301ee783536ba1015183fd69ba258177dd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 27 Nov 2024 19:19:51 +0000 Subject: [PATCH 113/290] draft-release: updated php_download_url --- .github/workflows/draft-release.yml | 3 ++- build/dump-version-info.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 0a07a738b..cd1841e4f 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -64,6 +64,7 @@ jobs: id: get-pm-version run: | echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT + echo PM_MAJOR=$(php build/dump-version-info.php major_version) >> $GITHUB_OUTPUT echo MCPE_VERSION=$(php build/dump-version-info.php mcpe_version) >> $GITHUB_OUTPUT echo CHANGELOG_FILE_NAME=$(php build/dump-version-info.php changelog_file_name) >> $GITHUB_OUTPUT echo CHANGELOG_MD_HEADER=$(php build/dump-version-info.php changelog_md_header) >> $GITHUB_OUTPUT @@ -72,7 +73,7 @@ jobs: - name: Generate PHP binary download URL id: php-binary-url run: | - echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT + echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT - name: Generate build info run: | diff --git a/build/dump-version-info.php b/build/dump-version-info.php index 8898d7cab..166264d98 100644 --- a/build/dump-version-info.php +++ b/build/dump-version-info.php @@ -36,6 +36,7 @@ require dirname(__DIR__) . '/vendor/autoload.php'; */ $options = [ "base_version" => VersionInfo::BASE_VERSION, + "major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0], "mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK, "is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD, "changelog_file_name" => function() : string{ From d666f17ec63bb0db4c7e19cea29c2be67026e3c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:46:30 +0000 Subject: [PATCH 114/290] Bump build/php from `a51259d` to `8a396c6` (#6540) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index a51259d7a..8a396c63f 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit a51259d7a6ea649d64f409fc0276baa59cf4f19a +Subproject commit 8a396c63fc5e79ea2849bfca100ea21a49ba2933 From cd2a1fdc1d2c327894b3b489937e9d6abb34e472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:03:27 +0000 Subject: [PATCH 115/290] Bump build/php from `8a396c6` to `5016e0a` (#6541) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 8a396c63f..5016e0a3d 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 8a396c63fc5e79ea2849bfca100ea21a49ba2933 +Subproject commit 5016e0a3d54c714c12b331ea0474a6f500ffc0a3 From 2bd9f4108bdf1261fefba78bc7e61526a0097334 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Fri, 29 Nov 2024 20:42:14 +0000 Subject: [PATCH 116/290] 5.21.2 (#6545) --- changelogs/5.21.md | 16 ++++++++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/changelogs/5.21.md b/changelogs/5.21.md index 9b3c2f89a..54bebf5db 100644 --- a/changelogs/5.21.md +++ b/changelogs/5.21.md @@ -110,3 +110,19 @@ Released 12th November 2024. - Fixed garbage collector cycle count increase on player disconnect. - Fixed weakness effect being applied to all attack types, causing damage splash potions to become weaker. - Fixed Enchanted Golden Apple regeneration effect amplifier to match vanilla. + +# 5.21.2 +Released 29th November 2024. + +## Fixes +- Fixed blocks destroyable by water being able to be placed inside water. +- Fixed deprecation warnings about nullable typehints on PHP 8.4. +- Fixed `Utils::getNiceClosureName()` not working correctly on PHP 8.4. +- Fixed incorrect break animations when breaking the block behind an instantly-breakable block like a torch. +- Fixed candle extinguish logic. +- Fixed various documentation issues around array key types. +- Introduced a new PHPStan rule along with `Utils::promoteKeys()` to improve PHPStan error reporting around unspecified array key types. Previously, some errors were missed due to PHPStan's BenevolentUnionType. + +## DevOps +- `pmmp/server-developers` team is now automatically requested for review on any new PR. +- `Status: Waiting on Author` label is now automatically removed from PRs when they are updated. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index bc1b24c62..7b574b4f0 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.21.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 9cab72ed5907b3d26040e88f89b7353b0de03ec3 Mon Sep 17 00:00:00 2001 From: "pmmp-restrictedactions-bot[bot]" <188621379+pmmp-restrictedactions-bot[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:43:17 +0000 Subject: [PATCH 117/290] 5.21.3 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12090411351 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 7b574b4f0..efb38d71b 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.21.2"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.21.3"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 44771c892d93ab7e6ab3007e5df473cf43b3cd87 Mon Sep 17 00:00:00 2001 From: Muqsit Date: Sun, 1 Dec 2024 21:14:30 +0800 Subject: [PATCH 118/290] Use PlayerAuthInputPacket::SNEAKING flag to test for sneaking (#6544) This binds internal sneaking to whether or not the player is currently pressing the shift key, which fixes #5792 and fixes #5903. However, it does introduce visual issues with sneaking, as explained in #6548. This needs to be worked on separately. For now, it's better we trade 2 functional bugs for 1 visual bug. --- src/network/mcpe/handler/InGamePacketHandler.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index cc1f3eab4..f1cfaec19 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -215,7 +215,10 @@ class InGamePacketHandler extends PacketHandler{ if($inputFlags !== $this->lastPlayerAuthInputFlags){ $this->lastPlayerAuthInputFlags = $inputFlags; - $sneaking = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SNEAKING, PlayerAuthInputFlags::STOP_SNEAKING); + $sneaking = $packet->hasFlag(PlayerAuthInputFlags::SNEAKING); + if($this->player->isSneaking() === $sneaking){ + $sneaking = null; + } $sprinting = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SPRINTING, PlayerAuthInputFlags::STOP_SPRINTING); $swimming = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_SWIMMING, PlayerAuthInputFlags::STOP_SWIMMING); $gliding = $this->resolveOnOffInputFlags($inputFlags, PlayerAuthInputFlags::START_GLIDING, PlayerAuthInputFlags::STOP_GLIDING); From 61560ec375f7ffbb705602a16775137ca5a63c2c Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 1 Dec 2024 14:49:27 +0000 Subject: [PATCH 119/290] Support for collecting timings from threads, and implement async task timings (#6333) The following callbacks can now be registered in timings, to allow threads to be notified of these events: - Turning on/off (`TimingsHandler::getToggleCallbacks()->add(...)`) - Reset (`TimingsHandler::getReloadCallbacks()->add(...)`) - Collect (`TimingsHandler::getCollectCallbacks()->add(...)`) Collect callbacks must return `list`. The promises must be `resolve()`d with `list` of printed timings records, as returned by `TimingsHandler::printCurrentThreadRecords()`. It's recommended to use 1 promise per thread. A timings report will be produced once all promises have been resolved. This system is used internally to collect timings for async tasks (closes #6166). For timings viewer developers: Timings format version has been bumped to 3 to accommodate this change. Timings groups should now include a `ThreadId` at the end of timings group names to ensure that their record IDs are segregated correctly, as they could otherwise conflict between threads. The main thread is not required to specify a thread ID. See pmmp/timings@13cefa6279ee8866dc584b9a7977a9c09cecd732 for implementation examples. New PHPStan error is caused by phpstan/phpstan#10924 --- composer.json | 2 +- composer.lock | 14 +-- src/Server.php | 34 ++++- src/command/defaults/TimingsCommand.php | 159 +++++++++++++----------- src/lang/KnownTranslationFactory.php | 4 + src/lang/KnownTranslationKeys.php | 1 + src/scheduler/AsyncTask.php | 10 +- src/scheduler/TimingsCollectionTask.php | 60 +++++++++ src/scheduler/TimingsControlTask.php | 60 +++++++++ src/timings/Timings.php | 21 ++++ src/timings/TimingsHandler.php | 133 +++++++++++++++++++- tests/phpstan/configs/phpstan-bugs.neon | 5 + 12 files changed, 410 insertions(+), 93 deletions(-) create mode 100644 src/scheduler/TimingsCollectionTask.php create mode 100644 src/scheduler/TimingsControlTask.php diff --git a/composer.json b/composer.json index 9747cb567..16bed54b7 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.21.0", + "pocketmine/locale-data": "~2.22.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", diff --git a/composer.lock b/composer.lock index c1a0b0073..7eda66b35 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "476374fb3d22e26a97c1dea8c6736faf", + "content-hash": "c57e8f52250edfd03906219fe14fc240", "packages": [ { "name": "adhocore/json-comment", @@ -420,16 +420,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.21.1", + "version": "2.22.0", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167" + "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/fdba0f764d6281f64e5968dca94fdab96bf4e167", - "reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167", + "url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", + "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", "shasum": "" }, "type": "library", @@ -437,9 +437,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.21.1" + "source": "https://github.com/pmmp/Language/tree/2.22.0" }, - "time": "2024-11-14T23:11:22+00:00" + "time": "2024-11-16T13:28:01+00:00" }, { "name": "pocketmine/log", diff --git a/src/Server.php b/src/Server.php index 074088068..9003a7f9c 100644 --- a/src/Server.php +++ b/src/Server.php @@ -89,6 +89,8 @@ use pocketmine\promise\Promise; use pocketmine\promise\PromiseResolver; use pocketmine\resourcepacks\ResourcePackManager; use pocketmine\scheduler\AsyncPool; +use pocketmine\scheduler\TimingsCollectionTask; +use pocketmine\scheduler\TimingsControlTask; use pocketmine\snooze\SleeperHandler; use pocketmine\stats\SendUsageTask; use pocketmine\thread\log\AttachableThreadSafeLogger; @@ -894,7 +896,36 @@ class Server{ $poolSize = max(1, (int) $poolSize); } + TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false)); + $this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND); + $this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper); + $this->asyncPool->addWorkerStartHook(function(int $i) : void{ + if(TimingsHandler::isEnabled()){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i); + } + }); + TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{ + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId); + } + }); + TimingsHandler::getReloadCallbacks()->add(function() : void{ + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId); + } + }); + TimingsHandler::getCollectCallbacks()->add(function() : array{ + $promises = []; + foreach($this->asyncPool->getRunningWorkers() as $workerId){ + $resolver = new PromiseResolver(); + $this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId); + + $promises[] = $resolver->getPromise(); + } + + return $promises; + }); $netCompressionThreshold = -1; if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){ @@ -968,9 +999,6 @@ class Server{ ))); $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName()))); - TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false)); - $this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND); - DefaultPermissions::registerCorePermissions(); $this->commandMap = new SimpleCommandMap($this); diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php index 3c0701ea4..ec26f3efe 100644 --- a/src/command/defaults/TimingsCommand.php +++ b/src/command/defaults/TimingsCommand.php @@ -26,28 +26,28 @@ namespace pocketmine\command\defaults; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\command\utils\InvalidCommandSyntaxException; +use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; use pocketmine\scheduler\BulkCurlTask; use pocketmine\scheduler\BulkCurlTaskOperation; use pocketmine\timings\TimingsHandler; +use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\InternetException; use pocketmine\utils\InternetRequestResult; -use pocketmine\utils\Utils; use pocketmine\YmlServerProperties; use Symfony\Component\Filesystem\Path; use function count; use function fclose; use function file_exists; use function fopen; -use function fseek; use function fwrite; use function http_build_query; +use function implode; use function is_array; use function json_decode; use function mkdir; -use function stream_get_contents; use function strtolower; use const CURLOPT_AUTOREFERER; use const CURLOPT_FOLLOWLOCATION; @@ -101,82 +101,91 @@ class TimingsCommand extends VanillaCommand{ TimingsHandler::reload(); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset()); }elseif($mode === "merged" || $mode === "report" || $paste){ - $timings = ""; - if($paste){ - $fileTimings = Utils::assumeNotFalse(fopen("php://temp", "r+b"), "Opening php://temp should never fail"); - }else{ - $index = 0; - $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings"); - - if(!file_exists($timingFolder)){ - mkdir($timingFolder, 0777); - } - $timings = Path::join($timingFolder, "timings.txt"); - while(file_exists($timings)){ - $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt"); - } - - $fileTimings = fopen($timings, "a+b"); - } - $lines = TimingsHandler::printTimings(); - foreach($lines as $line){ - fwrite($fileTimings, $line . PHP_EOL); - } - - if($paste){ - fseek($fileTimings, 0); - $data = [ - "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), - "data" => $content = stream_get_contents($fileTimings) - ]; - fclose($fileTimings); - - $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); - - $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( - [new BulkCurlTaskOperation( - "https://$host?upload=true", - 10, - [], - [ - CURLOPT_HTTPHEADER => [ - "User-Agent: $agent", - "Content-Type: application/x-www-form-urlencoded" - ], - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => http_build_query($data), - CURLOPT_AUTOREFERER => false, - CURLOPT_FOLLOWLOCATION => false - ] - )], - function(array $results) use ($sender, $host) : void{ - /** @phpstan-var array $results */ - if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender - return; - } - $result = $results[0]; - if($result instanceof InternetException){ - $sender->getServer()->getLogger()->logException($result); - return; - } - $response = json_decode($result->getBody(), true); - if(is_array($response) && isset($response["id"])){ - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( - "https://" . $host . "/?id=" . $response["id"])); - }else{ - $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody()); - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError()); - } - } - )); - }else{ - fclose($fileTimings); - Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings)); - } + $timingsPromise = TimingsHandler::requestPrintTimings(); + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect()); + $timingsPromise->onCompletion( + fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender), + fn() => throw new AssumptionFailedError("This promise is not expected to be rejected") + ); }else{ throw new InvalidCommandSyntaxException(); } return true; } + + /** + * @param string[] $lines + * @phpstan-param list $lines + */ + private function createReportFile(array $lines, CommandSender $sender) : void{ + $index = 0; + $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings"); + + if(!file_exists($timingFolder)){ + mkdir($timingFolder, 0777); + } + $timings = Path::join($timingFolder, "timings.txt"); + while(file_exists($timings)){ + $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt"); + } + + $fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b")); + foreach($lines as $line){ + fwrite($fileTimings, $line . PHP_EOL); + } + fclose($fileTimings); + + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings)); + } + + /** + * @param string[] $lines + * @phpstan-param list $lines + */ + private function uploadReport(array $lines, CommandSender $sender) : void{ + $data = [ + "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), + "data" => implode("\n", $lines) + ]; + + $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); + + $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( + [new BulkCurlTaskOperation( + "https://$host?upload=true", + 10, + [], + [ + CURLOPT_HTTPHEADER => [ + "User-Agent: $agent", + "Content-Type: application/x-www-form-urlencoded" + ], + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query($data), + CURLOPT_AUTOREFERER => false, + CURLOPT_FOLLOWLOCATION => false + ] + )], + function(array $results) use ($sender, $host) : void{ + /** @phpstan-var array $results */ + if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender + return; + } + $result = $results[0]; + if($result instanceof InternetException){ + $sender->getServer()->getLogger()->logException($result); + return; + } + $response = json_decode($result->getBody(), true); + if(is_array($response) && isset($response["id"])){ + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( + "https://" . $host . "/?id=" . $response["id"])); + }else{ + $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody()); + Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError()); + } + } + )); + } } diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 8153a80d6..f0b70ffd1 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -1441,6 +1441,10 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED, []); } + public static function pocketmine_command_timings_collect() : Translatable{ + return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_COLLECT, []); + } + public static function pocketmine_command_timings_description() : Translatable{ return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_DESCRIPTION, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index 4805d0c56..1c1fef20f 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -315,6 +315,7 @@ final class KnownTranslationKeys{ public const POCKETMINE_COMMAND_TIME_DESCRIPTION = "pocketmine.command.time.description"; public const POCKETMINE_COMMAND_TIME_USAGE = "pocketmine.command.time.usage"; public const POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED = "pocketmine.command.timings.alreadyEnabled"; + public const POCKETMINE_COMMAND_TIMINGS_COLLECT = "pocketmine.command.timings.collect"; public const POCKETMINE_COMMAND_TIMINGS_DESCRIPTION = "pocketmine.command.timings.description"; public const POCKETMINE_COMMAND_TIMINGS_DISABLE = "pocketmine.command.timings.disable"; public const POCKETMINE_COMMAND_TIMINGS_ENABLE = "pocketmine.command.timings.enable"; diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index b0b64347a..33a0949d4 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -27,6 +27,7 @@ use pmmp\thread\Runnable; use pmmp\thread\ThreadSafe; use pmmp\thread\ThreadSafeArray; use pocketmine\thread\NonThreadSafeValue; +use pocketmine\timings\Timings; use function array_key_exists; use function igbinary_serialize; use function igbinary_unserialize; @@ -78,7 +79,14 @@ abstract class AsyncTask extends Runnable{ public function run() : void{ $this->result = null; - $this->onRun(); + $timings = Timings::getAsyncTaskRunTimings($this); + $timings->startTiming(); + + try{ + $this->onRun(); + }finally{ + $timings->stopTiming(); + } $this->finished = true; AsyncWorker::getNotifier()->wakeupSleeper(); diff --git a/src/scheduler/TimingsCollectionTask.php b/src/scheduler/TimingsCollectionTask.php new file mode 100644 index 000000000..25cd41166 --- /dev/null +++ b/src/scheduler/TimingsCollectionTask.php @@ -0,0 +1,60 @@ +> + */ +final class TimingsCollectionTask extends AsyncTask{ + private const TLS_KEY_RESOLVER = "resolver"; + + /** + * @phpstan-param PromiseResolver> $promiseResolver + */ + public function __construct(PromiseResolver $promiseResolver){ + $this->storeLocal(self::TLS_KEY_RESOLVER, $promiseResolver); + } + + public function onRun() : void{ + $this->setResult(TimingsHandler::printCurrentThreadRecords()); + } + + public function onCompletion() : void{ + /** + * @var string[] $result + * @phpstan-var list $result + */ + $result = $this->getResult(); + /** + * @var PromiseResolver $promiseResolver + * @phpstan-var PromiseResolver> $promiseResolver + */ + $promiseResolver = $this->fetchLocal(self::TLS_KEY_RESOLVER); + + $promiseResolver->resolve($result); + } +} diff --git a/src/scheduler/TimingsControlTask.php b/src/scheduler/TimingsControlTask.php new file mode 100644 index 000000000..51e906e6b --- /dev/null +++ b/src/scheduler/TimingsControlTask.php @@ -0,0 +1,60 @@ +operation === self::ENABLE){ + TimingsHandler::setEnabled(true); + \GlobalLogger::get()->debug("Enabled timings"); + }elseif($this->operation === self::DISABLE){ + TimingsHandler::setEnabled(false); + \GlobalLogger::get()->debug("Disabled timings"); + }elseif($this->operation === self::RELOAD){ + TimingsHandler::reload(); + \GlobalLogger::get()->debug("Reset timings"); + }else{ + throw new \InvalidArgumentException("Invalid operation $this->operation"); + } + } +} diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 77f8efee6..8372e14c5 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -124,11 +124,16 @@ abstract class Timings{ /** @var TimingsHandler[] */ private static array $asyncTaskProgressUpdate = []; + /** @var TimingsHandler[] */ private static array $asyncTaskCompletion = []; /** @var TimingsHandler[] */ private static array $asyncTaskError = []; + private static TimingsHandler $asyncTaskWorkers; + /** @var TimingsHandler[] */ + private static array $asyncTaskRun = []; + public static function init() : void{ if(self::$initialized){ return; @@ -188,6 +193,8 @@ abstract class Timings{ self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync); self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync); + self::$asyncTaskWorkers = new TimingsHandler("Async Task Workers"); + self::$playerCommand = new TimingsHandler("Player Command"); self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache"); @@ -357,4 +364,18 @@ abstract class Timings{ return self::$asyncTaskError[$taskClass]; } + + public static function getAsyncTaskRunTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ + $taskClass = $task::class; + if(!isset(self::$asyncTaskRun[$taskClass])){ + self::init(); + self::$asyncTaskRun[$taskClass] = new TimingsHandler( + "AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Run", + self::$asyncTaskWorkers, + $group + ); + } + + return self::$asyncTaskRun[$taskClass]; + } } diff --git a/src/timings/TimingsHandler.php b/src/timings/TimingsHandler.php index 574dd6d2b..95f7dbacc 100644 --- a/src/timings/TimingsHandler.php +++ b/src/timings/TimingsHandler.php @@ -23,20 +23,66 @@ declare(strict_types=1); namespace pocketmine\timings; +use pmmp\thread\Thread as NativeThread; +use pocketmine\promise\Promise; +use pocketmine\promise\PromiseResolver; use pocketmine\Server; +use pocketmine\utils\ObjectSet; use pocketmine\utils\Utils; +use function array_merge; +use function array_push; use function hrtime; use function implode; use function spl_object_id; +/** + * @phpstan-type CollectPromise Promise> + */ class TimingsHandler{ - private const FORMAT_VERSION = 2; //peak timings fix + private const FORMAT_VERSION = 3; //thread timings collection private static bool $enabled = false; private static int $timingStart = 0; - /** @return string[] */ - public static function printTimings() : array{ + /** @phpstan-var ObjectSet<\Closure(bool $enable) : void> */ + private static ?ObjectSet $toggleCallbacks = null; + /** @phpstan-var ObjectSet<\Closure() : void> */ + private static ?ObjectSet $reloadCallbacks = null; + /** @phpstan-var ObjectSet<\Closure() : list> */ + private static ?ObjectSet $collectCallbacks = null; + + /** + * @phpstan-template T of object + * @phpstan-param ?ObjectSet $where + * @phpstan-param-out ObjectSet $where + * @phpstan-return ObjectSet + */ + private static function lazyGetSet(?ObjectSet &$where) : ObjectSet{ + //workaround for phpstan bug - allows us to ignore 1 error instead of 6 without suppressing other errors + return $where ??= new ObjectSet(); + } + + /** + * @phpstan-return ObjectSet<\Closure(bool $enable) : void> + */ + public static function getToggleCallbacks() : ObjectSet{ return self::lazyGetSet(self::$toggleCallbacks); } + + /** + * @phpstan-return ObjectSet<\Closure() : void> + */ + public static function getReloadCallbacks() : ObjectSet{ return self::lazyGetSet(self::$reloadCallbacks); } + + /** + * @phpstan-return ObjectSet<\Closure() : list> + */ + public static function getCollectCallbacks() : ObjectSet{ return self::lazyGetSet(self::$collectCallbacks); } + + /** + * @return string[] + * @phpstan-return list + */ + public static function printCurrentThreadRecords() : array{ + $threadId = NativeThread::getCurrentThread()?->getThreadId(); $groups = []; foreach(TimingsRecord::getAll() as $timings){ @@ -49,7 +95,7 @@ class TimingsHandler{ $avg = $time / $count; - $group = $timings->getGroup(); + $group = $timings->getGroup() . ($threadId !== null ? " ThreadId: $threadId" : ""); $groups[$group][] = implode(" ", [ $timings->getName(), "Time: $time", @@ -72,6 +118,15 @@ class TimingsHandler{ } } + return $result; + } + + /** + * @return string[] + */ + private static function printFooter() : array{ + $result = []; + $result[] = "# Version " . Server::getInstance()->getVersion(); $result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion(); @@ -79,29 +134,95 @@ class TimingsHandler{ $sampleTime = hrtime(true) - self::$timingStart; $result[] = "Sample time $sampleTime (" . ($sampleTime / 1000000000) . "s)"; + return $result; } + /** + * @deprecated This only collects timings from the main thread. Collecting timings from all threads is an async + * operation, so it can't be done synchronously. + * + * @return string[] + */ + public static function printTimings() : array{ + $records = self::printCurrentThreadRecords(); + $footer = self::printFooter(); + + return [...$records, ...$footer]; + } + + /** + * Collects timings asynchronously, allowing timings from multiple threads to be aggregated into a single report. + * + * NOTE: You need to add a callback to collectCallbacks if you want to include timings from other threads. They + * won't be automatically collected if you don't, since the main thread has no way to access them. + * + * This is an asynchronous operation, and the result is returned as a promise. + * The caller must add a callback to the returned promise to get the complete timings report. + * + * @phpstan-return Promise> + */ + public static function requestPrintTimings() : Promise{ + $thisThreadRecords = self::printCurrentThreadRecords(); + + $otherThreadRecordPromises = []; + if(self::$collectCallbacks !== null){ + foreach(self::$collectCallbacks as $callback){ + $callbackPromises = $callback(); + array_push($otherThreadRecordPromises, ...$callbackPromises); + } + } + + $resolver = new PromiseResolver(); + Promise::all($otherThreadRecordPromises)->onCompletion( + function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{ + $resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]); + }, + function() : void{ + throw new \AssertionError("This promise is not expected to be rejected"); + } + ); + + return $resolver->getPromise(); + } + public static function isEnabled() : bool{ return self::$enabled; } public static function setEnabled(bool $enable = true) : void{ + if($enable === self::$enabled){ + return; + } self::$enabled = $enable; - self::reload(); + self::internalReload(); + if(self::$toggleCallbacks !== null){ + foreach(self::$toggleCallbacks as $callback){ + $callback($enable); + } + } } public static function getStartTime() : float{ return self::$timingStart; } - public static function reload() : void{ + private static function internalReload() : void{ TimingsRecord::reset(); if(self::$enabled){ self::$timingStart = hrtime(true); } } + public static function reload() : void{ + self::internalReload(); + if(self::$reloadCallbacks !== null){ + foreach(self::$reloadCallbacks as $callback){ + $callback(); + } + } + } + public static function tick(bool $measure = true) : void{ if(self::$enabled){ TimingsRecord::tick($measure); diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 6f2c94223..ea5e1c62a 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -90,6 +90,11 @@ parameters: count: 1 path: ../../../src/plugin/PluginManager.php + - + message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\ but returns pocketmine\\\\utils\\\\ObjectSet\\\\.$#" + count: 1 + path: ../../../src/timings/TimingsHandler.php + - message: "#^Casting to int something that's already int\\.$#" count: 1 From a593180ef9f5f3f7bf4023fd3a2a72c7def4e8b4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 1 Dec 2024 15:01:41 +0000 Subject: [PATCH 120/290] Deprecate some stuff --- src/block/Campfire.php | 8 ++++++++ src/timings/Timings.php | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/block/Campfire.php b/src/block/Campfire.php index ce759ee87..9f4c42a9c 100644 --- a/src/block/Campfire.php +++ b/src/block/Campfire.php @@ -69,6 +69,10 @@ class Campfire extends Transparent{ private const UPDATE_INTERVAL_TICKS = 10; + /** + * @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block + * has never been set in the world. + */ protected CampfireInventory $inventory; /** @@ -129,6 +133,10 @@ class Campfire extends Transparent{ return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)]; } + /** + * @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block + * has never been set in the world. + */ public function getInventory() : CampfireInventory{ return $this->inventory; } diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 8372e14c5..e71700023 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -37,6 +37,7 @@ use function str_starts_with; abstract class Timings{ public const GROUP_MINECRAFT = "Minecraft"; + /** @deprecated No longer used */ public const GROUP_BREAKDOWN = "Minecraft - Breakdown"; private static bool $initialized = false; @@ -351,6 +352,9 @@ abstract class Timings{ return self::$asyncTaskCompletion[$taskClass]; } + /** + * @deprecated No longer used + */ public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ $taskClass = $task::class; if(!isset(self::$asyncTaskError[$taskClass])){ From 93a9007f3c684a1e8057dd97ec58c92b70a5ebae Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 1 Dec 2024 15:10:07 +0000 Subject: [PATCH 121/290] Added ClosureCommand (#6063) this is intended to replace PluginCommand and CommandExecutor, both of which are overengineered and unfit for purpose. Allowing a closure allows much greater flexibility. We can't use this within the core yet, as plugins will expect PluginBase->getCommand() to return PluginCommand (with its associated setExecutor() and similar APIs). However, I think this is useful enough to add by itself. --- src/command/ClosureCommand.php | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/command/ClosureCommand.php diff --git a/src/command/ClosureCommand.php b/src/command/ClosureCommand.php new file mode 100644 index 000000000..289c82853 --- /dev/null +++ b/src/command/ClosureCommand.php @@ -0,0 +1,60 @@ + $args) : mixed + */ +final class ClosureCommand extends Command{ + /** @phpstan-var Execute */ + private \Closure $execute; + + /** + * @param string[] $permissions + * @phpstan-param Execute $execute + */ + public function __construct( + string $name, + \Closure $execute, + array $permissions, + Translatable|string $description = "", + Translatable|string|null $usageMessage = null, + array $aliases = [] + ){ + Utils::validateCallableSignature( + fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1, + $execute, + ); + $this->execute = $execute; + parent::__construct($name, $description, $usageMessage, $aliases); + $this->setPermissions($permissions); + } + + public function execute(CommandSender $sender, string $commandLabel, array $args){ + return ($this->execute)($sender, $this, $commandLabel, $args); + } +} From 12214792b32a63a0668a2ecbc175f148bd7fe222 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 1 Dec 2024 17:42:26 +0000 Subject: [PATCH 122/290] Allow eating in creative & peaceful closes #5923 closes #6056 --- src/entity/FoodSource.php | 1 + src/entity/Human.php | 11 ++++++++++- src/entity/HungerManager.php | 3 ++- src/item/Food.php | 2 +- src/player/Player.php | 4 ++++ 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/entity/FoodSource.php b/src/entity/FoodSource.php index 98478b4a1..028c76783 100644 --- a/src/entity/FoodSource.php +++ b/src/entity/FoodSource.php @@ -34,6 +34,7 @@ interface FoodSource extends Consumable{ /** * Returns whether a Human eating this FoodSource must have a non-full hunger bar. + * This is ignored in creative mode and in peaceful difficulty. */ public function requiresHunger() : bool; } diff --git a/src/entity/Human.php b/src/entity/Human.php index f2c4c7a74..833196651 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -68,6 +68,7 @@ use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket; use pocketmine\player\Player; use pocketmine\world\sound\TotemUseSound; +use pocketmine\world\World; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; use function array_fill; @@ -189,8 +190,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ return $this->hungerManager; } + /** + * Returns whether the Human can eat food. This may return a different result than {@link HungerManager::isHungry()}, + * as HungerManager only handles the hunger bar. + */ + public function canEat() : bool{ + return $this->hungerManager->isHungry() || $this->getWorld()->getDifficulty() === World::DIFFICULTY_PEACEFUL; + } + public function consumeObject(Consumable $consumable) : bool{ - if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->hungerManager->isHungry()){ + if($consumable instanceof FoodSource && $consumable->requiresHunger() && !$this->canEat()){ return false; } diff --git a/src/entity/HungerManager.php b/src/entity/HungerManager.php index a31855891..7e3b40e74 100644 --- a/src/entity/HungerManager.php +++ b/src/entity/HungerManager.php @@ -88,7 +88,8 @@ class HungerManager{ } /** - * Returns whether this Human may consume objects requiring hunger. + * Returns whether the food level is below the maximum. + * This doesn't decide if the entity can eat food. Use {@link Human::canEat()} for that. */ public function isHungry() : bool{ return $this->getFood() < $this->getMaxFood(); diff --git a/src/item/Food.php b/src/item/Food.php index 1950c4b14..d01ce9e18 100644 --- a/src/item/Food.php +++ b/src/item/Food.php @@ -44,6 +44,6 @@ abstract class Food extends Item implements FoodSourceItem{ } public function canStartUsingItem(Player $player) : bool{ - return !$this->requiresHunger() || $player->getHungerManager()->isHungry(); + return !$this->requiresHunger() || $player->canEat(); } } diff --git a/src/player/Player.php b/src/player/Player.php index e4c74c4ef..f20945b9a 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1471,6 +1471,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return true; } + public function canEat() : bool{ + return $this->isCreative() || parent::canEat(); + } + public function canBreathe() : bool{ return $this->isCreative() || parent::canBreathe(); } From f3763ae691ab73db8ca53d44db32b7b4e7439b90 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Sun, 1 Dec 2024 13:25:45 -0500 Subject: [PATCH 123/290] Implement Recovery compass (#5502) Co-authored-by: Dylan K. Taylor --- .../ItemSerializerDeserializerRegistrar.php | 1 + src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 1 + src/item/VanillaItems.php | 2 + src/player/Player.php | 55 +++++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 8240bf063..df1db4211 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -352,6 +352,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::RAW_COPPER, Items::RAW_COPPER()); $this->map1to1Item(Ids::RAW_GOLD, Items::RAW_GOLD()); $this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON()); + $this->map1to1Item(Ids::RECOVERY_COMPASS, Items::RECOVERY_COMPASS()); $this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST()); $this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH()); diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index f3ad406a6..c93c23e81 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -327,8 +327,9 @@ final class ItemTypeIds{ public const GOAT_HORN = 20288; public const END_CRYSTAL = 20289; public const ICE_BOMB = 20290; + public const RECOVERY_COMPASS = 20291; - public const FIRST_UNUSED_ITEM_ID = 20291; + public const FIRST_UNUSED_ITEM_ID = 20292; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 4cc9d29eb..09c93d5d9 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1481,6 +1481,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("record_strad", fn() => Items::RECORD_STRAD()); $result->register("record_wait", fn() => Items::RECORD_WAIT()); $result->register("record_ward", fn() => Items::RECORD_WARD()); + $result->register("recovery_compass", fn() => Items::RECOVERY_COMPASS()); $result->register("redstone", fn() => Items::REDSTONE_DUST()); $result->register("redstone_dust", fn() => Items::REDSTONE_DUST()); $result->register("rib_armor_trim_smithing_template", fn() => Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index dcf59daf6..6768ed8f0 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -284,6 +284,7 @@ use function strtolower; * @method static Record RECORD_STRAD() * @method static Record RECORD_WAIT() * @method static Record RECORD_WARD() + * @method static Item RECOVERY_COMPASS() * @method static Redstone REDSTONE_DUST() * @method static Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static RottenFlesh ROTTEN_FLESH() @@ -574,6 +575,7 @@ final class VanillaItems{ self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad")); self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait")); self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward")); + self::register("recovery_compass", fn(IID $id) => new Item($id, "Recovery Compass")); self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone")); self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh")); self::register("scute", fn(IID $id) => new Item($id, "Scute")); diff --git a/src/player/Player.php b/src/player/Player.php index f20945b9a..bf911a2ff 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -110,6 +110,7 @@ use pocketmine\network\mcpe\protocol\AnimatePacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; use pocketmine\network\mcpe\protocol\SetActorMotionPacket; use pocketmine\network\mcpe\protocol\types\BlockPosition; +use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; @@ -191,6 +192,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private const TAG_SPAWN_X = "SpawnX"; //TAG_Int private const TAG_SPAWN_Y = "SpawnY"; //TAG_Int private const TAG_SPAWN_Z = "SpawnZ"; //TAG_Int + private const TAG_DEATH_WORLD = "DeathLevel"; //TAG_String + private const TAG_DEATH_X = "DeathPositionX"; //TAG_Int + private const TAG_DEATH_Y = "DeathPositionY"; //TAG_Int + private const TAG_DEATH_Z = "DeathPositionZ"; //TAG_Int public const TAG_LEVEL = "Level"; //TAG_String public const TAG_LAST_KNOWN_XUID = "LastKnownXUID"; //TAG_String @@ -273,6 +278,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private bool $respawnLocked = false; + private ?Position $deathPosition = null; + //TODO: Abilities protected bool $autoJump = true; protected bool $allowFlight = false; @@ -391,6 +398,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_SPAWN_WORLD, ""))) instanceof World){ $this->spawnPosition = new Position($nbt->getInt(self::TAG_SPAWN_X), $nbt->getInt(self::TAG_SPAWN_Y), $nbt->getInt(self::TAG_SPAWN_Z), $world); } + if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_DEATH_WORLD, ""))) instanceof World){ + $this->deathPosition = new Position($nbt->getInt(self::TAG_DEATH_X), $nbt->getInt(self::TAG_DEATH_Y), $nbt->getInt(self::TAG_DEATH_Z), $world); + } } public function getLeaveMessage() : Translatable|string{ @@ -1032,6 +1042,30 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } } + public function getDeathPosition() : ?Position{ + if($this->deathPosition !== null && !$this->deathPosition->isValid()){ + $this->deathPosition = null; + } + return $this->deathPosition; + } + + /** + * @param Vector3|Position|null $pos + */ + public function setDeathPosition(?Vector3 $pos) : void{ + if($pos !== null){ + if($pos instanceof Position && $pos->world !== null){ + $world = $pos->world; + }else{ + $world = $this->getWorld(); + } + $this->deathPosition = new Position($pos->x, $pos->y, $pos->z, $world); + }else{ + $this->deathPosition = null; + } + $this->networkPropertiesDirty = true; + } + /** * @return Position */ @@ -2336,6 +2370,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ unset($this->cursorInventory); unset($this->craftingGrid); $this->spawnPosition = null; + $this->deathPosition = null; $this->blockBreakHandler = null; parent::destroyCycles(); } @@ -2377,6 +2412,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $nbt->setInt(self::TAG_SPAWN_Z, $spawn->getFloorZ()); } + if($this->deathPosition !== null && $this->deathPosition->isValid()){ + $nbt->setString(self::TAG_DEATH_WORLD, $this->deathPosition->getWorld()->getFolderName()); + $nbt->setInt(self::TAG_DEATH_X, $this->deathPosition->getFloorX()); + $nbt->setInt(self::TAG_DEATH_Y, $this->deathPosition->getFloorY()); + $nbt->setInt(self::TAG_DEATH_Z, $this->deathPosition->getFloorZ()); + } + $nbt->setInt(self::TAG_GAME_MODE, GameModeIdMap::getInstance()->toId($this->gamemode)); $nbt->setLong(self::TAG_FIRST_PLAYED, $this->firstPlayed); $nbt->setLong(self::TAG_LAST_PLAYED, (int) floor(microtime(true) * 1000)); @@ -2396,6 +2438,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ //main inventory and drops the rest on the ground. $this->removeCurrentWindow(); + $this->setDeathPosition($this->getPosition()); + $ev = new PlayerDeathEvent($this, $this->getDrops(), $this->getXpDropAmount(), null); $ev->call(); @@ -2524,6 +2568,17 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $properties->setPlayerFlag(PlayerMetadataFlags::SLEEP, $this->sleeping !== null); $properties->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $this->sleeping !== null ? BlockPosition::fromVector3($this->sleeping) : new BlockPosition(0, 0, 0)); + + if($this->deathPosition !== null && $this->deathPosition->world === $this->location->world){ + $properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, BlockPosition::fromVector3($this->deathPosition)); + //TODO: this should be updated when dimensions are implemented + $properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD); + $properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 1); + }else{ + $properties->setBlockPos(EntityMetadataProperties::PLAYER_DEATH_POSITION, new BlockPosition(0, 0, 0)); + $properties->setInt(EntityMetadataProperties::PLAYER_DEATH_DIMENSION, DimensionIds::OVERWORLD); + $properties->setByte(EntityMetadataProperties::PLAYER_HAS_DIED, 0); + } } public function sendData(?array $targets, ?array $data = null) : void{ From f1a3b42620c11a6e9be99f9d014084f1bd6fe490 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Sun, 1 Dec 2024 14:46:38 -0500 Subject: [PATCH 124/290] Implement frost walker enchantment (#5497) Co-authored-by: Dylan T. --- src/block/Magma.php | 2 +- src/data/bedrock/EnchantmentIdMap.php | 2 + src/entity/Living.php | 55 +++++++++++++++++++ .../AvailableEnchantmentRegistry.php | 1 + .../enchantment/StringToEnchantmentParser.php | 1 + src/item/enchantment/VanillaEnchantments.php | 11 ++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/block/Magma.php b/src/block/Magma.php index d2f309325..7b3fa5229 100644 --- a/src/block/Magma.php +++ b/src/block/Magma.php @@ -39,7 +39,7 @@ class Magma extends Opaque{ } public function onEntityInside(Entity $entity) : bool{ - if($entity instanceof Living && !$entity->isSneaking()){ + if($entity instanceof Living && !$entity->isSneaking() && $entity->getFrostWalkerLevel() === 0){ $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1); $entity->attack($ev); } diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php index e3d652b19..90a10dc20 100644 --- a/src/data/bedrock/EnchantmentIdMap.php +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -66,5 +66,7 @@ final class EnchantmentIdMap{ $this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING()); $this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK()); + + $this->register(EnchantmentIds::FROST_WALKER, VanillaEnchantments::FROST_WALKER()); } } diff --git a/src/entity/Living.php b/src/entity/Living.php index 1f27a5cac..852344784 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -25,6 +25,8 @@ namespace pocketmine\entity; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; +use pocketmine\block\VanillaBlocks; +use pocketmine\block\Water; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\entity\animation\DeathAnimation; use pocketmine\entity\animation\HurtAnimation; @@ -44,6 +46,7 @@ use pocketmine\item\Durable; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; +use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\math\VoxelRayTrace; use pocketmine\nbt\tag\CompoundTag; @@ -64,6 +67,7 @@ use pocketmine\world\sound\EntityLandSound; use pocketmine\world\sound\EntityLongFallSound; use pocketmine\world\sound\EntityShortFallSound; use pocketmine\world\sound\ItemBreakSound; +use function abs; use function array_shift; use function atan2; use function ceil; @@ -128,6 +132,8 @@ abstract class Living extends Entity{ protected bool $gliding = false; protected bool $swimming = false; + private ?int $frostWalkerLevel = null; + protected function getInitialDragMultiplier() : float{ return 0.02; } protected function getInitialGravity() : float{ return 0.08; } @@ -151,6 +157,14 @@ abstract class Living extends Entity{ $this->getViewers(), fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this) ))); + $this->armorInventory->getListeners()->add(new CallbackInventoryListener( + onSlotChange: function(Inventory $inventory, int $slot) : void{ + if($slot === ArmorInventory::SLOT_FEET){ + $this->frostWalkerLevel = null; + } + }, + onContentChange: function() : void{ $this->frostWalkerLevel = null; } + )); $health = $this->getMaxHealth(); @@ -687,6 +701,47 @@ abstract class Living extends Entity{ return $hasUpdate; } + protected function move(float $dx, float $dy, float $dz) : void{ + $oldX = $this->location->x; + $oldZ = $this->location->z; + + parent::move($dx, $dy, $dz); + + $frostWalkerLevel = $this->getFrostWalkerLevel(); + if($frostWalkerLevel > 0 && (abs($this->location->x - $oldX) > self::MOTION_THRESHOLD || abs($this->location->z - $oldZ) > self::MOTION_THRESHOLD)){ + $this->applyFrostWalker($frostWalkerLevel); + } + } + + protected function applyFrostWalker(int $level) : void{ + $radius = $level + 2; + $world = $this->getWorld(); + + $baseX = $this->location->getFloorX(); + $y = $this->location->getFloorY() - 1; + $baseZ = $this->location->getFloorZ(); + + $frostedIce = VanillaBlocks::FROSTED_ICE(); + for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){ + for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){ + $block = $world->getBlockAt($x, $y, $z); + if( + !$block instanceof Water || + !$block->isSource() || + $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR || + count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0 + ){ + continue; + } + $world->setBlockAt($x, $y, $z, $frostedIce); + } + } + } + + public function getFrostWalkerLevel() : int{ + return $this->frostWalkerLevel ??= $this->armorInventory->getBoots()->getEnchantmentLevel(VanillaEnchantments::FROST_WALKER()); + } + /** * Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water. */ diff --git a/src/item/enchantment/AvailableEnchantmentRegistry.php b/src/item/enchantment/AvailableEnchantmentRegistry.php index cae94c666..eed7bff52 100644 --- a/src/item/enchantment/AvailableEnchantmentRegistry.php +++ b/src/item/enchantment/AvailableEnchantmentRegistry.php @@ -57,6 +57,7 @@ final class AvailableEnchantmentRegistry{ $this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]); $this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []); $this->register(Enchantments::AQUA_AFFINITY(), [Tags::HELMET], []); + $this->register(Enchantments::FROST_WALKER(), [/* no primary items */], [Tags::BOOTS]); $this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []); $this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []); $this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []); diff --git a/src/item/enchantment/StringToEnchantmentParser.php b/src/item/enchantment/StringToEnchantmentParser.php index 47a750ff2..b6763e491 100644 --- a/src/item/enchantment/StringToEnchantmentParser.php +++ b/src/item/enchantment/StringToEnchantmentParser.php @@ -44,6 +44,7 @@ final class StringToEnchantmentParser extends StringToTParser{ $result->register("fire_protection", fn() => VanillaEnchantments::FIRE_PROTECTION()); $result->register("flame", fn() => VanillaEnchantments::FLAME()); $result->register("fortune", fn() => VanillaEnchantments::FORTUNE()); + $result->register("frost_walker", fn() => VanillaEnchantments::FROST_WALKER()); $result->register("infinity", fn() => VanillaEnchantments::INFINITY()); $result->register("knockback", fn() => VanillaEnchantments::KNOCKBACK()); $result->register("mending", fn() => VanillaEnchantments::MENDING()); diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index 19ce39716..1132dc9c6 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -41,6 +41,7 @@ use pocketmine\utils\RegistryTrait; * @method static ProtectionEnchantment FIRE_PROTECTION() * @method static Enchantment FLAME() * @method static Enchantment FORTUNE() + * @method static Enchantment FROST_WALKER() * @method static Enchantment INFINITY() * @method static KnockbackEnchantment KNOCKBACK() * @method static Enchantment MENDING() @@ -145,6 +146,16 @@ final class VanillaEnchantments{ fn(int $level) : int => 10 * $level, 30 )); + + self::register("FROST_WALKER", new Enchantment( + KnownTranslationFactory::enchantment_frostwalker(), + Rarity::RARE, + 0, + 0, + 2, + fn(int $level) : int => 10 * $level, + 15 + )); self::register("AQUA_AFFINITY", new Enchantment( KnownTranslationFactory::enchantment_waterWorker(), Rarity::RARE, From 0dae786a21630303fd91b9607493c12663320676 Mon Sep 17 00:00:00 2001 From: Doge Date: Sun, 1 Dec 2024 23:24:50 +0300 Subject: [PATCH 125/290] feat(Server): add a setter for `maxPlayers` (#6261) --- src/Server.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Server.php b/src/Server.php index a8d6ddb5e..650329471 100644 --- a/src/Server.php +++ b/src/Server.php @@ -345,6 +345,10 @@ class Server{ return $this->maxPlayers; } + public function setMaxPlayers(int $maxPlayers) : void{ + $this->maxPlayers = $maxPlayers; + } + /** * Returns whether the server requires that players be authenticated to Xbox Live. If true, connecting players who * are not logged into Xbox Live will be disconnected. From fcef015f32c0b0709cfd4d6f1b3c32f484b25bde Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 2 Dec 2024 00:40:55 +0000 Subject: [PATCH 126/290] L link --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3490542c5..799c9d99c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,7 +123,7 @@ The following are required as a minimum for pull requests. PRs that don't meet t - Remember, PRs with small diffs are much easier to review. Small PRs are generally reviewed and merged much faster than large ones. - **Start small.** Try fixing minor bugs or doing something isolated (e.g. adding a new block or item) before attempting larger changes. - This helps you get familiar with the codebase, the contribution process, and the expectations of maintainers. - - Check out the [issues page]() for something that you could tackle without too much effort. + - Check out ["Easy task" issues](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue+is%3Aopen+label%3A%22Easy+task%22) on the issues page for something that you could tackle without too much effort. - **Do not copy-paste other people's code**. Many PRs involve discussion about the changes, and changes are often requested by reviewers. If you don't understand the code you're copy-pasting, your PR is likely to fail. - **Do not edit code directly on github.com.** We recommend learning how to use [`git`](https://git-scm.com). `git` allows you to "clone" a repository onto your computer, so that you can make changes using an IDE. - **Use an IDE, not a text editor.** We recommend PhpStorm or VSCode. From 49da50659f08472ddc41b6925098006ba95de3f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:36:12 +0000 Subject: [PATCH 127/290] Bump docker/build-push-action from 6.9.0 to 6.10.0 (#6553) --- .github/workflows/build-docker-image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 4325c63f2..94856fa44 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: push: true context: ./pocketmine-mp From 06028aac97d876ce3ef249d64f70013b2f76d1c1 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 3 Dec 2024 02:07:58 +0000 Subject: [PATCH 128/290] issues: don't recommend forums to get help --- .github/ISSUE_TEMPLATE/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d13fb4498..d18b277e7 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,9 +3,6 @@ contact_links: - name: Help & support on Discord url: https://discord.gg/bmSAZBG about: We don't accept support requests on the issue tracker. Please try asking on Discord instead. - - name: Help & support on forums - url: https://forums.pmmp.io - about: We don't accept support requests on the issue tracker. Please try asking on forums instead. - name: Documentation url: https://pmmp.rtfd.io about: PocketMine-MP documentation From c56d4d3e3c965eff18688be9290fc22c3c6ada07 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 3 Dec 2024 14:56:22 +0000 Subject: [PATCH 129/290] dependabot: update github actions deps together, monthly --- .github/dependabot.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 97607ab8f..13721f0ba 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -37,4 +37,7 @@ updates: - package-ecosystem: github-actions directory: "/" schedule: - interval: weekly + interval: monthly + groups: + github-actions: + patterns: ["*"] From 2d0321ff021d312c33c37a8b085fcd597783fb2b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 3 Dec 2024 15:19:38 +0000 Subject: [PATCH 130/290] Switch back to official JsonMapper the issues that led to the need for a fork have been addressed in the 5.0.0 release. --- composer.json | 2 +- composer.lock | 107 ++++++++++++++++++++++++-------------------------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/composer.json b/composer.json index 16bed54b7..c67ffbeeb 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", - "pocketmine/netresearch-jsonmapper": "~v4.4.999", + "netresearch/jsonmapper": "~v5.0.0", "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40", "pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40", "pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40", diff --git a/composer.lock b/composer.lock index 7eda66b35..b0ff16095 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c57e8f52250edfd03906219fe14fc240", + "content-hash": "119f9b72703588e608a010cefa55db0b", "packages": [ { "name": "adhocore/json-comment", @@ -125,6 +125,57 @@ ], "time": "2023-11-29T23:19:16+00:00" }, + { + "name": "netresearch/jsonmapper", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0" + }, + "time": "2024-09-08T10:20:00+00:00" + }, { "name": "pocketmine/bedrock-block-upgrade-schema", "version": "5.0.0", @@ -565,60 +616,6 @@ }, "time": "2023-07-14T13:01:49+00:00" }, - { - "name": "pocketmine/netresearch-jsonmapper", - "version": "v4.4.999", - "source": { - "type": "git", - "url": "https://github.com/pmmp/netresearch-jsonmapper.git", - "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pmmp/netresearch-jsonmapper/zipball/9a6610033d56e358e86a3e4fd5f87063c7318833", - "reference": "9a6610033d56e358e86a3e4fd5f87063c7318833", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" - }, - "replace": { - "netresearch/jsonmapper": "~4.2.0" - }, - "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", - "squizlabs/php_codesniffer": "~3.5" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonMapper": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" - } - ], - "description": "Fork of netresearch/jsonmapper with security fixes needed by pocketmine/pocketmine-mp", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/pmmp/netresearch-jsonmapper/tree/v4.4.999" - }, - "time": "2024-02-23T13:17:01+00:00" - }, { "name": "pocketmine/raklib", "version": "1.1.1", From ba6828c6bd6ad76c0d99f8f8243b95cd13bc2463 Mon Sep 17 00:00:00 2001 From: Dries C Date: Wed, 4 Dec 2024 14:36:52 +0100 Subject: [PATCH 131/290] Release 5.22.0 (Bedrock 1.21.50 support) (#6559) Co-authored-by: Dylan K. Taylor --- changelogs/5.22.md | 16 ++++++++ composer.json | 6 +-- composer.lock | 38 +++++++++---------- src/VersionInfo.php | 4 +- src/data/bedrock/BedrockDataFiles.php | 1 + src/data/bedrock/BiomeIds.php | 1 + src/data/bedrock/block/BlockStateNames.php | 6 +++ .../bedrock/block/BlockStateStringValues.php | 16 ++++++++ src/data/bedrock/block/BlockTypeNames.php | 34 +++++++++++++++++ src/data/bedrock/item/ItemTypeNames.php | 8 ++++ .../mcpe/handler/InGamePacketHandler.php | 15 ++++---- .../mcpe/handler/ItemStackResponseBuilder.php | 1 + .../handler/ResourcePacksPacketHandler.php | 7 +++- 13 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 changelogs/5.22.md diff --git a/changelogs/5.22.md b/changelogs/5.22.md new file mode 100644 index 000000000..6c9ba6d7e --- /dev/null +++ b/changelogs/5.22.md @@ -0,0 +1,16 @@ +# 5.22.0 +Released 4th December 2024. + +**For Minecraft: Bedrock Edition 1.21.50** + +This is a support release for Minecraft: Bedrock Edition 1.21.50. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.50. +- Removed support for earlier versions. diff --git a/composer.json b/composer.json index ed5bb582f..4a6ad5daf 100644 --- a/composer.json +++ b/composer.json @@ -34,9 +34,9 @@ "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40", - "pocketmine/bedrock-data": "~2.14.0+bedrock-1.21.40", - "pocketmine/bedrock-item-upgrade-schema": "~1.13.0+bedrock-1.21.40", - "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.40", + "pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50", + "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", + "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index eb1061ff5..89e7b3af4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b2fbf6e7a9d650341dc71fa4dd124681", + "content-hash": "26d10b9381ab4e19684ca0b7a5a11a42", "packages": [ { "name": "adhocore/json-comment", @@ -153,16 +153,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "2.14.1+bedrock-1.21.40", + "version": "2.15.0+bedrock-1.21.50", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4" + "reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/4a41864ed09613ecec6791e2ae076a8ec7089cc4", - "reference": "4a41864ed09613ecec6791e2ae076a8ec7089cc4", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad", + "reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad", "shasum": "" }, "type": "library", @@ -173,22 +173,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/2.14.1+bedrock-1.21.40" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50" }, - "time": "2024-11-12T21:36:20+00:00" + "time": "2024-12-04T12:59:12+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", - "version": "1.13.1", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockItemUpgradeSchema.git", - "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03" + "reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/1cf81305f2ffcf7dde9577c4f16a55c765192b03", - "reference": "1cf81305f2ffcf7dde9577c4f16a55c765192b03", + "url": "https://api.github.com/repos/pmmp/BedrockItemUpgradeSchema/zipball/9fc7c9bbb558a017395c1cb7dd819c033ee971bb", + "reference": "9fc7c9bbb558a017395c1cb7dd819c033ee971bb", "shasum": "" }, "type": "library", @@ -199,22 +199,22 @@ "description": "JSON schemas for upgrading items found in older Minecraft: Bedrock world saves", "support": { "issues": "https://github.com/pmmp/BedrockItemUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.13.1" + "source": "https://github.com/pmmp/BedrockItemUpgradeSchema/tree/1.14.0" }, - "time": "2024-11-12T21:33:17+00:00" + "time": "2024-12-04T12:22:49+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "35.0.0+bedrock-1.21.40", + "version": "35.0.0+bedrock-1.21.50", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459" + "reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459", - "reference": "6aa7cbeb4a7ec6fa58f9024aeaddad7c5c65a459", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", + "reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", "shasum": "" }, "require": { @@ -245,9 +245,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.40" + "source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.0+bedrock-1.21.50" }, - "time": "2024-10-24T15:45:43+00:00" + "time": "2024-12-04T13:02:00+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/VersionInfo.php b/src/VersionInfo.php index efb38d71b..460e34bf7 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.21.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.22.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** diff --git a/src/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php index 5c476ca1c..3341c0503 100644 --- a/src/data/bedrock/BedrockDataFiles.php +++ b/src/data/bedrock/BedrockDataFiles.php @@ -45,6 +45,7 @@ final class BedrockDataFiles{ public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json'; public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json'; public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json'; + public const PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.json'; public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin'; public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json'; public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json'; diff --git a/src/data/bedrock/BiomeIds.php b/src/data/bedrock/BiomeIds.php index 1169a51ea..f3c38d3ed 100644 --- a/src/data/bedrock/BiomeIds.php +++ b/src/data/bedrock/BiomeIds.php @@ -122,4 +122,5 @@ final class BiomeIds{ public const DEEP_DARK = 190; public const MANGROVE_SWAMP = 191; public const CHERRY_GROVE = 192; + public const PALE_GARDEN = 193; } diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index 0f4b87426..68a3e1b1d 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -93,12 +93,17 @@ final class BlockStateNames{ public const MC_VERTICAL_HALF = "minecraft:vertical_half"; public const MOISTURIZED_AMOUNT = "moisturized_amount"; public const MULTI_FACE_DIRECTION_BITS = "multi_face_direction_bits"; + public const NATURAL = "natural"; public const OCCUPIED_BIT = "occupied_bit"; public const OMINOUS = "ominous"; public const OPEN_BIT = "open_bit"; public const ORIENTATION = "orientation"; public const OUTPUT_LIT_BIT = "output_lit_bit"; public const OUTPUT_SUBTRACT_BIT = "output_subtract_bit"; + public const PALE_MOSS_CARPET_SIDE_EAST = "pale_moss_carpet_side_east"; + public const PALE_MOSS_CARPET_SIDE_NORTH = "pale_moss_carpet_side_north"; + public const PALE_MOSS_CARPET_SIDE_SOUTH = "pale_moss_carpet_side_south"; + public const PALE_MOSS_CARPET_SIDE_WEST = "pale_moss_carpet_side_west"; public const PERSISTENT_BIT = "persistent_bit"; public const PILLAR_AXIS = "pillar_axis"; public const PORTAL_AXIS = "portal_axis"; @@ -116,6 +121,7 @@ final class BlockStateNames{ public const STABILITY_CHECK = "stability_check"; public const STRUCTURE_BLOCK_TYPE = "structure_block_type"; public const SUSPENDED_BIT = "suspended_bit"; + public const TIP = "tip"; public const TOGGLE_BIT = "toggle_bit"; public const TORCH_FACING_DIRECTION = "torch_facing_direction"; public const TRIAL_SPAWNER_STATE = "trial_spawner_state"; diff --git a/src/data/bedrock/block/BlockStateStringValues.php b/src/data/bedrock/block/BlockStateStringValues.php index 9dfdcfb63..e8171e4da 100644 --- a/src/data/bedrock/block/BlockStateStringValues.php +++ b/src/data/bedrock/block/BlockStateStringValues.php @@ -106,6 +106,22 @@ final class BlockStateStringValues{ public const ORIENTATION_UP_WEST = "up_west"; public const ORIENTATION_WEST_UP = "west_up"; + public const PALE_MOSS_CARPET_SIDE_EAST_NONE = "none"; + public const PALE_MOSS_CARPET_SIDE_EAST_SHORT = "short"; + public const PALE_MOSS_CARPET_SIDE_EAST_TALL = "tall"; + + public const PALE_MOSS_CARPET_SIDE_NORTH_NONE = "none"; + public const PALE_MOSS_CARPET_SIDE_NORTH_SHORT = "short"; + public const PALE_MOSS_CARPET_SIDE_NORTH_TALL = "tall"; + + public const PALE_MOSS_CARPET_SIDE_SOUTH_NONE = "none"; + public const PALE_MOSS_CARPET_SIDE_SOUTH_SHORT = "short"; + public const PALE_MOSS_CARPET_SIDE_SOUTH_TALL = "tall"; + + public const PALE_MOSS_CARPET_SIDE_WEST_NONE = "none"; + public const PALE_MOSS_CARPET_SIDE_WEST_SHORT = "short"; + public const PALE_MOSS_CARPET_SIDE_WEST_TALL = "tall"; + public const PILLAR_AXIS_X = "x"; public const PILLAR_AXIS_Y = "y"; public const PILLAR_AXIS_Z = "z"; diff --git a/src/data/bedrock/block/BlockTypeNames.php b/src/data/bedrock/block/BlockTypeNames.php index 029a5f6aa..eec1ab8d1 100644 --- a/src/data/bedrock/block/BlockTypeNames.php +++ b/src/data/bedrock/block/BlockTypeNames.php @@ -192,6 +192,7 @@ final class BlockTypeNames{ public const CAVE_VINES_HEAD_WITH_BERRIES = "minecraft:cave_vines_head_with_berries"; public const CHAIN = "minecraft:chain"; public const CHAIN_COMMAND_BLOCK = "minecraft:chain_command_block"; + public const CHALKBOARD = "minecraft:chalkboard"; public const CHEMICAL_HEAT = "minecraft:chemical_heat"; public const CHERRY_BUTTON = "minecraft:cherry_button"; public const CHERRY_DOOR = "minecraft:cherry_door"; @@ -219,6 +220,7 @@ final class BlockTypeNames{ public const CHISELED_POLISHED_BLACKSTONE = "minecraft:chiseled_polished_blackstone"; public const CHISELED_QUARTZ_BLOCK = "minecraft:chiseled_quartz_block"; public const CHISELED_RED_SANDSTONE = "minecraft:chiseled_red_sandstone"; + public const CHISELED_RESIN_BRICKS = "minecraft:chiseled_resin_bricks"; public const CHISELED_SANDSTONE = "minecraft:chiseled_sandstone"; public const CHISELED_STONE_BRICKS = "minecraft:chiseled_stone_bricks"; public const CHISELED_TUFF = "minecraft:chiseled_tuff"; @@ -227,6 +229,7 @@ final class BlockTypeNames{ public const CHORUS_PLANT = "minecraft:chorus_plant"; public const CLAY = "minecraft:clay"; public const CLIENT_REQUEST_PLACEHOLDER_BLOCK = "minecraft:client_request_placeholder_block"; + public const CLOSED_EYEBLOSSOM = "minecraft:closed_eyeblossom"; public const COAL_BLOCK = "minecraft:coal_block"; public const COAL_ORE = "minecraft:coal_ore"; public const COARSE_DIRT = "minecraft:coarse_dirt"; @@ -262,6 +265,7 @@ final class BlockTypeNames{ public const CRACKED_STONE_BRICKS = "minecraft:cracked_stone_bricks"; public const CRAFTER = "minecraft:crafter"; public const CRAFTING_TABLE = "minecraft:crafting_table"; + public const CREAKING_HEART = "minecraft:creaking_heart"; public const CREEPER_HEAD = "minecraft:creeper_head"; public const CRIMSON_BUTTON = "minecraft:crimson_button"; public const CRIMSON_DOOR = "minecraft:crimson_door"; @@ -831,6 +835,7 @@ final class BlockTypeNames{ public const OBSERVER = "minecraft:observer"; public const OBSIDIAN = "minecraft:obsidian"; public const OCHRE_FROGLIGHT = "minecraft:ochre_froglight"; + public const OPEN_EYEBLOSSOM = "minecraft:open_eyeblossom"; public const ORANGE_CANDLE = "minecraft:orange_candle"; public const ORANGE_CANDLE_CAKE = "minecraft:orange_candle_cake"; public const ORANGE_CARPET = "minecraft:orange_carpet"; @@ -856,6 +861,26 @@ final class BlockTypeNames{ public const OXIDIZED_DOUBLE_CUT_COPPER_SLAB = "minecraft:oxidized_double_cut_copper_slab"; public const PACKED_ICE = "minecraft:packed_ice"; public const PACKED_MUD = "minecraft:packed_mud"; + public const PALE_HANGING_MOSS = "minecraft:pale_hanging_moss"; + public const PALE_MOSS_BLOCK = "minecraft:pale_moss_block"; + public const PALE_MOSS_CARPET = "minecraft:pale_moss_carpet"; + public const PALE_OAK_BUTTON = "minecraft:pale_oak_button"; + public const PALE_OAK_DOOR = "minecraft:pale_oak_door"; + public const PALE_OAK_DOUBLE_SLAB = "minecraft:pale_oak_double_slab"; + public const PALE_OAK_FENCE = "minecraft:pale_oak_fence"; + public const PALE_OAK_FENCE_GATE = "minecraft:pale_oak_fence_gate"; + public const PALE_OAK_HANGING_SIGN = "minecraft:pale_oak_hanging_sign"; + public const PALE_OAK_LEAVES = "minecraft:pale_oak_leaves"; + public const PALE_OAK_LOG = "minecraft:pale_oak_log"; + public const PALE_OAK_PLANKS = "minecraft:pale_oak_planks"; + public const PALE_OAK_PRESSURE_PLATE = "minecraft:pale_oak_pressure_plate"; + public const PALE_OAK_SAPLING = "minecraft:pale_oak_sapling"; + public const PALE_OAK_SLAB = "minecraft:pale_oak_slab"; + public const PALE_OAK_STAIRS = "minecraft:pale_oak_stairs"; + public const PALE_OAK_STANDING_SIGN = "minecraft:pale_oak_standing_sign"; + public const PALE_OAK_TRAPDOOR = "minecraft:pale_oak_trapdoor"; + public const PALE_OAK_WALL_SIGN = "minecraft:pale_oak_wall_sign"; + public const PALE_OAK_WOOD = "minecraft:pale_oak_wood"; public const PEARLESCENT_FROGLIGHT = "minecraft:pearlescent_froglight"; public const PEONY = "minecraft:peony"; public const PETRIFIED_OAK_DOUBLE_SLAB = "minecraft:petrified_oak_double_slab"; @@ -994,6 +1019,13 @@ final class BlockTypeNames{ public const REINFORCED_DEEPSLATE = "minecraft:reinforced_deepslate"; public const REPEATING_COMMAND_BLOCK = "minecraft:repeating_command_block"; public const RESERVED6 = "minecraft:reserved6"; + public const RESIN_BLOCK = "minecraft:resin_block"; + public const RESIN_BRICK_DOUBLE_SLAB = "minecraft:resin_brick_double_slab"; + public const RESIN_BRICK_SLAB = "minecraft:resin_brick_slab"; + public const RESIN_BRICK_STAIRS = "minecraft:resin_brick_stairs"; + public const RESIN_BRICK_WALL = "minecraft:resin_brick_wall"; + public const RESIN_BRICKS = "minecraft:resin_bricks"; + public const RESIN_CLUMP = "minecraft:resin_clump"; public const RESPAWN_ANCHOR = "minecraft:respawn_anchor"; public const ROSE_BUSH = "minecraft:rose_bush"; public const SAND = "minecraft:sand"; @@ -1096,6 +1128,8 @@ final class BlockTypeNames{ public const STRIPPED_MANGROVE_WOOD = "minecraft:stripped_mangrove_wood"; public const STRIPPED_OAK_LOG = "minecraft:stripped_oak_log"; public const STRIPPED_OAK_WOOD = "minecraft:stripped_oak_wood"; + public const STRIPPED_PALE_OAK_LOG = "minecraft:stripped_pale_oak_log"; + public const STRIPPED_PALE_OAK_WOOD = "minecraft:stripped_pale_oak_wood"; public const STRIPPED_SPRUCE_LOG = "minecraft:stripped_spruce_log"; public const STRIPPED_SPRUCE_WOOD = "minecraft:stripped_spruce_wood"; public const STRIPPED_WARPED_HYPHAE = "minecraft:stripped_warped_hyphae"; diff --git a/src/data/bedrock/item/ItemTypeNames.php b/src/data/bedrock/item/ItemTypeNames.php index 03b32d482..b0d49fc31 100644 --- a/src/data/bedrock/item/ItemTypeNames.php +++ b/src/data/bedrock/item/ItemTypeNames.php @@ -75,6 +75,7 @@ final class ItemTypeNames{ public const BLEACH = "minecraft:bleach"; public const BLUE_BUNDLE = "minecraft:blue_bundle"; public const BLUE_DYE = "minecraft:blue_dye"; + public const BOARD = "minecraft:board"; public const BOAT = "minecraft:boat"; public const BOGGED_SPAWN_EGG = "minecraft:bogged_spawn_egg"; public const BOLT_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:bolt_armor_trim_smithing_template"; @@ -154,6 +155,7 @@ final class ItemTypeNames{ public const CORAL_FAN = "minecraft:coral_fan"; public const CORAL_FAN_DEAD = "minecraft:coral_fan_dead"; public const COW_SPAWN_EGG = "minecraft:cow_spawn_egg"; + public const CREAKING_SPAWN_EGG = "minecraft:creaking_spawn_egg"; public const CREEPER_BANNER_PATTERN = "minecraft:creeper_banner_pattern"; public const CREEPER_SPAWN_EGG = "minecraft:creeper_spawn_egg"; public const CRIMSON_DOOR = "minecraft:crimson_door"; @@ -398,6 +400,11 @@ final class ItemTypeNames{ public const ORANGE_DYE = "minecraft:orange_dye"; public const OXIDIZED_COPPER_DOOR = "minecraft:oxidized_copper_door"; public const PAINTING = "minecraft:painting"; + public const PALE_OAK_BOAT = "minecraft:pale_oak_boat"; + public const PALE_OAK_CHEST_BOAT = "minecraft:pale_oak_chest_boat"; + public const PALE_OAK_DOOR = "minecraft:pale_oak_door"; + public const PALE_OAK_HANGING_SIGN = "minecraft:pale_oak_hanging_sign"; + public const PALE_OAK_SIGN = "minecraft:pale_oak_sign"; public const PANDA_SPAWN_EGG = "minecraft:panda_spawn_egg"; public const PAPER = "minecraft:paper"; public const PARROT_SPAWN_EGG = "minecraft:parrot_spawn_egg"; @@ -448,6 +455,7 @@ final class ItemTypeNames{ public const RED_FLOWER = "minecraft:red_flower"; public const REDSTONE = "minecraft:redstone"; public const REPEATER = "minecraft:repeater"; + public const RESIN_BRICK = "minecraft:resin_brick"; public const RIB_ARMOR_TRIM_SMITHING_TEMPLATE = "minecraft:rib_armor_trim_smithing_template"; public const ROTTEN_FLESH = "minecraft:rotten_flesh"; public const SADDLE = "minecraft:saddle"; diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index f1cfaec19..8a42371e6 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -77,6 +77,7 @@ use pocketmine\network\mcpe\protocol\PlayerHotbarPacket; use pocketmine\network\mcpe\protocol\PlayerInputPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; +use pocketmine\network\mcpe\protocol\serializer\BitSet; use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket; use pocketmine\network\mcpe\protocol\SetActorMotionPacket; use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; @@ -135,7 +136,7 @@ class InGamePacketHandler extends PacketHandler{ protected ?Vector3 $lastPlayerAuthInputPosition = null; protected ?float $lastPlayerAuthInputYaw = null; protected ?float $lastPlayerAuthInputPitch = null; - protected ?int $lastPlayerAuthInputFlags = null; + protected ?BitSet $lastPlayerAuthInputFlags = null; public bool $forceMoveSync = false; @@ -161,9 +162,9 @@ class InGamePacketHandler extends PacketHandler{ return true; } - private function resolveOnOffInputFlags(int $inputFlags, int $startFlag, int $stopFlag) : ?bool{ - $enabled = ($inputFlags & (1 << $startFlag)) !== 0; - $disabled = ($inputFlags & (1 << $stopFlag)) !== 0; + private function resolveOnOffInputFlags(BitSet $inputFlags, int $startFlag, int $stopFlag) : ?bool{ + $enabled = $inputFlags->get($startFlag); + $disabled = $inputFlags->get($stopFlag); if($enabled !== $disabled){ return $enabled; } @@ -215,7 +216,7 @@ class InGamePacketHandler extends PacketHandler{ if($inputFlags !== $this->lastPlayerAuthInputFlags){ $this->lastPlayerAuthInputFlags = $inputFlags; - $sneaking = $packet->hasFlag(PlayerAuthInputFlags::SNEAKING); + $sneaking = $inputFlags->get(PlayerAuthInputFlags::SNEAKING); if($this->player->isSneaking() === $sneaking){ $sneaking = null; } @@ -233,10 +234,10 @@ class InGamePacketHandler extends PacketHandler{ $this->player->sendData([$this->player]); } - if($packet->hasFlag(PlayerAuthInputFlags::START_JUMPING)){ + if($inputFlags->get(PlayerAuthInputFlags::START_JUMPING)){ $this->player->jump(); } - if($packet->hasFlag(PlayerAuthInputFlags::MISSED_SWING)){ + if($inputFlags->get(PlayerAuthInputFlags::MISSED_SWING)){ $this->player->missSwing(); } } diff --git a/src/network/mcpe/handler/ItemStackResponseBuilder.php b/src/network/mcpe/handler/ItemStackResponseBuilder.php index 1369e3ba7..faf479ee2 100644 --- a/src/network/mcpe/handler/ItemStackResponseBuilder.php +++ b/src/network/mcpe/handler/ItemStackResponseBuilder.php @@ -93,6 +93,7 @@ final class ItemStackResponseBuilder{ $item->getCount(), $itemStackInfo->getStackId(), $item->getCustomName(), + $item->getCustomName(), $item instanceof Durable ? $item->getDamage() : 0, ); } diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index 5e2671394..a1df394da 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -37,6 +37,7 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType; use pocketmine\resourcepacks\ResourcePack; +use Ramsey\Uuid\Uuid; use function array_keys; use function array_map; use function ceil; @@ -103,7 +104,7 @@ class ResourcePacksPacketHandler extends PacketHandler{ //TODO: more stuff return new ResourcePackInfoEntry( - $pack->getPackId(), + Uuid::fromString($pack->getPackId()), $pack->getPackVersion(), $pack->getPackSize(), $this->encryptionKeys[$pack->getPackId()] ?? "", @@ -117,7 +118,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ resourcePackEntries: $resourcePackEntries, mustAccept: $this->mustAccept, hasAddons: false, - hasScripts: false + hasScripts: false, + worldTemplateId: Uuid::fromString(Uuid::NIL), + worldTemplateVersion: "" )); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } From a1448bfb88c6662e7a850272a5656162b7551227 Mon Sep 17 00:00:00 2001 From: "pmmp-restrictedactions-bot[bot]" <188621379+pmmp-restrictedactions-bot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:38:41 +0000 Subject: [PATCH 132/290] 5.22.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12160926590 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 460e34bf7..9c1fdf230 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.22.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.22.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 205aabe11f8d7b6a0fbc4c36ae9e3e1513fd0506 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 4 Dec 2024 15:27:09 +0000 Subject: [PATCH 133/290] Fixed merge error --- src/item/enchantment/VanillaEnchantments.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index 9bee89a67..5cdcd5225 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -136,8 +136,6 @@ final class VanillaEnchantments{ self::register("FROST_WALKER", new Enchantment( KnownTranslationFactory::enchantment_frostwalker(), Rarity::RARE, - 0, - 0, 2, fn(int $level) : int => 10 * $level, 15 From 35a90d24ec9fd5055e9d0bef480341017c66fd5b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 12:57:26 +0000 Subject: [PATCH 134/290] AsyncTask: deprecate progress update related stuff --- src/scheduler/AsyncTask.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index 33a0949d4..1175b6fc4 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -68,7 +68,10 @@ abstract class AsyncTask extends Runnable{ */ private static array $threadLocalStorage = []; - /** @phpstan-var ThreadSafeArray|null */ + /** + * @phpstan-var ThreadSafeArray|null + * @deprecated + */ private ?ThreadSafeArray $progressUpdates = null; private ThreadSafe|string|int|bool|null|float $result = null; @@ -161,6 +164,8 @@ abstract class AsyncTask extends Runnable{ } /** + * @deprecated + * * Call this method from {@link AsyncTask::onRun} (AsyncTask execution thread) to schedule a call to * {@link AsyncTask::onProgressUpdate} from the main thread with the given progress parameter. * @@ -175,6 +180,7 @@ abstract class AsyncTask extends Runnable{ } /** + * @deprecated * @internal Only call from AsyncPool.php on the main thread */ public function checkProgressUpdates() : void{ @@ -187,6 +193,8 @@ abstract class AsyncTask extends Runnable{ } /** + * @deprecated + * * Called from the main thread after {@link AsyncTask::publishProgress} is called. * All {@link AsyncTask::publishProgress} calls should result in {@link AsyncTask::onProgressUpdate} calls before * {@link AsyncTask::onCompletion} is called. From 0aaf4238a8cb6a036c238538351f3a26e4d2b921 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 13:02:09 +0000 Subject: [PATCH 135/290] more deprecations in line with major-next --- src/plugin/DiskResourceProvider.php | 2 ++ src/plugin/ResourceProvider.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/plugin/DiskResourceProvider.php b/src/plugin/DiskResourceProvider.php index efdc9cd17..9649f565f 100644 --- a/src/plugin/DiskResourceProvider.php +++ b/src/plugin/DiskResourceProvider.php @@ -36,6 +36,8 @@ use const DIRECTORY_SEPARATOR; /** * Provides resources from the given plugin directory on disk. The path may be prefixed with a specific access protocol * to enable special types of access. + * + * @deprecated */ class DiskResourceProvider implements ResourceProvider{ private string $file; diff --git a/src/plugin/ResourceProvider.php b/src/plugin/ResourceProvider.php index 3594d7eee..5ff8db882 100644 --- a/src/plugin/ResourceProvider.php +++ b/src/plugin/ResourceProvider.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\plugin; +/** + * @deprecated + */ interface ResourceProvider{ /** * Gets an embedded resource on the plugin file. From fa7bc78e7c0ed5e31682de57b65c0edc1e74d50f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 14:29:17 +0000 Subject: [PATCH 136/290] Prepare 5.23.0 release --- changelogs/5.23.md | 103 ++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +- 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.23.md diff --git a/changelogs/5.23.md b/changelogs/5.23.md new file mode 100644 index 000000000..4d7ba1808 --- /dev/null +++ b/changelogs/5.23.md @@ -0,0 +1,103 @@ +# 5.23.0 +Released 5th December 2024. + +This is a minor feature release, including new gameplay features, internals improvements, API additions and +deprecations, and improvements to timings. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- `/timings` now supports collecting timings from async task workers. These new timings will be shown alongside `Full Server Tick` timings, but will not be counted in total load. +- Added `/xp` command. +- `start.sh` will now emit warnings when the server process exits with an unusual exit code. This helps to detect unexpected segmentation faults and other kinds of native errors. + +## Gameplay +- Added the following new items: + - End Crystal + - Goat Horn (all variants) + - Recovery Compass +- Added the following enchantments: + - Frost Walker +- Sugarcane now self-destructs when there is no water adjacent to the base block. +- Added basic support for middle-clicking on entities to get their spawn eggs. +- Added sounds when drinking potions. +- Eating food is now allowed in creative mode and in peaceful difficulty. + +## API +### `pocketmine\block` +- Extracted `MultiAnyFacingTrait` and `MultiAnySupportTrait` from `GlowLichen` to enable reuse in other blocks. +- The following API methods have been deprecated: + - `Campfire->getInventory()` - this was added by mistake and can't be well-supported given the way that blocks work + +### `pocketmine\command` +- The following classes have been added: + - `ClosureCommand` - allows registering a closure to execute a command + +### `pocketmine\event` +- Added APIs to `PlayerInteractEvent` to allow toggling item and block interactions. + - This allows various customisations, such as allowing interactions when sneaking, selectively disabling item or block reactions, etc. + - If both item and block interactions are disabled, the event is **not** cancelled (blocks can still be placed). + - The following API methods have been added: + - `public PlayerInteractEvent->setUseBlock(bool $useBlock) : void` + - `public PlayerInteractEvent->setUseItem(bool $useItem) : void` + - `public PlayerInteractEvent->useBlock() : bool` - returns whether the block can respond to the interaction (toggling levers, opening/closing doors, etc). + - `public PlayerInteractEvent->useItem() : bool` - returns whether the item can respond to the interaction (spawn eggs, flint & steel, etc). +- The following new classes have been added: + - `player\PlayerEntityPickEvent` - called when a player middle-clicks on an entity + +### `pocketmine\inventory\transaction` +- The following API methods have been deprecated: + - `InventoryAction->onAddToTransaction()` + +### `pocketmine\permission` +- The following API methods have been deprecated: + - `PermissionManager->getPermissionSubscriptions()` + - `PermissionManager->subscribeToPermission()` + - `PermissionManager->unsubscribeFromAllPermissions()` + - `PermissionManager->unsubscribeFromPermission()` + +### `pocketmine\plugin` +- The following classes have been deprecated: + - `DiskResourceProvider` + - `ResourceProvider` + +### `pocketmine\promise` +- `Promise::all()` now accepts zero promises. This will return an already-resolved promise with an empty array. + +### `pocketmine\scheduler` +- Added PHPStan generic types to `TaskHandler` and related APIs in `TaskScheduler` and `Task`. +- The following API methods have been deprecated + - `AsyncTask->publishProgress()` + - `AsyncTask->onProgressUpdate()` + +### `pocketmine\timings` +- Timings can now notify other code when timings are enabled/disabled, reloaded, or collected. + - The intent of this is to facilitate timings usage on other threads, and have the results collected into a single timings report. + - Timings cannot directly control timings on other threads, so these callbacks allow plugins to use custom mechanisms to toggle, reset and collect timings. + - PocketMine-MP currently uses this to collect timings from async task workers. More internal threads may be supported in the future. +- The following API methods have been added: + - `public static TimingsHandler::getCollectCallbacks() : ObjectSet<\Closure() : list>>` - callbacks for (asynchronously) collecting timings (typically from other threads). The returned promises should be resolved with the result of `TimingsHandler::printCurrentThreadRecords()`. + - `public static TimingsHandler::getReloadCallbacks() : ObjectSet<\Closure() : void>` - callbacks called when timings are reset + - `public static TimingsHandler::getToggleCallbacks() : ObjectSet<\Closure(bool $enable) : void>` - callbacks called when timings are enabled/disabled + - `public static TimingsHandler::requestPrintTimings() : Promise>` - asynchronously collects timing results from all threads and assembles them into a single report +- The following API methods have been deprecated: + - `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead. + +### `pocketmine\utils` +- The following API methods have been added: + - `public static Utils::getRandomFloat() : float` - returns a random float between 0 and 1. Drop-in replacement for `lcg_value()` in PHP 8.4. + +## Internals +- Blocks are now always synced with the client during a right-click-block interaction. This clears mispredictions on the client in case the new `PlayerInteractEvent` flags were customized by plugins. +- `VanillaBlocks` and `VanillaItems` now use reflection to lookup TypeId constants by registration name, instead of requiring TypeIds to be manually specified. + - While this is obviously a hack, it prevents incorrect constants from being used when adding new blocks, and guarantees that the names of constants in `BlockTypeIds` and `ItemTypeIds` will match their corresponding entries in `VanillaBlocks` and `VanillaItems` respectively. + - It also significantly improves readability of `VanillaBlocks` and `VanillaItems`, as well as eliminating ugly code like `WoodLikeBlockIdHelper`. + - In PM6, the team is exploring options to redesign `VanillaBlocks` and `VanillaItems` to eliminate the need for defining separate TypeIds entirely. +- `ConsoleReader` now uses socket support in `proc_open()` to transmit IPC messages to the server process. Previously, a temporary socket server was used, which was unreliable in some conditions. +- Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies. +- Fixed various deprecation warnings in PHP 8.4. +- `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 9c1fdf230..90c1a7df4 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.22.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.23.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From ea068d490781d977cb594654955af2d311b3fec9 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 5 Dec 2024 15:01:49 +0000 Subject: [PATCH 137/290] Update 5.23.md --- changelogs/5.23.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelogs/5.23.md b/changelogs/5.23.md index 4d7ba1808..4cdb81625 100644 --- a/changelogs/5.23.md +++ b/changelogs/5.23.md @@ -19,6 +19,7 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - Added the following new items: - End Crystal - Goat Horn (all variants) + - Ice Bomb (from Education Edition) - Recovery Compass - Added the following enchantments: - Frost Walker From 62e1d87f5e02ff5759c8db88dd822795fbf49534 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 15:47:34 +0000 Subject: [PATCH 138/290] Mention internal timings deprecations plugins shouldn't be using these, but since it's not marked as internal, we can't be sure. --- changelogs/5.23.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelogs/5.23.md b/changelogs/5.23.md index 4cdb81625..f5b08593d 100644 --- a/changelogs/5.23.md +++ b/changelogs/5.23.md @@ -87,6 +87,9 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - `public static TimingsHandler::requestPrintTimings() : Promise>` - asynchronously collects timing results from all threads and assembles them into a single report - The following API methods have been deprecated: - `TimingsHandler::printTimings()` - this function cannot support async timings collection. Use `TimingsHandler::requestPrintTimings()` instead. + - `Timings::getAsyncTaskErrorTimings()` - internal method that is no longer needed +- The following constants have been deprecated: + - `Timings::GROUP_BREAKDOWN` - no longer used ### `pocketmine\utils` - The following API methods have been added: From 15e8895e5433b66b55307981ad3ec0b4bc8ce518 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:52:16 +0000 Subject: [PATCH 139/290] 5.23.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12183301507 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 90c1a7df4..d983060f4 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.23.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.23.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 2ef02a2c5ec7f9cebdc0c0766c214ad0937cd5fc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 19:57:13 +0000 Subject: [PATCH 140/290] Upgraded block consistency check to detect tile changes --- tests/phpunit/block/BlockTest.php | 70 +- .../block_factory_consistency_check.json | 1478 +++++++++-------- .../block/regenerate_consistency_check.php | 7 +- 3 files changed, 825 insertions(+), 730 deletions(-) diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php index 841917787..971564720 100644 --- a/tests/phpunit/block/BlockTest.php +++ b/tests/phpunit/block/BlockTest.php @@ -27,6 +27,7 @@ use PHPUnit\Framework\TestCase; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; +use function get_debug_type; use function implode; use function is_array; use function is_int; @@ -95,11 +96,12 @@ class BlockTest extends TestCase{ } /** - * @return int[] - * @phpstan-return array + * @return int[][]|string[][] + * @phpstan-return array{array, array} */ public static function computeConsistencyCheckTable(RuntimeBlockStateRegistry $blockStateRegistry) : array{ $newTable = []; + $newTileMap = []; $idNameLookup = []; //if we ever split up block registration into multiple registries (e.g. separating chemistry blocks), @@ -118,36 +120,70 @@ class BlockTest extends TestCase{ } $idName = $idNameLookup[$block->getTypeId()]; $newTable[$idName] = ($newTable[$idName] ?? 0) + 1; - } - return $newTable; + $tileClass = $block->getIdInfo()->getTileClass(); + if($tileClass !== null){ + if(isset($newTileMap[$idName]) && $newTileMap[$idName] !== $tileClass){ + throw new AssumptionFailedError("Tile entity $tileClass for $idName is inconsistent"); + } + $newTileMap[$idName] = $tileClass; + } + } + return [$newTable, $newTileMap]; } /** - * @phpstan-param array $actual + * @phpstan-param array $actualStateCounts + * @phpstan-param array $actualTiles * * @return string[] */ - public static function computeConsistencyCheckDiff(string $expectedFile, array $actual) : array{ - $expected = json_decode(Filesystem::fileGetContents($expectedFile), true, 2, JSON_THROW_ON_ERROR); + public static function computeConsistencyCheckDiff(string $expectedFile, array $actualStateCounts, array $actualTiles) : array{ + $expected = json_decode(Filesystem::fileGetContents($expectedFile), true, 3, JSON_THROW_ON_ERROR); if(!is_array($expected)){ - throw new AssumptionFailedError("Old table should be array"); + throw new AssumptionFailedError("Old table should be array{stateCounts: array, tiles: array}"); + } + $expectedStates = $expected["stateCounts"] ?? []; + $expectedTiles = $expected["tiles"] ?? []; + if(!is_array($expectedStates)){ + throw new AssumptionFailedError("stateCounts should be an array, but have " . get_debug_type($expectedStates)); + } + if(!is_array($expectedTiles)){ + throw new AssumptionFailedError("tiles should be an array, but have " . get_debug_type($expectedTiles)); } $errors = []; - foreach(Utils::promoteKeys($expected) as $typeName => $numStates){ + foreach(Utils::promoteKeys($expectedStates) as $typeName => $numStates){ if(!is_string($typeName) || !is_int($numStates)){ throw new AssumptionFailedError("Old table should be array"); } - if(!isset($actual[$typeName])){ + if(!isset($actualStateCounts[$typeName])){ $errors[] = "Removed block type $typeName ($numStates permutations)"; - }elseif($actual[$typeName] !== $numStates){ - $errors[] = "Block type $typeName permutation count changed: $numStates -> " . $actual[$typeName]; + }elseif($actualStateCounts[$typeName] !== $numStates){ + $errors[] = "Block type $typeName permutation count changed: $numStates -> " . $actualStateCounts[$typeName]; } } - foreach(Utils::stringifyKeys($actual) as $typeName => $numStates){ - if(!isset($expected[$typeName])){ - $errors[] = "Added block type $typeName (" . $actual[$typeName] . " permutations)"; + foreach(Utils::stringifyKeys($actualStateCounts) as $typeName => $numStates){ + if(!isset($expectedStates[$typeName])){ + $errors[] = "Added block type $typeName (" . $actualStateCounts[$typeName] . " permutations)"; + } + } + + foreach(Utils::promoteKeys($expectedTiles) as $typeName => $tile){ + if(!is_string($typeName) || !is_string($tile)){ + throw new AssumptionFailedError("Tile table should be array"); + } + if(isset($actualStateCounts[$typeName])){ + if(!isset($actualTiles[$typeName])){ + $errors[] = "$typeName no longer has a tile"; + }elseif($actualTiles[$typeName] !== $tile){ + $errors[] = "$typeName has changed tile ($tile -> " . $actualTiles[$typeName] . ")"; + } + } + } + foreach(Utils::promoteKeys($actualTiles) as $typeName => $tile){ + if(isset($expectedStates[$typeName]) && !isset($expectedTiles[$typeName])){ + $errors[] = "$typeName has a tile when it previously didn't ($tile)"; } } @@ -155,8 +191,8 @@ class BlockTest extends TestCase{ } public function testConsistency() : void{ - $newTable = self::computeConsistencyCheckTable($this->blockFactory); - $errors = self::computeConsistencyCheckDiff(__DIR__ . '/block_factory_consistency_check.json', $newTable); + [$newTable, $newTileMap] = self::computeConsistencyCheckTable($this->blockFactory); + $errors = self::computeConsistencyCheckDiff(__DIR__ . '/block_factory_consistency_check.json', $newTable, $newTileMap); self::assertEmpty($errors, "Block factory consistency check failed:\n" . implode("\n", $errors)); } diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 79804d8cb..d14a85fab 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1,712 +1,770 @@ { - "ACACIA_BUTTON": 12, - "ACACIA_DOOR": 32, - "ACACIA_FENCE": 1, - "ACACIA_FENCE_GATE": 16, - "ACACIA_LEAVES": 4, - "ACACIA_LOG": 6, - "ACACIA_PLANKS": 1, - "ACACIA_PRESSURE_PLATE": 2, - "ACACIA_SAPLING": 2, - "ACACIA_SIGN": 16, - "ACACIA_SLAB": 3, - "ACACIA_STAIRS": 8, - "ACACIA_TRAPDOOR": 16, - "ACACIA_WALL_SIGN": 4, - "ACACIA_WOOD": 6, - "ACTIVATOR_RAIL": 12, - "AIR": 1, - "ALLIUM": 1, - "ALL_SIDED_MUSHROOM_STEM": 1, - "AMETHYST": 1, - "AMETHYST_CLUSTER": 24, - "ANCIENT_DEBRIS": 1, - "ANDESITE": 1, - "ANDESITE_SLAB": 3, - "ANDESITE_STAIRS": 8, - "ANDESITE_WALL": 162, - "ANVIL": 12, - "AZALEA_LEAVES": 4, - "AZURE_BLUET": 1, - "BAMBOO": 12, - "BAMBOO_SAPLING": 2, - "BANNER": 256, - "BARREL": 12, - "BARRIER": 1, - "BASALT": 3, - "BEACON": 1, - "BED": 256, - "BEDROCK": 2, - "BEETROOTS": 8, - "BELL": 16, - "BIG_DRIPLEAF_HEAD": 16, - "BIG_DRIPLEAF_STEM": 4, - "BIRCH_BUTTON": 12, - "BIRCH_DOOR": 32, - "BIRCH_FENCE": 1, - "BIRCH_FENCE_GATE": 16, - "BIRCH_LEAVES": 4, - "BIRCH_LOG": 6, - "BIRCH_PLANKS": 1, - "BIRCH_PRESSURE_PLATE": 2, - "BIRCH_SAPLING": 2, - "BIRCH_SIGN": 16, - "BIRCH_SLAB": 3, - "BIRCH_STAIRS": 8, - "BIRCH_TRAPDOOR": 16, - "BIRCH_WALL_SIGN": 4, - "BIRCH_WOOD": 6, - "BLACKSTONE": 1, - "BLACKSTONE_SLAB": 3, - "BLACKSTONE_STAIRS": 8, - "BLACKSTONE_WALL": 162, - "BLAST_FURNACE": 8, - "BLUE_ICE": 1, - "BLUE_ORCHID": 1, - "BLUE_TORCH": 5, - "BONE_BLOCK": 3, - "BOOKSHELF": 1, - "BREWING_STAND": 8, - "BRICKS": 1, - "BRICK_SLAB": 3, - "BRICK_STAIRS": 8, - "BRICK_WALL": 162, - "BROWN_MUSHROOM": 1, - "BROWN_MUSHROOM_BLOCK": 11, - "BUDDING_AMETHYST": 1, - "CACTUS": 16, - "CAKE": 7, - "CAKE_WITH_CANDLE": 2, - "CAKE_WITH_DYED_CANDLE": 32, - "CALCITE": 1, - "CAMPFIRE": 8, - "CANDLE": 8, - "CARPET": 16, - "CARROTS": 8, - "CARTOGRAPHY_TABLE": 1, - "CARVED_PUMPKIN": 4, - "CAULDRON": 1, - "CAVE_VINES": 104, - "CHAIN": 3, - "CHEMICAL_HEAT": 1, - "CHERRY_BUTTON": 12, - "CHERRY_DOOR": 32, - "CHERRY_FENCE": 1, - "CHERRY_FENCE_GATE": 16, - "CHERRY_LEAVES": 4, - "CHERRY_LOG": 6, - "CHERRY_PLANKS": 1, - "CHERRY_PRESSURE_PLATE": 2, - "CHERRY_SIGN": 16, - "CHERRY_SLAB": 3, - "CHERRY_STAIRS": 8, - "CHERRY_TRAPDOOR": 16, - "CHERRY_WALL_SIGN": 4, - "CHERRY_WOOD": 6, - "CHEST": 4, - "CHISELED_BOOKSHELF": 256, - "CHISELED_COPPER": 8, - "CHISELED_DEEPSLATE": 1, - "CHISELED_NETHER_BRICKS": 1, - "CHISELED_POLISHED_BLACKSTONE": 1, - "CHISELED_QUARTZ": 3, - "CHISELED_RED_SANDSTONE": 1, - "CHISELED_SANDSTONE": 1, - "CHISELED_STONE_BRICKS": 1, - "CHISELED_TUFF": 1, - "CHISELED_TUFF_BRICKS": 1, - "CHORUS_FLOWER": 6, - "CHORUS_PLANT": 1, - "CLAY": 1, - "COAL": 1, - "COAL_ORE": 1, - "COBBLED_DEEPSLATE": 1, - "COBBLED_DEEPSLATE_SLAB": 3, - "COBBLED_DEEPSLATE_STAIRS": 8, - "COBBLED_DEEPSLATE_WALL": 162, - "COBBLESTONE": 1, - "COBBLESTONE_SLAB": 3, - "COBBLESTONE_STAIRS": 8, - "COBBLESTONE_WALL": 162, - "COBWEB": 1, - "COCOA_POD": 12, - "COMPOUND_CREATOR": 4, - "CONCRETE": 16, - "CONCRETE_POWDER": 16, - "COPPER": 8, - "COPPER_BULB": 32, - "COPPER_DOOR": 256, - "COPPER_GRATE": 8, - "COPPER_ORE": 1, - "COPPER_TRAPDOOR": 128, - "CORAL": 10, - "CORAL_BLOCK": 10, - "CORAL_FAN": 20, - "CORNFLOWER": 1, - "CRACKED_DEEPSLATE_BRICKS": 1, - "CRACKED_DEEPSLATE_TILES": 1, - "CRACKED_NETHER_BRICKS": 1, - "CRACKED_POLISHED_BLACKSTONE_BRICKS": 1, - "CRACKED_STONE_BRICKS": 1, - "CRAFTING_TABLE": 1, - "CRIMSON_BUTTON": 12, - "CRIMSON_DOOR": 32, - "CRIMSON_FENCE": 1, - "CRIMSON_FENCE_GATE": 16, - "CRIMSON_HYPHAE": 6, - "CRIMSON_PLANKS": 1, - "CRIMSON_PRESSURE_PLATE": 2, - "CRIMSON_ROOTS": 1, - "CRIMSON_SIGN": 16, - "CRIMSON_SLAB": 3, - "CRIMSON_STAIRS": 8, - "CRIMSON_STEM": 6, - "CRIMSON_TRAPDOOR": 16, - "CRIMSON_WALL_SIGN": 4, - "CRYING_OBSIDIAN": 1, - "CUT_COPPER": 8, - "CUT_COPPER_SLAB": 24, - "CUT_COPPER_STAIRS": 64, - "CUT_RED_SANDSTONE": 1, - "CUT_RED_SANDSTONE_SLAB": 3, - "CUT_SANDSTONE": 1, - "CUT_SANDSTONE_SLAB": 3, - "DANDELION": 1, - "DARK_OAK_BUTTON": 12, - "DARK_OAK_DOOR": 32, - "DARK_OAK_FENCE": 1, - "DARK_OAK_FENCE_GATE": 16, - "DARK_OAK_LEAVES": 4, - "DARK_OAK_LOG": 6, - "DARK_OAK_PLANKS": 1, - "DARK_OAK_PRESSURE_PLATE": 2, - "DARK_OAK_SAPLING": 2, - "DARK_OAK_SIGN": 16, - "DARK_OAK_SLAB": 3, - "DARK_OAK_STAIRS": 8, - "DARK_OAK_TRAPDOOR": 16, - "DARK_OAK_WALL_SIGN": 4, - "DARK_OAK_WOOD": 6, - "DARK_PRISMARINE": 1, - "DARK_PRISMARINE_SLAB": 3, - "DARK_PRISMARINE_STAIRS": 8, - "DAYLIGHT_SENSOR": 32, - "DEAD_BUSH": 1, - "DEEPSLATE": 3, - "DEEPSLATE_BRICKS": 1, - "DEEPSLATE_BRICK_SLAB": 3, - "DEEPSLATE_BRICK_STAIRS": 8, - "DEEPSLATE_BRICK_WALL": 162, - "DEEPSLATE_COAL_ORE": 1, - "DEEPSLATE_COPPER_ORE": 1, - "DEEPSLATE_DIAMOND_ORE": 1, - "DEEPSLATE_EMERALD_ORE": 1, - "DEEPSLATE_GOLD_ORE": 1, - "DEEPSLATE_IRON_ORE": 1, - "DEEPSLATE_LAPIS_LAZULI_ORE": 1, - "DEEPSLATE_REDSTONE_ORE": 2, - "DEEPSLATE_TILES": 1, - "DEEPSLATE_TILE_SLAB": 3, - "DEEPSLATE_TILE_STAIRS": 8, - "DEEPSLATE_TILE_WALL": 162, - "DETECTOR_RAIL": 12, - "DIAMOND": 1, - "DIAMOND_ORE": 1, - "DIORITE": 1, - "DIORITE_SLAB": 3, - "DIORITE_STAIRS": 8, - "DIORITE_WALL": 162, - "DIRT": 3, - "DOUBLE_PITCHER_CROP": 4, - "DOUBLE_TALLGRASS": 2, - "DRAGON_EGG": 1, - "DRIED_KELP": 1, - "DYED_CANDLE": 128, - "DYED_SHULKER_BOX": 16, - "ELEMENT_ACTINIUM": 1, - "ELEMENT_ALUMINUM": 1, - "ELEMENT_AMERICIUM": 1, - "ELEMENT_ANTIMONY": 1, - "ELEMENT_ARGON": 1, - "ELEMENT_ARSENIC": 1, - "ELEMENT_ASTATINE": 1, - "ELEMENT_BARIUM": 1, - "ELEMENT_BERKELIUM": 1, - "ELEMENT_BERYLLIUM": 1, - "ELEMENT_BISMUTH": 1, - "ELEMENT_BOHRIUM": 1, - "ELEMENT_BORON": 1, - "ELEMENT_BROMINE": 1, - "ELEMENT_CADMIUM": 1, - "ELEMENT_CALCIUM": 1, - "ELEMENT_CALIFORNIUM": 1, - "ELEMENT_CARBON": 1, - "ELEMENT_CERIUM": 1, - "ELEMENT_CESIUM": 1, - "ELEMENT_CHLORINE": 1, - "ELEMENT_CHROMIUM": 1, - "ELEMENT_COBALT": 1, - "ELEMENT_CONSTRUCTOR": 4, - "ELEMENT_COPERNICIUM": 1, - "ELEMENT_COPPER": 1, - "ELEMENT_CURIUM": 1, - "ELEMENT_DARMSTADTIUM": 1, - "ELEMENT_DUBNIUM": 1, - "ELEMENT_DYSPROSIUM": 1, - "ELEMENT_EINSTEINIUM": 1, - "ELEMENT_ERBIUM": 1, - "ELEMENT_EUROPIUM": 1, - "ELEMENT_FERMIUM": 1, - "ELEMENT_FLEROVIUM": 1, - "ELEMENT_FLUORINE": 1, - "ELEMENT_FRANCIUM": 1, - "ELEMENT_GADOLINIUM": 1, - "ELEMENT_GALLIUM": 1, - "ELEMENT_GERMANIUM": 1, - "ELEMENT_GOLD": 1, - "ELEMENT_HAFNIUM": 1, - "ELEMENT_HASSIUM": 1, - "ELEMENT_HELIUM": 1, - "ELEMENT_HOLMIUM": 1, - "ELEMENT_HYDROGEN": 1, - "ELEMENT_INDIUM": 1, - "ELEMENT_IODINE": 1, - "ELEMENT_IRIDIUM": 1, - "ELEMENT_IRON": 1, - "ELEMENT_KRYPTON": 1, - "ELEMENT_LANTHANUM": 1, - "ELEMENT_LAWRENCIUM": 1, - "ELEMENT_LEAD": 1, - "ELEMENT_LITHIUM": 1, - "ELEMENT_LIVERMORIUM": 1, - "ELEMENT_LUTETIUM": 1, - "ELEMENT_MAGNESIUM": 1, - "ELEMENT_MANGANESE": 1, - "ELEMENT_MEITNERIUM": 1, - "ELEMENT_MENDELEVIUM": 1, - "ELEMENT_MERCURY": 1, - "ELEMENT_MOLYBDENUM": 1, - "ELEMENT_MOSCOVIUM": 1, - "ELEMENT_NEODYMIUM": 1, - "ELEMENT_NEON": 1, - "ELEMENT_NEPTUNIUM": 1, - "ELEMENT_NICKEL": 1, - "ELEMENT_NIHONIUM": 1, - "ELEMENT_NIOBIUM": 1, - "ELEMENT_NITROGEN": 1, - "ELEMENT_NOBELIUM": 1, - "ELEMENT_OGANESSON": 1, - "ELEMENT_OSMIUM": 1, - "ELEMENT_OXYGEN": 1, - "ELEMENT_PALLADIUM": 1, - "ELEMENT_PHOSPHORUS": 1, - "ELEMENT_PLATINUM": 1, - "ELEMENT_PLUTONIUM": 1, - "ELEMENT_POLONIUM": 1, - "ELEMENT_POTASSIUM": 1, - "ELEMENT_PRASEODYMIUM": 1, - "ELEMENT_PROMETHIUM": 1, - "ELEMENT_PROTACTINIUM": 1, - "ELEMENT_RADIUM": 1, - "ELEMENT_RADON": 1, - "ELEMENT_RHENIUM": 1, - "ELEMENT_RHODIUM": 1, - "ELEMENT_ROENTGENIUM": 1, - "ELEMENT_RUBIDIUM": 1, - "ELEMENT_RUTHENIUM": 1, - "ELEMENT_RUTHERFORDIUM": 1, - "ELEMENT_SAMARIUM": 1, - "ELEMENT_SCANDIUM": 1, - "ELEMENT_SEABORGIUM": 1, - "ELEMENT_SELENIUM": 1, - "ELEMENT_SILICON": 1, - "ELEMENT_SILVER": 1, - "ELEMENT_SODIUM": 1, - "ELEMENT_STRONTIUM": 1, - "ELEMENT_SULFUR": 1, - "ELEMENT_TANTALUM": 1, - "ELEMENT_TECHNETIUM": 1, - "ELEMENT_TELLURIUM": 1, - "ELEMENT_TENNESSINE": 1, - "ELEMENT_TERBIUM": 1, - "ELEMENT_THALLIUM": 1, - "ELEMENT_THORIUM": 1, - "ELEMENT_THULIUM": 1, - "ELEMENT_TIN": 1, - "ELEMENT_TITANIUM": 1, - "ELEMENT_TUNGSTEN": 1, - "ELEMENT_URANIUM": 1, - "ELEMENT_VANADIUM": 1, - "ELEMENT_XENON": 1, - "ELEMENT_YTTERBIUM": 1, - "ELEMENT_YTTRIUM": 1, - "ELEMENT_ZERO": 1, - "ELEMENT_ZINC": 1, - "ELEMENT_ZIRCONIUM": 1, - "EMERALD": 1, - "EMERALD_ORE": 1, - "ENCHANTING_TABLE": 1, - "ENDER_CHEST": 4, - "END_PORTAL_FRAME": 8, - "END_ROD": 6, - "END_STONE": 1, - "END_STONE_BRICKS": 1, - "END_STONE_BRICK_SLAB": 3, - "END_STONE_BRICK_STAIRS": 8, - "END_STONE_BRICK_WALL": 162, - "FAKE_WOODEN_SLAB": 3, - "FARMLAND": 1304, - "FERN": 1, - "FIRE": 16, - "FLETCHING_TABLE": 1, - "FLOWERING_AZALEA_LEAVES": 4, - "FLOWER_POT": 1, - "FROGLIGHT": 9, - "FROSTED_ICE": 4, - "FURNACE": 8, - "GILDED_BLACKSTONE": 1, - "GLASS": 1, - "GLASS_PANE": 1, - "GLAZED_TERRACOTTA": 64, - "GLOWING_ITEM_FRAME": 12, - "GLOWING_OBSIDIAN": 1, - "GLOWSTONE": 1, - "GLOW_LICHEN": 64, - "GOLD": 1, - "GOLD_ORE": 1, - "GRANITE": 1, - "GRANITE_SLAB": 3, - "GRANITE_STAIRS": 8, - "GRANITE_WALL": 162, - "GRASS": 1, - "GRASS_PATH": 1, - "GRAVEL": 1, - "GREEN_TORCH": 5, - "HANGING_ROOTS": 1, - "HARDENED_CLAY": 1, - "HARDENED_GLASS": 1, - "HARDENED_GLASS_PANE": 1, - "HAY_BALE": 3, - "HONEYCOMB": 1, - "HOPPER": 10, - "ICE": 1, - "INFESTED_CHISELED_STONE_BRICK": 1, - "INFESTED_COBBLESTONE": 1, - "INFESTED_CRACKED_STONE_BRICK": 1, - "INFESTED_MOSSY_STONE_BRICK": 1, - "INFESTED_STONE": 1, - "INFESTED_STONE_BRICK": 1, - "INFO_UPDATE": 1, - "INFO_UPDATE2": 1, - "INVISIBLE_BEDROCK": 1, - "IRON": 1, - "IRON_BARS": 1, - "IRON_DOOR": 32, - "IRON_ORE": 1, - "IRON_TRAPDOOR": 16, - "ITEM_FRAME": 12, - "JUKEBOX": 1, - "JUNGLE_BUTTON": 12, - "JUNGLE_DOOR": 32, - "JUNGLE_FENCE": 1, - "JUNGLE_FENCE_GATE": 16, - "JUNGLE_LEAVES": 4, - "JUNGLE_LOG": 6, - "JUNGLE_PLANKS": 1, - "JUNGLE_PRESSURE_PLATE": 2, - "JUNGLE_SAPLING": 2, - "JUNGLE_SIGN": 16, - "JUNGLE_SLAB": 3, - "JUNGLE_STAIRS": 8, - "JUNGLE_TRAPDOOR": 16, - "JUNGLE_WALL_SIGN": 4, - "JUNGLE_WOOD": 6, - "LAB_TABLE": 4, - "LADDER": 4, - "LANTERN": 2, - "LAPIS_LAZULI": 1, - "LAPIS_LAZULI_ORE": 1, - "LARGE_FERN": 2, - "LAVA": 32, - "LAVA_CAULDRON": 6, - "LECTERN": 8, - "LEGACY_STONECUTTER": 1, - "LEVER": 16, - "LIGHT": 16, - "LIGHTNING_ROD": 6, - "LILAC": 2, - "LILY_OF_THE_VALLEY": 1, - "LILY_PAD": 1, - "LIT_PUMPKIN": 4, - "LOOM": 4, - "MAGMA": 1, - "MANGROVE_BUTTON": 12, - "MANGROVE_DOOR": 32, - "MANGROVE_FENCE": 1, - "MANGROVE_FENCE_GATE": 16, - "MANGROVE_LEAVES": 4, - "MANGROVE_LOG": 6, - "MANGROVE_PLANKS": 1, - "MANGROVE_PRESSURE_PLATE": 2, - "MANGROVE_ROOTS": 1, - "MANGROVE_SIGN": 16, - "MANGROVE_SLAB": 3, - "MANGROVE_STAIRS": 8, - "MANGROVE_TRAPDOOR": 16, - "MANGROVE_WALL_SIGN": 4, - "MANGROVE_WOOD": 6, - "MATERIAL_REDUCER": 4, - "MELON": 1, - "MELON_STEM": 40, - "MOB_HEAD": 35, - "MONSTER_SPAWNER": 1, - "MOSSY_COBBLESTONE": 1, - "MOSSY_COBBLESTONE_SLAB": 3, - "MOSSY_COBBLESTONE_STAIRS": 8, - "MOSSY_COBBLESTONE_WALL": 162, - "MOSSY_STONE_BRICKS": 1, - "MOSSY_STONE_BRICK_SLAB": 3, - "MOSSY_STONE_BRICK_STAIRS": 8, - "MOSSY_STONE_BRICK_WALL": 162, - "MUD": 1, - "MUDDY_MANGROVE_ROOTS": 3, - "MUD_BRICKS": 1, - "MUD_BRICK_SLAB": 3, - "MUD_BRICK_STAIRS": 8, - "MUD_BRICK_WALL": 162, - "MUSHROOM_STEM": 1, - "MYCELIUM": 1, - "NETHERITE": 1, - "NETHERRACK": 1, - "NETHER_BRICKS": 1, - "NETHER_BRICK_FENCE": 1, - "NETHER_BRICK_SLAB": 3, - "NETHER_BRICK_STAIRS": 8, - "NETHER_BRICK_WALL": 162, - "NETHER_GOLD_ORE": 1, - "NETHER_PORTAL": 2, - "NETHER_QUARTZ_ORE": 1, - "NETHER_REACTOR_CORE": 1, - "NETHER_WART": 4, - "NETHER_WART_BLOCK": 1, - "NOTE_BLOCK": 1, - "OAK_BUTTON": 12, - "OAK_DOOR": 32, - "OAK_FENCE": 1, - "OAK_FENCE_GATE": 16, - "OAK_LEAVES": 4, - "OAK_LOG": 6, - "OAK_PLANKS": 1, - "OAK_PRESSURE_PLATE": 2, - "OAK_SAPLING": 2, - "OAK_SIGN": 16, - "OAK_SLAB": 3, - "OAK_STAIRS": 8, - "OAK_TRAPDOOR": 16, - "OAK_WALL_SIGN": 4, - "OAK_WOOD": 6, - "OBSIDIAN": 1, - "ORANGE_TULIP": 1, - "OXEYE_DAISY": 1, - "PACKED_ICE": 1, - "PACKED_MUD": 1, - "PEONY": 2, - "PINK_PETALS": 16, - "PINK_TULIP": 1, - "PITCHER_CROP": 3, - "PITCHER_PLANT": 2, - "PODZOL": 1, - "POLISHED_ANDESITE": 1, - "POLISHED_ANDESITE_SLAB": 3, - "POLISHED_ANDESITE_STAIRS": 8, - "POLISHED_BASALT": 3, - "POLISHED_BLACKSTONE": 1, - "POLISHED_BLACKSTONE_BRICKS": 1, - "POLISHED_BLACKSTONE_BRICK_SLAB": 3, - "POLISHED_BLACKSTONE_BRICK_STAIRS": 8, - "POLISHED_BLACKSTONE_BRICK_WALL": 162, - "POLISHED_BLACKSTONE_BUTTON": 12, - "POLISHED_BLACKSTONE_PRESSURE_PLATE": 2, - "POLISHED_BLACKSTONE_SLAB": 3, - "POLISHED_BLACKSTONE_STAIRS": 8, - "POLISHED_BLACKSTONE_WALL": 162, - "POLISHED_DEEPSLATE": 1, - "POLISHED_DEEPSLATE_SLAB": 3, - "POLISHED_DEEPSLATE_STAIRS": 8, - "POLISHED_DEEPSLATE_WALL": 162, - "POLISHED_DIORITE": 1, - "POLISHED_DIORITE_SLAB": 3, - "POLISHED_DIORITE_STAIRS": 8, - "POLISHED_GRANITE": 1, - "POLISHED_GRANITE_SLAB": 3, - "POLISHED_GRANITE_STAIRS": 8, - "POLISHED_TUFF": 1, - "POLISHED_TUFF_SLAB": 3, - "POLISHED_TUFF_STAIRS": 8, - "POLISHED_TUFF_WALL": 162, - "POPPY": 1, - "POTATOES": 8, - "POTION_CAULDRON": 6, - "POWERED_RAIL": 12, - "PRISMARINE": 1, - "PRISMARINE_BRICKS": 1, - "PRISMARINE_BRICKS_SLAB": 3, - "PRISMARINE_BRICKS_STAIRS": 8, - "PRISMARINE_SLAB": 3, - "PRISMARINE_STAIRS": 8, - "PRISMARINE_WALL": 162, - "PUMPKIN": 1, - "PUMPKIN_STEM": 40, - "PURPLE_TORCH": 5, - "PURPUR": 1, - "PURPUR_PILLAR": 3, - "PURPUR_SLAB": 3, - "PURPUR_STAIRS": 8, - "QUARTZ": 1, - "QUARTZ_BRICKS": 1, - "QUARTZ_PILLAR": 3, - "QUARTZ_SLAB": 3, - "QUARTZ_STAIRS": 8, - "RAIL": 10, - "RAW_COPPER": 1, - "RAW_GOLD": 1, - "RAW_IRON": 1, - "REDSTONE": 1, - "REDSTONE_COMPARATOR": 16, - "REDSTONE_LAMP": 2, - "REDSTONE_ORE": 2, - "REDSTONE_REPEATER": 32, - "REDSTONE_TORCH": 10, - "REDSTONE_WIRE": 16, - "RED_MUSHROOM": 1, - "RED_MUSHROOM_BLOCK": 11, - "RED_NETHER_BRICKS": 1, - "RED_NETHER_BRICK_SLAB": 3, - "RED_NETHER_BRICK_STAIRS": 8, - "RED_NETHER_BRICK_WALL": 162, - "RED_SAND": 1, - "RED_SANDSTONE": 1, - "RED_SANDSTONE_SLAB": 3, - "RED_SANDSTONE_STAIRS": 8, - "RED_SANDSTONE_WALL": 162, - "RED_TORCH": 5, - "RED_TULIP": 1, - "REINFORCED_DEEPSLATE": 1, - "RESERVED6": 1, - "ROSE_BUSH": 2, - "SAND": 1, - "SANDSTONE": 1, - "SANDSTONE_SLAB": 3, - "SANDSTONE_STAIRS": 8, - "SANDSTONE_WALL": 162, - "SCULK": 1, - "SEA_LANTERN": 1, - "SEA_PICKLE": 8, - "SHROOMLIGHT": 1, - "SHULKER_BOX": 1, - "SLIME": 1, - "SMALL_DRIPLEAF": 8, - "SMITHING_TABLE": 1, - "SMOKER": 8, - "SMOOTH_BASALT": 1, - "SMOOTH_QUARTZ": 1, - "SMOOTH_QUARTZ_SLAB": 3, - "SMOOTH_QUARTZ_STAIRS": 8, - "SMOOTH_RED_SANDSTONE": 1, - "SMOOTH_RED_SANDSTONE_SLAB": 3, - "SMOOTH_RED_SANDSTONE_STAIRS": 8, - "SMOOTH_SANDSTONE": 1, - "SMOOTH_SANDSTONE_SLAB": 3, - "SMOOTH_SANDSTONE_STAIRS": 8, - "SMOOTH_STONE": 1, - "SMOOTH_STONE_SLAB": 3, - "SNOW": 1, - "SNOW_LAYER": 8, - "SOUL_CAMPFIRE": 8, - "SOUL_FIRE": 1, - "SOUL_LANTERN": 2, - "SOUL_SAND": 1, - "SOUL_SOIL": 1, - "SOUL_TORCH": 5, - "SPONGE": 2, - "SPORE_BLOSSOM": 1, - "SPRUCE_BUTTON": 12, - "SPRUCE_DOOR": 32, - "SPRUCE_FENCE": 1, - "SPRUCE_FENCE_GATE": 16, - "SPRUCE_LEAVES": 4, - "SPRUCE_LOG": 6, - "SPRUCE_PLANKS": 1, - "SPRUCE_PRESSURE_PLATE": 2, - "SPRUCE_SAPLING": 2, - "SPRUCE_SIGN": 16, - "SPRUCE_SLAB": 3, - "SPRUCE_STAIRS": 8, - "SPRUCE_TRAPDOOR": 16, - "SPRUCE_WALL_SIGN": 4, - "SPRUCE_WOOD": 6, - "STAINED_CLAY": 16, - "STAINED_GLASS": 16, - "STAINED_GLASS_PANE": 16, - "STAINED_HARDENED_GLASS": 16, - "STAINED_HARDENED_GLASS_PANE": 16, - "STONE": 1, - "STONECUTTER": 4, - "STONE_BRICKS": 1, - "STONE_BRICK_SLAB": 3, - "STONE_BRICK_STAIRS": 8, - "STONE_BRICK_WALL": 162, - "STONE_BUTTON": 12, - "STONE_PRESSURE_PLATE": 2, - "STONE_SLAB": 3, - "STONE_STAIRS": 8, - "SUGARCANE": 16, - "SUNFLOWER": 2, - "SWEET_BERRY_BUSH": 4, - "TALL_GRASS": 1, - "TINTED_GLASS": 1, - "TNT": 4, - "TORCH": 5, - "TORCHFLOWER": 1, - "TORCHFLOWER_CROP": 2, - "TRAPPED_CHEST": 4, - "TRIPWIRE": 16, - "TRIPWIRE_HOOK": 16, - "TUFF": 1, - "TUFF_BRICKS": 1, - "TUFF_BRICK_SLAB": 3, - "TUFF_BRICK_STAIRS": 8, - "TUFF_BRICK_WALL": 162, - "TUFF_SLAB": 3, - "TUFF_STAIRS": 8, - "TUFF_WALL": 162, - "TWISTING_VINES": 26, - "UNDERWATER_TORCH": 5, - "VINES": 16, - "WALL_BANNER": 64, - "WALL_CORAL_FAN": 40, - "WARPED_BUTTON": 12, - "WARPED_DOOR": 32, - "WARPED_FENCE": 1, - "WARPED_FENCE_GATE": 16, - "WARPED_HYPHAE": 6, - "WARPED_PLANKS": 1, - "WARPED_PRESSURE_PLATE": 2, - "WARPED_ROOTS": 1, - "WARPED_SIGN": 16, - "WARPED_SLAB": 3, - "WARPED_STAIRS": 8, - "WARPED_STEM": 6, - "WARPED_TRAPDOOR": 16, - "WARPED_WALL_SIGN": 4, - "WARPED_WART_BLOCK": 1, - "WATER": 32, - "WATER_CAULDRON": 6, - "WEEPING_VINES": 26, - "WEIGHTED_PRESSURE_PLATE_HEAVY": 16, - "WEIGHTED_PRESSURE_PLATE_LIGHT": 16, - "WHEAT": 8, - "WHITE_TULIP": 1, - "WITHER_ROSE": 1, - "WOOL": 16 + "stateCounts": { + "ACACIA_BUTTON": 12, + "ACACIA_DOOR": 32, + "ACACIA_FENCE": 1, + "ACACIA_FENCE_GATE": 16, + "ACACIA_LEAVES": 4, + "ACACIA_LOG": 6, + "ACACIA_PLANKS": 1, + "ACACIA_PRESSURE_PLATE": 2, + "ACACIA_SAPLING": 2, + "ACACIA_SIGN": 16, + "ACACIA_SLAB": 3, + "ACACIA_STAIRS": 8, + "ACACIA_TRAPDOOR": 16, + "ACACIA_WALL_SIGN": 4, + "ACACIA_WOOD": 6, + "ACTIVATOR_RAIL": 12, + "AIR": 1, + "ALLIUM": 1, + "ALL_SIDED_MUSHROOM_STEM": 1, + "AMETHYST": 1, + "AMETHYST_CLUSTER": 24, + "ANCIENT_DEBRIS": 1, + "ANDESITE": 1, + "ANDESITE_SLAB": 3, + "ANDESITE_STAIRS": 8, + "ANDESITE_WALL": 162, + "ANVIL": 12, + "AZALEA_LEAVES": 4, + "AZURE_BLUET": 1, + "BAMBOO": 12, + "BAMBOO_SAPLING": 2, + "BANNER": 256, + "BARREL": 12, + "BARRIER": 1, + "BASALT": 3, + "BEACON": 1, + "BED": 256, + "BEDROCK": 2, + "BEETROOTS": 8, + "BELL": 16, + "BIG_DRIPLEAF_HEAD": 16, + "BIG_DRIPLEAF_STEM": 4, + "BIRCH_BUTTON": 12, + "BIRCH_DOOR": 32, + "BIRCH_FENCE": 1, + "BIRCH_FENCE_GATE": 16, + "BIRCH_LEAVES": 4, + "BIRCH_LOG": 6, + "BIRCH_PLANKS": 1, + "BIRCH_PRESSURE_PLATE": 2, + "BIRCH_SAPLING": 2, + "BIRCH_SIGN": 16, + "BIRCH_SLAB": 3, + "BIRCH_STAIRS": 8, + "BIRCH_TRAPDOOR": 16, + "BIRCH_WALL_SIGN": 4, + "BIRCH_WOOD": 6, + "BLACKSTONE": 1, + "BLACKSTONE_SLAB": 3, + "BLACKSTONE_STAIRS": 8, + "BLACKSTONE_WALL": 162, + "BLAST_FURNACE": 8, + "BLUE_ICE": 1, + "BLUE_ORCHID": 1, + "BLUE_TORCH": 5, + "BONE_BLOCK": 3, + "BOOKSHELF": 1, + "BREWING_STAND": 8, + "BRICKS": 1, + "BRICK_SLAB": 3, + "BRICK_STAIRS": 8, + "BRICK_WALL": 162, + "BROWN_MUSHROOM": 1, + "BROWN_MUSHROOM_BLOCK": 11, + "BUDDING_AMETHYST": 1, + "CACTUS": 16, + "CAKE": 7, + "CAKE_WITH_CANDLE": 2, + "CAKE_WITH_DYED_CANDLE": 32, + "CALCITE": 1, + "CAMPFIRE": 8, + "CANDLE": 8, + "CARPET": 16, + "CARROTS": 8, + "CARTOGRAPHY_TABLE": 1, + "CARVED_PUMPKIN": 4, + "CAULDRON": 1, + "CAVE_VINES": 104, + "CHAIN": 3, + "CHEMICAL_HEAT": 1, + "CHERRY_BUTTON": 12, + "CHERRY_DOOR": 32, + "CHERRY_FENCE": 1, + "CHERRY_FENCE_GATE": 16, + "CHERRY_LEAVES": 4, + "CHERRY_LOG": 6, + "CHERRY_PLANKS": 1, + "CHERRY_PRESSURE_PLATE": 2, + "CHERRY_SIGN": 16, + "CHERRY_SLAB": 3, + "CHERRY_STAIRS": 8, + "CHERRY_TRAPDOOR": 16, + "CHERRY_WALL_SIGN": 4, + "CHERRY_WOOD": 6, + "CHEST": 4, + "CHISELED_BOOKSHELF": 256, + "CHISELED_COPPER": 8, + "CHISELED_DEEPSLATE": 1, + "CHISELED_NETHER_BRICKS": 1, + "CHISELED_POLISHED_BLACKSTONE": 1, + "CHISELED_QUARTZ": 3, + "CHISELED_RED_SANDSTONE": 1, + "CHISELED_SANDSTONE": 1, + "CHISELED_STONE_BRICKS": 1, + "CHISELED_TUFF": 1, + "CHISELED_TUFF_BRICKS": 1, + "CHORUS_FLOWER": 6, + "CHORUS_PLANT": 1, + "CLAY": 1, + "COAL": 1, + "COAL_ORE": 1, + "COBBLED_DEEPSLATE": 1, + "COBBLED_DEEPSLATE_SLAB": 3, + "COBBLED_DEEPSLATE_STAIRS": 8, + "COBBLED_DEEPSLATE_WALL": 162, + "COBBLESTONE": 1, + "COBBLESTONE_SLAB": 3, + "COBBLESTONE_STAIRS": 8, + "COBBLESTONE_WALL": 162, + "COBWEB": 1, + "COCOA_POD": 12, + "COMPOUND_CREATOR": 4, + "CONCRETE": 16, + "CONCRETE_POWDER": 16, + "COPPER": 8, + "COPPER_BULB": 32, + "COPPER_DOOR": 256, + "COPPER_GRATE": 8, + "COPPER_ORE": 1, + "COPPER_TRAPDOOR": 128, + "CORAL": 10, + "CORAL_BLOCK": 10, + "CORAL_FAN": 20, + "CORNFLOWER": 1, + "CRACKED_DEEPSLATE_BRICKS": 1, + "CRACKED_DEEPSLATE_TILES": 1, + "CRACKED_NETHER_BRICKS": 1, + "CRACKED_POLISHED_BLACKSTONE_BRICKS": 1, + "CRACKED_STONE_BRICKS": 1, + "CRAFTING_TABLE": 1, + "CRIMSON_BUTTON": 12, + "CRIMSON_DOOR": 32, + "CRIMSON_FENCE": 1, + "CRIMSON_FENCE_GATE": 16, + "CRIMSON_HYPHAE": 6, + "CRIMSON_PLANKS": 1, + "CRIMSON_PRESSURE_PLATE": 2, + "CRIMSON_ROOTS": 1, + "CRIMSON_SIGN": 16, + "CRIMSON_SLAB": 3, + "CRIMSON_STAIRS": 8, + "CRIMSON_STEM": 6, + "CRIMSON_TRAPDOOR": 16, + "CRIMSON_WALL_SIGN": 4, + "CRYING_OBSIDIAN": 1, + "CUT_COPPER": 8, + "CUT_COPPER_SLAB": 24, + "CUT_COPPER_STAIRS": 64, + "CUT_RED_SANDSTONE": 1, + "CUT_RED_SANDSTONE_SLAB": 3, + "CUT_SANDSTONE": 1, + "CUT_SANDSTONE_SLAB": 3, + "DANDELION": 1, + "DARK_OAK_BUTTON": 12, + "DARK_OAK_DOOR": 32, + "DARK_OAK_FENCE": 1, + "DARK_OAK_FENCE_GATE": 16, + "DARK_OAK_LEAVES": 4, + "DARK_OAK_LOG": 6, + "DARK_OAK_PLANKS": 1, + "DARK_OAK_PRESSURE_PLATE": 2, + "DARK_OAK_SAPLING": 2, + "DARK_OAK_SIGN": 16, + "DARK_OAK_SLAB": 3, + "DARK_OAK_STAIRS": 8, + "DARK_OAK_TRAPDOOR": 16, + "DARK_OAK_WALL_SIGN": 4, + "DARK_OAK_WOOD": 6, + "DARK_PRISMARINE": 1, + "DARK_PRISMARINE_SLAB": 3, + "DARK_PRISMARINE_STAIRS": 8, + "DAYLIGHT_SENSOR": 32, + "DEAD_BUSH": 1, + "DEEPSLATE": 3, + "DEEPSLATE_BRICKS": 1, + "DEEPSLATE_BRICK_SLAB": 3, + "DEEPSLATE_BRICK_STAIRS": 8, + "DEEPSLATE_BRICK_WALL": 162, + "DEEPSLATE_COAL_ORE": 1, + "DEEPSLATE_COPPER_ORE": 1, + "DEEPSLATE_DIAMOND_ORE": 1, + "DEEPSLATE_EMERALD_ORE": 1, + "DEEPSLATE_GOLD_ORE": 1, + "DEEPSLATE_IRON_ORE": 1, + "DEEPSLATE_LAPIS_LAZULI_ORE": 1, + "DEEPSLATE_REDSTONE_ORE": 2, + "DEEPSLATE_TILES": 1, + "DEEPSLATE_TILE_SLAB": 3, + "DEEPSLATE_TILE_STAIRS": 8, + "DEEPSLATE_TILE_WALL": 162, + "DETECTOR_RAIL": 12, + "DIAMOND": 1, + "DIAMOND_ORE": 1, + "DIORITE": 1, + "DIORITE_SLAB": 3, + "DIORITE_STAIRS": 8, + "DIORITE_WALL": 162, + "DIRT": 3, + "DOUBLE_PITCHER_CROP": 4, + "DOUBLE_TALLGRASS": 2, + "DRAGON_EGG": 1, + "DRIED_KELP": 1, + "DYED_CANDLE": 128, + "DYED_SHULKER_BOX": 16, + "ELEMENT_ACTINIUM": 1, + "ELEMENT_ALUMINUM": 1, + "ELEMENT_AMERICIUM": 1, + "ELEMENT_ANTIMONY": 1, + "ELEMENT_ARGON": 1, + "ELEMENT_ARSENIC": 1, + "ELEMENT_ASTATINE": 1, + "ELEMENT_BARIUM": 1, + "ELEMENT_BERKELIUM": 1, + "ELEMENT_BERYLLIUM": 1, + "ELEMENT_BISMUTH": 1, + "ELEMENT_BOHRIUM": 1, + "ELEMENT_BORON": 1, + "ELEMENT_BROMINE": 1, + "ELEMENT_CADMIUM": 1, + "ELEMENT_CALCIUM": 1, + "ELEMENT_CALIFORNIUM": 1, + "ELEMENT_CARBON": 1, + "ELEMENT_CERIUM": 1, + "ELEMENT_CESIUM": 1, + "ELEMENT_CHLORINE": 1, + "ELEMENT_CHROMIUM": 1, + "ELEMENT_COBALT": 1, + "ELEMENT_CONSTRUCTOR": 4, + "ELEMENT_COPERNICIUM": 1, + "ELEMENT_COPPER": 1, + "ELEMENT_CURIUM": 1, + "ELEMENT_DARMSTADTIUM": 1, + "ELEMENT_DUBNIUM": 1, + "ELEMENT_DYSPROSIUM": 1, + "ELEMENT_EINSTEINIUM": 1, + "ELEMENT_ERBIUM": 1, + "ELEMENT_EUROPIUM": 1, + "ELEMENT_FERMIUM": 1, + "ELEMENT_FLEROVIUM": 1, + "ELEMENT_FLUORINE": 1, + "ELEMENT_FRANCIUM": 1, + "ELEMENT_GADOLINIUM": 1, + "ELEMENT_GALLIUM": 1, + "ELEMENT_GERMANIUM": 1, + "ELEMENT_GOLD": 1, + "ELEMENT_HAFNIUM": 1, + "ELEMENT_HASSIUM": 1, + "ELEMENT_HELIUM": 1, + "ELEMENT_HOLMIUM": 1, + "ELEMENT_HYDROGEN": 1, + "ELEMENT_INDIUM": 1, + "ELEMENT_IODINE": 1, + "ELEMENT_IRIDIUM": 1, + "ELEMENT_IRON": 1, + "ELEMENT_KRYPTON": 1, + "ELEMENT_LANTHANUM": 1, + "ELEMENT_LAWRENCIUM": 1, + "ELEMENT_LEAD": 1, + "ELEMENT_LITHIUM": 1, + "ELEMENT_LIVERMORIUM": 1, + "ELEMENT_LUTETIUM": 1, + "ELEMENT_MAGNESIUM": 1, + "ELEMENT_MANGANESE": 1, + "ELEMENT_MEITNERIUM": 1, + "ELEMENT_MENDELEVIUM": 1, + "ELEMENT_MERCURY": 1, + "ELEMENT_MOLYBDENUM": 1, + "ELEMENT_MOSCOVIUM": 1, + "ELEMENT_NEODYMIUM": 1, + "ELEMENT_NEON": 1, + "ELEMENT_NEPTUNIUM": 1, + "ELEMENT_NICKEL": 1, + "ELEMENT_NIHONIUM": 1, + "ELEMENT_NIOBIUM": 1, + "ELEMENT_NITROGEN": 1, + "ELEMENT_NOBELIUM": 1, + "ELEMENT_OGANESSON": 1, + "ELEMENT_OSMIUM": 1, + "ELEMENT_OXYGEN": 1, + "ELEMENT_PALLADIUM": 1, + "ELEMENT_PHOSPHORUS": 1, + "ELEMENT_PLATINUM": 1, + "ELEMENT_PLUTONIUM": 1, + "ELEMENT_POLONIUM": 1, + "ELEMENT_POTASSIUM": 1, + "ELEMENT_PRASEODYMIUM": 1, + "ELEMENT_PROMETHIUM": 1, + "ELEMENT_PROTACTINIUM": 1, + "ELEMENT_RADIUM": 1, + "ELEMENT_RADON": 1, + "ELEMENT_RHENIUM": 1, + "ELEMENT_RHODIUM": 1, + "ELEMENT_ROENTGENIUM": 1, + "ELEMENT_RUBIDIUM": 1, + "ELEMENT_RUTHENIUM": 1, + "ELEMENT_RUTHERFORDIUM": 1, + "ELEMENT_SAMARIUM": 1, + "ELEMENT_SCANDIUM": 1, + "ELEMENT_SEABORGIUM": 1, + "ELEMENT_SELENIUM": 1, + "ELEMENT_SILICON": 1, + "ELEMENT_SILVER": 1, + "ELEMENT_SODIUM": 1, + "ELEMENT_STRONTIUM": 1, + "ELEMENT_SULFUR": 1, + "ELEMENT_TANTALUM": 1, + "ELEMENT_TECHNETIUM": 1, + "ELEMENT_TELLURIUM": 1, + "ELEMENT_TENNESSINE": 1, + "ELEMENT_TERBIUM": 1, + "ELEMENT_THALLIUM": 1, + "ELEMENT_THORIUM": 1, + "ELEMENT_THULIUM": 1, + "ELEMENT_TIN": 1, + "ELEMENT_TITANIUM": 1, + "ELEMENT_TUNGSTEN": 1, + "ELEMENT_URANIUM": 1, + "ELEMENT_VANADIUM": 1, + "ELEMENT_XENON": 1, + "ELEMENT_YTTERBIUM": 1, + "ELEMENT_YTTRIUM": 1, + "ELEMENT_ZERO": 1, + "ELEMENT_ZINC": 1, + "ELEMENT_ZIRCONIUM": 1, + "EMERALD": 1, + "EMERALD_ORE": 1, + "ENCHANTING_TABLE": 1, + "ENDER_CHEST": 4, + "END_PORTAL_FRAME": 8, + "END_ROD": 6, + "END_STONE": 1, + "END_STONE_BRICKS": 1, + "END_STONE_BRICK_SLAB": 3, + "END_STONE_BRICK_STAIRS": 8, + "END_STONE_BRICK_WALL": 162, + "FAKE_WOODEN_SLAB": 3, + "FARMLAND": 1304, + "FERN": 1, + "FIRE": 16, + "FLETCHING_TABLE": 1, + "FLOWERING_AZALEA_LEAVES": 4, + "FLOWER_POT": 1, + "FROGLIGHT": 9, + "FROSTED_ICE": 4, + "FURNACE": 8, + "GILDED_BLACKSTONE": 1, + "GLASS": 1, + "GLASS_PANE": 1, + "GLAZED_TERRACOTTA": 64, + "GLOWING_ITEM_FRAME": 12, + "GLOWING_OBSIDIAN": 1, + "GLOWSTONE": 1, + "GLOW_LICHEN": 64, + "GOLD": 1, + "GOLD_ORE": 1, + "GRANITE": 1, + "GRANITE_SLAB": 3, + "GRANITE_STAIRS": 8, + "GRANITE_WALL": 162, + "GRASS": 1, + "GRASS_PATH": 1, + "GRAVEL": 1, + "GREEN_TORCH": 5, + "HANGING_ROOTS": 1, + "HARDENED_CLAY": 1, + "HARDENED_GLASS": 1, + "HARDENED_GLASS_PANE": 1, + "HAY_BALE": 3, + "HONEYCOMB": 1, + "HOPPER": 10, + "ICE": 1, + "INFESTED_CHISELED_STONE_BRICK": 1, + "INFESTED_COBBLESTONE": 1, + "INFESTED_CRACKED_STONE_BRICK": 1, + "INFESTED_MOSSY_STONE_BRICK": 1, + "INFESTED_STONE": 1, + "INFESTED_STONE_BRICK": 1, + "INFO_UPDATE": 1, + "INFO_UPDATE2": 1, + "INVISIBLE_BEDROCK": 1, + "IRON": 1, + "IRON_BARS": 1, + "IRON_DOOR": 32, + "IRON_ORE": 1, + "IRON_TRAPDOOR": 16, + "ITEM_FRAME": 12, + "JUKEBOX": 1, + "JUNGLE_BUTTON": 12, + "JUNGLE_DOOR": 32, + "JUNGLE_FENCE": 1, + "JUNGLE_FENCE_GATE": 16, + "JUNGLE_LEAVES": 4, + "JUNGLE_LOG": 6, + "JUNGLE_PLANKS": 1, + "JUNGLE_PRESSURE_PLATE": 2, + "JUNGLE_SAPLING": 2, + "JUNGLE_SIGN": 16, + "JUNGLE_SLAB": 3, + "JUNGLE_STAIRS": 8, + "JUNGLE_TRAPDOOR": 16, + "JUNGLE_WALL_SIGN": 4, + "JUNGLE_WOOD": 6, + "LAB_TABLE": 4, + "LADDER": 4, + "LANTERN": 2, + "LAPIS_LAZULI": 1, + "LAPIS_LAZULI_ORE": 1, + "LARGE_FERN": 2, + "LAVA": 32, + "LAVA_CAULDRON": 6, + "LECTERN": 8, + "LEGACY_STONECUTTER": 1, + "LEVER": 16, + "LIGHT": 16, + "LIGHTNING_ROD": 6, + "LILAC": 2, + "LILY_OF_THE_VALLEY": 1, + "LILY_PAD": 1, + "LIT_PUMPKIN": 4, + "LOOM": 4, + "MAGMA": 1, + "MANGROVE_BUTTON": 12, + "MANGROVE_DOOR": 32, + "MANGROVE_FENCE": 1, + "MANGROVE_FENCE_GATE": 16, + "MANGROVE_LEAVES": 4, + "MANGROVE_LOG": 6, + "MANGROVE_PLANKS": 1, + "MANGROVE_PRESSURE_PLATE": 2, + "MANGROVE_ROOTS": 1, + "MANGROVE_SIGN": 16, + "MANGROVE_SLAB": 3, + "MANGROVE_STAIRS": 8, + "MANGROVE_TRAPDOOR": 16, + "MANGROVE_WALL_SIGN": 4, + "MANGROVE_WOOD": 6, + "MATERIAL_REDUCER": 4, + "MELON": 1, + "MELON_STEM": 40, + "MOB_HEAD": 35, + "MONSTER_SPAWNER": 1, + "MOSSY_COBBLESTONE": 1, + "MOSSY_COBBLESTONE_SLAB": 3, + "MOSSY_COBBLESTONE_STAIRS": 8, + "MOSSY_COBBLESTONE_WALL": 162, + "MOSSY_STONE_BRICKS": 1, + "MOSSY_STONE_BRICK_SLAB": 3, + "MOSSY_STONE_BRICK_STAIRS": 8, + "MOSSY_STONE_BRICK_WALL": 162, + "MUD": 1, + "MUDDY_MANGROVE_ROOTS": 3, + "MUD_BRICKS": 1, + "MUD_BRICK_SLAB": 3, + "MUD_BRICK_STAIRS": 8, + "MUD_BRICK_WALL": 162, + "MUSHROOM_STEM": 1, + "MYCELIUM": 1, + "NETHERITE": 1, + "NETHERRACK": 1, + "NETHER_BRICKS": 1, + "NETHER_BRICK_FENCE": 1, + "NETHER_BRICK_SLAB": 3, + "NETHER_BRICK_STAIRS": 8, + "NETHER_BRICK_WALL": 162, + "NETHER_GOLD_ORE": 1, + "NETHER_PORTAL": 2, + "NETHER_QUARTZ_ORE": 1, + "NETHER_REACTOR_CORE": 1, + "NETHER_WART": 4, + "NETHER_WART_BLOCK": 1, + "NOTE_BLOCK": 1, + "OAK_BUTTON": 12, + "OAK_DOOR": 32, + "OAK_FENCE": 1, + "OAK_FENCE_GATE": 16, + "OAK_LEAVES": 4, + "OAK_LOG": 6, + "OAK_PLANKS": 1, + "OAK_PRESSURE_PLATE": 2, + "OAK_SAPLING": 2, + "OAK_SIGN": 16, + "OAK_SLAB": 3, + "OAK_STAIRS": 8, + "OAK_TRAPDOOR": 16, + "OAK_WALL_SIGN": 4, + "OAK_WOOD": 6, + "OBSIDIAN": 1, + "ORANGE_TULIP": 1, + "OXEYE_DAISY": 1, + "PACKED_ICE": 1, + "PACKED_MUD": 1, + "PEONY": 2, + "PINK_PETALS": 16, + "PINK_TULIP": 1, + "PITCHER_CROP": 3, + "PITCHER_PLANT": 2, + "PODZOL": 1, + "POLISHED_ANDESITE": 1, + "POLISHED_ANDESITE_SLAB": 3, + "POLISHED_ANDESITE_STAIRS": 8, + "POLISHED_BASALT": 3, + "POLISHED_BLACKSTONE": 1, + "POLISHED_BLACKSTONE_BRICKS": 1, + "POLISHED_BLACKSTONE_BRICK_SLAB": 3, + "POLISHED_BLACKSTONE_BRICK_STAIRS": 8, + "POLISHED_BLACKSTONE_BRICK_WALL": 162, + "POLISHED_BLACKSTONE_BUTTON": 12, + "POLISHED_BLACKSTONE_PRESSURE_PLATE": 2, + "POLISHED_BLACKSTONE_SLAB": 3, + "POLISHED_BLACKSTONE_STAIRS": 8, + "POLISHED_BLACKSTONE_WALL": 162, + "POLISHED_DEEPSLATE": 1, + "POLISHED_DEEPSLATE_SLAB": 3, + "POLISHED_DEEPSLATE_STAIRS": 8, + "POLISHED_DEEPSLATE_WALL": 162, + "POLISHED_DIORITE": 1, + "POLISHED_DIORITE_SLAB": 3, + "POLISHED_DIORITE_STAIRS": 8, + "POLISHED_GRANITE": 1, + "POLISHED_GRANITE_SLAB": 3, + "POLISHED_GRANITE_STAIRS": 8, + "POLISHED_TUFF": 1, + "POLISHED_TUFF_SLAB": 3, + "POLISHED_TUFF_STAIRS": 8, + "POLISHED_TUFF_WALL": 162, + "POPPY": 1, + "POTATOES": 8, + "POTION_CAULDRON": 6, + "POWERED_RAIL": 12, + "PRISMARINE": 1, + "PRISMARINE_BRICKS": 1, + "PRISMARINE_BRICKS_SLAB": 3, + "PRISMARINE_BRICKS_STAIRS": 8, + "PRISMARINE_SLAB": 3, + "PRISMARINE_STAIRS": 8, + "PRISMARINE_WALL": 162, + "PUMPKIN": 1, + "PUMPKIN_STEM": 40, + "PURPLE_TORCH": 5, + "PURPUR": 1, + "PURPUR_PILLAR": 3, + "PURPUR_SLAB": 3, + "PURPUR_STAIRS": 8, + "QUARTZ": 1, + "QUARTZ_BRICKS": 1, + "QUARTZ_PILLAR": 3, + "QUARTZ_SLAB": 3, + "QUARTZ_STAIRS": 8, + "RAIL": 10, + "RAW_COPPER": 1, + "RAW_GOLD": 1, + "RAW_IRON": 1, + "REDSTONE": 1, + "REDSTONE_COMPARATOR": 16, + "REDSTONE_LAMP": 2, + "REDSTONE_ORE": 2, + "REDSTONE_REPEATER": 32, + "REDSTONE_TORCH": 10, + "REDSTONE_WIRE": 16, + "RED_MUSHROOM": 1, + "RED_MUSHROOM_BLOCK": 11, + "RED_NETHER_BRICKS": 1, + "RED_NETHER_BRICK_SLAB": 3, + "RED_NETHER_BRICK_STAIRS": 8, + "RED_NETHER_BRICK_WALL": 162, + "RED_SAND": 1, + "RED_SANDSTONE": 1, + "RED_SANDSTONE_SLAB": 3, + "RED_SANDSTONE_STAIRS": 8, + "RED_SANDSTONE_WALL": 162, + "RED_TORCH": 5, + "RED_TULIP": 1, + "REINFORCED_DEEPSLATE": 1, + "RESERVED6": 1, + "ROSE_BUSH": 2, + "SAND": 1, + "SANDSTONE": 1, + "SANDSTONE_SLAB": 3, + "SANDSTONE_STAIRS": 8, + "SANDSTONE_WALL": 162, + "SCULK": 1, + "SEA_LANTERN": 1, + "SEA_PICKLE": 8, + "SHROOMLIGHT": 1, + "SHULKER_BOX": 1, + "SLIME": 1, + "SMALL_DRIPLEAF": 8, + "SMITHING_TABLE": 1, + "SMOKER": 8, + "SMOOTH_BASALT": 1, + "SMOOTH_QUARTZ": 1, + "SMOOTH_QUARTZ_SLAB": 3, + "SMOOTH_QUARTZ_STAIRS": 8, + "SMOOTH_RED_SANDSTONE": 1, + "SMOOTH_RED_SANDSTONE_SLAB": 3, + "SMOOTH_RED_SANDSTONE_STAIRS": 8, + "SMOOTH_SANDSTONE": 1, + "SMOOTH_SANDSTONE_SLAB": 3, + "SMOOTH_SANDSTONE_STAIRS": 8, + "SMOOTH_STONE": 1, + "SMOOTH_STONE_SLAB": 3, + "SNOW": 1, + "SNOW_LAYER": 8, + "SOUL_CAMPFIRE": 8, + "SOUL_FIRE": 1, + "SOUL_LANTERN": 2, + "SOUL_SAND": 1, + "SOUL_SOIL": 1, + "SOUL_TORCH": 5, + "SPONGE": 2, + "SPORE_BLOSSOM": 1, + "SPRUCE_BUTTON": 12, + "SPRUCE_DOOR": 32, + "SPRUCE_FENCE": 1, + "SPRUCE_FENCE_GATE": 16, + "SPRUCE_LEAVES": 4, + "SPRUCE_LOG": 6, + "SPRUCE_PLANKS": 1, + "SPRUCE_PRESSURE_PLATE": 2, + "SPRUCE_SAPLING": 2, + "SPRUCE_SIGN": 16, + "SPRUCE_SLAB": 3, + "SPRUCE_STAIRS": 8, + "SPRUCE_TRAPDOOR": 16, + "SPRUCE_WALL_SIGN": 4, + "SPRUCE_WOOD": 6, + "STAINED_CLAY": 16, + "STAINED_GLASS": 16, + "STAINED_GLASS_PANE": 16, + "STAINED_HARDENED_GLASS": 16, + "STAINED_HARDENED_GLASS_PANE": 16, + "STONE": 1, + "STONECUTTER": 4, + "STONE_BRICKS": 1, + "STONE_BRICK_SLAB": 3, + "STONE_BRICK_STAIRS": 8, + "STONE_BRICK_WALL": 162, + "STONE_BUTTON": 12, + "STONE_PRESSURE_PLATE": 2, + "STONE_SLAB": 3, + "STONE_STAIRS": 8, + "SUGARCANE": 16, + "SUNFLOWER": 2, + "SWEET_BERRY_BUSH": 4, + "TALL_GRASS": 1, + "TINTED_GLASS": 1, + "TNT": 4, + "TORCH": 5, + "TORCHFLOWER": 1, + "TORCHFLOWER_CROP": 2, + "TRAPPED_CHEST": 4, + "TRIPWIRE": 16, + "TRIPWIRE_HOOK": 16, + "TUFF": 1, + "TUFF_BRICKS": 1, + "TUFF_BRICK_SLAB": 3, + "TUFF_BRICK_STAIRS": 8, + "TUFF_BRICK_WALL": 162, + "TUFF_SLAB": 3, + "TUFF_STAIRS": 8, + "TUFF_WALL": 162, + "TWISTING_VINES": 26, + "UNDERWATER_TORCH": 5, + "VINES": 16, + "WALL_BANNER": 64, + "WALL_CORAL_FAN": 40, + "WARPED_BUTTON": 12, + "WARPED_DOOR": 32, + "WARPED_FENCE": 1, + "WARPED_FENCE_GATE": 16, + "WARPED_HYPHAE": 6, + "WARPED_PLANKS": 1, + "WARPED_PRESSURE_PLATE": 2, + "WARPED_ROOTS": 1, + "WARPED_SIGN": 16, + "WARPED_SLAB": 3, + "WARPED_STAIRS": 8, + "WARPED_STEM": 6, + "WARPED_TRAPDOOR": 16, + "WARPED_WALL_SIGN": 4, + "WARPED_WART_BLOCK": 1, + "WATER": 32, + "WATER_CAULDRON": 6, + "WEEPING_VINES": 26, + "WEIGHTED_PRESSURE_PLATE_HEAVY": 16, + "WEIGHTED_PRESSURE_PLATE_LIGHT": 16, + "WHEAT": 8, + "WHITE_TULIP": 1, + "WITHER_ROSE": 1, + "WOOL": 16 + }, + "tiles": { + "ACACIA_SIGN": "pocketmine\\block\\tile\\Sign", + "ACACIA_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "BANNER": "pocketmine\\block\\tile\\Banner", + "BARREL": "pocketmine\\block\\tile\\Barrel", + "BEACON": "pocketmine\\block\\tile\\Beacon", + "BED": "pocketmine\\block\\tile\\Bed", + "BELL": "pocketmine\\block\\tile\\Bell", + "BIRCH_SIGN": "pocketmine\\block\\tile\\Sign", + "BIRCH_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "BLAST_FURNACE": "pocketmine\\block\\tile\\BlastFurnace", + "BREWING_STAND": "pocketmine\\block\\tile\\BrewingStand", + "CAMPFIRE": "pocketmine\\block\\tile\\Campfire", + "CAULDRON": "pocketmine\\block\\tile\\Cauldron", + "CHERRY_SIGN": "pocketmine\\block\\tile\\Sign", + "CHERRY_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "CHEST": "pocketmine\\block\\tile\\Chest", + "CHISELED_BOOKSHELF": "pocketmine\\block\\tile\\ChiseledBookshelf", + "CRIMSON_SIGN": "pocketmine\\block\\tile\\Sign", + "CRIMSON_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "DAYLIGHT_SENSOR": "pocketmine\\block\\tile\\DaylightSensor", + "DYED_SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", + "ENCHANTING_TABLE": "pocketmine\\block\\tile\\EnchantTable", + "ENDER_CHEST": "pocketmine\\block\\tile\\EnderChest", + "FLOWER_POT": "pocketmine\\block\\tile\\FlowerPot", + "FURNACE": "pocketmine\\block\\tile\\NormalFurnace", + "GLOWING_ITEM_FRAME": "pocketmine\\block\\tile\\GlowingItemFrame", + "HOPPER": "pocketmine\\block\\tile\\Hopper", + "ITEM_FRAME": "pocketmine\\block\\tile\\ItemFrame", + "JUKEBOX": "pocketmine\\block\\tile\\Jukebox", + "JUNGLE_SIGN": "pocketmine\\block\\tile\\Sign", + "JUNGLE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "LAVA_CAULDRON": "pocketmine\\block\\tile\\Cauldron", + "LECTERN": "pocketmine\\block\\tile\\Lectern", + "MANGROVE_SIGN": "pocketmine\\block\\tile\\Sign", + "MANGROVE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "MOB_HEAD": "pocketmine\\block\\tile\\MobHead", + "MONSTER_SPAWNER": "pocketmine\\block\\tile\\MonsterSpawner", + "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", + "OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", + "REDSTONE_COMPARATOR": "pocketmine\\block\\tile\\Comparator", + "SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", + "SMOKER": "pocketmine\\block\\tile\\Smoker", + "SOUL_CAMPFIRE": "pocketmine\\block\\tile\\Campfire", + "SPRUCE_SIGN": "pocketmine\\block\\tile\\Sign", + "SPRUCE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "TRAPPED_CHEST": "pocketmine\\block\\tile\\Chest", + "WALL_BANNER": "pocketmine\\block\\tile\\Banner", + "WARPED_SIGN": "pocketmine\\block\\tile\\Sign", + "WARPED_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "WATER_CAULDRON": "pocketmine\\block\\tile\\Cauldron" + } } \ No newline at end of file diff --git a/tests/phpunit/block/regenerate_consistency_check.php b/tests/phpunit/block/regenerate_consistency_check.php index e86f70d70..eb4ccf6c8 100644 --- a/tests/phpunit/block/regenerate_consistency_check.php +++ b/tests/phpunit/block/regenerate_consistency_check.php @@ -28,11 +28,11 @@ require dirname(__DIR__, 3) . '/vendor/autoload.php'; /* This script needs to be re-run after any intentional blockfactory change (adding or removing a block state). */ -$newTable = BlockTest::computeConsistencyCheckTable(RuntimeBlockStateRegistry::getInstance()); +[$newTable, $newTiles] = BlockTest::computeConsistencyCheckTable(RuntimeBlockStateRegistry::getInstance()); $oldTablePath = __DIR__ . '/block_factory_consistency_check.json'; if(file_exists($oldTablePath)){ - $errors = BlockTest::computeConsistencyCheckDiff($oldTablePath, $newTable); + $errors = BlockTest::computeConsistencyCheckDiff($oldTablePath, $newTable, $newTiles); if(count($errors) > 0){ echo count($errors) . " changes detected:\n"; @@ -47,5 +47,6 @@ if(file_exists($oldTablePath)){ } ksort($newTable, SORT_STRING); +ksort($newTiles, SORT_STRING); -file_put_contents($oldTablePath, json_encode($newTable, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); +file_put_contents($oldTablePath, json_encode(["stateCounts" => $newTable, "tiles" => $newTiles], JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); From 6b2da15b80f1e70130aef471fdd82ff3ba259303 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2024 19:58:52 +0000 Subject: [PATCH 141/290] Fixed signs --- src/block/VanillaBlocks.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 54cf90a0c..9d7ed91f4 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -54,6 +54,7 @@ use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner; use pocketmine\block\tile\NormalFurnace as TileNormalFurnace; use pocketmine\block\tile\Note as TileNote; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; +use pocketmine\block\tile\Sign as TileSign; use pocketmine\block\tile\Smoker as TileSmoker; use pocketmine\block\tile\Tile; use pocketmine\block\utils\AmethystTrait; @@ -1359,8 +1360,8 @@ final class VanillaBlocks{ WoodType::WARPED => VanillaItems::WARPED_SIGN(...), WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...), }; - self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem)); - self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem)); + self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); + self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); } } From 2b0daebc2a72784d6ea1744d6b0b91e7ab66baba Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 5 Dec 2024 20:04:43 +0000 Subject: [PATCH 142/290] 5.23.1 (#6562) --- changelogs/5.23.md | 9 +++++++++ src/VersionInfo.php | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelogs/5.23.md b/changelogs/5.23.md index f5b08593d..7f40b40af 100644 --- a/changelogs/5.23.md +++ b/changelogs/5.23.md @@ -105,3 +105,12 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - Event handler tests have been converted to PHPUnit tests by mocking `Server` and `Plugin` instances. Previously, these required integration tests for these dependencies. - Fixed various deprecation warnings in PHP 8.4. - `netresearch/jsonmapper` is now used at `5.0.0`. The PMMP fork of this library has been removed, as it is no longer needed. + +# 5.23.1 +Released 5th December 2024. + +## Fixes +- Fixed signs not creating a tile when placed. + +## Internals +- Improved blockstate consistency check to detect tiles disappearing during refactors. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index d983060f4..59c6919bb 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.23.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 8efdf501adcacdf34046682285e45eb1c1e7e770 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 20:05:36 +0000 Subject: [PATCH 143/290] 5.23.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12187209543 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 59c6919bb..1eca900cf 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.23.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.23.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 1481977f35b69b5b77551107584dc6503e1e5286 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 5 Dec 2024 20:47:46 +0000 Subject: [PATCH 144/290] Create pr-stale.yml --- .github/workflows/pr-stale.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/pr-stale.yml diff --git a/.github/workflows/pr-stale.yml b/.github/workflows/pr-stale.yml new file mode 100644 index 000000000..23518b2cf --- /dev/null +++ b/.github/workflows/pr-stale.yml @@ -0,0 +1,29 @@ +name: 'Clean up stale PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + days-before-issue-stale: -1 + days-before-issue-close: -1 + stale-pr-message: | + This PR has been marked as "Waiting on Author", but we haven't seen any activity in 7 days. + + If there is no further activity, it will be closed in 28 days. + + Note for maintainers: Adding an assignee to the PR will prevent it from being marked as stale. + + close-pr-message: | + As this PR hasn't been updated for a while, unfortunately we'll have to close it. + + days-before-pr-stale: 7 + days-before-pr-close: 28 + only-labels: "Status: Waiting on Author" + close-pr-label: "Resolution: Abandoned" + exempt-all-assignees: true + From 45917d495c14f3ce37bbf0848262ad8a470030f3 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 8 Dec 2024 16:52:33 +0000 Subject: [PATCH 145/290] Fixed CrashDump incorrectly detecting phar core crashes as plugin crashes (#6564) fixes #6563 Since #6217 was merged, \pocketmine\PATH no longer includes the path of the original phar. This means that the frame originating from the phar stub would not get its path cleaned up, leading to it being incorrectly detected as a plugin frame. We should probably explore better methods of detecting plugin crashes in the future; however this fix should solve the immediate issue. --- build/server-phar-stub.php | 2 ++ src/PocketMine.php | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/build/server-phar-stub.php b/build/server-phar-stub.php index b4018e3a7..c713636c4 100644 --- a/build/server-phar-stub.php +++ b/build/server-phar-stub.php @@ -25,6 +25,7 @@ namespace pocketmine\server_phar_stub; use function clearstatcache; use function copy; +use function define; use function fclose; use function fflush; use function flock; @@ -165,4 +166,5 @@ $start = hrtime(true); $cacheName = preparePharCache($tmpDir, __FILE__); echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n"; +define('pocketmine\ORIGINAL_PHAR_PATH', __FILE__); require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php'; diff --git a/src/PocketMine.php b/src/PocketMine.php index b2e1cd046..ffcfd91db 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -282,6 +282,11 @@ JIT_WARNING exit(0); } + if(defined('pocketmine\ORIGINAL_PHAR_PATH')){ + //if we're inside a phar cache, \pocketmine\PATH will not include the original phar + Filesystem::addCleanedPath(ORIGINAL_PHAR_PATH, Filesystem::CLEAN_PATH_SRC_PREFIX); + } + $cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd()))); $dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd; $pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins"; From fe7c282052af55f4ed23d9e6629e1e7c0a501121 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:03:09 +0000 Subject: [PATCH 146/290] Bump pocketmine/locale-data in the production-patch-updates group (#6568) --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 1cf141588..54f65014f 100644 --- a/composer.lock +++ b/composer.lock @@ -471,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.22.0", + "version": "2.22.1", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d" + "reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", - "reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d", + "url": "https://api.github.com/repos/pmmp/Language/zipball/fa4e377c437391cfcfdedd08eea3a848eabd1b49", + "reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49", "shasum": "" }, "type": "library", @@ -488,9 +488,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.22.0" + "source": "https://github.com/pmmp/Language/tree/2.22.1" }, - "time": "2024-11-16T13:28:01+00:00" + "time": "2024-12-06T14:44:17+00:00" }, { "name": "pocketmine/log", From a8eaa43bc8beaf273ce806ff314d1eecf1545fef Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 9 Dec 2024 16:36:26 +0000 Subject: [PATCH 147/290] Recombine release workflows having two different workflows able to trigger releases is a pain for build number continuity. perhaps longer term we should source the build number a different way, but these workflows needed restructuring anyway. --- .github/workflows/draft-release-from-pr.yml | 65 -------------- .github/workflows/draft-release-from-tag.yml | 13 --- .github/workflows/draft-release.yml | 94 +++++++++++++++----- 3 files changed, 70 insertions(+), 102 deletions(-) delete mode 100644 .github/workflows/draft-release-from-pr.yml delete mode 100644 .github/workflows/draft-release-from-tag.yml diff --git a/.github/workflows/draft-release-from-pr.yml b/.github/workflows/draft-release-from-pr.yml deleted file mode 100644 index 8a347853b..000000000 --- a/.github/workflows/draft-release-from-pr.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Draft release from PR - -on: - #presume that pull_request_target is safe at this point, since the PR was approved and merged - #we need write access to prepare the release & create comments - pull_request_target: - types: - - closed - branches: - - stable - - minor-next - - major-next - - "legacy/*" - paths: - - "src/VersionInfo.php" - -jobs: - check: - name: Check release - uses: ./.github/workflows/draft-release-pr-check.yml - - draft: - name: Create GitHub draft release - needs: [check] - if: needs.check.outputs.valid == 'true' - - uses: ./.github/workflows/draft-release.yml - - post-draft-url-comment: - name: Post draft release URL as comment - needs: [draft] - - runs-on: ubuntu-20.04 - - steps: - - name: Post draft release URL on PR - uses: thollander/actions-comment-pull-request@v3 - with: - message: "[Draft release ${{ needs.draft.outputs.version }}](${{ needs.draft.outputs.draft-url }}) has been created for commit ${{ github.sha }}. Please review and publish it." - - trigger-post-release-workflow: - name: Trigger post-release RestrictedActions workflow - # Not sure if needs is actually needed here - needs: [check] - if: needs.check.outputs.valid == 'true' - - runs-on: ubuntu-20.04 - - steps: - - name: Generate access token - id: generate-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} - private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} - owner: ${{ github.repository_owner }} - repositories: RestrictedActions - - - name: Dispatch post-release restricted action - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ steps.generate-token.outputs.token }} - repository: ${{ github.repository_owner }}/RestrictedActions - event-type: pocketmine_mp_post_release - client-payload: '{"branch": "${{ github.ref }}"}' diff --git a/.github/workflows/draft-release-from-tag.yml b/.github/workflows/draft-release-from-tag.yml deleted file mode 100644 index f7a5df544..000000000 --- a/.github/workflows/draft-release-from-tag.yml +++ /dev/null @@ -1,13 +0,0 @@ -#Allows creating a release by pushing a tag -#This might be useful for retroactive releases -name: Draft release from git tag - -on: - push: - tags: "*" - -jobs: - draft: - name: Create GitHub draft release - if: "startsWith(github.event.head_commit.message, 'Release ')" - uses: ./.github/workflows/draft-release.yml diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index cd1841e4f..3374ff68f 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -1,28 +1,61 @@ name: Draft release on: - workflow_call: - outputs: - draft-url: - description: 'The URL of the draft release' - value: ${{ jobs.draft.outputs.draft-url }} - version: - description: 'PocketMine-MP version' - value: ${{ jobs.draft.outputs.version }} + #presume that pull_request_target is safe at this point, since the PR was approved and merged + #we need write access to prepare the release & create comments + pull_request_target: + types: + - closed + branches: + - stable + - minor-next + - major-next + - "legacy/*" + paths: + - "src/VersionInfo.php" + push: + tags: + - "*" + +env: + PHP_VERSION: "8.2" jobs: - draft: - name: Create GitHub draft release + check: + name: Check release + uses: ./.github/workflows/draft-release-pr-check.yml + + trigger-post-release-workflow: + name: Trigger post-release RestrictedActions workflow + needs: [check] + if: needs.check.outputs.valid == 'true' && github.ref_type != 'tag' #can't do post-commit for a tag runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - php-version: [8.2] - outputs: - draft-url: ${{ steps.create-draft.outputs.html_url }} - version: ${{ steps.get-pm-version.outputs.PM_VERSION }} + steps: + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.RESTRICTED_ACTIONS_DISPATCH_ID }} + private-key: ${{ secrets.RESTRICTED_ACTIONS_DISPATCH_KEY }} + owner: ${{ github.repository_owner }} + repositories: RestrictedActions + + - name: Dispatch post-release restricted action + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ steps.generate-token.outputs.token }} + repository: ${{ github.repository_owner }}/RestrictedActions + event-type: pocketmine_mp_post_release + client-payload: '{"branch": "${{ github.ref }}"}' + + draft: + name: Create GitHub draft release + needs: [check] + if: needs.check.outputs.valid == 'true' || github.ref_type == 'tag' #ignore validity check for tags + + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 @@ -32,7 +65,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@2.31.1 with: - php-version: ${{ matrix.php-version }} + php-version: ${{ env.PHP_VERSION }} - name: Restore Composer package cache uses: actions/cache@v4 @@ -50,7 +83,7 @@ jobs: - name: Calculate build number id: build-number run: | - BUILD_NUMBER=$((2000+$GITHUB_RUN_NUMBER)) #to stay above jenkins + BUILD_NUMBER=$((2300+$GITHUB_RUN_NUMBER)) #to stay above jenkins echo "Build number: $BUILD_NUMBER" echo BUILD_NUMBER=$BUILD_NUMBER >> $GITHUB_OUTPUT @@ -63,23 +96,31 @@ jobs: - name: Get PocketMine-MP release version id: get-pm-version run: | - echo PM_VERSION=$(php build/dump-version-info.php base_version) >> $GITHUB_OUTPUT + PM_VERSION=$(php build/dump-version-info.php base_version) + echo PM_VERSION=$PM_VERSION >> $GITHUB_OUTPUT echo PM_MAJOR=$(php build/dump-version-info.php major_version) >> $GITHUB_OUTPUT echo MCPE_VERSION=$(php build/dump-version-info.php mcpe_version) >> $GITHUB_OUTPUT echo CHANGELOG_FILE_NAME=$(php build/dump-version-info.php changelog_file_name) >> $GITHUB_OUTPUT echo CHANGELOG_MD_HEADER=$(php build/dump-version-info.php changelog_md_header) >> $GITHUB_OUTPUT echo PRERELEASE=$(php build/dump-version-info.php prerelease) >> $GITHUB_OUTPUT + if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)" + else + tag="$PM_VERSION" + fi + echo TAG_NAME=$tag >> $GITHUB_OUTPUT + - name: Generate PHP binary download URL id: php-binary-url run: | - echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ matrix.php-version }}-latest" >> $GITHUB_OUTPUT + echo PHP_BINARY_URL="${{ github.server_url }}/${{ github.repository_owner }}/PHP-Binaries/releases/tag/pm${{ steps.get-pm-version.outputs.PM_MAJOR }}-php-${{ env.PHP_VERSION }}-latest" >> $GITHUB_OUTPUT - name: Generate build info run: | php build/generate-build-info-json.php \ ${{ github.sha }} \ - ${{ steps.get-pm-version.outputs.PM_VERSION }} \ + ${{ steps.get-pm-version.outputs.TAG_NAME }} \ ${{ github.repository }} \ ${{ steps.build-number.outputs.BUILD_NUMBER }} \ ${{ github.run_id }} \ @@ -108,12 +149,17 @@ jobs: draft: true prerelease: ${{ steps.get-pm-version.outputs.PRERELEASE }} name: PocketMine-MP ${{ steps.get-pm-version.outputs.PM_VERSION }} - tag: ${{ steps.get-pm-version.outputs.PM_VERSION }} + tag: ${{ steps.get-pm-version.outputs.TAG_NAME }} token: ${{ secrets.GITHUB_TOKEN }} - skipIfReleaseExists: true #for release PRs, tags will be created on release publish and trigger the tag release workflow - don't create a second draft body: | **For Minecraft: Bedrock Edition ${{ steps.get-pm-version.outputs.MCPE_VERSION }}** Please see the [changelogs](${{ github.server_url }}/${{ github.repository }}/blob/${{ steps.get-pm-version.outputs.PM_VERSION }}/changelogs/${{ steps.get-pm-version.outputs.CHANGELOG_FILE_NAME }}#${{ steps.get-pm-version.outputs.CHANGELOG_MD_HEADER }}) for details. :information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}). + + - name: Post draft release URL on PR + if: github.event_name == 'pull_request_target' + uses: thollander/actions-comment-pull-request@v3 + with: + message: "[Draft release ${{ steps.get-pm-version.outputs.PM_VERSION }}](${{ steps.create-draft.outputs.html_url }}) has been created for commit ${{ github.sha }}. Please review and publish it." From ad6d34f1a61b8392e5541c94e3a35413c68aeb2e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 9 Dec 2024 16:44:07 +0000 Subject: [PATCH 148/290] Remove legacy make-release script we no longer use this release workflow, all releases should now be done via pull request --- build/make-release.php | 174 --------------------- tests/phpstan/configs/actual-problems.neon | 5 - 2 files changed, 179 deletions(-) delete mode 100644 build/make-release.php diff --git a/build/make-release.php b/build/make-release.php deleted file mode 100644 index 741f9d787..000000000 --- a/build/make-release.php +++ /dev/null @@ -1,174 +0,0 @@ - "Version to insert and tag", - "next" => "Version to put in the file after tagging", - "channel" => "Release channel to post this build into" -]; - -function systemWrapper(string $command, string $errorMessage) : void{ - system($command, $result); - if($result !== 0){ - echo "error: $errorMessage; aborting\n"; - exit(1); - } -} - -function main() : void{ - $filteredOpts = []; - $postCommitOnly = false; - foreach(Utils::stringifyKeys(getopt("", ["current:", "next:", "channel:", "help", "post"])) as $optName => $optValue){ - if($optName === "help"){ - fwrite(STDOUT, "Options:\n"); - - $maxLength = max(array_map(fn(string $str) => strlen($str), array_keys(ACCEPTED_OPTS))); - foreach(ACCEPTED_OPTS as $acceptedName => $description){ - fwrite(STDOUT, str_pad("--$acceptedName", $maxLength + 4, " ", STR_PAD_LEFT) . ": $description\n"); - } - exit(0); - } - if($optName === "post"){ - $postCommitOnly = true; - continue; - } - if(!is_string($optValue)){ - fwrite(STDERR, "--$optName expects exactly 1 value\n"); - exit(1); - } - $filteredOpts[$optName] = $optValue; - } - - $channel = $filteredOpts["channel"] ?? null; - if(isset($filteredOpts["current"])){ - $currentVer = new VersionString($filteredOpts["current"]); - }else{ - $currentVer = new VersionString(VersionInfo::BASE_VERSION); - } - - $nextVer = isset($filteredOpts["next"]) ? new VersionString($filteredOpts["next"]) : null; - - $suffix = $currentVer->getSuffix(); - if($suffix !== ""){ - if($channel === "stable"){ - fwrite(STDERR, "error: cannot release a suffixed build into the stable channel\n"); - exit(1); - } - if(preg_match('/^([A-Za-z]+)(\d+)$/', $suffix, $matches) !== 1){ - echo "error: invalid current version suffix \"$suffix\"; aborting\n"; - exit(1); - } - $nextVer ??= new VersionString(sprintf( - "%u.%u.%u-%s%u", - $currentVer->getMajor(), - $currentVer->getMinor(), - $currentVer->getPatch(), - $matches[1], - ((int) $matches[2]) + 1 - )); - $channel ??= strtolower($matches[1]); - }else{ - $nextVer ??= new VersionString(sprintf( - "%u.%u.%u", - $currentVer->getMajor(), - $currentVer->getMinor(), - $currentVer->getPatch() + 1 - )); - $channel ??= "stable"; - } - - $versionInfoPath = dirname(__DIR__) . '/src/VersionInfo.php'; - - if($postCommitOnly){ - echo "Skipping release commit & tag. Bumping to next version $nextVer directly.\n"; - }else{ - echo "About to tag version $currentVer. Next version will be $nextVer.\n"; - echo "$currentVer will be published on release channel \"$channel\".\n"; - echo "please add appropriate notes to the changelog and press enter..."; - fgets(STDIN); - systemWrapper('git add "' . dirname(__DIR__) . '/changelogs"', "failed to stage changelog changes"); - system('git diff --cached --quiet "' . dirname(__DIR__) . '/changelogs"', $result); - if($result === 0){ - echo "error: no changelog changes detected; aborting\n"; - exit(1); - } - replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false, $channel); - systemWrapper('git commit -m "Release ' . $currentVer->getBaseVersion() . '" --include "' . $versionInfoPath . '"', "failed to create release commit"); - systemWrapper('git tag ' . $currentVer->getBaseVersion(), "failed to create release tag"); - } - - replaceVersion($versionInfoPath, $nextVer->getBaseVersion(), true, $channel); - systemWrapper('git add "' . $versionInfoPath . '"', "failed to stage changes for post-release commit"); - systemWrapper('git commit -m "' . $nextVer->getBaseVersion() . ' is next" --include "' . $versionInfoPath . '"', "failed to create post-release commit"); -} - -main(); diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index f15dc9d53..b071d2a7e 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 2 - path: ../../../build/make-release.php - - message: "#^Parameter \\#1 \\$strings of function pocketmine\\\\build\\\\server_phar\\\\preg_quote_array expects array\\, array\\ given\\.$#" count: 1 From bba525da02e9c0d8878927578ae4a25ec9984934 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 9 Dec 2024 16:44:25 +0000 Subject: [PATCH 149/290] Remove dead PHPStan ignored errors --- tests/phpstan/configs/actual-problems.neon | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index b071d2a7e..1bb9329f0 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -455,26 +455,6 @@ parameters: count: 1 path: ../../../src/command/defaults/TimeCommand.php - - - message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#" - count: 2 - path: ../../../src/command/defaults/TimingsCommand.php - - - - message: "#^Parameter \\#1 \\$stream of function fseek expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/command/defaults/TimingsCommand.php - - - - message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/command/defaults/TimingsCommand.php - - - - message: "#^Parameter \\#1 \\$stream of function stream_get_contents expects resource, resource\\|false given\\.$#" - count: 1 - path: ../../../src/command/defaults/TimingsCommand.php - - message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#" count: 1 From 6f197bc1bb90c248a4361669a02f11f3ee4fa86f Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 9 Dec 2024 16:51:41 +0000 Subject: [PATCH 150/290] 5.23.2 (#6569) --- changelogs/5.23.md | 13 +++++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/changelogs/5.23.md b/changelogs/5.23.md index 7f40b40af..6e72e9403 100644 --- a/changelogs/5.23.md +++ b/changelogs/5.23.md @@ -114,3 +114,16 @@ Released 5th December 2024. ## Internals - Improved blockstate consistency check to detect tiles disappearing during refactors. + +# 5.23.2 +Released 9th December 2024. + +## General +- Updated translations for Russian and Korean. + +## Fixes +- Fixed server build number. +- Fixed some crashes being misreported as plugin-involved. + +## Internals +- Removed legacy `build/make-release.php` script. This script is no longer used, as all releases should now follow the PR workflow. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 1eca900cf..ba46bd63b 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.23.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 67b9d6222d17e77c8000a52bdfe61de7e51ebd5c Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:52:50 +0000 Subject: [PATCH 151/290] 5.23.3 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12240364052 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index ba46bd63b..48ce7dc9e 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.23.2"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.23.3"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 68172156833e2b969dd7917c438902f2f8bc50f0 Mon Sep 17 00:00:00 2001 From: Maxence <40174895+roimee6@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:40:03 +0100 Subject: [PATCH 152/290] TextFormat: Added new material colours for armor trims (#5838) Unfortunately, these new formatting codes conflict with the Java strikethrough and underline, so we can't support these anymore. A TextFormat::javaToBedrock() is provided to strip these codes, or (if these formats become supported via different codes) to convert them to Bedrock variants. Co-authored-by: Dylan T. --- src/utils/Terminal.php | 60 ++++++++++++++++++++----- src/utils/TextFormat.php | 96 +++++++++++++++++++++++++++++++++------- 2 files changed, 130 insertions(+), 26 deletions(-) diff --git a/src/utils/Terminal.php b/src/utils/Terminal.php index 49b4224ec..2abdbc357 100644 --- a/src/utils/Terminal.php +++ b/src/utils/Terminal.php @@ -59,6 +59,16 @@ abstract class Terminal{ public static string $COLOR_YELLOW = ""; public static string $COLOR_WHITE = ""; public static string $COLOR_MINECOIN_GOLD = ""; + public static string $COLOR_MATERIAL_QUARTZ = ""; + public static string $COLOR_MATERIAL_IRON = ""; + public static string $COLOR_MATERIAL_NETHERITE = ""; + public static string $COLOR_MATERIAL_REDSTONE = ""; + public static string $COLOR_MATERIAL_COPPER = ""; + public static string $COLOR_MATERIAL_GOLD = ""; + public static string $COLOR_MATERIAL_EMERALD = ""; + public static string $COLOR_MATERIAL_DIAMOND = ""; + public static string $COLOR_MATERIAL_LAPIS = ""; + public static string $COLOR_MATERIAL_AMETHYST = ""; private static ?bool $formattingCodes = null; @@ -111,6 +121,16 @@ abstract class Terminal{ self::$COLOR_YELLOW = $color(227); self::$COLOR_WHITE = $color(231); self::$COLOR_MINECOIN_GOLD = $color(184); + self::$COLOR_MATERIAL_QUARTZ = $color(188); + self::$COLOR_MATERIAL_IRON = $color(251); + self::$COLOR_MATERIAL_NETHERITE = $color(237); + self::$COLOR_MATERIAL_REDSTONE = $color(88); + self::$COLOR_MATERIAL_COPPER = $color(131); + self::$COLOR_MATERIAL_GOLD = $color(178); + self::$COLOR_MATERIAL_EMERALD = $color(35); + self::$COLOR_MATERIAL_DIAMOND = $color(37); + self::$COLOR_MATERIAL_LAPIS = $color(24); + self::$COLOR_MATERIAL_AMETHYST = $color(98); } protected static function getEscapeCodes() : void{ @@ -144,15 +164,25 @@ abstract class Terminal{ self::$COLOR_YELLOW = $colors >= 256 ? $setaf(227) : $setaf(11); self::$COLOR_WHITE = $colors >= 256 ? $setaf(231) : $setaf(15); self::$COLOR_MINECOIN_GOLD = $colors >= 256 ? $setaf(184) : $setaf(11); + self::$COLOR_MATERIAL_QUARTZ = $colors >= 256 ? $setaf(188) : $setaf(7); + self::$COLOR_MATERIAL_IRON = $colors >= 256 ? $setaf(251) : $setaf(7); + self::$COLOR_MATERIAL_NETHERITE = $colors >= 256 ? $setaf(237) : $setaf(1); + self::$COLOR_MATERIAL_REDSTONE = $colors >= 256 ? $setaf(88) : $setaf(9); + self::$COLOR_MATERIAL_COPPER = $colors >= 256 ? $setaf(131) : $setaf(3); + self::$COLOR_MATERIAL_GOLD = $colors >= 256 ? $setaf(178) : $setaf(11); + self::$COLOR_MATERIAL_EMERALD = $colors >= 256 ? $setaf(35) : $setaf(2); + self::$COLOR_MATERIAL_DIAMOND = $colors >= 256 ? $setaf(37) : $setaf(14); + self::$COLOR_MATERIAL_LAPIS = $colors >= 256 ? $setaf(24) : $setaf(12); + self::$COLOR_MATERIAL_AMETHYST = $colors >= 256 ? $setaf(98) : $setaf(13); }else{ - self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = $setaf(0); - self::$COLOR_RED = self::$COLOR_DARK_RED = $setaf(1); - self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = $setaf(2); - self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = $setaf(3); - self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = $setaf(4); - self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = $setaf(5); - self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = $setaf(6); - self::$COLOR_GRAY = self::$COLOR_WHITE = $setaf(7); + self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = self::$COLOR_MATERIAL_NETHERITE = $setaf(0); + self::$COLOR_RED = self::$COLOR_DARK_RED = self::$COLOR_MATERIAL_REDSTONE = self::$COLOR_MATERIAL_COPPER = $setaf(1); + self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = self::$COLOR_MATERIAL_EMERALD = $setaf(2); + self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = self::$COLOR_MATERIAL_GOLD = $setaf(3); + self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = self::$COLOR_MATERIAL_LAPIS = $setaf(4); + self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = self::$COLOR_MATERIAL_AMETHYST = $setaf(5); + self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = self::$COLOR_MATERIAL_DIAMOND = $setaf(6); + self::$COLOR_GRAY = self::$COLOR_WHITE = self::$COLOR_MATERIAL_QUARTZ = self::$COLOR_MATERIAL_IRON = $setaf(7); } } @@ -191,12 +221,10 @@ abstract class Terminal{ public static function toANSI(string $string) : string{ $newString = ""; foreach(TextFormat::tokenize($string) as $token){ - $newString .= match($token){ + $newString .= match ($token) { TextFormat::BOLD => Terminal::$FORMAT_BOLD, TextFormat::OBFUSCATED => Terminal::$FORMAT_OBFUSCATED, TextFormat::ITALIC => Terminal::$FORMAT_ITALIC, - TextFormat::UNDERLINE => Terminal::$FORMAT_UNDERLINE, - TextFormat::STRIKETHROUGH => Terminal::$FORMAT_STRIKETHROUGH, TextFormat::RESET => Terminal::$FORMAT_RESET, TextFormat::BLACK => Terminal::$COLOR_BLACK, TextFormat::DARK_BLUE => Terminal::$COLOR_DARK_BLUE, @@ -215,6 +243,16 @@ abstract class Terminal{ TextFormat::YELLOW => Terminal::$COLOR_YELLOW, TextFormat::WHITE => Terminal::$COLOR_WHITE, TextFormat::MINECOIN_GOLD => Terminal::$COLOR_MINECOIN_GOLD, + TextFormat::MATERIAL_QUARTZ => Terminal::$COLOR_MATERIAL_QUARTZ, + TextFormat::MATERIAL_IRON => Terminal::$COLOR_MATERIAL_IRON, + TextFormat::MATERIAL_NETHERITE => Terminal::$COLOR_MATERIAL_NETHERITE, + TextFormat::MATERIAL_REDSTONE => Terminal::$COLOR_MATERIAL_REDSTONE, + TextFormat::MATERIAL_COPPER => Terminal::$COLOR_MATERIAL_COPPER, + TextFormat::MATERIAL_GOLD => Terminal::$COLOR_MATERIAL_GOLD, + TextFormat::MATERIAL_EMERALD => Terminal::$COLOR_MATERIAL_EMERALD, + TextFormat::MATERIAL_DIAMOND => Terminal::$COLOR_MATERIAL_DIAMOND, + TextFormat::MATERIAL_LAPIS => Terminal::$COLOR_MATERIAL_LAPIS, + TextFormat::MATERIAL_AMETHYST => Terminal::$COLOR_MATERIAL_AMETHYST, default => $token, }; } diff --git a/src/utils/TextFormat.php b/src/utils/TextFormat.php index dfd6a359a..56aca3f8a 100644 --- a/src/utils/TextFormat.php +++ b/src/utils/TextFormat.php @@ -63,6 +63,16 @@ abstract class TextFormat{ public const YELLOW = TextFormat::ESCAPE . "e"; public const WHITE = TextFormat::ESCAPE . "f"; public const MINECOIN_GOLD = TextFormat::ESCAPE . "g"; + public const MATERIAL_QUARTZ = TextFormat::ESCAPE . "h"; + public const MATERIAL_IRON = TextFormat::ESCAPE . "i"; + public const MATERIAL_NETHERITE = TextFormat::ESCAPE . "j"; + public const MATERIAL_REDSTONE = TextFormat::ESCAPE . "m"; + public const MATERIAL_COPPER = TextFormat::ESCAPE . "n"; + public const MATERIAL_GOLD = TextFormat::ESCAPE . "p"; + public const MATERIAL_EMERALD = TextFormat::ESCAPE . "q"; + public const MATERIAL_DIAMOND = TextFormat::ESCAPE . "s"; + public const MATERIAL_LAPIS = TextFormat::ESCAPE . "t"; + public const MATERIAL_AMETHYST = TextFormat::ESCAPE . "u"; public const COLORS = [ self::BLACK => self::BLACK, @@ -82,19 +92,29 @@ abstract class TextFormat{ self::YELLOW => self::YELLOW, self::WHITE => self::WHITE, self::MINECOIN_GOLD => self::MINECOIN_GOLD, + self::MATERIAL_QUARTZ => self::MATERIAL_QUARTZ, + self::MATERIAL_IRON => self::MATERIAL_IRON, + self::MATERIAL_NETHERITE => self::MATERIAL_NETHERITE, + self::MATERIAL_REDSTONE => self::MATERIAL_REDSTONE, + self::MATERIAL_COPPER => self::MATERIAL_COPPER, + self::MATERIAL_GOLD => self::MATERIAL_GOLD, + self::MATERIAL_EMERALD => self::MATERIAL_EMERALD, + self::MATERIAL_DIAMOND => self::MATERIAL_DIAMOND, + self::MATERIAL_LAPIS => self::MATERIAL_LAPIS, + self::MATERIAL_AMETHYST => self::MATERIAL_AMETHYST, ]; public const OBFUSCATED = TextFormat::ESCAPE . "k"; public const BOLD = TextFormat::ESCAPE . "l"; - public const STRIKETHROUGH = TextFormat::ESCAPE . "m"; - public const UNDERLINE = TextFormat::ESCAPE . "n"; + /** @deprecated */ + public const STRIKETHROUGH = ""; + /** @deprecated */ + public const UNDERLINE = ""; public const ITALIC = TextFormat::ESCAPE . "o"; public const FORMATS = [ self::OBFUSCATED => self::OBFUSCATED, self::BOLD => self::BOLD, - self::STRIKETHROUGH => self::STRIKETHROUGH, - self::UNDERLINE => self::UNDERLINE, self::ITALIC => self::ITALIC, ]; @@ -130,7 +150,7 @@ abstract class TextFormat{ * @return string[] */ public static function tokenize(string $string) : array{ - $result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-gk-or])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-u])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); if($result === false) throw self::makePcreError(); return $result; } @@ -144,7 +164,7 @@ abstract class TextFormat{ $string = mb_scrub($string, 'UTF-8'); $string = self::preg_replace("/[\x{E000}-\x{F8FF}]/u", "", $string); //remove unicode private-use-area characters (they might break the console) if($removeFormat){ - $string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-gk-or]/u", "", $string)); + $string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-u]/u", "", $string)); } return str_replace("\x1b", "", self::preg_replace("/\x1b[\\(\\][[0-9;\\[\\(]+[Bm]/u", "", $string)); } @@ -155,7 +175,7 @@ abstract class TextFormat{ * @param string $placeholder default "&" */ public static function colorize(string $string, string $placeholder = "&") : string{ - return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-gk-or])/u', TextFormat::ESCAPE . '$1', $string); + return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-u])/u', TextFormat::ESCAPE . '$1', $string); } /** @@ -183,6 +203,20 @@ abstract class TextFormat{ return $baseFormat . str_replace(TextFormat::RESET, $baseFormat, $string); } + /** + * Converts any Java formatting codes in the given string to Bedrock. + * + * As of 1.21.50, strikethrough (§m) and underline (§n) are not supported by Bedrock, and these symbols are instead + * used to represent additional colours in Bedrock. To avoid unintended formatting, this function currently strips + * those formatting codes to prevent unintended colour display in formatted text. + * + * If Bedrock starts to support these formats in the future, this function will be updated to translate them rather + * than removing them. + */ + public static function javaToBedrock(string $string) : string{ + return str_replace([TextFormat::ESCAPE . "m", TextFormat::ESCAPE . "n"], "", $string); + } + /** * Returns an HTML-formatted string with colors/markup */ @@ -203,14 +237,6 @@ abstract class TextFormat{ $newString .= ""; ++$tokens; break; - case TextFormat::UNDERLINE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::STRIKETHROUGH: - $newString .= ""; - ++$tokens; - break; case TextFormat::RESET: $newString .= str_repeat("", $tokens); $tokens = 0; @@ -285,6 +311,46 @@ abstract class TextFormat{ $newString .= ""; ++$tokens; break; + case TextFormat::MATERIAL_QUARTZ: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_IRON: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_NETHERITE: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_REDSTONE: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_COPPER: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_GOLD: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_EMERALD: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_DIAMOND: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_LAPIS: + $newString .= ""; + ++$tokens; + break; + case TextFormat::MATERIAL_AMETHYST: + $newString .= ""; + ++$tokens; + break; default: $newString .= $token; break; From ba93665fe797f7376b8b556c3387025b48299bc1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 10 Dec 2024 14:11:11 +0000 Subject: [PATCH 153/290] TextFormat: reduce hella duplicated code in toHTML() --- src/utils/TextFormat.php | 170 +++++++++------------------------------ 1 file changed, 40 insertions(+), 130 deletions(-) diff --git a/src/utils/TextFormat.php b/src/utils/TextFormat.php index 56aca3f8a..fadf6ea4e 100644 --- a/src/utils/TextFormat.php +++ b/src/utils/TextFormat.php @@ -224,136 +224,46 @@ abstract class TextFormat{ $newString = ""; $tokens = 0; foreach(self::tokenize($string) as $token){ - switch($token){ - case TextFormat::BOLD: - $newString .= ""; - ++$tokens; - break; - case TextFormat::OBFUSCATED: - //$newString .= ""; - //++$tokens; - break; - case TextFormat::ITALIC: - $newString .= ""; - ++$tokens; - break; - case TextFormat::RESET: - $newString .= str_repeat("", $tokens); - $tokens = 0; - break; - - //Colors - case TextFormat::BLACK: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_BLUE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_GREEN: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_AQUA: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_RED: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_PURPLE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::GOLD: - $newString .= ""; - ++$tokens; - break; - case TextFormat::GRAY: - $newString .= ""; - ++$tokens; - break; - case TextFormat::DARK_GRAY: - $newString .= ""; - ++$tokens; - break; - case TextFormat::BLUE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::GREEN: - $newString .= ""; - ++$tokens; - break; - case TextFormat::AQUA: - $newString .= ""; - ++$tokens; - break; - case TextFormat::RED: - $newString .= ""; - ++$tokens; - break; - case TextFormat::LIGHT_PURPLE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::YELLOW: - $newString .= ""; - ++$tokens; - break; - case TextFormat::WHITE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MINECOIN_GOLD: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_QUARTZ: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_IRON: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_NETHERITE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_REDSTONE: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_COPPER: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_GOLD: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_EMERALD: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_DIAMOND: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_LAPIS: - $newString .= ""; - ++$tokens; - break; - case TextFormat::MATERIAL_AMETHYST: - $newString .= ""; - ++$tokens; - break; - default: - $newString .= $token; - break; + $formatString = match($token){ + TextFormat::BLACK => "color:#000", + TextFormat::DARK_BLUE => "color:#00A", + TextFormat::DARK_GREEN => "color:#0A0", + TextFormat::DARK_AQUA => "color:#0AA", + TextFormat::DARK_RED => "color:#A00", + TextFormat::DARK_PURPLE => "color:#A0A", + TextFormat::GOLD => "color:#FA0", + TextFormat::GRAY => "color:#AAA", + TextFormat::DARK_GRAY => "color:#555", + TextFormat::BLUE => "color:#55F", + TextFormat::GREEN => "color:#5F5", + TextFormat::AQUA => "color:#5FF", + TextFormat::RED => "color:#F55", + TextFormat::LIGHT_PURPLE => "color:#F5F", + TextFormat::YELLOW => "color:#FF5", + TextFormat::WHITE => "color:#FFF", + TextFormat::MINECOIN_GOLD => "color:#dd0", + TextFormat::MATERIAL_QUARTZ => "color:#e2d3d1", + TextFormat::MATERIAL_IRON => "color:#cec9c9", + TextFormat::MATERIAL_NETHERITE => "color:#44393a", + TextFormat::MATERIAL_REDSTONE => "color:#961506", + TextFormat::MATERIAL_COPPER => "color:#b4684d", + TextFormat::MATERIAL_GOLD => "color:#deb02c", + TextFormat::MATERIAL_EMERALD => "color:#119f36", + TextFormat::MATERIAL_DIAMOND => "color:#2cb9a8", + TextFormat::MATERIAL_LAPIS => "color:#20487a", + TextFormat::MATERIAL_AMETHYST => "color:#9a5cc5", + TextFormat::BOLD => "font-weight:bold", + TextFormat::ITALIC => "font-style:italic", + default => null + }; + if($formatString !== null){ + $newString .= ""; + ++$tokens; + }elseif($token === TextFormat::RESET){ + $newString .= str_repeat("", $tokens); + $tokens = 0; + }else{ + $newString .= $token; } } From f7687af337d001ddbcc47b8e773f014a33faa662 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 12 Dec 2024 13:11:48 +0000 Subject: [PATCH 154/290] Fixed draft release being created on release publish --- .github/workflows/draft-release.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 3374ff68f..6000dd5a8 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -21,7 +21,31 @@ env: PHP_VERSION: "8.2" jobs: + skip: + name: Check whether to ignore this tag + runs-on: ubuntu-20.04 + + outputs: + skip: ${{ steps.exists.outputs.exists == 'true' }} + + steps: + - name: Check if release already exists + id: exists + env: + GH_TOKEN: ${{ github.token }} + run: | + exists=false + if [[ "${{ github.ref_type }}" == "tag" ]]; then + tag="$(echo "${{ github.ref }}" | cut -d/ -f3-)" + if gh release view "$tag" --repo "${{ github.repository }}"; then + exists=true + fi + fi + echo exists=$exists >> $GITHUB_OUTPUT + check: + needs: [skip] + if: needs.skip.outputs.skip != 'true' name: Check release uses: ./.github/workflows/draft-release-pr-check.yml From b3410787659c56ffe02ca00ab610b43fb5126e64 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:53:52 +0300 Subject: [PATCH 155/290] Implement new pale oak blocks (#6570) --- src/block/BlockTypeIds.php | 16 +++++++++++++++- src/block/Leaves.php | 1 + src/block/VanillaBlocks.php | 15 +++++++++++++++ src/block/utils/LeavesType.php | 4 +++- src/block/utils/WoodType.php | 2 ++ .../convert/BlockObjectToStateSerializer.php | 15 +++++++++++++++ .../convert/BlockStateToObjectDeserializer.php | 15 +++++++++++++++ .../item/ItemSerializerDeserializerRegistrar.php | 2 ++ src/item/ItemTypeIds.php | 3 ++- src/item/StringToItemParser.php | 15 +++++++++++++++ src/item/VanillaItems.php | 2 ++ tests/phpstan/configs/phpstan-bugs.neon | 5 +++++ .../block/block_factory_consistency_check.json | 16 ++++++++++++++++ 13 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 3914a4b74..033985179 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -765,8 +765,22 @@ final class BlockTypeIds{ public const COPPER_TRAPDOOR = 10735; public const CHISELED_COPPER = 10736; public const COPPER_GRATE = 10737; + public const PALE_OAK_BUTTON = 10738; + public const PALE_OAK_DOOR = 10739; + public const PALE_OAK_FENCE = 10740; + public const PALE_OAK_FENCE_GATE = 10741; + public const PALE_OAK_LEAVES = 10742; + public const PALE_OAK_LOG = 10743; + public const PALE_OAK_PLANKS = 10744; + public const PALE_OAK_PRESSURE_PLATE = 10745; + public const PALE_OAK_SIGN = 10746; + public const PALE_OAK_SLAB = 10747; + public const PALE_OAK_STAIRS = 10748; + public const PALE_OAK_TRAPDOOR = 10749; + public const PALE_OAK_WALL_SIGN = 10750; + public const PALE_OAK_WOOD = 10751; - public const FIRST_UNUSED_BLOCK_ID = 10738; + public const FIRST_UNUSED_BLOCK_ID = 10752; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/Leaves.php b/src/block/Leaves.php index 7fe9eae74..847536557 100644 --- a/src/block/Leaves.php +++ b/src/block/Leaves.php @@ -157,6 +157,7 @@ class Leaves extends Transparent{ LeavesType::MANGROVE, //TODO: mangrove propagule LeavesType::AZALEA, LeavesType::FLOWERING_AZALEA => null, //TODO: azalea LeavesType::CHERRY => null, //TODO: cherry + LeavesType::PALE_OAK => null, //TODO: pale oak })?->asItem(); if($sapling !== null){ $drops[] = $sapling; diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 9d7ed91f4..9755f9eda 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -590,6 +590,20 @@ use function strtolower; * @method static Flower OXEYE_DAISY() * @method static PackedIce PACKED_ICE() * @method static Opaque PACKED_MUD() + * @method static WoodenButton PALE_OAK_BUTTON() + * @method static WoodenDoor PALE_OAK_DOOR() + * @method static WoodenFence PALE_OAK_FENCE() + * @method static FenceGate PALE_OAK_FENCE_GATE() + * @method static Leaves PALE_OAK_LEAVES() + * @method static Wood PALE_OAK_LOG() + * @method static Planks PALE_OAK_PLANKS() + * @method static WoodenPressurePlate PALE_OAK_PRESSURE_PLATE() + * @method static FloorSign PALE_OAK_SIGN() + * @method static WoodenSlab PALE_OAK_SLAB() + * @method static WoodenStairs PALE_OAK_STAIRS() + * @method static WoodenTrapdoor PALE_OAK_TRAPDOOR() + * @method static WallSign PALE_OAK_WALL_SIGN() + * @method static Wood PALE_OAK_WOOD() * @method static DoublePlant PEONY() * @method static PinkPetals PINK_PETALS() * @method static Flower PINK_TULIP() @@ -1359,6 +1373,7 @@ final class VanillaBlocks{ WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...), WoodType::WARPED => VanillaItems::WARPED_SIGN(...), WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...), + WoodType::PALE_OAK => VanillaItems::PALE_OAK_SIGN(...), }; self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); diff --git a/src/block/utils/LeavesType.php b/src/block/utils/LeavesType.php index 975551ad6..4846feed0 100644 --- a/src/block/utils/LeavesType.php +++ b/src/block/utils/LeavesType.php @@ -53,6 +53,7 @@ enum LeavesType{ case AZALEA; case FLOWERING_AZALEA; case CHERRY; + case PALE_OAK; public function getDisplayName() : string{ return match($this){ @@ -65,7 +66,8 @@ enum LeavesType{ self::MANGROVE => "Mangrove", self::AZALEA => "Azalea", self::FLOWERING_AZALEA => "Flowering Azalea", - self::CHERRY => "Cherry" + self::CHERRY => "Cherry", + self::PALE_OAK => "Pale Oak", }; } } diff --git a/src/block/utils/WoodType.php b/src/block/utils/WoodType.php index f6195b9f9..c83a4ab00 100644 --- a/src/block/utils/WoodType.php +++ b/src/block/utils/WoodType.php @@ -53,6 +53,7 @@ enum WoodType{ case CRIMSON; case WARPED; case CHERRY; + case PALE_OAK; public function getDisplayName() : string{ return match($this){ @@ -66,6 +67,7 @@ enum WoodType{ self::CRIMSON => "Crimson", self::WARPED => "Warped", self::CHERRY => "Cherry", + self::PALE_OAK => "Pale Oak", }; } diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index aebfd67ff..99e756576 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -704,6 +704,20 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB); $this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS); + $this->map(Blocks::PALE_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::PALE_OAK_BUTTON))); + $this->map(Blocks::PALE_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::PALE_OAK_DOOR))); + $this->map(Blocks::PALE_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::PALE_OAK_FENCE_GATE))); + $this->map(Blocks::PALE_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::PALE_OAK_PRESSURE_PLATE))); + $this->map(Blocks::PALE_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::PALE_OAK_STANDING_SIGN))); + $this->map(Blocks::PALE_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::PALE_OAK_TRAPDOOR))); + $this->map(Blocks::PALE_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::PALE_OAK_WALL_SIGN))); + $this->mapLog(Blocks::PALE_OAK_LOG(), Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG); + $this->mapLog(Blocks::PALE_OAK_WOOD(), Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD); + $this->mapSimple(Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE); + $this->mapSimple(Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS); + $this->mapSlab(Blocks::PALE_OAK_SLAB(), Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB); + $this->mapStairs(Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS); + $this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON))); $this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR))); $this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE))); @@ -740,6 +754,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->map(Blocks::CHERRY_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::CHERRY_LEAVES))); $this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED))); $this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES))); + $this->map(Blocks::PALE_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::PALE_OAK_LEAVES))); //legacy mess $this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES))); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 5c0a427cc..998561ffc 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -608,6 +608,20 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB()); $this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS()); + $this->map(Ids::PALE_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::PALE_OAK_BUTTON(), $in)); + $this->map(Ids::PALE_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::PALE_OAK_DOOR(), $in)); + $this->map(Ids::PALE_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::PALE_OAK_FENCE_GATE(), $in)); + $this->map(Ids::PALE_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::PALE_OAK_PRESSURE_PLATE(), $in)); + $this->map(Ids::PALE_OAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::PALE_OAK_SIGN(), $in)); + $this->map(Ids::PALE_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::PALE_OAK_TRAPDOOR(), $in)); + $this->map(Ids::PALE_OAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::PALE_OAK_WALL_SIGN(), $in)); + $this->mapLog(Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG, fn() => Blocks::PALE_OAK_LOG()); + $this->mapLog(Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD, fn() => Blocks::PALE_OAK_WOOD()); + $this->mapSimple(Ids::PALE_OAK_FENCE, fn() => Blocks::PALE_OAK_FENCE()); + $this->mapSimple(Ids::PALE_OAK_PLANKS, fn() => Blocks::PALE_OAK_PLANKS()); + $this->mapSlab(Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB, fn() => Blocks::PALE_OAK_SLAB()); + $this->mapStairs(Ids::PALE_OAK_STAIRS, fn() => Blocks::PALE_OAK_STAIRS()); + $this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in)); $this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in)); $this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in)); @@ -647,6 +661,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in)); $this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in)); $this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in)); + $this->map(Ids::PALE_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::PALE_OAK_LEAVES(), $in)); $this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in)); } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index df1db4211..30c774af7 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -151,6 +151,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Block(Ids::JUNGLE_DOOR, Blocks::JUNGLE_DOOR()); $this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR()); $this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART()); + $this->map1to1Block(Ids::PALE_OAK_DOOR, Blocks::PALE_OAK_DOOR()); $this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER()); $this->map1to1Block(Ids::SOUL_CAMPFIRE, Blocks::SOUL_CAMPFIRE()); $this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR()); @@ -331,6 +332,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::OAK_BOAT, Items::OAK_BOAT()); $this->map1to1Item(Ids::OAK_SIGN, Items::OAK_SIGN()); $this->map1to1Item(Ids::PAINTING, Items::PAINTING()); + $this->map1to1Item(Ids::PALE_OAK_SIGN, Items::PALE_OAK_SIGN()); $this->map1to1Item(Ids::PAPER, Items::PAPER()); $this->map1to1Item(Ids::PHANTOM_MEMBRANE, Items::PHANTOM_MEMBRANE()); $this->map1to1Item(Ids::PITCHER_POD, Items::PITCHER_POD()); diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index c93c23e81..acb0275c6 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -328,8 +328,9 @@ final class ItemTypeIds{ public const END_CRYSTAL = 20289; public const ICE_BOMB = 20290; public const RECOVERY_COMPASS = 20291; + public const PALE_OAK_SIGN = 20292; - public const FIRST_UNUSED_ITEM_ID = 20292; + public const FIRST_UNUSED_ITEM_ID = 20293; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 09c93d5d9..b58b98154 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -872,6 +872,19 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("oxeye_daisy", fn() => Blocks::OXEYE_DAISY()); $result->registerBlock("packed_ice", fn() => Blocks::PACKED_ICE()); $result->registerBlock("packed_mud", fn() => Blocks::PACKED_MUD()); + $result->registerBlock("pale_oak_button", fn() => Blocks::PALE_OAK_BUTTON()); + $result->registerBlock("pale_oak_door", fn() => Blocks::PALE_OAK_DOOR()); + $result->registerBlock("pale_oak_fence", fn() => Blocks::PALE_OAK_FENCE()); + $result->registerBlock("pale_oak_fence_gate", fn() => Blocks::PALE_OAK_FENCE_GATE()); + $result->registerBlock("pale_oak_leaves", fn() => Blocks::PALE_OAK_LEAVES()); + $result->registerBlock("pale_oak_log", fn() => Blocks::PALE_OAK_LOG()->setStripped(false)); + $result->registerBlock("pale_oak_planks", fn() => Blocks::PALE_OAK_PLANKS()); + $result->registerBlock("pale_oak_pressure_plate", fn() => Blocks::PALE_OAK_PRESSURE_PLATE()); + $result->registerBlock("pale_oak_sign", fn() => Blocks::PALE_OAK_SIGN()); + $result->registerBlock("pale_oak_slab", fn() => Blocks::PALE_OAK_SLAB()); + $result->registerBlock("pale_oak_stairs", fn() => Blocks::PALE_OAK_STAIRS()); + $result->registerBlock("pale_oak_trapdoor", fn() => Blocks::PALE_OAK_TRAPDOOR()); + $result->registerBlock("pale_oak_wood", fn() => Blocks::PALE_OAK_WOOD()->setStripped(false)); $result->registerBlock("peony", fn() => Blocks::PEONY()); $result->registerBlock("pink_petals", fn() => Blocks::PINK_PETALS()); $result->registerBlock("pink_tulip", fn() => Blocks::PINK_TULIP()); @@ -1084,6 +1097,8 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("stripped_mangrove_wood", fn() => Blocks::MANGROVE_WOOD()->setStripped(true)); $result->registerBlock("stripped_oak_log", fn() => Blocks::OAK_LOG()->setStripped(true)); $result->registerBlock("stripped_oak_wood", fn() => Blocks::OAK_WOOD()->setStripped(true)); + $result->registerBlock("stripped_pale_oak_log", fn() => Blocks::PALE_OAK_LOG()->setStripped(true)); + $result->registerBlock("stripped_pale_oak_wood", fn() => Blocks::PALE_OAK_WOOD()->setStripped(true)); $result->registerBlock("stripped_spruce_log", fn() => Blocks::SPRUCE_LOG()->setStripped(true)); $result->registerBlock("stripped_spruce_wood", fn() => Blocks::SPRUCE_WOOD()->setStripped(true)); $result->registerBlock("stripped_warped_hyphae", fn() => Blocks::WARPED_HYPHAE()->setStripped(true)); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 6768ed8f0..e2e4ee450 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -243,6 +243,7 @@ use function strtolower; * @method static Boat OAK_BOAT() * @method static ItemBlockWallOrFloor OAK_SIGN() * @method static PaintingItem PAINTING() + * @method static ItemBlockWallOrFloor PALE_OAK_SIGN() * @method static Item PAPER() * @method static Item PHANTOM_MEMBRANE() * @method static PitcherPod PITCHER_POD() @@ -535,6 +536,7 @@ final class VanillaItems{ }); self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); + self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN())); self::register("paper", fn(IID $id) => new Item($id, "Paper")); self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane")); self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod")); diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index ea5e1c62a..1ae740d66 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -60,6 +60,11 @@ parameters: count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:PALE_OAK_SIGN\\(\\)\\.$#" + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: "#^Creating callable from a non\\-native static method pocketmine\\\\item\\\\VanillaItems\\:\\:SPRUCE_SIGN\\(\\)\\.$#" count: 1 diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index d14a85fab..9f5414c6e 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -511,6 +511,20 @@ "OXEYE_DAISY": 1, "PACKED_ICE": 1, "PACKED_MUD": 1, + "PALE_OAK_BUTTON": 12, + "PALE_OAK_DOOR": 32, + "PALE_OAK_FENCE": 1, + "PALE_OAK_FENCE_GATE": 16, + "PALE_OAK_LEAVES": 4, + "PALE_OAK_LOG": 6, + "PALE_OAK_PLANKS": 1, + "PALE_OAK_PRESSURE_PLATE": 2, + "PALE_OAK_SIGN": 16, + "PALE_OAK_SLAB": 3, + "PALE_OAK_STAIRS": 8, + "PALE_OAK_TRAPDOOR": 16, + "PALE_OAK_WALL_SIGN": 4, + "PALE_OAK_WOOD": 6, "PEONY": 2, "PINK_PETALS": 16, "PINK_TULIP": 1, @@ -754,6 +768,8 @@ "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", "OAK_SIGN": "pocketmine\\block\\tile\\Sign", "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "PALE_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "PALE_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", "REDSTONE_COMPARATOR": "pocketmine\\block\\tile\\Comparator", "SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", From 42094e676827d9adc0ced960aaa5db5778b6ac8f Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:21:41 +0300 Subject: [PATCH 156/290] Implement resin blocks & items (#6571) --- src/block/BlockTypeIds.php | 9 +++- src/block/ResinClump.php | 54 +++++++++++++++++++ src/block/VanillaBlocks.php | 20 +++++++ .../convert/BlockObjectToStateSerializer.php | 11 ++++ .../BlockStateToObjectDeserializer.php | 7 +++ .../ItemSerializerDeserializerRegistrar.php | 1 + src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 9 ++++ src/item/VanillaItems.php | 2 + .../block_factory_consistency_check.json | 7 +++ 10 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 src/block/ResinClump.php diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 033985179..c440cefdc 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -779,8 +779,15 @@ final class BlockTypeIds{ public const PALE_OAK_TRAPDOOR = 10749; public const PALE_OAK_WALL_SIGN = 10750; public const PALE_OAK_WOOD = 10751; + public const RESIN = 10752; + public const RESIN_BRICK_SLAB = 10753; + public const RESIN_BRICK_STAIRS = 10754; + public const RESIN_BRICK_WALL = 10755; + public const RESIN_BRICKS = 10756; + public const RESIN_CLUMP = 10757; + public const CHISELED_RESIN_BRICKS = 10758; - public const FIRST_UNUSED_BLOCK_ID = 10752; + public const FIRST_UNUSED_BLOCK_ID = 10759; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/ResinClump.php b/src/block/ResinClump.php new file mode 100644 index 000000000..75126edf3 --- /dev/null +++ b/src/block/ResinClump.php @@ -0,0 +1,54 @@ +faces : []; + } + + protected function recalculateCollisionBoxes() : array{ + return []; + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 9755f9eda..ce3087a9b 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -191,6 +191,7 @@ use function strtolower; * @method static Opaque CHISELED_POLISHED_BLACKSTONE() * @method static SimplePillar CHISELED_QUARTZ() * @method static Opaque CHISELED_RED_SANDSTONE() + * @method static Opaque CHISELED_RESIN_BRICKS() * @method static Opaque CHISELED_SANDSTONE() * @method static Opaque CHISELED_STONE_BRICKS() * @method static Opaque CHISELED_TUFF() @@ -687,6 +688,12 @@ use function strtolower; * @method static Flower RED_TULIP() * @method static Opaque REINFORCED_DEEPSLATE() * @method static Reserved6 RESERVED6() + * @method static Opaque RESIN() + * @method static Opaque RESIN_BRICKS() + * @method static Slab RESIN_BRICK_SLAB() + * @method static Stair RESIN_BRICK_STAIRS() + * @method static Wall RESIN_BRICK_WALL() + * @method static ResinClump RESIN_CLUMP() * @method static DoublePlant ROSE_BUSH() * @method static Sand SAND() * @method static Opaque SANDSTONE() @@ -1326,6 +1333,7 @@ final class VanillaBlocks{ self::registerBlocksR17(); self::registerBlocksR18(); self::registerMudBlocks(); + self::registerResinBlocks(); self::registerTuffBlocks(); self::registerCraftingTables(); @@ -1743,6 +1751,18 @@ final class VanillaBlocks{ self::register("mud_brick_wall", fn(BID $id) => new Wall($id, "Mud Brick Wall", $mudBricksBreakInfo)); } + private static function registerResinBlocks() : void{ + self::register("resin", fn(BID $id) => new Opaque($id, "Block of Resin", new Info(BreakInfo::instant()))); + self::register("resin_clump", fn(BID $id) => new ResinClump($id, "Resin Clump", new Info(BreakInfo::instant()))); + + $resinBricksInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD)); + self::register("resin_brick_slab", fn(BID $id) => new Slab($id, "Resin Brick", $resinBricksInfo)); + self::register("resin_brick_stairs", fn(BID $id) => new Stair($id, "Resin Brick Stairs", $resinBricksInfo)); + self::register("resin_brick_wall", fn(BID $id) => new Wall($id, "Resin Brick Wall", $resinBricksInfo)); + self::register("resin_bricks", fn(BID $id) => new Opaque($id, "Resin Bricks", $resinBricksInfo)); + self::register("chiseled_resin_bricks", fn(BID $id) => new Opaque($id, "Chiseled Resin Bricks", $resinBricksInfo)); + } + private static function registerTuffBlocks() : void{ $tuffBreakInfo = new Info(BreakInfo::pickaxe(1.5, ToolTier::WOOD, 30.0)); diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 99e756576..e41e82054 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -121,6 +121,7 @@ use pocketmine\block\RedstoneOre; use pocketmine\block\RedstoneRepeater; use pocketmine\block\RedstoneTorch; use pocketmine\block\RedstoneWire; +use pocketmine\block\ResinClump; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\Sapling; use pocketmine\block\SeaPickle; @@ -810,6 +811,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); $this->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); $this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); + $this->mapSimple(Blocks::CHISELED_RESIN_BRICKS(), Ids::CHISELED_RESIN_BRICKS); $this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); $this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); $this->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF); @@ -1051,6 +1053,8 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); $this->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); $this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); + $this->mapSimple(Blocks::RESIN(), Ids::RESIN_BLOCK); + $this->mapSimple(Blocks::RESIN_BRICKS(), Ids::RESIN_BRICKS); $this->mapSimple(Blocks::SAND(), Ids::SAND); $this->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); $this->mapSimple(Blocks::SCULK(), Ids::SCULK); @@ -1735,6 +1739,13 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_SANDSTONE_WALL))); $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_RED))); + $this->mapSlab(Blocks::RESIN_BRICK_SLAB(), Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB); + $this->map(Blocks::RESIN_BRICK_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::RESIN_BRICK_STAIRS))); + $this->map(Blocks::RESIN_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RESIN_BRICK_WALL))); + $this->map(Blocks::RESIN_CLUMP(), function(ResinClump $block) : Writer{ + return Writer::create(Ids::RESIN_CLUMP) + ->writeFacingFlags($block->getFaces()); + }); $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 998561ffc..cb9a6e7ae 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -735,6 +735,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::CHISELED_NETHER_BRICKS, fn() => Blocks::CHISELED_NETHER_BRICKS()); $this->mapSimple(Ids::CHISELED_POLISHED_BLACKSTONE, fn() => Blocks::CHISELED_POLISHED_BLACKSTONE()); $this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE()); + $this->mapSimple(Ids::CHISELED_RESIN_BRICKS, fn() => Blocks::CHISELED_RESIN_BRICKS()); $this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE()); $this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS()); $this->mapSimple(Ids::CHISELED_TUFF, fn() => Blocks::CHISELED_TUFF()); @@ -972,6 +973,8 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE()); $this->mapSimple(Ids::REINFORCED_DEEPSLATE, fn() => Blocks::REINFORCED_DEEPSLATE()); $this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6()); + $this->mapSimple(Ids::RESIN_BLOCK, fn() => Blocks::RESIN()); + $this->mapSimple(Ids::RESIN_BRICKS, fn() => Blocks::RESIN_BRICKS()); $this->mapSimple(Ids::SAND, fn() => Blocks::SAND()); $this->mapSimple(Ids::SANDSTONE, fn() => Blocks::SANDSTONE()); $this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK()); @@ -1582,6 +1585,10 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::SUGARCANE() ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); }); + $this->mapSlab(Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB, fn() => Blocks::RESIN_BRICK_SLAB()); + $this->mapStairs(Ids::RESIN_BRICK_STAIRS, fn() => Blocks::RESIN_BRICK_STAIRS()); + $this->map(Ids::RESIN_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RESIN_BRICK_WALL(), $in)); + $this->map(Ids::RESIN_CLUMP, fn(Reader $in) => Blocks::RESIN_CLUMP()->setFaces($in->readFacingFlags())); $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 30c774af7..e72e2fe4e 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -356,6 +356,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::RAW_IRON, Items::RAW_IRON()); $this->map1to1Item(Ids::RECOVERY_COMPASS, Items::RECOVERY_COMPASS()); $this->map1to1Item(Ids::REDSTONE, Items::REDSTONE_DUST()); + $this->map1to1Item(Ids::RESIN_BRICK, Items::RESIN_BRICK()); $this->map1to1Item(Ids::RIB_ARMOR_TRIM_SMITHING_TEMPLATE, Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::ROTTEN_FLESH, Items::ROTTEN_FLESH()); $this->map1to1Item(Ids::SALMON, Items::RAW_SALMON()); diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index acb0275c6..fb3a08161 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -329,8 +329,9 @@ final class ItemTypeIds{ public const ICE_BOMB = 20290; public const RECOVERY_COMPASS = 20291; public const PALE_OAK_SIGN = 20292; + public const RESIN_BRICK = 20293; - public const FIRST_UNUSED_ITEM_ID = 20293; + public const FIRST_UNUSED_ITEM_ID = 20294; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index b58b98154..19def35e7 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -243,6 +243,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("chiseled_polished_blackstone", fn() => Blocks::CHISELED_POLISHED_BLACKSTONE()); $result->registerBlock("chiseled_quartz", fn() => Blocks::CHISELED_QUARTZ()); $result->registerBlock("chiseled_red_sandstone", fn() => Blocks::CHISELED_RED_SANDSTONE()); + $result->registerBlock("chiseled_resin_bricks", fn() => Blocks::CHISELED_RESIN_BRICKS()); $result->registerBlock("chiseled_sandstone", fn() => Blocks::CHISELED_SANDSTONE()); $result->registerBlock("chiseled_stone_bricks", fn() => Blocks::CHISELED_STONE_BRICKS()); $result->registerBlock("chiseled_tuff", fn() => Blocks::CHISELED_TUFF()); @@ -985,6 +986,13 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("repeater", fn() => Blocks::REDSTONE_REPEATER()); $result->registerBlock("repeater_block", fn() => Blocks::REDSTONE_REPEATER()); $result->registerBlock("reserved6", fn() => Blocks::RESERVED6()); + $result->registerBlock("resin", fn() => Blocks::RESIN()); + $result->registerBlock("resin_block", fn() => Blocks::RESIN()); + $result->registerBlock("resin_brick_slab", fn() => Blocks::RESIN_BRICK_SLAB()); + $result->registerBlock("resin_brick_stairs", fn() => Blocks::RESIN_BRICK_STAIRS()); + $result->registerBlock("resin_brick_wall", fn() => Blocks::RESIN_BRICK_WALL()); + $result->registerBlock("resin_bricks", fn() => Blocks::RESIN_BRICKS()); + $result->registerBlock("resin_clump", fn() => Blocks::RESIN_CLUMP()); $result->registerBlock("rooted_dirt", fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); $result->registerBlock("rose", fn() => Blocks::POPPY()); $result->registerBlock("rose_bush", fn() => Blocks::ROSE_BUSH()); @@ -1499,6 +1507,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("recovery_compass", fn() => Items::RECOVERY_COMPASS()); $result->register("redstone", fn() => Items::REDSTONE_DUST()); $result->register("redstone_dust", fn() => Items::REDSTONE_DUST()); + $result->register("resin_brick", fn() => Items::RESIN_BRICK()); $result->register("rib_armor_trim_smithing_template", fn() => Items::RIB_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("rotten_flesh", fn() => Items::ROTTEN_FLESH()); $result->register("salmon", fn() => Items::RAW_SALMON()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index e2e4ee450..adc89259e 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -287,6 +287,7 @@ use function strtolower; * @method static Record RECORD_WARD() * @method static Item RECOVERY_COMPASS() * @method static Redstone REDSTONE_DUST() + * @method static Item RESIN_BRICK() * @method static Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static RottenFlesh ROTTEN_FLESH() * @method static Item SCUTE() @@ -579,6 +580,7 @@ final class VanillaItems{ self::register("record_ward", fn(IID $id) => new Record($id, RecordType::DISK_WARD, "Record Ward")); self::register("recovery_compass", fn(IID $id) => new Item($id, "Recovery Compass")); self::register("redstone_dust", fn(IID $id) => new Redstone($id, "Redstone")); + self::register("resin_brick", fn(IID $id) => new Item($id, "Resin Brick")); self::register("rotten_flesh", fn(IID $id) => new RottenFlesh($id, "Rotten Flesh")); self::register("scute", fn(IID $id) => new Item($id, "Scute")); self::register("shears", fn(IID $id) => new Shears($id, "Shears", [EnchantmentTags::SHEARS])); diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 9f5414c6e..0b9150988 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -112,6 +112,7 @@ "CHISELED_POLISHED_BLACKSTONE": 1, "CHISELED_QUARTZ": 3, "CHISELED_RED_SANDSTONE": 1, + "CHISELED_RESIN_BRICKS": 1, "CHISELED_SANDSTONE": 1, "CHISELED_STONE_BRICKS": 1, "CHISELED_TUFF": 1, @@ -608,6 +609,12 @@ "RED_TULIP": 1, "REINFORCED_DEEPSLATE": 1, "RESERVED6": 1, + "RESIN": 1, + "RESIN_BRICKS": 1, + "RESIN_BRICK_SLAB": 3, + "RESIN_BRICK_STAIRS": 8, + "RESIN_BRICK_WALL": 162, + "RESIN_CLUMP": 64, "ROSE_BUSH": 2, "SAND": 1, "SANDSTONE": 1, From de66d84d29c56cf566eb67cd1d8660ba679c0852 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:10:34 +0300 Subject: [PATCH 157/290] Implement new 1.20 and 1.21 records (#6572) --- src/block/utils/RecordType.php | 8 ++++++++ .../bedrock/item/ItemSerializerDeserializerRegistrar.php | 4 ++++ src/item/ItemTypeIds.php | 6 +++++- src/item/StringToItemParser.php | 4 ++++ src/item/VanillaItems.php | 8 ++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/block/utils/RecordType.php b/src/block/utils/RecordType.php index e63cee920..0757db09b 100644 --- a/src/block/utils/RecordType.php +++ b/src/block/utils/RecordType.php @@ -59,11 +59,15 @@ enum RecordType{ case DISK_CAT; case DISK_BLOCKS; case DISK_CHIRP; + case DISK_CREATOR; + case DISK_CREATOR_MUSIC_BOX; case DISK_FAR; case DISK_MALL; case DISK_MELLOHI; case DISK_OTHERSIDE; case DISK_PIGSTEP; + case DISK_PRECIPICE; + case DISK_RELIC; case DISK_STAL; case DISK_STRAD; case DISK_WARD; @@ -83,11 +87,15 @@ enum RecordType{ self::DISK_CAT => ["C418 - cat", LevelSoundEvent::RECORD_CAT, KnownTranslationFactory::item_record_cat_desc()], self::DISK_BLOCKS => ["C418 - blocks", LevelSoundEvent::RECORD_BLOCKS, KnownTranslationFactory::item_record_blocks_desc()], self::DISK_CHIRP => ["C418 - chirp", LevelSoundEvent::RECORD_CHIRP, KnownTranslationFactory::item_record_chirp_desc()], + self::DISK_CREATOR => ["Lena Raine - Creator", LevelSoundEvent::RECORD_CREATOR, KnownTranslationFactory::item_record_creator_desc()], + self::DISK_CREATOR_MUSIC_BOX => ["Lena Raine - Creator (Music Box)", LevelSoundEvent::RECORD_CREATOR_MUSIC_BOX, KnownTranslationFactory::item_record_creator_music_box_desc()], self::DISK_FAR => ["C418 - far", LevelSoundEvent::RECORD_FAR, KnownTranslationFactory::item_record_far_desc()], self::DISK_MALL => ["C418 - mall", LevelSoundEvent::RECORD_MALL, KnownTranslationFactory::item_record_mall_desc()], self::DISK_MELLOHI => ["C418 - mellohi", LevelSoundEvent::RECORD_MELLOHI, KnownTranslationFactory::item_record_mellohi_desc()], self::DISK_OTHERSIDE => ["Lena Raine - otherside", LevelSoundEvent::RECORD_OTHERSIDE, KnownTranslationFactory::item_record_otherside_desc()], self::DISK_PIGSTEP => ["Lena Raine - Pigstep", LevelSoundEvent::RECORD_PIGSTEP, KnownTranslationFactory::item_record_pigstep_desc()], + self::DISK_PRECIPICE => ["Aaron Cherof - Precipice", LevelSoundEvent::RECORD_PRECIPICE, KnownTranslationFactory::item_record_precipice_desc()], + self::DISK_RELIC => ["Aaron Cherof - Relic", LevelSoundEvent::RECORD_RELIC, KnownTranslationFactory::item_record_relic_desc()], self::DISK_STAL => ["C418 - stal", LevelSoundEvent::RECORD_STAL, KnownTranslationFactory::item_record_stal_desc()], self::DISK_STRAD => ["C418 - strad", LevelSoundEvent::RECORD_STRAD, KnownTranslationFactory::item_record_strad_desc()], self::DISK_WARD => ["C418 - ward", LevelSoundEvent::RECORD_WARD, KnownTranslationFactory::item_record_ward_desc()], diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index e72e2fe4e..771154d46 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -303,11 +303,15 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::MUSIC_DISC_BLOCKS, Items::RECORD_BLOCKS()); $this->map1to1Item(Ids::MUSIC_DISC_CAT, Items::RECORD_CAT()); $this->map1to1Item(Ids::MUSIC_DISC_CHIRP, Items::RECORD_CHIRP()); + $this->map1to1Item(Ids::MUSIC_DISC_CREATOR, Items::RECORD_CREATOR()); + $this->map1to1Item(Ids::MUSIC_DISC_CREATOR_MUSIC_BOX, Items::RECORD_CREATOR_MUSIC_BOX()); $this->map1to1Item(Ids::MUSIC_DISC_FAR, Items::RECORD_FAR()); $this->map1to1Item(Ids::MUSIC_DISC_MALL, Items::RECORD_MALL()); $this->map1to1Item(Ids::MUSIC_DISC_MELLOHI, Items::RECORD_MELLOHI()); $this->map1to1Item(Ids::MUSIC_DISC_OTHERSIDE, Items::RECORD_OTHERSIDE()); $this->map1to1Item(Ids::MUSIC_DISC_PIGSTEP, Items::RECORD_PIGSTEP()); + $this->map1to1Item(Ids::MUSIC_DISC_PRECIPICE, Items::RECORD_PRECIPICE()); + $this->map1to1Item(Ids::MUSIC_DISC_RELIC, Items::RECORD_RELIC()); $this->map1to1Item(Ids::MUSIC_DISC_STAL, Items::RECORD_STAL()); $this->map1to1Item(Ids::MUSIC_DISC_STRAD, Items::RECORD_STRAD()); $this->map1to1Item(Ids::MUSIC_DISC_WAIT, Items::RECORD_WAIT()); diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index fb3a08161..c63046c6b 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -330,8 +330,12 @@ final class ItemTypeIds{ public const RECOVERY_COMPASS = 20291; public const PALE_OAK_SIGN = 20292; public const RESIN_BRICK = 20293; + public const RECORD_RELIC = 20294; + public const RECORD_CREATOR = 20295; + public const RECORD_CREATOR_MUSIC_BOX = 20296; + public const RECORD_PRECIPICE = 20297; - public const FIRST_UNUSED_ITEM_ID = 20294; + public const FIRST_UNUSED_ITEM_ID = 20298; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 19def35e7..a3bd7b872 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1495,11 +1495,15 @@ final class StringToItemParser extends StringToTParser{ $result->register("record_blocks", fn() => Items::RECORD_BLOCKS()); $result->register("record_cat", fn() => Items::RECORD_CAT()); $result->register("record_chirp", fn() => Items::RECORD_CHIRP()); + $result->register("record_creator", fn() => Items::RECORD_CREATOR()); + $result->register("record_creator_music_box", fn() => Items::RECORD_CREATOR_MUSIC_BOX()); $result->register("record_far", fn() => Items::RECORD_FAR()); $result->register("record_mall", fn() => Items::RECORD_MALL()); $result->register("record_mellohi", fn() => Items::RECORD_MELLOHI()); $result->register("record_otherside", fn() => Items::RECORD_OTHERSIDE()); $result->register("record_pigstep", fn() => Items::RECORD_PIGSTEP()); + $result->register("record_precipice", fn() => Items::RECORD_PRECIPICE()); + $result->register("record_relic", fn() => Items::RECORD_RELIC()); $result->register("record_stal", fn() => Items::RECORD_STAL()); $result->register("record_strad", fn() => Items::RECORD_STRAD()); $result->register("record_wait", fn() => Items::RECORD_WAIT()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index adc89259e..f76cf369f 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -276,11 +276,15 @@ use function strtolower; * @method static Record RECORD_BLOCKS() * @method static Record RECORD_CAT() * @method static Record RECORD_CHIRP() + * @method static Record RECORD_CREATOR() + * @method static Record RECORD_CREATOR_MUSIC_BOX() * @method static Record RECORD_FAR() * @method static Record RECORD_MALL() * @method static Record RECORD_MELLOHI() * @method static Record RECORD_OTHERSIDE() * @method static Record RECORD_PIGSTEP() + * @method static Record RECORD_PRECIPICE() + * @method static Record RECORD_RELIC() * @method static Record RECORD_STAL() * @method static Record RECORD_STRAD() * @method static Record RECORD_WAIT() @@ -569,11 +573,15 @@ final class VanillaItems{ self::register("record_blocks", fn(IID $id) => new Record($id, RecordType::DISK_BLOCKS, "Record Blocks")); self::register("record_cat", fn(IID $id) => new Record($id, RecordType::DISK_CAT, "Record Cat")); self::register("record_chirp", fn(IID $id) => new Record($id, RecordType::DISK_CHIRP, "Record Chirp")); + self::register("record_creator", fn(IID $id) => new Record($id, RecordType::DISK_CREATOR, "Record Creator")); + self::register("record_creator_music_box", fn(IID $id) => new Record($id, RecordType::DISK_CREATOR_MUSIC_BOX, "Record Creator (Music Box)")); self::register("record_far", fn(IID $id) => new Record($id, RecordType::DISK_FAR, "Record Far")); self::register("record_mall", fn(IID $id) => new Record($id, RecordType::DISK_MALL, "Record Mall")); self::register("record_mellohi", fn(IID $id) => new Record($id, RecordType::DISK_MELLOHI, "Record Mellohi")); self::register("record_otherside", fn(IID $id) => new Record($id, RecordType::DISK_OTHERSIDE, "Record Otherside")); self::register("record_pigstep", fn(IID $id) => new Record($id, RecordType::DISK_PIGSTEP, "Record Pigstep")); + self::register("record_precipice", fn(IID $id) => new Record($id, RecordType::DISK_PRECIPICE, "Record Precipice")); + self::register("record_relic", fn(IID $id) => new Record($id, RecordType::DISK_RELIC, "Record Relic")); self::register("record_stal", fn(IID $id) => new Record($id, RecordType::DISK_STAL, "Record Stal")); self::register("record_strad", fn(IID $id) => new Record($id, RecordType::DISK_STRAD, "Record Strad")); self::register("record_wait", fn(IID $id) => new Record($id, RecordType::DISK_WAIT, "Record Wait")); From b10caf74376c00f0f0c31f797fb4f6307e142c03 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sat, 14 Dec 2024 00:54:48 +0300 Subject: [PATCH 158/290] Remove tool tier of some blocks to match vanilla (#6573) --- src/block/VanillaBlocks.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index ce3087a9b..231004dfa 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -879,12 +879,12 @@ final class VanillaBlocks{ self::register("bedrock", fn(BID $id) => new Bedrock($id, "Bedrock", new Info(BreakInfo::indestructible()))); self::register("beetroots", fn(BID $id) => new Beetroot($id, "Beetroot Block", new Info(BreakInfo::instant()))); - self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD))), TileBell::class); + self::register("bell", fn(BID $id) => new Bell($id, "Bell", new Info(BreakInfo::pickaxe(5.0))), TileBell::class); self::register("blue_ice", fn(BID $id) => new BlueIce($id, "Blue Ice", new Info(BreakInfo::pickaxe(2.8)))); self::register("bone_block", fn(BID $id) => new BoneBlock($id, "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD)))); self::register("bookshelf", fn(BID $id) => new Bookshelf($id, "Bookshelf", new Info(BreakInfo::axe(1.5)))); self::register("chiseled_bookshelf", fn(BID $id) => new ChiseledBookshelf($id, "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))), TileChiseledBookshelf::class); - self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))), TileBrewingStand::class); + self::register("brewing_stand", fn(BID $id) => new BrewingStand($id, "Brewing Stand", new Info(BreakInfo::pickaxe(0.5))), TileBrewingStand::class); $bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); self::register("brick_stairs", fn(BID $id) => new Stair($id, "Brick Stairs", $bricksBreakInfo)); @@ -942,7 +942,7 @@ final class VanillaBlocks{ self::register("end_stone_bricks", fn(BID $id) => new Opaque($id, "End Stone Bricks", $endBrickBreakInfo)); self::register("end_stone_brick_stairs", fn(BID $id) => new Stair($id, "End Stone Brick Stairs", $endBrickBreakInfo)); - self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, ToolTier::WOOD, 3000.0))), TileEnderChest::class); + self::register("ender_chest", fn(BID $id) => new EnderChest($id, "Ender Chest", new Info(BreakInfo::pickaxe(22.5, blastResistance: 3000.0))), TileEnderChest::class); self::register("farmland", fn(BID $id) => new Farmland($id, "Farmland", new Info(BreakInfo::shovel(0.6), [Tags::DIRT]))); self::register("fire", fn(BID $id) => new Fire($id, "Fire Block", new Info(BreakInfo::instant(), [Tags::FIRE]))); @@ -998,9 +998,9 @@ final class VanillaBlocks{ $ironBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::STONE, 30.0)); self::register("iron", fn(BID $id) => new Opaque($id, "Iron Block", $ironBreakInfo)); self::register("iron_bars", fn(BID $id) => new Thin($id, "Iron Bars", $ironBreakInfo)); - $ironDoorBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD, 25.0)); - self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", $ironDoorBreakInfo)); - self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", $ironDoorBreakInfo)); + + self::register("iron_door", fn(BID $id) => new Door($id, "Iron Door", new Info(BreakInfo::pickaxe(5.0)))); + self::register("iron_trapdoor", fn(BID $id) => new Trapdoor($id, "Iron Trapdoor", new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)))); $itemFrameInfo = new Info(new BreakInfo(0.25)); self::register("item_frame", fn(BID $id) => new ItemFrame($id, "Item Frame", $itemFrameInfo), TileItemFrame::class); @@ -1009,7 +1009,7 @@ final class VanillaBlocks{ self::register("jukebox", fn(BID $id) => new Jukebox($id, "Jukebox", new Info(BreakInfo::axe(0.8))), TileJukebox::class); //TODO: in PC the hardness is 2.0, not 0.8, unsure if this is a MCPE bug or not self::register("ladder", fn(BID $id) => new Ladder($id, "Ladder", new Info(BreakInfo::axe(0.4)))); - $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0, ToolTier::WOOD)); + $lanternBreakInfo = new Info(BreakInfo::pickaxe(5.0)); self::register("lantern", fn(BID $id) => new Lantern($id, "Lantern", $lanternBreakInfo, 15)); self::register("soul_lantern", fn(BID $id) => new Lantern($id, "Soul Lantern", $lanternBreakInfo, 10)); @@ -1144,7 +1144,7 @@ final class VanillaBlocks{ self::register("mossy_stone_brick_stairs", fn(BID $id) => new Stair($id, "Mossy Stone Brick Stairs", $stoneBreakInfo)); self::register("stone_button", fn(BID $id) => new StoneButton($id, "Stone Button", new Info(BreakInfo::pickaxe(0.5)))); self::register("stonecutter", fn(BID $id) => new Stonecutter($id, "Stonecutter", new Info(BreakInfo::pickaxe(3.5)))); - self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)))); + self::register("stone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, "Stone Pressure Plate", new Info(BreakInfo::pickaxe(0.5)))); //TODO: in the future this won't be the same for all the types $stoneSlabBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0)); @@ -1200,7 +1200,7 @@ final class VanillaBlocks{ self::register("water", fn(BID $id) => new Water($id, "Water", new Info(BreakInfo::indestructible(500.0)))); self::register("lily_pad", fn(BID $id) => new WaterLily($id, "Lily Pad", new Info(BreakInfo::instant()))); - $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)); + $weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5)); self::register("weighted_pressure_plate_heavy", fn(BID $id) => new WeightedPressurePlateHeavy( $id, "Weighted Pressure Plate Heavy", @@ -1606,7 +1606,7 @@ final class VanillaBlocks{ $prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : ""); self::register("polished_blackstone", fn(BID $id) => new Opaque($id, $prefix(""), $blackstoneBreakInfo)); self::register("polished_blackstone_button", fn(BID $id) => new StoneButton($id, $prefix("Button"), new Info(BreakInfo::pickaxe(0.5)))); - self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD)), 20)); + self::register("polished_blackstone_pressure_plate", fn(BID $id) => new StonePressurePlate($id, $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5)), 20)); self::register("polished_blackstone_slab", fn(BID $id) => new Slab($id, $prefix(""), $slabBreakInfo)); self::register("polished_blackstone_stairs", fn(BID $id) => new Stair($id, $prefix("Stairs"), $blackstoneBreakInfo)); self::register("polished_blackstone_wall", fn(BID $id) => new Wall($id, $prefix("Wall"), $blackstoneBreakInfo)); @@ -1713,9 +1713,8 @@ final class VanillaBlocks{ self::register("cut_copper_stairs", fn(BID $id) => new CopperStairs($id, "Cut Copper Stairs", $copperBreakInfo)); self::register("copper_bulb", fn(BID $id) => new CopperBulb($id, "Copper Bulb", $copperBreakInfo)); - $copperDoorBreakInfo = new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)); - self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", $copperDoorBreakInfo)); - self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", $copperDoorBreakInfo)); + self::register("copper_door", fn(BID $id) => new CopperDoor($id, "Copper Door", new Info(BreakInfo::pickaxe(3.0, blastResistance: 30.0)))); + self::register("copper_trapdoor", fn(BID $id) => new CopperTrapdoor($id, "Copper Trapdoor", new Info(BreakInfo::pickaxe(3.0, ToolTier::STONE, 30.0)))); $candleBreakInfo = new Info(new BreakInfo(0.1)); self::register("candle", fn(BID $id) => new Candle($id, "Candle", $candleBreakInfo)); From 8f8fe948c1ebbbe07f1451ff4505c1cd7ab614d5 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 15 Dec 2024 16:26:39 +0000 Subject: [PATCH 159/290] MemoryManager: Control when cycle garbage collection is run (#6554) This PR replicates the mechanism by which PHP's own GC is triggered: using a dynamically adjusted threshold based on the number of roots and the number of destroyed cycles. This approach was chosen to minimize behavioural changes. This currently only applies to the main thread. Doing this for other threads is a bit more complicated (and in the case of RakLib, possibly not necessary anyway). By doing this, we can get more accurate performance profiling. Instead of GC happening in random pathways and throwing off GC numbers, we trigger it in a predictable place, where timings can record it. This change may also produce minor performance improvements in code touching lots of objects (such as `CraftingDataPacket` encoding`), which previously might've triggered multiple GC runs within a single tick. Now that GC runs wait for `MemoryManager`, it can touch as many objects as it wants during a tick without paying a performance penalty. While working on this change I came across a number of issues that should probably be addressed in the future: 1) Objects like Server, World and Player that can't possibly be GC'd repeatedly end up in the GC root buffer because the refcounts fluctuate frequently. Because of the dependency chains in these objects, they all drag each other into GC, causing an almost guaranteed parasitic performance cost to GC. This is discussed in php/php-src#17131, as the proper solution to this is probably generational GC, or perhaps some way to explicitly mark objects to be ignored by GC. 2) World's `blockCache` blows up the GC root threshold due to poor size management. This leads to infrequent, but extremely expensive GC runs due to the sheer number of objects being scanned. We could avoid a lot of this cost by managing caches like this more effectively. 3) StringToItemParser and many of the pocketmine\data classes which make heavy use of closures are afflicted by thousands of reference cycles. This doesn't present a major performance issue in most cases because the cycles are simple, but this could easily be fixed with some simple refactors. --- src/MemoryManager.php | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 4308167d3..638a2613a 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -41,11 +41,12 @@ use function fopen; use function fwrite; use function gc_collect_cycles; use function gc_disable; -use function gc_enable; use function gc_mem_caches; +use function gc_status; use function get_class; use function get_declared_classes; use function get_defined_functions; +use function hrtime; use function ini_get; use function ini_set; use function intdiv; @@ -55,9 +56,11 @@ use function is_object; use function is_resource; use function is_string; use function json_encode; +use function max; use function mb_strtoupper; use function min; use function mkdir; +use function number_format; use function preg_match; use function print_r; use function round; @@ -75,6 +78,14 @@ class MemoryManager{ private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2; private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND; + //These constants are copied from Zend/zend_gc.c as of PHP 8.3.14 + //TODO: These values could be adjusted to better suit PM, but for now we just want to mirror PHP GC to minimize + //behavioural changes. + private const GC_THRESHOLD_TRIGGER = 100; + private const GC_THRESHOLD_MAX = 1_000_000_000; + private const GC_THRESHOLD_DEFAULT = 10_001; + private const GC_THRESHOLD_STEP = 10_000; + private int $memoryLimit; private int $globalMemoryLimit; private int $checkRate; @@ -91,6 +102,9 @@ class MemoryManager{ private bool $garbageCollectionTrigger; private bool $garbageCollectionAsync; + private int $cycleCollectionThreshold = self::GC_THRESHOLD_DEFAULT; + private int $cycleCollectionTimeTotalNs = 0; + private int $lowMemChunkRadiusOverride; private bool $lowMemChunkGC; @@ -107,6 +121,7 @@ class MemoryManager{ $this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager"); $this->init($server->getConfigGroup()); + gc_disable(); } private function init(ServerConfigGroup $config) : void{ @@ -152,7 +167,6 @@ class MemoryManager{ $this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true); $this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true); - gc_enable(); } public function isLowMemory() : bool{ @@ -204,6 +218,18 @@ class MemoryManager{ $this->logger->debug(sprintf("Freed %gMB, $cycles cycles", round(($ev->getMemoryFreed() / 1024) / 1024, 2))); } + private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{ + //TODO Very simple heuristic for dynamic GC buffer resizing: + //If there are "too few" collections, increase the collection threshold + //by a fixed step + //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14 + if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->cycleCollectionThreshold){ + $this->cycleCollectionThreshold = min(self::GC_THRESHOLD_MAX, $this->cycleCollectionThreshold + self::GC_THRESHOLD_STEP); + }elseif($this->cycleCollectionThreshold > self::GC_THRESHOLD_DEFAULT){ + $this->cycleCollectionThreshold = max(self::GC_THRESHOLD_DEFAULT, $this->cycleCollectionThreshold - self::GC_THRESHOLD_STEP); + } + } + /** * Called every tick to update the memory manager state. */ @@ -236,9 +262,25 @@ class MemoryManager{ } } + $rootsBefore = gc_status()["roots"]; if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){ $this->garbageCollectionTicker = 0; $this->triggerGarbageCollector(); + }elseif($rootsBefore >= $this->cycleCollectionThreshold){ + Timings::$garbageCollector->startTiming(); + + $start = hrtime(true); + $cycles = gc_collect_cycles(); + $end = hrtime(true); + + $rootsAfter = gc_status()["roots"]; + $this->adjustGcThreshold($cycles, $rootsAfter); + + Timings::$garbageCollector->stopTiming(); + + $time = $end - $start; + $this->cycleCollectionTimeTotalNs += $time; + $this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->cycleCollectionTimeTotalNs) . " ns"); } Timings::$memoryManager->stopTiming(); @@ -465,7 +507,6 @@ class MemoryManager{ $logger->info("Finished!"); ini_set('memory_limit', $hardLimit); - gc_enable(); } /** From 0aa6cde259e550cd3259e7bdfdaa660031e1a752 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 16:41:54 +0000 Subject: [PATCH 160/290] Remove stupid MemoryManager settings No one in their right mind is going to change the defaults for these anyway. All this crap does is overwhelm users with stuff they don't understand. Most of this stuff has no business being modified by non-developers anyway. --- resources/pocketmine.yml | 16 ------------ src/MemoryManager.php | 50 ++++++++++++------------------------- src/YmlServerProperties.php | 6 ----- 3 files changed, 16 insertions(+), 56 deletions(-) diff --git a/resources/pocketmine.yml b/resources/pocketmine.yml index 408b5b95b..531924973 100644 --- a/resources/pocketmine.yml +++ b/resources/pocketmine.yml @@ -54,12 +54,6 @@ memory: #This only affects the main thread. Other threads should fire their own collections period: 36000 - #Fire asynchronous tasks to collect garbage from workers - collect-async-worker: true - - #Trigger on low memory - low-memory-trigger: true - #Settings controlling memory dump handling. memory-dump: #Dump memory from async workers as well as the main thread. If you have issues with segfaults when dumping memory, disable this setting. @@ -69,16 +63,6 @@ memory: #Cap maximum render distance per player when low memory is triggered. Set to 0 to disable cap. chunk-radius: 4 - #Do chunk garbage collection on trigger - trigger-chunk-collect: true - - world-caches: - #Disallow adding to world chunk-packet caches when memory is low - disable-chunk-cache: true - #Clear world caches when memory is low - low-memory-trigger: true - - network: #Threshold for batching packets, in bytes. Only these packets will be compressed #Set to 0 to compress everything, -1 to disable. diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 638a2613a..da520d077 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -99,17 +99,11 @@ class MemoryManager{ private int $garbageCollectionPeriod; private int $garbageCollectionTicker = 0; - private bool $garbageCollectionTrigger; - private bool $garbageCollectionAsync; private int $cycleCollectionThreshold = self::GC_THRESHOLD_DEFAULT; private int $cycleCollectionTimeTotalNs = 0; private int $lowMemChunkRadiusOverride; - private bool $lowMemChunkGC; - - private bool $lowMemDisableChunkCache; - private bool $lowMemClearWorldCache; private bool $dumpWorkers = true; @@ -157,14 +151,8 @@ class MemoryManager{ $this->continuousTriggerRate = $config->getPropertyInt(Yml::MEMORY_CONTINUOUS_TRIGGER_RATE, self::DEFAULT_CONTINUOUS_TRIGGER_RATE); $this->garbageCollectionPeriod = $config->getPropertyInt(Yml::MEMORY_GARBAGE_COLLECTION_PERIOD, self::DEFAULT_TICKS_PER_GC); - $this->garbageCollectionTrigger = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER, true); - $this->garbageCollectionAsync = $config->getPropertyBool(Yml::MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER, true); $this->lowMemChunkRadiusOverride = $config->getPropertyInt(Yml::MEMORY_MAX_CHUNKS_CHUNK_RADIUS, 4); - $this->lowMemChunkGC = $config->getPropertyBool(Yml::MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT, true); - - $this->lowMemDisableChunkCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE, true); - $this->lowMemClearWorldCache = $config->getPropertyBool(Yml::MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER, true); $this->dumpWorkers = $config->getPropertyBool(Yml::MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER, true); } @@ -177,8 +165,11 @@ class MemoryManager{ return $this->globalMemoryLimit; } + /** + * @deprecated + */ public function canUseChunkCache() : bool{ - return !$this->lowMemory || !$this->lowMemDisableChunkCache; + return !$this->lowMemory; } /** @@ -194,26 +185,19 @@ class MemoryManager{ public function trigger(int $memory, int $limit, bool $global = false, int $triggerCount = 0) : void{ $this->logger->debug(sprintf("%sLow memory triggered, limit %gMB, using %gMB", $global ? "Global " : "", round(($limit / 1024) / 1024, 2), round(($memory / 1024) / 1024, 2))); - if($this->lowMemClearWorldCache){ - foreach($this->server->getWorldManager()->getWorlds() as $world){ - $world->clearCache(true); - } - ChunkCache::pruneCaches(); + foreach($this->server->getWorldManager()->getWorlds() as $world){ + $world->clearCache(true); } + ChunkCache::pruneCaches(); - if($this->lowMemChunkGC){ - foreach($this->server->getWorldManager()->getWorlds() as $world){ - $world->doChunkGarbageCollection(); - } + foreach($this->server->getWorldManager()->getWorlds() as $world){ + $world->doChunkGarbageCollection(); } $ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount); $ev->call(); - $cycles = 0; - if($this->garbageCollectionTrigger){ - $cycles = $this->triggerGarbageCollector(); - } + $cycles = $this->triggerGarbageCollector(); $this->logger->debug(sprintf("Freed %gMB, $cycles cycles", round(($ev->getMemoryFreed() / 1024) / 1024, 2))); } @@ -289,14 +273,12 @@ class MemoryManager{ public function triggerGarbageCollector() : int{ Timings::$garbageCollector->startTiming(); - if($this->garbageCollectionAsync){ - $pool = $this->server->getAsyncPool(); - if(($w = $pool->shutdownUnusedWorkers()) > 0){ - $this->logger->debug("Shut down $w idle async pool workers"); - } - foreach($pool->getRunningWorkers() as $i){ - $pool->submitTaskToWorker(new GarbageCollectionTask(), $i); - } + $pool = $this->server->getAsyncPool(); + if(($w = $pool->shutdownUnusedWorkers()) > 0){ + $this->logger->debug("Shut down $w idle async pool workers"); + } + foreach($pool->getRunningWorkers() as $i){ + $pool->submitTaskToWorker(new GarbageCollectionTask(), $i); } $cycles = gc_collect_cycles(); diff --git a/src/YmlServerProperties.php b/src/YmlServerProperties.php index 9bd203eef..282b0b3cd 100644 --- a/src/YmlServerProperties.php +++ b/src/YmlServerProperties.php @@ -75,20 +75,14 @@ final class YmlServerProperties{ public const MEMORY_CONTINUOUS_TRIGGER = 'memory.continuous-trigger'; public const MEMORY_CONTINUOUS_TRIGGER_RATE = 'memory.continuous-trigger-rate'; public const MEMORY_GARBAGE_COLLECTION = 'memory.garbage-collection'; - public const MEMORY_GARBAGE_COLLECTION_COLLECT_ASYNC_WORKER = 'memory.garbage-collection.collect-async-worker'; - public const MEMORY_GARBAGE_COLLECTION_LOW_MEMORY_TRIGGER = 'memory.garbage-collection.low-memory-trigger'; public const MEMORY_GARBAGE_COLLECTION_PERIOD = 'memory.garbage-collection.period'; public const MEMORY_GLOBAL_LIMIT = 'memory.global-limit'; public const MEMORY_MAIN_HARD_LIMIT = 'memory.main-hard-limit'; public const MEMORY_MAIN_LIMIT = 'memory.main-limit'; public const MEMORY_MAX_CHUNKS = 'memory.max-chunks'; public const MEMORY_MAX_CHUNKS_CHUNK_RADIUS = 'memory.max-chunks.chunk-radius'; - public const MEMORY_MAX_CHUNKS_TRIGGER_CHUNK_COLLECT = 'memory.max-chunks.trigger-chunk-collect'; public const MEMORY_MEMORY_DUMP = 'memory.memory-dump'; public const MEMORY_MEMORY_DUMP_DUMP_ASYNC_WORKER = 'memory.memory-dump.dump-async-worker'; - public const MEMORY_WORLD_CACHES = 'memory.world-caches'; - public const MEMORY_WORLD_CACHES_DISABLE_CHUNK_CACHE = 'memory.world-caches.disable-chunk-cache'; - public const MEMORY_WORLD_CACHES_LOW_MEMORY_TRIGGER = 'memory.world-caches.low-memory-trigger'; public const NETWORK = 'network'; public const NETWORK_ASYNC_COMPRESSION = 'network.async-compression'; public const NETWORK_ASYNC_COMPRESSION_THRESHOLD = 'network.async-compression-threshold'; From cf1b360a62d986661ea4a9b5e00a7d5ec202ced2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 18:40:32 +0000 Subject: [PATCH 161/290] World: Prevent block cache from getting too big This has been a long-standing issue since at least 2016, and probably longer. Heavy use of getBlock(At) could cause the cache to blow up and use all available memory. Recently, it's become clear that unmanaged cache size is also a problem for GC, because the large number of objects blows up the GC root buffer. At first, this causes more frequent GC runs; later, the frequency of GC runs drops, but the performance cost of them goes up substantially because of the sheer number of objects. We can avoid this by trimming the cache when we detect that it's exceeded limits. I've implemented this in such a way that failing to update blockCacheSize in new code won't have lasting impacts, since the cache count will be recalculated during scheduled cache cleaning anyway. Closes #152. --- src/world/World.php | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index ff65377c0..c2d153921 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -167,6 +167,9 @@ class World implements ChunkManager{ public const DEFAULT_TICKED_BLOCKS_PER_SUBCHUNK_PER_TICK = 3; + //TODO: this could probably do with being a lot bigger + private const BLOCK_CACHE_SIZE_CAP = 2048; + /** * @var Player[] entity runtime ID => Player * @phpstan-var array @@ -202,6 +205,7 @@ class World implements ChunkManager{ * @phpstan-var array> */ private array $blockCache = []; + private int $blockCacheSize = 0; /** * @var AxisAlignedBB[][][] chunkHash => [relativeBlockHash => AxisAlignedBB[]] * @phpstan-var array>> @@ -653,6 +657,7 @@ class World implements ChunkManager{ $this->provider->close(); $this->blockCache = []; + $this->blockCacheSize = 0; $this->blockCollisionBoxCache = []; $this->unloaded = true; @@ -1138,13 +1143,16 @@ class World implements ChunkManager{ public function clearCache(bool $force = false) : void{ if($force){ $this->blockCache = []; + $this->blockCacheSize = 0; $this->blockCollisionBoxCache = []; }else{ - $count = 0; + //Recalculate this when we're asked - blockCacheSize may be higher than the real size + $this->blockCacheSize = 0; foreach($this->blockCache as $list){ - $count += count($list); - if($count > 2048){ + $this->blockCacheSize += count($list); + if($this->blockCacheSize > self::BLOCK_CACHE_SIZE_CAP){ $this->blockCache = []; + $this->blockCacheSize = 0; break; } } @@ -1152,7 +1160,7 @@ class World implements ChunkManager{ $count = 0; foreach($this->blockCollisionBoxCache as $list){ $count += count($list); - if($count > 2048){ + if($count > self::BLOCK_CACHE_SIZE_CAP){ //TODO: Is this really the best logic? $this->blockCollisionBoxCache = []; break; @@ -1161,6 +1169,19 @@ class World implements ChunkManager{ } } + private function trimBlockCache() : void{ + $before = $this->blockCacheSize; + //Since PHP maintains key order, earliest in foreach should be the oldest entries + //Older entries are less likely to be hot, so destroying these should usually have the lowest impact on performance + foreach($this->blockCache as $chunkHash => $blocks){ + unset($this->blockCache[$chunkHash]); + $this->blockCacheSize -= count($blocks); + if($this->blockCacheSize < self::BLOCK_CACHE_SIZE_CAP){ + break; + } + } + } + /** * @return true[] fullID => dummy * @phpstan-return array @@ -1921,6 +1942,10 @@ class World implements ChunkManager{ if($addToCache && $relativeBlockHash !== null){ $this->blockCache[$chunkHash][$relativeBlockHash] = $block; + + if(++$this->blockCacheSize >= self::BLOCK_CACHE_SIZE_CAP){ + $this->trimBlockCache(); + } } return $block; @@ -1967,6 +1992,7 @@ class World implements ChunkManager{ $relativeBlockHash = World::chunkBlockHash($x, $y, $z); unset($this->blockCache[$chunkHash][$relativeBlockHash]); + $this->blockCacheSize--; unset($this->blockCollisionBoxCache[$chunkHash][$relativeBlockHash]); //blocks like fences have collision boxes that reach into neighbouring blocks, so we need to invalidate the //caches for those blocks as well @@ -2570,6 +2596,7 @@ class World implements ChunkManager{ $this->chunks[$chunkHash] = $chunk; + $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); @@ -2854,6 +2881,8 @@ class World implements ChunkManager{ $this->logger->debug("Chunk $x $z has been upgraded, will be saved at the next autosave opportunity"); } $this->chunks[$chunkHash] = $chunk; + + $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); unset($this->blockCollisionBoxCache[$chunkHash]); @@ -3013,6 +3042,7 @@ class World implements ChunkManager{ } unset($this->chunks[$chunkHash]); + $this->blockCacheSize -= count($this->blockCache[$chunkHash] ?? []); unset($this->blockCache[$chunkHash]); unset($this->blockCollisionBoxCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); From 742aa46b88b4446d7922f8f1a30bd8db672516fd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 20:44:00 +0000 Subject: [PATCH 162/290] Separate memory dumping utilities from MemoryManager --- src/MemoryDump.php | 299 +++++++++++++++++++++++++ src/MemoryManager.php | 264 +--------------------- src/scheduler/DumpWorkerMemoryTask.php | 4 +- 3 files changed, 305 insertions(+), 262 deletions(-) create mode 100644 src/MemoryDump.php diff --git a/src/MemoryDump.php b/src/MemoryDump.php new file mode 100644 index 000000000..5f721e041 --- /dev/null +++ b/src/MemoryDump.php @@ -0,0 +1,299 @@ +getProperties() as $property){ + if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){ + continue; + } + + if(!$property->isInitialized()){ + continue; + } + + $staticCount++; + $staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + + if(count($staticProperties[$className]) === 0){ + unset($staticProperties[$className]); + } + + foreach($reflection->getMethods() as $method){ + if($method->getDeclaringClass()->getName() !== $reflection->getName()){ + continue; + } + $methodStatics = []; + foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){ + $methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + if(count($methodStatics) > 0){ + $functionStaticVars[$className . "::" . $method->getName()] = $methodStatics; + $functionStaticVarsCount += count($functionStaticVars); + } + } + } + + file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + $logger->info("Wrote $staticCount static properties"); + + $globalVariables = []; + $globalCount = 0; + + $ignoredGlobals = [ + 'GLOBALS' => true, + '_SERVER' => true, + '_REQUEST' => true, + '_POST' => true, + '_GET' => true, + '_FILES' => true, + '_ENV' => true, + '_COOKIE' => true, + '_SESSION' => true + ]; + + foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){ + if(isset($ignoredGlobals[$varName])){ + continue; + } + + $globalCount++; + $globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + + file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + $logger->info("Wrote $globalCount global variables"); + + foreach(get_defined_functions()["user"] as $function){ + $reflect = new \ReflectionFunction($function); + + $vars = []; + foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){ + $vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + if(count($vars) > 0){ + $functionStaticVars[$function] = $vars; + $functionStaticVarsCount += count($vars); + } + } + file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + $logger->info("Wrote $functionStaticVarsCount function static variables"); + + $data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + + do{ + $continue = false; + foreach(Utils::stringifyKeys($objects) as $hash => $object){ + if(!is_object($object)){ + continue; + } + $continue = true; + + $className = get_class($object); + if(!isset($instanceCounts[$className])){ + $instanceCounts[$className] = 1; + }else{ + $instanceCounts[$className]++; + } + + $objects[$hash] = true; + $info = [ + "information" => "$hash@$className", + ]; + if($object instanceof \Closure){ + $info["definition"] = Utils::getNiceClosureName($object); + $info["referencedVars"] = []; + $reflect = new \ReflectionFunction($object); + if(($closureThis = $reflect->getClosureThis()) !== null){ + $info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + + foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){ + $info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + }else{ + $reflection = new \ReflectionObject($object); + + $info["properties"] = []; + + for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){ + foreach($reflection->getProperties() as $property){ + if($property->isStatic()){ + continue; + } + + $name = $property->getName(); + if($reflection !== $original){ + if($property->isPrivate()){ + $name = $reflection->getName() . ":" . $name; + }else{ + continue; + } + } + if(!$property->isInitialized($object)){ + continue; + } + + $info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize); + } + } + } + + fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n"); + } + + }while($continue); + + $logger->info("Wrote " . count($objects) . " objects"); + + fclose($obData); + + file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + + arsort($instanceCounts, SORT_NUMERIC); + file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + + $logger->info("Finished!"); + + ini_set('memory_limit', $hardLimit); + } + + /** + * @param object[] $objects reference parameter + * @param int[] $refCounts reference parameter + * + * @phpstan-param array $objects + * @phpstan-param array $refCounts + * @phpstan-param-out array $objects + * @phpstan-param-out array $refCounts + */ + private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{ + if($maxNesting <= 0){ + return "(error) NESTING LIMIT REACHED"; + } + + --$maxNesting; + + if(is_object($from)){ + if(!isset($objects[$hash = spl_object_hash($from)])){ + $objects[$hash] = $from; + $refCounts[$hash] = 0; + } + + ++$refCounts[$hash]; + + $data = "(object) $hash"; + }elseif(is_array($from)){ + if($recursion >= 5){ + return "(error) ARRAY RECURSION LIMIT REACHED"; + } + $data = []; + $numeric = 0; + foreach(Utils::promoteKeys($from) as $key => $value){ + $data[$numeric] = [ + "k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), + "v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), + ]; + $numeric++; + } + }elseif(is_string($from)){ + $data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize); + }elseif(is_resource($from)){ + $data = "(resource) " . print_r($from, true); + }elseif(is_float($from)){ + $data = "(float) $from"; + }else{ + $data = $from; + } + + return $data; + } +} diff --git a/src/MemoryManager.php b/src/MemoryManager.php index da520d077..b83e99fa5 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -29,49 +29,21 @@ use pocketmine\scheduler\DumpWorkerMemoryTask; use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\timings\Timings; use pocketmine\utils\Process; -use pocketmine\utils\Utils; use pocketmine\YmlServerProperties as Yml; -use Symfony\Component\Filesystem\Path; -use function arsort; -use function count; -use function fclose; -use function file_exists; -use function file_put_contents; -use function fopen; -use function fwrite; use function gc_collect_cycles; use function gc_disable; use function gc_mem_caches; use function gc_status; -use function get_class; -use function get_declared_classes; -use function get_defined_functions; use function hrtime; -use function ini_get; use function ini_set; use function intdiv; -use function is_array; -use function is_float; -use function is_object; -use function is_resource; -use function is_string; -use function json_encode; use function max; use function mb_strtoupper; use function min; -use function mkdir; use function number_format; use function preg_match; -use function print_r; use function round; -use function spl_object_hash; use function sprintf; -use function strlen; -use function substr; -use const JSON_PRETTY_PRINT; -use const JSON_THROW_ON_ERROR; -use const JSON_UNESCAPED_SLASHES; -use const SORT_NUMERIC; class MemoryManager{ private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND; @@ -295,7 +267,7 @@ class MemoryManager{ public function dumpServerMemory(string $outputFolder, int $maxNesting, int $maxStringSize) : void{ $logger = new \PrefixedLogger($this->server->getLogger(), "Memory Dump"); $logger->notice("After the memory dump is done, the server might crash"); - self::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger); + MemoryDump::dumpMemory($this->server, $outputFolder, $maxNesting, $maxStringSize, $logger); if($this->dumpWorkers){ $pool = $this->server->getAsyncPool(); @@ -307,238 +279,10 @@ class MemoryManager{ /** * Static memory dumper accessible from any thread. + * @deprecated + * @see MemoryDump */ public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{ - $hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist"); - ini_set('memory_limit', '-1'); - gc_disable(); - - if(!file_exists($outputFolder)){ - mkdir($outputFolder, 0777, true); - } - - $obData = Utils::assumeNotFalse(fopen(Path::join($outputFolder, "objects.js"), "wb+")); - - $objects = []; - - $refCounts = []; - - $instanceCounts = []; - - $staticProperties = []; - $staticCount = 0; - - $functionStaticVars = []; - $functionStaticVarsCount = 0; - - foreach(get_declared_classes() as $className){ - $reflection = new \ReflectionClass($className); - $staticProperties[$className] = []; - foreach($reflection->getProperties() as $property){ - if(!$property->isStatic() || $property->getDeclaringClass()->getName() !== $className){ - continue; - } - - if(!$property->isInitialized()){ - continue; - } - - $staticCount++; - $staticProperties[$className][$property->getName()] = self::continueDump($property->getValue(), $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - - if(count($staticProperties[$className]) === 0){ - unset($staticProperties[$className]); - } - - foreach($reflection->getMethods() as $method){ - if($method->getDeclaringClass()->getName() !== $reflection->getName()){ - continue; - } - $methodStatics = []; - foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){ - $methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - if(count($methodStatics) > 0){ - $functionStaticVars[$className . "::" . $method->getName()] = $methodStatics; - $functionStaticVarsCount += count($functionStaticVars); - } - } - } - - file_put_contents(Path::join($outputFolder, "staticProperties.js"), json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - $logger->info("Wrote $staticCount static properties"); - - $globalVariables = []; - $globalCount = 0; - - $ignoredGlobals = [ - 'GLOBALS' => true, - '_SERVER' => true, - '_REQUEST' => true, - '_POST' => true, - '_GET' => true, - '_FILES' => true, - '_ENV' => true, - '_COOKIE' => true, - '_SESSION' => true - ]; - - foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){ - if(isset($ignoredGlobals[$varName])){ - continue; - } - - $globalCount++; - $globalVariables[$varName] = self::continueDump($value, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - - file_put_contents(Path::join($outputFolder, "globalVariables.js"), json_encode($globalVariables, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - $logger->info("Wrote $globalCount global variables"); - - foreach(get_defined_functions()["user"] as $function){ - $reflect = new \ReflectionFunction($function); - - $vars = []; - foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){ - $vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - if(count($vars) > 0){ - $functionStaticVars[$function] = $vars; - $functionStaticVarsCount += count($vars); - } - } - file_put_contents(Path::join($outputFolder, 'functionStaticVars.js'), json_encode($functionStaticVars, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - $logger->info("Wrote $functionStaticVarsCount function static variables"); - - $data = self::continueDump($startingObject, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - - do{ - $continue = false; - foreach(Utils::stringifyKeys($objects) as $hash => $object){ - if(!is_object($object)){ - continue; - } - $continue = true; - - $className = get_class($object); - if(!isset($instanceCounts[$className])){ - $instanceCounts[$className] = 1; - }else{ - $instanceCounts[$className]++; - } - - $objects[$hash] = true; - $info = [ - "information" => "$hash@$className", - ]; - if($object instanceof \Closure){ - $info["definition"] = Utils::getNiceClosureName($object); - $info["referencedVars"] = []; - $reflect = new \ReflectionFunction($object); - if(($closureThis = $reflect->getClosureThis()) !== null){ - $info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - - foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){ - $info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - }else{ - $reflection = new \ReflectionObject($object); - - $info["properties"] = []; - - for($original = $reflection; $reflection !== false; $reflection = $reflection->getParentClass()){ - foreach($reflection->getProperties() as $property){ - if($property->isStatic()){ - continue; - } - - $name = $property->getName(); - if($reflection !== $original){ - if($property->isPrivate()){ - $name = $reflection->getName() . ":" . $name; - }else{ - continue; - } - } - if(!$property->isInitialized($object)){ - continue; - } - - $info["properties"][$name] = self::continueDump($property->getValue($object), $objects, $refCounts, 0, $maxNesting, $maxStringSize); - } - } - } - - fwrite($obData, json_encode($info, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n"); - } - - }while($continue); - - $logger->info("Wrote " . count($objects) . " objects"); - - fclose($obData); - - file_put_contents(Path::join($outputFolder, "serverEntry.js"), json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - file_put_contents(Path::join($outputFolder, "referenceCounts.js"), json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - - arsort($instanceCounts, SORT_NUMERIC); - file_put_contents(Path::join($outputFolder, "instanceCounts.js"), json_encode($instanceCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); - - $logger->info("Finished!"); - - ini_set('memory_limit', $hardLimit); - } - - /** - * @param object[] $objects reference parameter - * @param int[] $refCounts reference parameter - * - * @phpstan-param array $objects - * @phpstan-param array $refCounts - * @phpstan-param-out array $objects - * @phpstan-param-out array $refCounts - */ - private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{ - if($maxNesting <= 0){ - return "(error) NESTING LIMIT REACHED"; - } - - --$maxNesting; - - if(is_object($from)){ - if(!isset($objects[$hash = spl_object_hash($from)])){ - $objects[$hash] = $from; - $refCounts[$hash] = 0; - } - - ++$refCounts[$hash]; - - $data = "(object) $hash"; - }elseif(is_array($from)){ - if($recursion >= 5){ - return "(error) ARRAY RECURSION LIMIT REACHED"; - } - $data = []; - $numeric = 0; - foreach(Utils::promoteKeys($from) as $key => $value){ - $data[$numeric] = [ - "k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), - "v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize), - ]; - $numeric++; - } - }elseif(is_string($from)){ - $data = "(string) len(" . strlen($from) . ") " . substr(Utils::printable($from), 0, $maxStringSize); - }elseif(is_resource($from)){ - $data = "(resource) " . print_r($from, true); - }elseif(is_float($from)){ - $data = "(float) $from"; - }else{ - $data = $from; - } - - return $data; + MemoryDump::dumpMemory($startingObject, $outputFolder, $maxNesting, $maxStringSize, $logger); } } diff --git a/src/scheduler/DumpWorkerMemoryTask.php b/src/scheduler/DumpWorkerMemoryTask.php index 5ef787b5b..fac8d6368 100644 --- a/src/scheduler/DumpWorkerMemoryTask.php +++ b/src/scheduler/DumpWorkerMemoryTask.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\scheduler; use pmmp\thread\Thread as NativeThread; -use pocketmine\MemoryManager; +use pocketmine\MemoryDump; use Symfony\Component\Filesystem\Path; use function assert; @@ -41,7 +41,7 @@ class DumpWorkerMemoryTask extends AsyncTask{ public function onRun() : void{ $worker = NativeThread::getCurrentThread(); assert($worker instanceof AsyncWorker); - MemoryManager::dumpMemory( + MemoryDump::dumpMemory( $worker, Path::join($this->outputFolder, "AsyncWorker#" . $worker->getAsyncWorkerId()), $this->maxNesting, From 45482e868de8100d4eeaf27ec44f3f2d0ed4a5e9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 20:45:51 +0000 Subject: [PATCH 163/290] Fixed AsyncWorker GC not getting re-enabled after memory dump async workers still use automatic GC for now. We should probably switch to manual GC at some point, but it's not a priority right now. --- src/MemoryDump.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MemoryDump.php b/src/MemoryDump.php index 5f721e041..0503f747c 100644 --- a/src/MemoryDump.php +++ b/src/MemoryDump.php @@ -33,6 +33,7 @@ use function file_put_contents; use function fopen; use function fwrite; use function gc_disable; +use function gc_enabled; use function get_class; use function get_declared_classes; use function get_defined_functions; @@ -66,6 +67,7 @@ final class MemoryDump{ public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{ $hardLimit = Utils::assumeNotFalse(ini_get('memory_limit'), "memory_limit INI directive should always exist"); ini_set('memory_limit', '-1'); + $gcEnabled = gc_enabled(); gc_disable(); if(!file_exists($outputFolder)){ @@ -244,6 +246,9 @@ final class MemoryDump{ $logger->info("Finished!"); ini_set('memory_limit', $hardLimit); + if($gcEnabled){ + gc_enable(); + } } /** From 8f536e6f214c7192ca1bbc2b3182613ed99e9a2a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 20:46:10 +0000 Subject: [PATCH 164/290] always the CS --- src/MemoryDump.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MemoryDump.php b/src/MemoryDump.php index 0503f747c..5a9c274cb 100644 --- a/src/MemoryDump.php +++ b/src/MemoryDump.php @@ -33,6 +33,7 @@ use function file_put_contents; use function fopen; use function fwrite; use function gc_disable; +use function gc_enable; use function gc_enabled; use function get_class; use function get_declared_classes; From 42f90e94ffb7cdd30162eb450be073e2be6e5a06 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 21:25:32 +0000 Subject: [PATCH 165/290] AsyncWorker now manually triggers GC at the end of each task run, similar to the main thread this avoids costly GC runs during hot code. --- src/GarbageCollectorManager.php | 102 +++++++++++++++++++++++ src/MemoryManager.php | 48 +---------- src/network/mcpe/raklib/RakLibServer.php | 4 + src/scheduler/AsyncTask.php | 1 + src/scheduler/AsyncWorker.php | 13 ++- 5 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 src/GarbageCollectorManager.php diff --git a/src/GarbageCollectorManager.php b/src/GarbageCollectorManager.php new file mode 100644 index 000000000..c5cd1f9ed --- /dev/null +++ b/src/GarbageCollectorManager.php @@ -0,0 +1,102 @@ +logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector"); + $this->timings = new TimingsHandler("Cyclic Garbage Collector"); + } + + private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{ + //TODO Very simple heuristic for dynamic GC buffer resizing: + //If there are "too few" collections, increase the collection threshold + //by a fixed step + //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14 + if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){ + $this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP); + }elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){ + $this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP); + } + } + + public function getThreshold() : int{ return $this->threshold; } + + public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; } + + public function maybeCollectCycles() : int{ + $rootsBefore = gc_status()["roots"]; + if($rootsBefore < $this->threshold){ + return 0; + } + + $this->timings->startTiming(); + + $start = hrtime(true); + $cycles = gc_collect_cycles(); + $end = hrtime(true); + + $rootsAfter = gc_status()["roots"]; + $this->adjustGcThreshold($cycles, $rootsAfter); + + $this->timings->stopTiming(); + + $time = $end - $start; + $this->collectionTimeTotalNs += $time; + $this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->collectionTimeTotalNs) . " ns"); + + return $cycles; + } +} diff --git a/src/MemoryManager.php b/src/MemoryManager.php index b83e99fa5..f541514dd 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -31,16 +31,11 @@ use pocketmine\timings\Timings; use pocketmine\utils\Process; use pocketmine\YmlServerProperties as Yml; use function gc_collect_cycles; -use function gc_disable; use function gc_mem_caches; -use function gc_status; -use function hrtime; use function ini_set; use function intdiv; -use function max; use function mb_strtoupper; use function min; -use function number_format; use function preg_match; use function round; use function sprintf; @@ -50,13 +45,7 @@ class MemoryManager{ private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2; private const DEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND; - //These constants are copied from Zend/zend_gc.c as of PHP 8.3.14 - //TODO: These values could be adjusted to better suit PM, but for now we just want to mirror PHP GC to minimize - //behavioural changes. - private const GC_THRESHOLD_TRIGGER = 100; - private const GC_THRESHOLD_MAX = 1_000_000_000; - private const GC_THRESHOLD_DEFAULT = 10_001; - private const GC_THRESHOLD_STEP = 10_000; + private GarbageCollectorManager $cycleGcManager; private int $memoryLimit; private int $globalMemoryLimit; @@ -72,9 +61,6 @@ class MemoryManager{ private int $garbageCollectionPeriod; private int $garbageCollectionTicker = 0; - private int $cycleCollectionThreshold = self::GC_THRESHOLD_DEFAULT; - private int $cycleCollectionTimeTotalNs = 0; - private int $lowMemChunkRadiusOverride; private bool $dumpWorkers = true; @@ -85,9 +71,9 @@ class MemoryManager{ private Server $server ){ $this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager"); + $this->cycleGcManager = new GarbageCollectorManager($this->logger); $this->init($server->getConfigGroup()); - gc_disable(); } private function init(ServerConfigGroup $config) : void{ @@ -174,18 +160,6 @@ class MemoryManager{ $this->logger->debug(sprintf("Freed %gMB, $cycles cycles", round(($ev->getMemoryFreed() / 1024) / 1024, 2))); } - private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{ - //TODO Very simple heuristic for dynamic GC buffer resizing: - //If there are "too few" collections, increase the collection threshold - //by a fixed step - //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14 - if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->cycleCollectionThreshold){ - $this->cycleCollectionThreshold = min(self::GC_THRESHOLD_MAX, $this->cycleCollectionThreshold + self::GC_THRESHOLD_STEP); - }elseif($this->cycleCollectionThreshold > self::GC_THRESHOLD_DEFAULT){ - $this->cycleCollectionThreshold = max(self::GC_THRESHOLD_DEFAULT, $this->cycleCollectionThreshold - self::GC_THRESHOLD_STEP); - } - } - /** * Called every tick to update the memory manager state. */ @@ -218,25 +192,11 @@ class MemoryManager{ } } - $rootsBefore = gc_status()["roots"]; if($this->garbageCollectionPeriod > 0 && ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){ $this->garbageCollectionTicker = 0; $this->triggerGarbageCollector(); - }elseif($rootsBefore >= $this->cycleCollectionThreshold){ - Timings::$garbageCollector->startTiming(); - - $start = hrtime(true); - $cycles = gc_collect_cycles(); - $end = hrtime(true); - - $rootsAfter = gc_status()["roots"]; - $this->adjustGcThreshold($cycles, $rootsAfter); - - Timings::$garbageCollector->stopTiming(); - - $time = $end - $start; - $this->cycleCollectionTimeTotalNs += $time; - $this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->cycleCollectionTimeTotalNs) . " ns"); + }else{ + $this->cycleGcManager->maybeCollectCycles(); } Timings::$memoryManager->stopTiming(); diff --git a/src/network/mcpe/raklib/RakLibServer.php b/src/network/mcpe/raklib/RakLibServer.php index 5137b94ba..3b4b8da74 100644 --- a/src/network/mcpe/raklib/RakLibServer.php +++ b/src/network/mcpe/raklib/RakLibServer.php @@ -82,7 +82,11 @@ class RakLibServer extends Thread{ } protected function onRun() : void{ + //TODO: switch to manually triggered GC + //the best time to do it is between ticks when the server would otherwise be sleeping, but RakLib's current + //design doesn't allow this as of 1.1.1 gc_enable(); + ini_set("display_errors", '1'); ini_set("display_startup_errors", '1'); \GlobalLogger::set($this->logger); diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index 1175b6fc4..a9b466f40 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -93,6 +93,7 @@ abstract class AsyncTask extends Runnable{ $this->finished = true; AsyncWorker::getNotifier()->wakeupSleeper(); + AsyncWorker::maybeCollectCycles(); } /** diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index 5fdfb1ebb..5f911bc1c 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -24,12 +24,12 @@ declare(strict_types=1); namespace pocketmine\scheduler; use pmmp\thread\Thread as NativeThread; +use pocketmine\GarbageCollectorManager; use pocketmine\snooze\SleeperHandlerEntry; use pocketmine\snooze\SleeperNotifier; use pocketmine\thread\log\ThreadSafeLogger; use pocketmine\thread\Worker; use pocketmine\utils\AssumptionFailedError; -use function gc_enable; use function ini_set; class AsyncWorker extends Worker{ @@ -37,6 +37,7 @@ class AsyncWorker extends Worker{ private static array $store = []; private static ?SleeperNotifier $notifier = null; + private static ?GarbageCollectorManager $cycleGcManager = null; public function __construct( private ThreadSafeLogger $logger, @@ -52,11 +53,16 @@ class AsyncWorker extends Worker{ throw new AssumptionFailedError("SleeperNotifier not found in thread-local storage"); } + public static function maybeCollectCycles() : void{ + if(self::$cycleGcManager === null){ + throw new AssumptionFailedError("GarbageCollectorManager not found in thread-local storage"); + } + self::$cycleGcManager->maybeCollectCycles(); + } + protected function onRun() : void{ \GlobalLogger::set($this->logger); - gc_enable(); - if($this->memoryLimit > 0){ ini_set('memory_limit', $this->memoryLimit . 'M'); $this->logger->debug("Set memory limit to " . $this->memoryLimit . " MB"); @@ -66,6 +72,7 @@ class AsyncWorker extends Worker{ } self::$notifier = $this->sleeperEntry->createNotifier(); + self::$cycleGcManager = new GarbageCollectorManager($this->logger); } public function getLogger() : ThreadSafeLogger{ From 80899ea72c0b6a8bdedb606205b83c6775fb8f69 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 15 Dec 2024 21:34:16 +0000 Subject: [PATCH 166/290] Make sure timings are counted under the proper parents --- src/GarbageCollectorManager.php | 3 ++- src/MemoryManager.php | 2 +- src/scheduler/AsyncWorker.php | 3 ++- src/timings/Timings.php | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/GarbageCollectorManager.php b/src/GarbageCollectorManager.php index c5cd1f9ed..a8912a90d 100644 --- a/src/GarbageCollectorManager.php +++ b/src/GarbageCollectorManager.php @@ -54,10 +54,11 @@ final class GarbageCollectorManager{ public function __construct( \Logger $logger, + ?TimingsHandler $parentTimings, ){ gc_disable(); $this->logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector"); - $this->timings = new TimingsHandler("Cyclic Garbage Collector"); + $this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings); } private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{ diff --git a/src/MemoryManager.php b/src/MemoryManager.php index f541514dd..2b7f5f1d3 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -71,7 +71,7 @@ class MemoryManager{ private Server $server ){ $this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager"); - $this->cycleGcManager = new GarbageCollectorManager($this->logger); + $this->cycleGcManager = new GarbageCollectorManager($this->logger, Timings::$memoryManager); $this->init($server->getConfigGroup()); } diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index 5f911bc1c..e208d427c 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -29,6 +29,7 @@ use pocketmine\snooze\SleeperHandlerEntry; use pocketmine\snooze\SleeperNotifier; use pocketmine\thread\log\ThreadSafeLogger; use pocketmine\thread\Worker; +use pocketmine\timings\Timings; use pocketmine\utils\AssumptionFailedError; use function ini_set; @@ -72,7 +73,7 @@ class AsyncWorker extends Worker{ } self::$notifier = $this->sleeperEntry->createNotifier(); - self::$cycleGcManager = new GarbageCollectorManager($this->logger); + self::$cycleGcManager = new GarbageCollectorManager($this->logger, Timings::$asyncTaskWorkers); } public function getLogger() : ThreadSafeLogger{ diff --git a/src/timings/Timings.php b/src/timings/Timings.php index e71700023..ee737f537 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -131,7 +131,7 @@ abstract class Timings{ /** @var TimingsHandler[] */ private static array $asyncTaskError = []; - private static TimingsHandler $asyncTaskWorkers; + public static TimingsHandler $asyncTaskWorkers; /** @var TimingsHandler[] */ private static array $asyncTaskRun = []; From aee358d3296550d777383c7791423f072d6e2cba Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Mon, 16 Dec 2024 03:11:07 +0000 Subject: [PATCH 167/290] This timings handler management is a crap design This has bitten me on the ass so many times now --- src/scheduler/AsyncWorker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index e208d427c..528d632d1 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -73,6 +73,7 @@ class AsyncWorker extends Worker{ } self::$notifier = $this->sleeperEntry->createNotifier(); + Timings::init(); self::$cycleGcManager = new GarbageCollectorManager($this->logger, Timings::$asyncTaskWorkers); } From 3e69ee87e40a2fb993f6b9e7fc631588084550e7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 00:14:18 +0000 Subject: [PATCH 168/290] Remove deprecated stuff except Permission subscriptions; turns out we still need those perhaps they should be marked @internal --- src/MemoryManager.php | 16 ------ src/block/SweetBerryBush.php | 11 +--- src/block/tile/Spawnable.php | 14 ----- src/data/runtime/RuntimeDataDescriber.php | 14 ----- src/data/runtime/RuntimeDataReader.php | 23 -------- .../runtime/RuntimeDataSizeCalculator.php | 17 ------ src/data/runtime/RuntimeDataWriter.php | 23 -------- .../transaction/InventoryTransaction.php | 1 - .../transaction/action/InventoryAction.php | 9 ---- src/scheduler/AsyncPool.php | 17 ------ src/scheduler/AsyncTask.php | 52 ------------------- src/scheduler/AsyncWorker.php | 46 ---------------- src/timings/Timings.php | 43 --------------- src/timings/TimingsHandler.php | 13 ----- src/utils/TextFormat.php | 4 -- src/wizard/SetupWizard.php | 7 --- src/world/World.php | 19 ------- tests/phpunit/scheduler/AsyncPoolTest.php | 9 ---- .../PublishProgressRaceAsyncTask.php | 40 -------------- 19 files changed, 2 insertions(+), 376 deletions(-) delete mode 100644 tests/phpunit/scheduler/PublishProgressRaceAsyncTask.php diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 2b7f5f1d3..9a2cbd2ad 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -123,13 +123,6 @@ class MemoryManager{ return $this->globalMemoryLimit; } - /** - * @deprecated - */ - public function canUseChunkCache() : bool{ - return !$this->lowMemory; - } - /** * Returns the allowed chunk radius based on the current memory usage. */ @@ -236,13 +229,4 @@ class MemoryManager{ } } } - - /** - * Static memory dumper accessible from any thread. - * @deprecated - * @see MemoryDump - */ - public static function dumpMemory(mixed $startingObject, string $outputFolder, int $maxNesting, int $maxStringSize, \Logger $logger) : void{ - MemoryDump::dumpMemory($startingObject, $outputFolder, $maxNesting, $maxStringSize, $logger); - } } diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index eabdde118..5fd7fcaa1 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -58,17 +58,10 @@ class SweetBerryBush extends Flowable{ return 0; } - /** - * @deprecated - */ - protected function canBeSupportedBy(Block $block) : bool{ - return $block->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?) - ($block->hasTypeTag(BlockTypeTags::DIRT) || $block->hasTypeTag(BlockTypeTags::MUD)); - } - private function canBeSupportedAt(Block $block) : bool{ $supportBlock = $block->getSide(Facing::DOWN); - return $this->canBeSupportedBy($supportBlock); + return $supportBlock->getTypeId() !== BlockTypeIds::FARMLAND && //bedrock-specific thing (bug?) + ($supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD)); } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ diff --git a/src/block/tile/Spawnable.php b/src/block/tile/Spawnable.php index 67bc72fd9..956652eac 100644 --- a/src/block/tile/Spawnable.php +++ b/src/block/tile/Spawnable.php @@ -35,20 +35,6 @@ abstract class Spawnable extends Tile{ /** @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null */ private ?CacheableNbt $spawnCompoundCache = null; - /** - * @deprecated - */ - public function isDirty() : bool{ - return $this->spawnCompoundCache === null; - } - - /** - * @deprecated - */ - public function setDirty(bool $dirty = true) : void{ - $this->clearSpawnCompoundCache(); - } - public function clearSpawnCompoundCache() : void{ $this->spawnCompoundCache = null; } diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 8a2868555..8df675f70 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Facing; @@ -38,11 +37,6 @@ use pocketmine\math\Facing; interface RuntimeDataDescriber{ public function int(int $bits, int &$value) : void; - /** - * @deprecated Use {@link RuntimeDataDescriber::boundedIntAuto()} instead. - */ - public function boundedInt(int $bits, int $min, int $max, int &$value) : void; - /** * Same as boundedInt() but automatically calculates the required number of bits from the range. * The range bounds must be constant. @@ -77,14 +71,6 @@ interface RuntimeDataDescriber{ */ public function wallConnections(array &$connections) : void; - /** - * @param BrewingStandSlot[] $slots - * @phpstan-param array $slots - * - * @deprecated Use {@link enumSet()} instead. - */ - public function brewingStandSlots(array &$slots) : void; - public function railShape(int &$railShape) : void; public function straightOnlyRailShape(int &$railShape) : void; diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index 1dd0c681b..c230a52ae 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\RailConnectionInfo; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; @@ -57,18 +56,6 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $value = $this->readInt($bits); } - /** - * @deprecated Use {@link self::boundedIntAuto()} instead. - */ - public function boundedInt(int $bits, int $min, int $max, int &$value) : void{ - $offset = $this->offset; - $this->boundedIntAuto($min, $max, $value); - $actualBits = $this->offset - $offset; - if($this->offset !== $offset + $bits){ - throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation."); - } - } - private function readBoundedIntAuto(int $min, int $max) : int{ $bits = ((int) log($max - $min, 2)) + 1; $result = $this->readInt($bits) + $min; @@ -190,16 +177,6 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $connections = $result; } - /** - * @param BrewingStandSlot[] $slots - * @phpstan-param array $slots - * - * @deprecated Use {@link enumSet()} instead. - */ - public function brewingStandSlots(array &$slots) : void{ - $this->enumSet($slots, BrewingStandSlot::cases()); - } - public function railShape(int &$railShape) : void{ $result = $this->readInt(4); if(!isset(RailConnectionInfo::CONNECTIONS[$result]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$result])){ diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 1563e4886..6725aace6 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\BrewingStandSlot; use pocketmine\math\Facing; use function count; use function log; @@ -43,18 +42,6 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits($bits); } - /** - * @deprecated Use {@link self::boundedIntAuto()} instead. - */ - public function boundedInt(int $bits, int $min, int $max, int &$value) : void{ - $currentBits = $this->bits; - $this->boundedIntAuto($min, $max, $value); - $actualBits = $this->bits - $currentBits; - if($actualBits !== $bits){ - throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation."); - } - } - public function boundedIntAuto(int $min, int $max, int &$value) : void{ $this->addBits(((int) log($max - $min, 2)) + 1); } @@ -95,10 +82,6 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ $this->addBits(7); } - public function brewingStandSlots(array &$slots) : void{ - $this->addBits(count(BrewingStandSlot::cases())); - } - public function railShape(int &$railShape) : void{ $this->addBits(4); } diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index a26c842cd..382aa9bf6 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; @@ -55,18 +54,6 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeInt($bits, $value); } - /** - * @deprecated Use {@link self::boundedIntAuto()} instead. - */ - public function boundedInt(int $bits, int $min, int $max, int &$value) : void{ - $offset = $this->offset; - $this->writeBoundedIntAuto($min, $max, $value); - $actualBits = $this->offset - $offset; - if($actualBits !== $bits){ - throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation."); - } - } - private function writeBoundedIntAuto(int $min, int $max, int $value) : void{ if($value < $min || $value > $max){ throw new \InvalidArgumentException("Value $value is outside the range $min - $max"); @@ -168,16 +155,6 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->writeBoundedIntAuto(0, (3 ** 4) - 1, $packed); } - /** - * @param BrewingStandSlot[] $slots - * @phpstan-param array $slots - * - * @deprecated Use {@link enumSet()} instead. - */ - public function brewingStandSlots(array &$slots) : void{ - $this->enumSet($slots, BrewingStandSlot::cases()); - } - public function railShape(int &$railShape) : void{ $this->int(4, $railShape); } diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index 47290e401..cb37edfa2 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -110,7 +110,6 @@ class InventoryTransaction{ public function addAction(InventoryAction $action) : void{ if(!isset($this->actions[$hash = spl_object_id($action)])){ $this->actions[$hash] = $action; - $action->onAddToTransaction($this); if($action instanceof SlotChangeAction && !isset($this->inventories[$inventoryId = spl_object_id($action->getInventory())])){ $this->inventories[$inventoryId] = $action->getInventory(); } diff --git a/src/inventory/transaction/action/InventoryAction.php b/src/inventory/transaction/action/InventoryAction.php index 2f0db083c..e24498519 100644 --- a/src/inventory/transaction/action/InventoryAction.php +++ b/src/inventory/transaction/action/InventoryAction.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\inventory\transaction\action; -use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\Item; use pocketmine\player\Player; @@ -58,14 +57,6 @@ abstract class InventoryAction{ */ abstract public function validate(Player $source) : void; - /** - * Called when the action is added to the specified InventoryTransaction. - * @deprecated - */ - public function onAddToTransaction(InventoryTransaction $transaction) : void{ - - } - /** * Called by inventory transactions before any actions are processed. If this returns false, the transaction will * be cancelled. diff --git a/src/scheduler/AsyncPool.php b/src/scheduler/AsyncPool.php index bb79df507..3ed8f8095 100644 --- a/src/scheduler/AsyncPool.php +++ b/src/scheduler/AsyncPool.php @@ -266,22 +266,11 @@ class AsyncPool{ $this->checkCrashedWorker($worker, $task); throw new AssumptionFailedError("checkCrashedWorker() should have thrown an exception, making this unreachable"); }else{ - /* - * It's possible for a task to submit a progress update and then finish before the progress - * update is detected by the parent thread, so here we consume any missed updates. - * - * When this happens, it's possible for a progress update to arrive between the previous - * checkProgressUpdates() call and the next isGarbage() call, causing progress updates to be - * lost. Thus, it's necessary to do one last check here to make sure all progress updates have - * been consumed before completing. - */ - $this->checkTaskProgressUpdates($task); Timings::getAsyncTaskCompletionTimings($task)->time(function() use ($task) : void{ $task->onCompletion(); }); } }else{ - $this->checkTaskProgressUpdates($task); $more = true; break; //current task is still running, skip to next worker } @@ -329,10 +318,4 @@ class AsyncPool{ } $this->workers = []; } - - private function checkTaskProgressUpdates(AsyncTask $task) : void{ - Timings::getAsyncTaskProgressUpdateTimings($task)->time(function() use ($task) : void{ - $task->checkProgressUpdates(); - }); - } } diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index 0e9b06d7b..9e9dd7a77 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -25,12 +25,9 @@ namespace pocketmine\scheduler; use pmmp\thread\Runnable; use pmmp\thread\ThreadSafe; -use pmmp\thread\ThreadSafeArray; use pocketmine\thread\NonThreadSafeValue; use pocketmine\timings\Timings; use function array_key_exists; -use function igbinary_serialize; -use function igbinary_unserialize; use function is_null; use function is_scalar; use function spl_object_id; @@ -68,12 +65,6 @@ abstract class AsyncTask extends Runnable{ */ private static array $threadLocalStorage = []; - /** - * @phpstan-var ThreadSafeArray|null - * @deprecated - */ - private ?ThreadSafeArray $progressUpdates = null; - private ThreadSafe|string|int|bool|null|float $result = null; private bool $submitted = false; @@ -143,49 +134,6 @@ abstract class AsyncTask extends Runnable{ } - /** - * @deprecated - * - * Call this method from {@link AsyncTask::onRun} (AsyncTask execution thread) to schedule a call to - * {@link AsyncTask::onProgressUpdate} from the main thread with the given progress parameter. - * - * @param mixed $progress A value that can be safely serialize()'ed. - */ - public function publishProgress(mixed $progress) : void{ - $progressUpdates = $this->progressUpdates; - if($progressUpdates === null){ - $progressUpdates = $this->progressUpdates = new ThreadSafeArray(); - } - $progressUpdates[] = igbinary_serialize($progress) ?? throw new \InvalidArgumentException("Progress must be serializable"); - } - - /** - * @deprecated - * @internal Only call from AsyncPool.php on the main thread - */ - public function checkProgressUpdates() : void{ - $progressUpdates = $this->progressUpdates; - if($progressUpdates !== null){ - while(($progress = $progressUpdates->shift()) !== null){ - $this->onProgressUpdate(igbinary_unserialize($progress)); - } - } - } - - /** - * @deprecated - * - * Called from the main thread after {@link AsyncTask::publishProgress} is called. - * All {@link AsyncTask::publishProgress} calls should result in {@link AsyncTask::onProgressUpdate} calls before - * {@link AsyncTask::onCompletion} is called. - * - * @param mixed $progress The parameter passed to {@link AsyncTask#publishProgress}. It is serialize()'ed - * and then unserialize()'ed, as if it has been cloned. - */ - public function onProgressUpdate($progress) : void{ - - } - /** * Saves mixed data in thread-local storage. Data stored using this storage is **only accessible from the thread it * was stored on**. Data stored using this method will **not** be serialized. diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index 528d632d1..6684d551f 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\scheduler; -use pmmp\thread\Thread as NativeThread; use pocketmine\GarbageCollectorManager; use pocketmine\snooze\SleeperHandlerEntry; use pocketmine\snooze\SleeperNotifier; @@ -34,9 +33,6 @@ use pocketmine\utils\AssumptionFailedError; use function ini_set; class AsyncWorker extends Worker{ - /** @var mixed[] */ - private static array $store = []; - private static ?SleeperNotifier $notifier = null; private static ?GarbageCollectorManager $cycleGcManager = null; @@ -88,46 +84,4 @@ class AsyncWorker extends Worker{ public function getAsyncWorkerId() : int{ return $this->id; } - - /** - * Saves mixed data into the worker's thread-local object store. This can be used to store objects which you - * want to use on this worker thread from multiple AsyncTasks. - * - * @deprecated Use static class properties instead. - */ - public function saveToThreadStore(string $identifier, mixed $value) : void{ - if(NativeThread::getCurrentThread() !== $this){ - throw new \LogicException("Thread-local data can only be stored in the thread context"); - } - self::$store[$identifier] = $value; - } - - /** - * Retrieves mixed data from the worker's thread-local object store. - * - * Note that the thread-local object store could be cleared and your data might not exist, so your code should - * account for the possibility that what you're trying to retrieve might not exist. - * - * Objects stored in this storage may ONLY be retrieved while the task is running. - * - * @deprecated Use static class properties instead. - */ - public function getFromThreadStore(string $identifier) : mixed{ - if(NativeThread::getCurrentThread() !== $this){ - throw new \LogicException("Thread-local data can only be fetched in the thread context"); - } - return self::$store[$identifier] ?? null; - } - - /** - * Removes previously-stored mixed data from the worker's thread-local object store. - * - * @deprecated Use static class properties instead. - */ - public function removeFromThreadStore(string $identifier) : void{ - if(NativeThread::getCurrentThread() !== $this){ - throw new \LogicException("Thread-local data can only be removed in the thread context"); - } - unset(self::$store[$identifier]); - } } diff --git a/src/timings/Timings.php b/src/timings/Timings.php index ee737f537..b46c27d58 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -37,8 +37,6 @@ use function str_starts_with; abstract class Timings{ public const GROUP_MINECRAFT = "Minecraft"; - /** @deprecated No longer used */ - public const GROUP_BREAKDOWN = "Minecraft - Breakdown"; private static bool $initialized = false; @@ -119,17 +117,9 @@ abstract class Timings{ /** @var TimingsHandler[][] */ private static array $eventHandlers = []; - private static TimingsHandler $asyncTaskProgressUpdateParent; private static TimingsHandler $asyncTaskCompletionParent; - private static TimingsHandler $asyncTaskErrorParent; - - /** @var TimingsHandler[] */ - private static array $asyncTaskProgressUpdate = []; - /** @var TimingsHandler[] */ private static array $asyncTaskCompletion = []; - /** @var TimingsHandler[] */ - private static array $asyncTaskError = []; public static TimingsHandler $asyncTaskWorkers; /** @var TimingsHandler[] */ @@ -190,9 +180,7 @@ abstract class Timings{ self::$schedulerSync = new TimingsHandler("Scheduler - Sync Tasks"); self::$schedulerAsync = new TimingsHandler("Scheduler - Async Tasks"); - self::$asyncTaskProgressUpdateParent = new TimingsHandler("Async Tasks - Progress Updates", self::$schedulerAsync); self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync); - self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync); self::$asyncTaskWorkers = new TimingsHandler("Async Task Workers"); @@ -324,20 +312,6 @@ abstract class Timings{ return self::$eventHandlers[$event][$handlerName]; } - public static function getAsyncTaskProgressUpdateTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ - $taskClass = $task::class; - if(!isset(self::$asyncTaskProgressUpdate[$taskClass])){ - self::init(); - self::$asyncTaskProgressUpdate[$taskClass] = new TimingsHandler( - "AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Progress Updates", - self::$asyncTaskProgressUpdateParent, - $group - ); - } - - return self::$asyncTaskProgressUpdate[$taskClass]; - } - public static function getAsyncTaskCompletionTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ $taskClass = $task::class; if(!isset(self::$asyncTaskCompletion[$taskClass])){ @@ -352,23 +326,6 @@ abstract class Timings{ return self::$asyncTaskCompletion[$taskClass]; } - /** - * @deprecated No longer used - */ - public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ - $taskClass = $task::class; - if(!isset(self::$asyncTaskError[$taskClass])){ - self::init(); - self::$asyncTaskError[$taskClass] = new TimingsHandler( - "AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Error Handler", - self::$asyncTaskErrorParent, - $group - ); - } - - return self::$asyncTaskError[$taskClass]; - } - public static function getAsyncTaskRunTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{ $taskClass = $task::class; if(!isset(self::$asyncTaskRun[$taskClass])){ diff --git a/src/timings/TimingsHandler.php b/src/timings/TimingsHandler.php index 95f7dbacc..e86cb0383 100644 --- a/src/timings/TimingsHandler.php +++ b/src/timings/TimingsHandler.php @@ -138,19 +138,6 @@ class TimingsHandler{ return $result; } - /** - * @deprecated This only collects timings from the main thread. Collecting timings from all threads is an async - * operation, so it can't be done synchronously. - * - * @return string[] - */ - public static function printTimings() : array{ - $records = self::printCurrentThreadRecords(); - $footer = self::printFooter(); - - return [...$records, ...$footer]; - } - /** * Collects timings asynchronously, allowing timings from multiple threads to be aggregated into a single report. * diff --git a/src/utils/TextFormat.php b/src/utils/TextFormat.php index fadf6ea4e..852771205 100644 --- a/src/utils/TextFormat.php +++ b/src/utils/TextFormat.php @@ -106,10 +106,6 @@ abstract class TextFormat{ public const OBFUSCATED = TextFormat::ESCAPE . "k"; public const BOLD = TextFormat::ESCAPE . "l"; - /** @deprecated */ - public const STRIKETHROUGH = ""; - /** @deprecated */ - public const UNDERLINE = ""; public const ITALIC = TextFormat::ESCAPE . "o"; public const FORMATS = [ diff --git a/src/wizard/SetupWizard.php b/src/wizard/SetupWizard.php index 0f1a99f4b..84329bbb4 100644 --- a/src/wizard/SetupWizard.php +++ b/src/wizard/SetupWizard.php @@ -48,13 +48,6 @@ use const PHP_EOL; use const STDIN; class SetupWizard{ - /** @deprecated */ - public const DEFAULT_NAME = Server::DEFAULT_SERVER_NAME; - /** @deprecated */ - public const DEFAULT_PORT = Server::DEFAULT_PORT_IPV4; - /** @deprecated */ - public const DEFAULT_PLAYERS = Server::DEFAULT_MAX_PLAYERS; - private Language $lang; public function __construct( diff --git a/src/world/World.php b/src/world/World.php index c2d153921..9551b8cbe 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1614,25 +1614,6 @@ class World implements ChunkManager{ return $collides; } - /** - * @deprecated Use {@link World::getBlockCollisionBoxes()} instead (alongside {@link World::getCollidingEntities()} - * if entity collision boxes are also required). - * - * @return AxisAlignedBB[] - * @phpstan-return list - */ - public function getCollisionBoxes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ - $collides = $this->getBlockCollisionBoxes($bb); - - if($entities){ - foreach($this->getCollidingEntities($bb->expandedCopy(0.25, 0.25, 0.25), $entity) as $ent){ - $collides[] = clone $ent->boundingBox; - } - } - - return $collides; - } - /** * Computes the percentage of a circle away from noon the sun is currently at. This can be multiplied by 2 * M_PI to * get an angle in radians, or by 360 to get an angle in degrees. diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 53ec15c12..dc43c41db 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -61,15 +61,6 @@ class AsyncPoolTest extends TestCase{ self::assertTrue(LeakTestAsyncTask::$destroyed, "Task was not destroyed after 30 seconds"); } - public function testPublishProgressRace() : void{ - $task = new PublishProgressRaceAsyncTask(); - $this->pool->submitTask($task); - while($this->pool->collectTasks()){ - usleep(50 * 1000); - } - self::assertTrue(PublishProgressRaceAsyncTask::$success, "Progress was not reported before task completion"); - } - public function testThreadSafeSetResult() : void{ $resolver = new PromiseResolver(); $resolver->getPromise()->onCompletion( diff --git a/tests/phpunit/scheduler/PublishProgressRaceAsyncTask.php b/tests/phpunit/scheduler/PublishProgressRaceAsyncTask.php deleted file mode 100644 index da25d16f3..000000000 --- a/tests/phpunit/scheduler/PublishProgressRaceAsyncTask.php +++ /dev/null @@ -1,40 +0,0 @@ -publishProgress("hello"); - } - - public function onProgressUpdate($progress) : void{ - if($progress === "hello"){ - // thread local on main thread - self::$success = true; - } - } -} From fea17fa4a99bb7d98a1489100797d21c58b8d95f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 20:33:40 +0000 Subject: [PATCH 169/290] RakLibServer: disable GC GC is not required for RakLib as it doesn't generate any unmanaged cycles. Cycles in general do exist (e.g. Server <-> ServerSession), but these are explicitly cleaned up, so GC wouldn't have any useful work to do. --- src/network/mcpe/raklib/RakLibServer.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/network/mcpe/raklib/RakLibServer.php b/src/network/mcpe/raklib/RakLibServer.php index 3b4b8da74..d5e825bee 100644 --- a/src/network/mcpe/raklib/RakLibServer.php +++ b/src/network/mcpe/raklib/RakLibServer.php @@ -38,7 +38,7 @@ use raklib\server\ServerSocket; use raklib\server\SimpleProtocolAcceptor; use raklib\utils\ExceptionTraceCleaner; use raklib\utils\InternetAddress; -use function gc_enable; +use function gc_disable; use function ini_set; class RakLibServer extends Thread{ @@ -82,10 +82,9 @@ class RakLibServer extends Thread{ } protected function onRun() : void{ - //TODO: switch to manually triggered GC - //the best time to do it is between ticks when the server would otherwise be sleeping, but RakLib's current - //design doesn't allow this as of 1.1.1 - gc_enable(); + //RakLib has cycles (e.g. ServerSession <-> Server) but these cycles are explicitly cleaned up anyway, and are + //very few, so it's pointless to waste CPU time on GC + gc_disable(); ini_set("display_errors", '1'); ini_set("display_startup_errors", '1'); From 506cfe0362b181c16b860aa3b42f3bf619901b4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:39:39 +0000 Subject: [PATCH 170/290] Bump build/php from `5016e0a` to `b1eaaa4` (#6579) Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `5016e0a` to `b1eaaa4`. - [Release notes](https://github.com/pmmp/php-build-scripts/releases) - [Commits](https://github.com/pmmp/php-build-scripts/compare/5016e0a3d54c714c12b331ea0474a6f500ffc0a3...b1eaaa48ecd5bbbcefcf07f7fc183a09845c91c3) --- updated-dependencies: - dependency-name: build/php dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 5016e0a3d..b1eaaa48e 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 5016e0a3d54c714c12b331ea0474a6f500ffc0a3 +Subproject commit b1eaaa48ecd5bbbcefcf07f7fc183a09845c91c3 From 6a1d80e021d0e5d2c8734663c5469b411cb5e53f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 21 Dec 2024 17:41:01 +0000 Subject: [PATCH 171/290] tools/convert-world: fixed some UI issues --- tools/convert-world.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/convert-world.php b/tools/convert-world.php index d4d15ce57..828ccb470 100644 --- a/tools/convert-world.php +++ b/tools/convert-world.php @@ -52,7 +52,7 @@ $writableFormats = array_filter($providerManager->getAvailableProviders(), fn(Wo $requiredOpts = [ "world" => "path to the input world for conversion", "backup" => "path to back up the original files", - "format" => "desired output format (can be one of: " . implode(",", array_keys($writableFormats)) . ")" + "format" => "desired output format (can be one of: " . implode(", ", array_keys($writableFormats)) . ")" ]; $usageMessage = "Options:\n"; foreach($requiredOpts as $_opt => $_desc){ @@ -89,7 +89,7 @@ if(count($oldProviderClasses) === 0){ exit(1); } if(count($oldProviderClasses) > 1){ - fwrite(STDERR, "Ambiguous input world format: matched " . count($oldProviderClasses) . " (" . implode(array_keys($oldProviderClasses)) . ")" . PHP_EOL); + fwrite(STDERR, "Ambiguous input world format: matched " . count($oldProviderClasses) . " (" . implode(", ", array_keys($oldProviderClasses)) . ")" . PHP_EOL); exit(1); } $oldProviderClass = array_shift($oldProviderClasses); From ada3acdba4a0a2cef5f4de284a66e42cc6dcf23e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 21 Dec 2024 17:43:59 +0000 Subject: [PATCH 172/290] FormatConverter: ensure we don't get stalled due to stdout buffer flood this can happen due to very noisy outputs during conversion, e.g. if there were many unknown blocks. --- src/world/format/io/FormatConverter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/world/format/io/FormatConverter.php b/src/world/format/io/FormatConverter.php index 1d485afa2..ea5af221c 100644 --- a/src/world/format/io/FormatConverter.php +++ b/src/world/format/io/FormatConverter.php @@ -33,6 +33,7 @@ use function basename; use function crc32; use function file_exists; use function floor; +use function flush; use function microtime; use function mkdir; use function random_bytes; @@ -150,6 +151,7 @@ class FormatConverter{ $diff = $time - $thisRound; $thisRound = $time; $this->logger->info("Converted $counter / $count chunks (" . floor($this->chunksPerProgressUpdate / $diff) . " chunks/sec)"); + flush(); } } $total = microtime(true) - $start; From 306623e890b046cf195e57860d1043d99cf986fb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 21 Dec 2024 17:47:16 +0000 Subject: [PATCH 173/290] FormatConverter: do periodic GC this reduces the risk of OOM during conversion of large worlds we probably ought to limit the size of region caches for regionized worlds, but that's a problem for another time. --- src/world/format/io/FormatConverter.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/world/format/io/FormatConverter.php b/src/world/format/io/FormatConverter.php index ea5af221c..421d707fa 100644 --- a/src/world/format/io/FormatConverter.php +++ b/src/world/format/io/FormatConverter.php @@ -153,6 +153,9 @@ class FormatConverter{ $this->logger->info("Converted $counter / $count chunks (" . floor($this->chunksPerProgressUpdate / $diff) . " chunks/sec)"); flush(); } + if(($counter % (2 ** 16)) === 0){ + $new->doGarbageCollection(); + } } $total = microtime(true) - $start; $this->logger->info("Converted $counter / $counter chunks in " . round($total, 3) . " seconds (" . floor($counter / $total) . " chunks/sec)"); From 8a5eb71432ebf4036624ffc97b65c4db1e6fd6f4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 23 Dec 2024 21:04:33 +0000 Subject: [PATCH 174/290] ChunkCache: track strings in cache directly instead of CompressBatchPromise this reduces memory footprint slightly, but more importantly reduces GC workload. Since it also reduces the work done on cache hit, it might *slightly* improve performance, but any improvement is likely to be minimal. --- src/network/mcpe/NetworkSession.php | 30 +++++++++----- src/network/mcpe/cache/ChunkCache.php | 57 ++++++++++++++++----------- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 78b11e27e..98460bef3 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -115,6 +115,7 @@ use pocketmine\utils\ObjectSet; use pocketmine\utils\TextFormat; use pocketmine\world\format\io\GlobalItemDataHandlers; use pocketmine\world\Position; +use pocketmine\world\World; use pocketmine\YmlServerProperties; use function array_map; use function array_values; @@ -1178,6 +1179,19 @@ class NetworkSession{ $this->sendDataPacket(ClientboundCloseFormPacket::create()); } + /** + * @phpstan-param \Closure() : void $onCompletion + */ + private function sendChunkPacket(string $chunkPacket, \Closure $onCompletion, World $world) : void{ + $world->timings->syncChunkSend->startTiming(); + try{ + $this->queueCompressed($chunkPacket); + $onCompletion(); + }finally{ + $world->timings->syncChunkSend->stopTiming(); + } + } + /** * Instructs the networksession to start using the chunk at the given coordinates. This may occur asynchronously. * @param \Closure $onCompletion To be called when chunk sending has completed. @@ -1185,8 +1199,12 @@ class NetworkSession{ */ public function startUsingChunk(int $chunkX, int $chunkZ, \Closure $onCompletion) : void{ $world = $this->player->getLocation()->getWorld(); - ChunkCache::getInstance($world, $this->compressor)->request($chunkX, $chunkZ)->onResolve( - + $promiseOrPacket = ChunkCache::getInstance($world, $this->compressor)->request($chunkX, $chunkZ); + if(is_string($promiseOrPacket)){ + $this->sendChunkPacket($promiseOrPacket, $onCompletion, $world); + return; + } + $promiseOrPacket->onResolve( //this callback may be called synchronously or asynchronously, depending on whether the promise is resolved yet function(CompressBatchPromise $promise) use ($world, $onCompletion, $chunkX, $chunkZ) : void{ if(!$this->isConnected()){ @@ -1204,13 +1222,7 @@ class NetworkSession{ //to NEEDED if they want to be resent. return; } - $world->timings->syncChunkSend->startTiming(); - try{ - $this->queueCompressed($promise); - $onCompletion(); - }finally{ - $world->timings->syncChunkSend->stopTiming(); - } + $this->sendChunkPacket($promise->getResult(), $onCompletion, $world); } ); } diff --git a/src/network/mcpe/cache/ChunkCache.php b/src/network/mcpe/cache/ChunkCache.php index 12e769776..2c5f67f08 100644 --- a/src/network/mcpe/cache/ChunkCache.php +++ b/src/network/mcpe/cache/ChunkCache.php @@ -32,6 +32,7 @@ use pocketmine\world\ChunkListener; use pocketmine\world\ChunkListenerNoOpTrait; use pocketmine\world\format\Chunk; use pocketmine\world\World; +use function is_string; use function spl_object_id; use function strlen; @@ -69,7 +70,7 @@ class ChunkCache implements ChunkListener{ foreach(self::$instances as $compressorMap){ foreach($compressorMap as $chunkCache){ foreach($chunkCache->caches as $chunkHash => $promise){ - if($promise->hasResult()){ + if(is_string($promise)){ //Do not clear promises that are not yet fulfilled; they will have requesters waiting on them unset($chunkCache->caches[$chunkHash]); } @@ -79,8 +80,8 @@ class ChunkCache implements ChunkListener{ } /** - * @var CompressBatchPromise[] - * @phpstan-var array + * @var CompressBatchPromise[]|string[] + * @phpstan-var array */ private array $caches = []; @@ -92,29 +93,17 @@ class ChunkCache implements ChunkListener{ private Compressor $compressor ){} - /** - * Requests asynchronous preparation of the chunk at the given coordinates. - * - * @return CompressBatchPromise a promise of resolution which will contain a compressed chunk packet. - */ - public function request(int $chunkX, int $chunkZ) : CompressBatchPromise{ + private function prepareChunkAsync(int $chunkX, int $chunkZ, int $chunkHash) : CompressBatchPromise{ $this->world->registerChunkListener($this, $chunkX, $chunkZ); $chunk = $this->world->getChunk($chunkX, $chunkZ); if($chunk === null){ throw new \InvalidArgumentException("Cannot request an unloaded chunk"); } - $chunkHash = World::chunkHash($chunkX, $chunkZ); - - if(isset($this->caches[$chunkHash])){ - ++$this->hits; - return $this->caches[$chunkHash]; - } - ++$this->misses; $this->world->timings->syncChunkSendPrepare->startTiming(); try{ - $this->caches[$chunkHash] = new CompressBatchPromise(); + $promise = new CompressBatchPromise(); $this->world->getServer()->getAsyncPool()->submitTask( new ChunkRequestTask( @@ -122,17 +111,39 @@ class ChunkCache implements ChunkListener{ $chunkZ, DimensionIds::OVERWORLD, //TODO: not hardcode this $chunk, - $this->caches[$chunkHash], + $promise, $this->compressor ) ); + $this->caches[$chunkHash] = $promise; + $promise->onResolve(function(CompressBatchPromise $promise) use ($chunkHash) : void{ + //the promise may have been discarded or replaced if the chunk was unloaded or modified in the meantime + if(($this->caches[$chunkHash] ?? null) === $promise){ + $this->caches[$chunkHash] = $promise->getResult(); + } + }); - return $this->caches[$chunkHash]; + return $promise; }finally{ $this->world->timings->syncChunkSendPrepare->stopTiming(); } } + /** + * Requests asynchronous preparation of the chunk at the given coordinates. + * + * @return CompressBatchPromise|string Compressed chunk packet, or a promise for one to be resolved asynchronously. + */ + public function request(int $chunkX, int $chunkZ) : CompressBatchPromise|string{ + $chunkHash = World::chunkHash($chunkX, $chunkZ); + if(isset($this->caches[$chunkHash])){ + ++$this->hits; + return $this->caches[$chunkHash]; + } + + return $this->prepareChunkAsync($chunkX, $chunkZ, $chunkHash); + } + private function destroy(int $chunkX, int $chunkZ) : bool{ $chunkHash = World::chunkHash($chunkX, $chunkZ); $existing = $this->caches[$chunkHash] ?? null; @@ -148,12 +159,12 @@ class ChunkCache implements ChunkListener{ $chunkPosHash = World::chunkHash($chunkX, $chunkZ); $cache = $this->caches[$chunkPosHash] ?? null; if($cache !== null){ - if(!$cache->hasResult()){ + if(!is_string($cache)){ //some requesters are waiting for this chunk, so their request needs to be fulfilled $cache->cancel(); unset($this->caches[$chunkPosHash]); - $this->request($chunkX, $chunkZ)->onResolve(...$cache->getResolveCallbacks()); + $this->prepareChunkAsync($chunkX, $chunkZ, $chunkPosHash)->onResolve(...$cache->getResolveCallbacks()); }else{ //dump the cache, it'll be regenerated the next time it's requested $this->destroy($chunkX, $chunkZ); @@ -199,8 +210,8 @@ class ChunkCache implements ChunkListener{ public function calculateCacheSize() : int{ $result = 0; foreach($this->caches as $cache){ - if($cache->hasResult()){ - $result += strlen($cache->getResult()); + if(is_string($cache)){ + $result += strlen($cache); } } return $result; From 81e3730b99944413188e0c5db6eb5ab1f4c762ec Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 24 Dec 2024 14:20:16 +0000 Subject: [PATCH 175/290] Fixed crashes containing PHP internal stack frames being flagged as plugin-caused --- src/utils/Utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index c8be174d6..9317aff3c 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -490,7 +490,7 @@ final class Utils{ $rawFrame = $rawTrace[$frameId]; $safeTrace[$frameId] = new ThreadCrashInfoFrame( $printableFrame, - $rawFrame["file"] ?? "unknown", + $rawFrame["file"] ?? null, $rawFrame["line"] ?? 0 ); } From debf8d18fc85b44d334251966502b3401e33fc37 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 24 Dec 2024 16:27:40 +0000 Subject: [PATCH 176/290] Upgrade issue templates --- .github/ISSUE_TEMPLATE/api-change-request.md | 19 ----- .github/ISSUE_TEMPLATE/bug-report.yml | 84 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 37 --------- .github/ISSUE_TEMPLATE/crash.md | 16 ---- .github/ISSUE_TEMPLATE/crash.yml | 24 ++++++ .github/ISSUE_TEMPLATE/feature-proposal.yml | 19 +++++ 6 files changed, 127 insertions(+), 72 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/api-change-request.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/crash.md create mode 100644 .github/ISSUE_TEMPLATE/crash.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-proposal.yml diff --git a/.github/ISSUE_TEMPLATE/api-change-request.md b/.github/ISSUE_TEMPLATE/api-change-request.md deleted file mode 100644 index e3d24ea0f..000000000 --- a/.github/ISSUE_TEMPLATE/api-change-request.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: API change request -about: Suggest a change, addition or removal to the plugin API -title: '' -labels: '' -assignees: '' - ---- - - -## Problem description - - - -## Proposed solution - - - -## Alternative solutions that don't require API changes diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..3a4e49100 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,84 @@ +name: Bug report +description: Report a feature of PocketMine-MP not working as expected +body: + - type: markdown + attributes: + value: | + ## Plugin information + + > [!IMPORTANT] + > It's strongly recommended to test for bugs without plugins before reporting an issue. + > This helps avoid wasting maintainers' time on bugs that are not actually caused by PocketMine-MP. + > + > If you're not sure whether a plugin might be causing your issue, please seek help on our [Discord](https://discord.gg/bmSAZBG) before writing an issue. + + - type: dropdown + attributes: + label: Plugin information + options: + - "I haven't tested without plugins" + - Bug happens without plugins + - Bug only happens with certain plugins (describe below) + validations: + required: true + + - type: markdown + attributes: + value: | + ## Bug description + + > [!TIP] + > Helpful information to include: + > - Steps to reproduce the issue + > - Error backtraces + > - Crashdumps + > - Plugin code that triggers the issue + > - List of installed plugins (use /plugins) + + > [!IMPORTANT] + > **Steps to reproduce are critical to finding the cause of the problem!** + > Without reproducing steps, the issue will probably not be solvable and may be closed. + + - type: textarea + attributes: + label: Problem description + description: Describe the problem, and how you encountered it + placeholder: e.g. Steps to reproduce the issue + validations: + required: true + - type: textarea + attributes: + label: Expected behaviour + description: What did you expect to happen? + validations: + required: true + + - type: markdown + attributes: + value: | + ## Version, OS and game info + + - type: input + attributes: + label: PocketMine-MP version + placeholder: Use the /version command in PocketMine-MP + validations: + required: true + - type: input + attributes: + label: PHP version + placeholder: Use the /version command in PocketMine-MP + validations: + required: true + - type: input + attributes: + label: Server OS + placeholder: Use the /version command in PocketMine-MP + validations: + required: true + - type: input + attributes: + label: Game version (if applicable) + placeholder: e.g. Android, iOS, Windows, Xbox, PS4, Switch + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 730d6e811..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Bug report -about: Unexpected non-crash behaviour (except missing gameplay features) -title: '' -labels: '' -assignees: '' - ---- - -### Issue description - -- Expected result: What were you expecting to happen? -- Actual result: What actually happened? - -### Steps to reproduce the issue -1. ... -2. ... - -### OS and versions - -* PocketMine-MP: -* PHP: -* Using JIT: yes/no (delete as appropriate) -* Server OS: -* Game version: Android/iOS/Win10/Xbox/PS4/Switch (delete as appropriate) - -### Plugins - - -- If you remove all plugins, does the issue still occur? -- If the issue is **not** reproducible without plugins: - - Have you asked for help on our forums before creating an issue? - - Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section - -### Crashdump, backtrace or other files - - diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md deleted file mode 100644 index ee91d230e..000000000 --- a/.github/ISSUE_TEMPLATE/crash.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Crash -about: Report a crash in PocketMine-MP (not plugins) -title: Server crashed -labels: '' -assignees: '' - ---- - - - - -Link to crashdump: - - -### Additional comments (optional) diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml new file mode 100644 index 000000000..d6d46c406 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -0,0 +1,24 @@ +name: Crash +description: Report a crash in PocketMine-MP (not plugins) +title: Server crashed +body: + - type: markdown + attributes: + value: | + > [!TIP] + > Submit crashdump `.log` files to the [Crash Archive](https://crash.pmmp.io/submit). + > If you can't submit the crashdump to the Crash Archive, paste it on a site like [GitHub Gist](https://gist.github.com) or [Pastebin](https://pastebin.com). + + > [!CAUTION] + > DON'T paste the crashdump data directly into an issue. + + - type: input + attributes: + label: Link to crashdump + validations: + required: true + + - type: textarea + attributes: + label: Additional comments (optional) + description: Any other information that might help us solve the problem diff --git a/.github/ISSUE_TEMPLATE/feature-proposal.yml b/.github/ISSUE_TEMPLATE/feature-proposal.yml new file mode 100644 index 000000000..e0d37ef06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-proposal.yml @@ -0,0 +1,19 @@ +name: Feature addition, change, or removal +description: Propose adding new features, or changing/removing existing ones +body: + - type: textarea + attributes: + label: Problem description + description: Explain why a change is needed + validations: + required: true + - type: textarea + attributes: + label: Proposed solution + description: Describe what changes you think should be made + validations: + required: true + - type: textarea + attributes: + label: "Alternative solutions or workarounds" + description: "Describe other ways you've explored to achieve your goal" From dc2e82df7f092ece19c700697e15d1d565f9a791 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Tue, 24 Dec 2024 16:37:18 +0000 Subject: [PATCH 177/290] crash.yml: add field ID for crash archive "report github issue" button --- .github/ISSUE_TEMPLATE/crash.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml index d6d46c406..735255de2 100644 --- a/.github/ISSUE_TEMPLATE/crash.yml +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -13,6 +13,7 @@ body: > DON'T paste the crashdump data directly into an issue. - type: input + id: crashdump-url attributes: label: Link to crashdump validations: From d634a5fa3dea366037e24f1b154f25fa668a526f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Dec 2024 13:06:46 +0000 Subject: [PATCH 178/290] Bump build/php from `b1eaaa4` to `9728fa5` (#6587) Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `b1eaaa4` to `9728fa5`. - [Release notes](https://github.com/pmmp/php-build-scripts/releases) - [Commits](https://github.com/pmmp/php-build-scripts/compare/b1eaaa48ecd5bbbcefcf07f7fc183a09845c91c3...9728fa57f72b9c935b7d3206055715f5afcda7ae) --- updated-dependencies: - dependency-name: build/php dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index b1eaaa48e..9728fa57f 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit b1eaaa48ecd5bbbcefcf07f7fc183a09845c91c3 +Subproject commit 9728fa57f72b9c935b7d3206055715f5afcda7ae From fbaa125d0ce21ffef98fc1630881a92bedfbaa73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:36:17 +0000 Subject: [PATCH 179/290] Bump build/php from `9728fa5` to `56cec11` (#6588) Bumps [build/php](https://github.com/pmmp/php-build-scripts) from `9728fa5` to `56cec11`. - [Release notes](https://github.com/pmmp/php-build-scripts/releases) - [Commits](https://github.com/pmmp/php-build-scripts/compare/9728fa57f72b9c935b7d3206055715f5afcda7ae...56cec11745bbd87719a303a0a1de41ee1d1d69c2) --- updated-dependencies: - dependency-name: build/php dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 9728fa57f..56cec1174 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 9728fa57f72b9c935b7d3206055715f5afcda7ae +Subproject commit 56cec11745bbd87719a303a0a1de41ee1d1d69c2 From da62eb9f3314daf4be83674ce7323850e1498601 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 3 Jan 2025 19:26:24 +0000 Subject: [PATCH 180/290] ... --- src/world/light/LightPopulationTask.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/world/light/LightPopulationTask.php b/src/world/light/LightPopulationTask.php index 5aa0ead65..7a4f5071d 100644 --- a/src/world/light/LightPopulationTask.php +++ b/src/world/light/LightPopulationTask.php @@ -90,7 +90,7 @@ class LightPopulationTask extends AsyncTask{ /** * @var \Closure - * @phpstan-var \Closure(array $blockLight, array $skyLight, array $heightMap>) : void + * @phpstan-var \Closure(array $blockLight, array $skyLight, array $heightMap) : void */ $callback = $this->fetchLocal(self::TLS_KEY_COMPLETION_CALLBACK); $callback($blockLightArrays, $skyLightArrays, $heightMapArray); From 4a4572131f27ab967701ceaaf2020cfbe26e375c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:26:14 +0000 Subject: [PATCH 181/290] Bump shivammathur/setup-php in the github-actions group (#6591) --- .github/workflows/discord-release-notify.yml | 2 +- .github/workflows/draft-release-pr-check.yml | 2 +- .github/workflows/draft-release.yml | 2 +- .github/workflows/main.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/discord-release-notify.yml b/.github/workflows/discord-release-notify.yml index 8d0add224..fde5e3099 100644 --- a/.github/workflows/discord-release-notify.yml +++ b/.github/workflows/discord-release-notify.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.31.1 + uses: shivammathur/setup-php@2.32.0 with: php-version: 8.2 diff --git a/.github/workflows/draft-release-pr-check.yml b/.github/workflows/draft-release-pr-check.yml index 4c8d0f685..131c0dde2 100644 --- a/.github/workflows/draft-release-pr-check.yml +++ b/.github/workflows/draft-release-pr-check.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP - uses: shivammathur/setup-php@2.31.1 + uses: shivammathur/setup-php@2.32.0 with: php-version: 8.2 diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 6000dd5a8..445a1f7a6 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -87,7 +87,7 @@ jobs: submodules: true - name: Setup PHP - uses: shivammathur/setup-php@2.31.1 + uses: shivammathur/setup-php@2.32.0 with: php-version: ${{ env.PHP_VERSION }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b5a9740b5..571868747 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup PHP and tools - uses: shivammathur/setup-php@2.31.1 + uses: shivammathur/setup-php@2.32.0 with: php-version: 8.2 tools: php-cs-fixer:3.49 From 8b2323153734c680c2b4cae17af3b271beb38670 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:37:11 +0000 Subject: [PATCH 182/290] Fixup PHPDoc for blocks --- src/block/Anvil.php | 3 --- src/block/Bamboo.php | 3 --- src/block/BaseBanner.php | 4 ---- src/block/BaseSign.php | 4 ---- src/block/Bed.php | 3 --- src/block/Block.php | 7 ++++++- src/block/Cactus.php | 3 --- src/block/Cake.php | 3 --- src/block/CakeWithCandle.php | 3 --- src/block/Carpet.php | 3 --- src/block/Chest.php | 3 --- src/block/CocoaBlock.php | 3 --- src/block/DaylightSensor.php | 3 --- src/block/Door.php | 3 --- src/block/EnchantingTable.php | 3 --- src/block/EndPortalFrame.php | 3 --- src/block/EndRod.php | 3 --- src/block/EnderChest.php | 3 --- src/block/Farmland.php | 3 --- src/block/Fence.php | 4 ---- src/block/FenceGate.php | 3 --- src/block/Flowable.php | 4 ---- src/block/FlowerPot.php | 3 --- src/block/GlowLichen.php | 4 ---- src/block/GrassPath.php | 3 --- src/block/Ladder.php | 3 --- src/block/Lantern.php | 3 --- src/block/Liquid.php | 4 ---- src/block/MobHead.php | 3 --- src/block/NetherPortal.php | 4 ---- src/block/RedstoneComparator.php | 3 --- src/block/RedstoneRepeater.php | 3 --- src/block/SeaPickle.php | 4 ---- src/block/Slab.php | 3 --- src/block/SnowLayer.php | 3 --- src/block/SoulSand.php | 3 --- src/block/Thin.php | 1 - src/block/Trapdoor.php | 3 --- src/block/WaterLily.php | 3 --- src/block/utils/CandleTrait.php | 5 ++++- src/block/utils/CopperTrait.php | 5 +++++ 41 files changed, 15 insertions(+), 122 deletions(-) diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 0a1a47070..2c48f9a7c 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -70,9 +70,6 @@ class Anvil extends Transparent implements Fallable{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)]; } diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php index 9f605bca6..fd64e10ef 100644 --- a/src/block/Bamboo.php +++ b/src/block/Bamboo.php @@ -87,9 +87,6 @@ class Bamboo extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ //this places the BB at the northwest corner, not the center $inset = 1 - (($this->thick ? 3 : 2) / 16); diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index 6b9e493d1..b56323453 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -30,7 +30,6 @@ use pocketmine\block\utils\SupportType; use pocketmine\item\Banner as ItemBanner; use pocketmine\item\Item; use pocketmine\item\VanillaItems; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -97,9 +96,6 @@ abstract class BaseBanner extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 5a905f8b8..0f5d77d58 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -34,7 +34,6 @@ use pocketmine\event\block\SignChangeEvent; use pocketmine\item\Dye; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\TextFormat; @@ -95,9 +94,6 @@ abstract class BaseSign extends Transparent{ return 16; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/Bed.php b/src/block/Bed.php index 8efbdfe01..133c4a9cc 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -76,9 +76,6 @@ class Bed extends Transparent{ } } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)]; } diff --git a/src/block/Block.php b/src/block/Block.php index 89fe39265..36e08fc0b 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -75,7 +75,10 @@ class Block{ protected BlockTypeInfo $typeInfo; protected Position $position; - /** @var AxisAlignedBB[]|null */ + /** + * @var AxisAlignedBB[]|null + * @phpstan-var list|null + */ protected ?array $collisionBoxes = null; private int $requiredBlockItemStateDataBits; @@ -907,6 +910,7 @@ class Block{ * - anti-cheat checks in plugins * * @return AxisAlignedBB[] + * @phpstan-return list */ final public function getCollisionBoxes() : array{ if($this->collisionBoxes === null){ @@ -931,6 +935,7 @@ class Block{ /** * @return AxisAlignedBB[] + * @phpstan-return list */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()]; diff --git a/src/block/Cactus.php b/src/block/Cactus.php index 6f2b04c8a..67b15b946 100644 --- a/src/block/Cactus.php +++ b/src/block/Cactus.php @@ -43,9 +43,6 @@ class Cactus extends Transparent{ return true; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ $shrinkSize = 1 / 16; return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)]; diff --git a/src/block/Cake.php b/src/block/Cake.php index 073fc62ac..e8c6dc93e 100644 --- a/src/block/Cake.php +++ b/src/block/Cake.php @@ -40,9 +40,6 @@ class Cake extends BaseCake{ $w->boundedIntAuto(0, self::MAX_BITES, $this->bites); } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 380d080c5..546843d6c 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -36,9 +36,6 @@ class CakeWithCandle extends BaseCake{ onInteract as onInteractCandle; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() diff --git a/src/block/Carpet.php b/src/block/Carpet.php index 1ee7240c5..2d8e7ea47 100644 --- a/src/block/Carpet.php +++ b/src/block/Carpet.php @@ -36,9 +36,6 @@ class Carpet extends Flowable{ return true; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)]; } diff --git a/src/block/Chest.php b/src/block/Chest.php index dca21576a..7d2650007 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -36,9 +36,6 @@ use pocketmine\player\Player; class Chest extends Transparent{ use FacesOppositePlacingPlayerTrait; - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ //these are slightly bigger than in PC return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)]; diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index 6d8ce1adc..83e1de34b 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -50,9 +50,6 @@ class CocoaBlock extends Flowable{ $w->boundedIntAuto(0, self::MAX_AGE, $this->age); } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index 4141a2b7e..5720af529 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -62,9 +62,6 @@ class DaylightSensor extends Transparent{ return 300; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)]; } diff --git a/src/block/Door.php b/src/block/Door.php index 82ddaab51..fa88267e1 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -95,9 +95,6 @@ class Door extends Transparent{ return false; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ //TODO: doors are 0.1825 blocks thick, instead of 0.1875 like JE (https://bugs.mojang.com/browse/MCPE-19214) return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)]; diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php index 6a6c936b2..53573d064 100644 --- a/src/block/EnchantingTable.php +++ b/src/block/EnchantingTable.php @@ -33,9 +33,6 @@ use pocketmine\player\Player; class EnchantingTable extends Transparent{ - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)]; } diff --git a/src/block/EndPortalFrame.php b/src/block/EndPortalFrame.php index 612cf3723..ed5b77433 100644 --- a/src/block/EndPortalFrame.php +++ b/src/block/EndPortalFrame.php @@ -50,9 +50,6 @@ class EndPortalFrame extends Opaque{ return 1; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 3 / 16)]; } diff --git a/src/block/EndRod.php b/src/block/EndRod.php index f0b28c26d..a6770f370 100644 --- a/src/block/EndRod.php +++ b/src/block/EndRod.php @@ -52,9 +52,6 @@ class EndRod extends Flowable{ return 14; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ $myAxis = Facing::axis($this->facing); diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index 9004f7c79..6a8cf108c 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -40,9 +40,6 @@ class EnderChest extends Transparent{ return 7; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ //these are slightly bigger than in PC return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)]; diff --git a/src/block/Farmland.php b/src/block/Farmland.php index b7a2500a8..83bc34561 100644 --- a/src/block/Farmland.php +++ b/src/block/Farmland.php @@ -94,9 +94,6 @@ class Farmland extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)]; } diff --git a/src/block/Fence.php b/src/block/Fence.php index 30caaa4cf..52256d9f0 100644 --- a/src/block/Fence.php +++ b/src/block/Fence.php @@ -54,13 +54,9 @@ class Fence extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ $inset = 0.5 - $this->getThickness() / 2; - /** @var AxisAlignedBB[] $bbs */ $bbs = []; $connectWest = isset($this->connections[Facing::WEST]); diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index 735456449..2bbfdf892 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -64,9 +64,6 @@ class FenceGate extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)]; } diff --git a/src/block/Flowable.php b/src/block/Flowable.php index 0328bcd74..355c9caea 100644 --- a/src/block/Flowable.php +++ b/src/block/Flowable.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\SupportType; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; /** @@ -46,9 +45,6 @@ abstract class Flowable extends Transparent{ parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock); } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php index fb3e78d82..79fb73b12 100644 --- a/src/block/FlowerPot.php +++ b/src/block/FlowerPot.php @@ -83,9 +83,6 @@ class FlowerPot extends Flowable{ return $block->hasTypeTag(BlockTypeTags::POTTABLE_PLANTS); } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)]; } diff --git a/src/block/GlowLichen.php b/src/block/GlowLichen.php index d30e25395..a44c4d035 100644 --- a/src/block/GlowLichen.php +++ b/src/block/GlowLichen.php @@ -28,7 +28,6 @@ use pocketmine\block\utils\MultiAnySupportTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Fertilizer; use pocketmine\item\Item; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -47,9 +46,6 @@ class GlowLichen extends Transparent{ return false; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/GrassPath.php b/src/block/GrassPath.php index 5b11bd374..ea56e4b95 100644 --- a/src/block/GrassPath.php +++ b/src/block/GrassPath.php @@ -29,9 +29,6 @@ use pocketmine\math\Facing; class GrassPath extends Transparent{ - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)]; } diff --git a/src/block/Ladder.php b/src/block/Ladder.php index 58f133f6e..09c0b8f6b 100644 --- a/src/block/Ladder.php +++ b/src/block/Ladder.php @@ -58,9 +58,6 @@ class Ladder extends Transparent{ return true; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)]; } diff --git a/src/block/Lantern.php b/src/block/Lantern.php index e9cbcc3fe..302e69fd7 100644 --- a/src/block/Lantern.php +++ b/src/block/Lantern.php @@ -59,9 +59,6 @@ class Lantern extends Transparent{ return $this->lightLevel; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [ AxisAlignedBB::one() diff --git a/src/block/Liquid.php b/src/block/Liquid.php index a37019d65..813d76904 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -30,7 +30,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\event\block\BlockSpreadEvent; use pocketmine\item\Item; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\utils\Utils; @@ -89,9 +88,6 @@ abstract class Liquid extends Transparent{ return false; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/MobHead.php b/src/block/MobHead.php index f4e945841..41e816c55 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -104,9 +104,6 @@ class MobHead extends Flowable{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ $collisionBox = AxisAlignedBB::one() ->contract(0.25, 0, 0.25) diff --git a/src/block/NetherPortal.php b/src/block/NetherPortal.php index 6a45fb7a0..1b199c603 100644 --- a/src/block/NetherPortal.php +++ b/src/block/NetherPortal.php @@ -28,7 +28,6 @@ use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\item\Item; use pocketmine\math\Axis; -use pocketmine\math\AxisAlignedBB; class NetherPortal extends Transparent{ @@ -62,9 +61,6 @@ class NetherPortal extends Transparent{ return false; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index ee63a77a9..40e1ef510 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -79,9 +79,6 @@ class RedstoneComparator extends Flowable{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)]; } diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index 7e6e73da8..bf9d0c5da 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -62,9 +62,6 @@ class RedstoneRepeater extends Flowable{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 8)]; } diff --git a/src/block/SeaPickle.php b/src/block/SeaPickle.php index 627af9bac..34f5c3e9e 100644 --- a/src/block/SeaPickle.php +++ b/src/block/SeaPickle.php @@ -26,7 +26,6 @@ namespace pocketmine\block; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; -use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -70,9 +69,6 @@ class SeaPickle extends Transparent{ return $this->underwater ? ($this->count + 1) * 3 : 0; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return []; } diff --git a/src/block/Slab.php b/src/block/Slab.php index 6000bec39..2bbb7528c 100644 --- a/src/block/Slab.php +++ b/src/block/Slab.php @@ -93,9 +93,6 @@ class Slab extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ if($this->slabType === SlabType::DOUBLE){ return [AxisAlignedBB::one()]; diff --git a/src/block/SnowLayer.php b/src/block/SnowLayer.php index cca8424a9..8549f0b31 100644 --- a/src/block/SnowLayer.php +++ b/src/block/SnowLayer.php @@ -65,9 +65,6 @@ class SnowLayer extends Flowable implements Fallable{ return $this->layers < self::MAX_LAYERS; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ //TODO: this zero-height BB is intended to stay in lockstep with a MCPE bug return [AxisAlignedBB::one()->trim(Facing::UP, $this->layers >= 4 ? 0.5 : 1)]; diff --git a/src/block/SoulSand.php b/src/block/SoulSand.php index 2c6453b6c..e1285d095 100644 --- a/src/block/SoulSand.php +++ b/src/block/SoulSand.php @@ -28,9 +28,6 @@ use pocketmine\math\Facing; class SoulSand extends Opaque{ - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 8)]; } diff --git a/src/block/Thin.php b/src/block/Thin.php index dde2d7d84..82010697a 100644 --- a/src/block/Thin.php +++ b/src/block/Thin.php @@ -56,7 +56,6 @@ class Thin extends Transparent{ protected function recalculateCollisionBoxes() : array{ $inset = 7 / 16; - /** @var AxisAlignedBB[] $bbs */ $bbs = []; if(isset($this->connections[Facing::WEST]) || isset($this->connections[Facing::EAST])){ diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index 20b6af2ab..a903e1b5e 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -62,9 +62,6 @@ class Trapdoor extends Transparent{ return $this; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)]; } diff --git a/src/block/WaterLily.php b/src/block/WaterLily.php index 5dfb0d74a..b04b1baed 100644 --- a/src/block/WaterLily.php +++ b/src/block/WaterLily.php @@ -33,9 +33,6 @@ class WaterLily extends Flowable{ canBePlacedAt as supportedWhenPlacedAt; } - /** - * @return AxisAlignedBB[] - */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->contract(1 / 16, 0, 1 / 16)->trim(Facing::UP, 63 / 64)]; } diff --git a/src/block/utils/CandleTrait.php b/src/block/utils/CandleTrait.php index c9da97ee0..0cbd13044 100644 --- a/src/block/utils/CandleTrait.php +++ b/src/block/utils/CandleTrait.php @@ -43,7 +43,10 @@ trait CandleTrait{ return $this->lit ? 3 : 0; } - /** @see Block::onInteract() */ + /** + * @param Item[] &$returnedItems + * @see Block::onInteract() + */ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ if($this->lit){ diff --git a/src/block/utils/CopperTrait.php b/src/block/utils/CopperTrait.php index 5ad8aa82d..2ed06b798 100644 --- a/src/block/utils/CopperTrait.php +++ b/src/block/utils/CopperTrait.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\block\Block; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Axe; use pocketmine\item\Item; @@ -58,6 +59,10 @@ trait CopperTrait{ return $this; } + /** + * @param Item[] &$returnedItems + * @see Block::onInteract() + */ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->waxed && $item->getTypeId() === ItemTypeIds::HONEYCOMB){ $this->waxed = true; From 5c905d9a95656aed463da44c2e6a24af78057757 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:38:32 +0000 Subject: [PATCH 183/290] BlockBreakInfo: use strict comparison weak compare isn't needed here since this can be float/float --- src/block/BlockBreakInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/BlockBreakInfo.php b/src/block/BlockBreakInfo.php index b49cb6f13..8cfa425dc 100644 --- a/src/block/BlockBreakInfo.php +++ b/src/block/BlockBreakInfo.php @@ -95,7 +95,7 @@ class BlockBreakInfo{ * Returns whether this block can be instantly broken. */ public function breaksInstantly() : bool{ - return $this->hardness == 0.0; + return $this->hardness === 0.0; } /** From 8ee70b209e14cff13239d95594dca58ec4e11be9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:38:54 +0000 Subject: [PATCH 184/290] MemoryDump: fix PHPDoc types --- src/MemoryDump.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MemoryDump.php b/src/MemoryDump.php index 5a9c274cb..bd1e0fc9a 100644 --- a/src/MemoryDump.php +++ b/src/MemoryDump.php @@ -253,12 +253,12 @@ final class MemoryDump{ } /** - * @param object[] $objects reference parameter - * @param int[] $refCounts reference parameter + * @param object[]|true[] $objects reference parameter + * @param int[] $refCounts reference parameter * - * @phpstan-param array $objects + * @phpstan-param array $objects * @phpstan-param array $refCounts - * @phpstan-param-out array $objects + * @phpstan-param-out array $objects * @phpstan-param-out array $refCounts */ private static function continueDump(mixed $from, array &$objects, array &$refCounts, int $recursion, int $maxNesting, int $maxStringSize) : mixed{ From 90f0b85d2e599681298e44bdce7e40e2f0342367 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:41:00 +0000 Subject: [PATCH 185/290] Eliminate weak comparisons in entity package Weak comparisons were used in cases when we were worried about comparing int and float. In some cases (particularly involving Vector3) we do need to be wary of this, so floatval() is used to avoid incorrect type comparisons. In other cases, we were already exclusively comparing float-float, so weak compare wasn't needed anyway. --- src/entity/Attribute.php | 6 +++--- src/entity/Entity.php | 31 ++++++++++++++++--------------- src/entity/Location.php | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/entity/Attribute.php b/src/entity/Attribute.php index 829e3d26c..3e9b7d7c2 100644 --- a/src/entity/Attribute.php +++ b/src/entity/Attribute.php @@ -76,7 +76,7 @@ class Attribute{ throw new \InvalidArgumentException("Minimum $minValue is greater than the maximum $max"); } - if($this->minValue != $minValue){ + if($this->minValue !== $minValue){ $this->desynchronized = true; $this->minValue = $minValue; } @@ -95,7 +95,7 @@ class Attribute{ throw new \InvalidArgumentException("Maximum $maxValue is less than the minimum $min"); } - if($this->maxValue != $maxValue){ + if($this->maxValue !== $maxValue){ $this->desynchronized = true; $this->maxValue = $maxValue; } @@ -140,7 +140,7 @@ class Attribute{ $value = min(max($value, $this->getMinValue()), $this->getMaxValue()); } - if($this->currentValue != $value){ + if($this->currentValue !== $value){ $this->desynchronized = true; $this->currentValue = $value; }elseif($forceSend){ diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 9bd0de9ea..7f0f6028b 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -72,6 +72,7 @@ use function assert; use function cos; use function count; use function deg2rad; +use function floatval; use function floor; use function fmod; use function get_class; @@ -591,7 +592,7 @@ abstract class Entity{ * Sets the health of the Entity. This won't send any update to the players */ public function setHealth(float $amount) : void{ - if($amount == $this->health){ + if($amount === $this->health){ return; } @@ -760,8 +761,8 @@ abstract class Entity{ $diffMotion = $this->motion->subtractVector($this->lastMotion)->lengthSquared(); - $still = $this->motion->lengthSquared() == 0.0; - $wasStill = $this->lastMotion->lengthSquared() == 0.0; + $still = $this->motion->lengthSquared() === 0.0; + $wasStill = $this->lastMotion->lengthSquared() === 0.0; if($wasStill !== $still){ //TODO: hack for client-side AI interference: prevent client sided movement when motion is 0 $this->setNoClientPredictions($still); @@ -1004,7 +1005,7 @@ abstract class Entity{ abs($this->motion->z) <= self::MOTION_THRESHOLD ? 0 : null ); - if($this->motion->x != 0 || $this->motion->y != 0 || $this->motion->z != 0){ + if(floatval($this->motion->x) !== 0.0 || floatval($this->motion->y) !== 0.0 || floatval($this->motion->z) !== 0.0){ $this->move($this->motion->x, $this->motion->y, $this->motion->z); } @@ -1058,9 +1059,9 @@ abstract class Entity{ public function hasMovementUpdate() : bool{ return ( $this->forceMovementUpdate || - $this->motion->x != 0 || - $this->motion->y != 0 || - $this->motion->z != 0 || + floatval($this->motion->x) !== 0.0 || + floatval($this->motion->y) !== 0.0 || + floatval($this->motion->z) !== 0.0 || !$this->onGround ); } @@ -1163,7 +1164,7 @@ abstract class Entity{ $moveBB->offset(0, $dy, 0); - $fallingFlag = ($this->onGround || ($dy != $wantedY && $wantedY < 0)); + $fallingFlag = ($this->onGround || ($dy !== $wantedY && $wantedY < 0)); foreach($list as $bb){ $dx = $bb->calculateXOffset($moveBB, $dx); @@ -1177,7 +1178,7 @@ abstract class Entity{ $moveBB->offset(0, 0, $dz); - if($this->stepHeight > 0 && $fallingFlag && ($wantedX != $dx || $wantedZ != $dz)){ + if($this->stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; @@ -1242,9 +1243,9 @@ abstract class Entity{ $postFallVerticalVelocity = $this->updateFallState($dy, $this->onGround); $this->motion = $this->motion->withComponents( - $wantedX != $dx ? 0 : null, - $postFallVerticalVelocity ?? ($wantedY != $dy ? 0 : null), - $wantedZ != $dz ? 0 : null + $wantedX !== $dx ? 0 : null, + $postFallVerticalVelocity ?? ($wantedY !== $dy ? 0 : null), + $wantedZ !== $dz ? 0 : null ); //TODO: vehicle collision events (first we need to spawn them!) @@ -1253,10 +1254,10 @@ abstract class Entity{ } protected function checkGroundState(float $wantedX, float $wantedY, float $wantedZ, float $dx, float $dy, float $dz) : void{ - $this->isCollidedVertically = $wantedY != $dy; - $this->isCollidedHorizontally = ($wantedX != $dx || $wantedZ != $dz); + $this->isCollidedVertically = $wantedY !== $dy; + $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz); $this->isCollided = ($this->isCollidedHorizontally || $this->isCollidedVertically); - $this->onGround = ($wantedY != $dy && $wantedY < 0); + $this->onGround = ($wantedY !== $dy && $wantedY < 0); } /** diff --git a/src/entity/Location.php b/src/entity/Location.php index 990108ac4..d9c101882 100644 --- a/src/entity/Location.php +++ b/src/entity/Location.php @@ -66,7 +66,7 @@ class Location extends Position{ public function equals(Vector3 $v) : bool{ if($v instanceof Location){ - return parent::equals($v) && $v->yaw == $this->yaw && $v->pitch == $this->pitch; + return parent::equals($v) && $v->yaw === $this->yaw && $v->pitch === $this->pitch; } return parent::equals($v); } From 59f6c8510577539785c60a43fa293c7f7839d1af Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:42:57 +0000 Subject: [PATCH 186/290] Command: mark execute $args as being list --- src/command/Command.php | 1 + src/command/FormattedCommandAlias.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/command/Command.php b/src/command/Command.php index 4c2c6815b..63276a123 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -80,6 +80,7 @@ abstract class Command{ /** * @param string[] $args + * @phpstan-param list $args * * @return mixed * @throws CommandException diff --git a/src/command/FormattedCommandAlias.php b/src/command/FormattedCommandAlias.php index 5086672f6..b47363397 100644 --- a/src/command/FormattedCommandAlias.php +++ b/src/command/FormattedCommandAlias.php @@ -121,6 +121,7 @@ class FormattedCommandAlias extends Command{ /** * @param string[] $args + * @phpstan-param list $args */ private function buildCommand(string $formatString, array $args) : ?string{ $index = 0; From e30ae487dc9fa2798f39f37a8c14afb940619ecf Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:43:34 +0000 Subject: [PATCH 187/290] SimpleCommandMap: ensure we always pass a list to Command::setAliases() some offsets may have been removed if the alias failed to be registered. --- src/command/SimpleCommandMap.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/command/SimpleCommandMap.php b/src/command/SimpleCommandMap.php index e0d8e6565..9f5441746 100644 --- a/src/command/SimpleCommandMap.php +++ b/src/command/SimpleCommandMap.php @@ -73,6 +73,7 @@ use pocketmine\timings\Timings; use pocketmine\utils\TextFormat; use pocketmine\utils\Utils; use function array_shift; +use function array_values; use function count; use function implode; use function str_contains; @@ -163,7 +164,7 @@ class SimpleCommandMap implements CommandMap{ unset($aliases[$index]); } } - $command->setAliases($aliases); + $command->setAliases(array_values($aliases)); if(!$registered){ $command->setLabel($fallbackPrefix . ":" . $label); From c5a1c153898af48e476e59aaf06d0764b0f3f042 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:44:04 +0000 Subject: [PATCH 188/290] TimingsCommand: beware crash on invalid timings server response --- src/command/defaults/TimingsCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/command/defaults/TimingsCommand.php b/src/command/defaults/TimingsCommand.php index ec26f3efe..08a8b82aa 100644 --- a/src/command/defaults/TimingsCommand.php +++ b/src/command/defaults/TimingsCommand.php @@ -46,6 +46,8 @@ use function fwrite; use function http_build_query; use function implode; use function is_array; +use function is_int; +use function is_string; use function json_decode; use function mkdir; use function strtolower; @@ -178,7 +180,7 @@ class TimingsCommand extends VanillaCommand{ return; } $response = json_decode($result->getBody(), true); - if(is_array($response) && isset($response["id"])){ + if(is_array($response) && isset($response["id"]) && (is_int($response["id"]) || is_string($response["id"]))){ Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( "https://" . $host . "/?id=" . $response["id"])); }else{ From b6bd3ef30cb88afb078d8f69ae8c6993ed24f1df Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:46:16 +0000 Subject: [PATCH 189/290] Improve PHPDocs in world package --- src/world/World.php | 4 +++- src/world/format/Chunk.php | 8 +++++++- src/world/format/HeightArray.php | 4 ++-- src/world/format/io/FastChunkSerializer.php | 1 - src/world/format/io/region/RegionWorldProvider.php | 8 ++++++-- src/world/light/LightPopulationTask.php | 9 ++++++--- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index c2d153921..134b1f091 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1562,6 +1562,7 @@ class World implements ChunkManager{ * Larger AABBs (>= 2 blocks on any axis) are not accounted for. * * @return AxisAlignedBB[] + * @phpstan-return list */ private function getBlockCollisionBoxesForCell(int $x, int $y, int $z) : array{ $block = $this->getBlockAt($x, $y, $z); @@ -2040,7 +2041,6 @@ class World implements ChunkManager{ * @phpstan-return list */ public function dropExperience(Vector3 $pos, int $amount) : array{ - /** @var ExperienceOrb[] $orbs */ $orbs = []; foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){ @@ -3083,6 +3083,7 @@ class World implements ChunkManager{ * @phpstan-return Promise */ public function requestSafeSpawn(?Vector3 $spawn = null) : Promise{ + /** @phpstan-var PromiseResolver $resolver */ $resolver = new PromiseResolver(); $spawn ??= $this->getSpawnLocation(); /* @@ -3254,6 +3255,7 @@ class World implements ChunkManager{ private function enqueuePopulationRequest(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{ $chunkHash = World::chunkHash($chunkX, $chunkZ); $this->addChunkHashToPopulationRequestQueue($chunkHash); + /** @phpstan-var PromiseResolver $resolver */ $resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver(); if($associatedChunkLoader === null){ $temporaryLoader = new class implements ChunkLoader{}; diff --git a/src/world/format/Chunk.php b/src/world/format/Chunk.php index e4c877dc9..9ea5d3f8e 100644 --- a/src/world/format/Chunk.php +++ b/src/world/format/Chunk.php @@ -57,7 +57,10 @@ class Chunk{ */ protected \SplFixedArray $subChunks; - /** @var Tile[] */ + /** + * @var Tile[] + * @phpstan-var array + */ protected array $tiles = []; protected HeightArray $heightMap; @@ -210,6 +213,7 @@ class Chunk{ /** * @return Tile[] + * @phpstan-return array */ public function getTiles() : array{ return $this->tiles; @@ -237,6 +241,7 @@ class Chunk{ /** * @return int[] + * @phpstan-return non-empty-list */ public function getHeightMapArray() : array{ return $this->heightMap->getValues(); @@ -244,6 +249,7 @@ class Chunk{ /** * @param int[] $values + * @phpstan-param non-empty-list $values */ public function setHeightMapArray(array $values) : void{ $this->heightMap = new HeightArray($values); diff --git a/src/world/format/HeightArray.php b/src/world/format/HeightArray.php index 27f9cecb7..03094c3c8 100644 --- a/src/world/format/HeightArray.php +++ b/src/world/format/HeightArray.php @@ -36,7 +36,7 @@ final class HeightArray{ /** * @param int[] $values ZZZZXXXX key bit order - * @phpstan-param list $values + * @phpstan-param non-empty-list $values */ public function __construct(array $values){ if(count($values) !== 256){ @@ -66,7 +66,7 @@ final class HeightArray{ /** * @return int[] ZZZZXXXX key bit order - * @phpstan-return list + * @phpstan-return non-empty-list */ public function getValues() : array{ return $this->array->toArray(); diff --git a/src/world/format/io/FastChunkSerializer.php b/src/world/format/io/FastChunkSerializer.php index 6e18f27ac..35a8ff42f 100644 --- a/src/world/format/io/FastChunkSerializer.php +++ b/src/world/format/io/FastChunkSerializer.php @@ -112,7 +112,6 @@ final class FastChunkSerializer{ $y = Binary::signByte($stream->getByte()); $airBlockId = $stream->getInt(); - /** @var PalettedBlockArray[] $layers */ $layers = []; for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){ $layers[] = self::deserializePalettedArray($stream); diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index 75fcfd083..0ab70300e 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -93,10 +93,12 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ } /** - * @param int $regionX reference parameter - * @param int $regionZ reference parameter + * @param int|null $regionX reference parameter + * @param int|null $regionZ reference parameter * @phpstan-param-out int $regionX * @phpstan-param-out int $regionZ + * + * TODO: make this private */ public static function getRegionIndex(int $chunkX, int $chunkZ, &$regionX, &$regionZ) : void{ $regionX = $chunkX >> 5; @@ -154,6 +156,8 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ /** * @return CompoundTag[] + * @phpstan-return list + * * @throws CorruptedChunkException */ protected static function getCompoundList(string $context, ListTag $list) : array{ diff --git a/src/world/light/LightPopulationTask.php b/src/world/light/LightPopulationTask.php index 7a4f5071d..29d957831 100644 --- a/src/world/light/LightPopulationTask.php +++ b/src/world/light/LightPopulationTask.php @@ -44,7 +44,7 @@ class LightPopulationTask extends AsyncTask{ private string $resultBlockLightArrays; /** - * @phpstan-param \Closure(array $blockLight, array $skyLight, array $heightMap) : void $onCompletion + * @phpstan-param \Closure(array $blockLight, array $skyLight, non-empty-list $heightMap) : void $onCompletion */ public function __construct(Chunk $chunk, \Closure $onCompletion){ $this->chunk = FastChunkSerializer::serializeTerrain($chunk); @@ -80,7 +80,10 @@ class LightPopulationTask extends AsyncTask{ } public function onCompletion() : void{ - /** @var int[] $heightMapArray */ + /** + * @var int[] $heightMapArray + * @phpstan-var non-empty-list $heightMapArray + */ $heightMapArray = igbinary_unserialize($this->resultHeightMap); /** @var LightArray[] $skyLightArrays */ @@ -90,7 +93,7 @@ class LightPopulationTask extends AsyncTask{ /** * @var \Closure - * @phpstan-var \Closure(array $blockLight, array $skyLight, array $heightMap) : void + * @phpstan-var \Closure(array $blockLight, array $skyLight, non-empty-list $heightMap) : void */ $callback = $this->fetchLocal(self::TLS_KEY_COMPLETION_CALLBACK); $callback($blockLightArrays, $skyLightArrays, $heightMapArray); From 20849d6268f229660efa23311fd03151af8247b3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:48:22 +0000 Subject: [PATCH 190/290] Fixed potential crashes in type ID tests if the constants had any non-stringable values, these would blow up. this would still be fine in the sense that the tests would fail, but better that they fail gracefully if possible. --- tests/phpunit/block/BlockTypeIdsTest.php | 6 +++++- tests/phpunit/item/ItemTypeIdsTest.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/block/BlockTypeIdsTest.php b/tests/phpunit/block/BlockTypeIdsTest.php index ce21a89ab..cbfc07eaf 100644 --- a/tests/phpunit/block/BlockTypeIdsTest.php +++ b/tests/phpunit/block/BlockTypeIdsTest.php @@ -35,8 +35,12 @@ class BlockTypeIdsTest extends TestCase{ $constants = $reflect->getConstants(); unset($constants['FIRST_UNUSED_BLOCK_ID']); + self::assertNotEmpty($constants, "We should never have zero type IDs"); - self::assertSame($reflect->getConstant('FIRST_UNUSED_BLOCK_ID'), max($constants) + 1, "FIRST_UNUSED_BLOCK_ID must be one higher than the highest fixed type ID"); + $max = max($constants); + self::assertIsInt($max, "Max type ID should always be an integer"); + + self::assertSame($reflect->getConstant('FIRST_UNUSED_BLOCK_ID'), $max + 1, "FIRST_UNUSED_BLOCK_ID must be one higher than the highest fixed type ID"); } public function testNoDuplicates() : void{ diff --git a/tests/phpunit/item/ItemTypeIdsTest.php b/tests/phpunit/item/ItemTypeIdsTest.php index 7336780b3..a30489f07 100644 --- a/tests/phpunit/item/ItemTypeIdsTest.php +++ b/tests/phpunit/item/ItemTypeIdsTest.php @@ -35,8 +35,12 @@ class ItemTypeIdsTest extends TestCase{ $constants = $reflect->getConstants(); unset($constants['FIRST_UNUSED_ITEM_ID']); + self::assertNotEmpty($constants, "We should never have zero type IDs"); - self::assertSame($reflect->getConstant('FIRST_UNUSED_ITEM_ID'), max($constants) + 1, "FIRST_UNUSED_ITEM_ID must be one higher than the highest fixed type ID"); + $max = max($constants); + self::assertIsInt($max, "Max type ID should always be an integer"); + + self::assertSame($reflect->getConstant('FIRST_UNUSED_ITEM_ID'), $max + 1, "FIRST_UNUSED_ITEM_ID must be one higher than the highest fixed type ID"); } public function testNoDuplicates() : void{ From 9a130bce320860031a7cda02a6d14f48ebbc288b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:50:25 +0000 Subject: [PATCH 191/290] Config: remove bad assumptions about string root keys these could just as easily be integers and the code should still work. --- src/utils/Config.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/utils/Config.php b/src/utils/Config.php index cabc8fe16..dbccc892e 100644 --- a/src/utils/Config.php +++ b/src/utils/Config.php @@ -72,7 +72,7 @@ class Config{ /** * @var mixed[] - * @phpstan-var array + * @phpstan-var array */ private array $config = []; @@ -450,7 +450,7 @@ class Config{ /** * @return mixed[] - * @phpstan-return list|array + * @phpstan-return list|array */ public function getAll(bool $keys = false) : array{ return ($keys ? array_keys($this->config) : $this->config); @@ -458,7 +458,6 @@ class Config{ /** * @param mixed[] $defaults - * @phpstan-param array $defaults */ public function setDefaults(array $defaults) : void{ $this->fillDefaults($defaults, $this->config); @@ -467,13 +466,11 @@ class Config{ /** * @param mixed[] $default * @param mixed[] $data reference parameter - * @phpstan-param array $default - * @phpstan-param array $data - * @phpstan-param-out array $data + * @phpstan-param-out array $data */ private function fillDefaults(array $default, array &$data) : int{ $changed = 0; - foreach(Utils::stringifyKeys($default) as $k => $v){ + foreach(Utils::promoteKeys($default) as $k => $v){ if(is_array($v)){ if(!isset($data[$k]) || !is_array($data[$k])){ $data[$k] = []; @@ -560,7 +557,7 @@ class Config{ }; break; } - $result[(string) $k] = $v; + $result[$k] = $v; } } From 97c5902ae2fea587faaee7487bbe14fa6100d67e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:52:05 +0000 Subject: [PATCH 192/290] Internet: make postURL() error reporting behaviour more predictable err is now always set to null when doing a new operation. previously, if the same var was used multiple times and a previous one failed, code might think that a previous error belonged to the current operation. --- src/utils/Internet.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/Internet.php b/src/utils/Internet.php index 07b3c7a33..f3dad2271 100644 --- a/src/utils/Internet.php +++ b/src/utils/Internet.php @@ -157,22 +157,21 @@ class Internet{ * POSTs data to an URL * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. * - * @phpstan-template TErrorVar of mixed - * * @param string[]|string $args * @param string[] $extraHeaders * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occurred during the operation. * @phpstan-param string|array $args * @phpstan-param list $extraHeaders - * @phpstan-param TErrorVar $err - * @phpstan-param-out TErrorVar|string $err + * @phpstan-param-out string|null $err */ public static function postURL(string $page, array|string $args, int $timeout = 10, array $extraHeaders = [], &$err = null) : ?InternetRequestResult{ try{ - return self::simpleCurl($page, $timeout, $extraHeaders, [ + $result = self::simpleCurl($page, $timeout, $extraHeaders, [ CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $args ]); + $err = null; + return $result; }catch(InternetException $ex){ $err = $ex->getMessage(); return null; From 0358b7dce4d4c6d74a4388aaf931168eb5d7a56b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 22:53:35 +0000 Subject: [PATCH 193/290] utils: avoid weak comparisons --- src/utils/Internet.php | 2 +- src/utils/Timezone.php | 8 ++++---- src/utils/Utils.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/Internet.php b/src/utils/Internet.php index f3dad2271..1811ce51e 100644 --- a/src/utils/Internet.php +++ b/src/utils/Internet.php @@ -100,7 +100,7 @@ class Internet{ } $ip = self::getURL("http://ifconfig.me/ip"); - if($ip !== null && ($addr = trim($ip->getBody())) != ""){ + if($ip !== null && ($addr = trim($ip->getBody())) !== ""){ return self::$ip = $addr; } diff --git a/src/utils/Timezone.php b/src/utils/Timezone.php index 7f1dcf0e4..6723b12eb 100644 --- a/src/utils/Timezone.php +++ b/src/utils/Timezone.php @@ -134,7 +134,7 @@ abstract class Timezone{ $offset = $matches[2]; - if($offset == ""){ + if($offset === ""){ return "UTC"; } @@ -156,7 +156,7 @@ abstract class Timezone{ $offset = trim(exec('date +%:z')); - if($offset == "+00:00"){ + if($offset === "+00:00"){ return "UTC"; } @@ -195,7 +195,7 @@ abstract class Timezone{ $offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second']; //After date_parse is done, put the sign back - if($negative_offset == true){ + if($negative_offset){ $offset = -abs($offset); } @@ -204,7 +204,7 @@ abstract class Timezone{ //That's been a bug in PHP since 2008! foreach(timezone_abbreviations_list() as $zones){ foreach($zones as $timezone){ - if($timezone['timezone_id'] !== null && $timezone['offset'] == $offset){ + if($timezone['timezone_id'] !== null && $timezone['offset'] === $offset){ return $timezone['timezone_id']; } } diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 9317aff3c..cc33e60e0 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -220,7 +220,7 @@ final class Utils{ $mac = implode("\n", $mac); if(preg_match_all("#Physical Address[. ]{1,}: ([0-9A-F\\-]{17})#", $mac, $matches) > 0){ foreach($matches[1] as $i => $v){ - if($v == "00-00-00-00-00-00"){ + if($v === "00-00-00-00-00-00"){ unset($matches[1][$i]); } } @@ -234,7 +234,7 @@ final class Utils{ $mac = implode("\n", $mac); if(preg_match_all("#HWaddr[ \t]{1,}([0-9a-f:]{17})#", $mac, $matches) > 0){ foreach($matches[1] as $i => $v){ - if($v == "00:00:00:00:00:00"){ + if($v === "00:00:00:00:00:00"){ unset($matches[1][$i]); } } From 357dfb5c7e54f3585d604f9b1258240f7d04c723 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:01:14 +0000 Subject: [PATCH 194/290] Fixed build --- src/utils/Config.php | 10 +++++----- src/world/World.php | 2 +- tests/phpstan/configs/actual-problems.neon | 2 +- tests/phpstan/configs/spl-fixed-array-sucks.neon | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/Config.php b/src/utils/Config.php index dbccc892e..7b6da6389 100644 --- a/src/utils/Config.php +++ b/src/utils/Config.php @@ -506,8 +506,8 @@ class Config{ } /** - * @param string[] $entries - * @phpstan-param list $entries + * @param string[]|int[] $entries + * @phpstan-param list $entries */ public static function writeList(array $entries) : string{ return implode("\n", $entries); @@ -515,11 +515,11 @@ class Config{ /** * @param string[]|int[]|float[]|bool[] $config - * @phpstan-param array $config + * @phpstan-param array $config */ public static function writeProperties(array $config) : string{ $content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n"; - foreach(Utils::stringifyKeys($config) as $k => $v){ + foreach(Utils::promoteKeys($config) as $k => $v){ if(is_bool($v)){ $v = $v ? "on" : "off"; } @@ -531,7 +531,7 @@ class Config{ /** * @return string[]|int[]|float[]|bool[] - * @phpstan-return array + * @phpstan-return array */ public static function parseProperties(string $content) : array{ $result = []; diff --git a/src/world/World.php b/src/world/World.php index 134b1f091..b6df7f2a4 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1364,7 +1364,7 @@ class World implements ChunkManager{ * TODO: phpstan can't infer these types yet :( * @phpstan-var array $blockLight * @phpstan-var array $skyLight - * @phpstan-var array $heightMap + * @phpstan-var non-empty-list $heightMap */ if($this->unloaded || ($chunk = $this->getChunk($chunkX, $chunkZ)) === null || $chunk->isLightPopulated() === true){ return; diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 1bb9329f0..f55fe1cbc 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -791,7 +791,7 @@ parameters: path: ../../../src/utils/Config.php - - message: "#^Parameter \\#1 \\$config of static method pocketmine\\\\utils\\\\Config\\:\\:writeProperties\\(\\) expects array\\, array\\ given\\.$#" + message: "#^Parameter \\#1 \\$config of static method pocketmine\\\\utils\\\\Config\\:\\:writeProperties\\(\\) expects array\\, array\\ given\\.$#" count: 1 path: ../../../src/utils/Config.php diff --git a/tests/phpstan/configs/spl-fixed-array-sucks.neon b/tests/phpstan/configs/spl-fixed-array-sucks.neon index daa6361dd..7c2b2b91a 100644 --- a/tests/phpstan/configs/spl-fixed-array-sucks.neon +++ b/tests/phpstan/configs/spl-fixed-array-sucks.neon @@ -16,7 +16,7 @@ parameters: path: ../../../src/world/format/Chunk.php - - message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return array\\ but returns array\\\\.$#" + message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return non\\-empty\\-array\\ but returns array\\\\.$#" count: 1 path: ../../../src/world/format/HeightArray.php From 84ec8b7abe99220528bae9020ba8b4a529907ff7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:02:18 +0000 Subject: [PATCH 195/290] Removed dead error patterns I do think these are PHPStan bugs, since the trait should inherit the parent class's doc comment But for the sake of catching more bugs, these doc comments have been manually added anyway. --- tests/phpstan/configs/phpstan-bugs.neon | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 1ae740d66..0ee2b68a0 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -1,20 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Method pocketmine\\\\block\\\\CakeWithCandle\\:\\:onInteractCandle\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" - count: 1 - path: ../../../src/block/CakeWithCandle.php - - - - message: "#^Method pocketmine\\\\block\\\\CopperDoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" - count: 1 - path: ../../../src/block/CopperDoor.php - - - - message: "#^Method pocketmine\\\\block\\\\CopperTrapdoor\\:\\:onInteractCopper\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" - count: 1 - path: ../../../src/block/CopperTrapdoor.php - - message: "#^Method pocketmine\\\\block\\\\DoubleTallGrass\\:\\:traitGetDropsForIncompatibleTool\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 From 1b2d2a3fe19e933eeb10f54d71dc5dc8037fd949 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:04:00 +0000 Subject: [PATCH 196/290] plugin: improve PHPDocs and type compliance --- src/plugin/PluginDescription.php | 28 ++++++++++++++++++++-------- src/plugin/PluginLoadTriage.php | 4 ++-- src/plugin/PluginManager.php | 10 +++++----- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/plugin/PluginDescription.php b/src/plugin/PluginDescription.php index 35ae2ba32..89ac19e05 100644 --- a/src/plugin/PluginDescription.php +++ b/src/plugin/PluginDescription.php @@ -84,11 +84,20 @@ class PluginDescription{ * @phpstan-var array> */ private array $extensions = []; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $depend = []; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $softDepend = []; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $loadBefore = []; private string $version; /** @@ -173,7 +182,7 @@ class PluginDescription{ } if(isset($plugin[self::KEY_DEPEND])){ - $this->depend = (array) $plugin[self::KEY_DEPEND]; + $this->depend = array_values((array) $plugin[self::KEY_DEPEND]); } if(isset($plugin[self::KEY_EXTENSIONS])){ $extensions = (array) $plugin[self::KEY_EXTENSIONS]; @@ -183,13 +192,13 @@ class PluginDescription{ $k = $v; $v = "*"; } - $this->extensions[(string) $k] = array_map('strval', is_array($v) ? $v : [$v]); + $this->extensions[(string) $k] = array_values(array_map('strval', is_array($v) ? $v : [$v])); } } - $this->softDepend = (array) ($plugin[self::KEY_SOFTDEPEND] ?? $this->softDepend); + $this->softDepend = array_values((array) ($plugin[self::KEY_SOFTDEPEND] ?? $this->softDepend)); - $this->loadBefore = (array) ($plugin[self::KEY_LOADBEFORE] ?? $this->loadBefore); + $this->loadBefore = array_values((array) ($plugin[self::KEY_LOADBEFORE] ?? $this->loadBefore)); $this->website = (string) ($plugin[self::KEY_WEBSITE] ?? $this->website); @@ -210,7 +219,7 @@ class PluginDescription{ $this->authors = []; if(isset($plugin[self::KEY_AUTHOR])){ if(is_array($plugin[self::KEY_AUTHOR])){ - $this->authors = $plugin[self::KEY_AUTHOR]; + $this->authors = array_values($plugin[self::KEY_AUTHOR]); }else{ $this->authors[] = $plugin[self::KEY_AUTHOR]; } @@ -284,6 +293,7 @@ class PluginDescription{ /** * @return string[] + * @phpstan-return list */ public function getDepend() : array{ return $this->depend; @@ -295,6 +305,7 @@ class PluginDescription{ /** * @return string[] + * @phpstan-return list */ public function getLoadBefore() : array{ return $this->loadBefore; @@ -324,6 +335,7 @@ class PluginDescription{ /** * @return string[] + * @phpstan-return list */ public function getSoftDepend() : array{ return $this->softDepend; diff --git a/src/plugin/PluginLoadTriage.php b/src/plugin/PluginLoadTriage.php index 77d102668..fcf32751e 100644 --- a/src/plugin/PluginLoadTriage.php +++ b/src/plugin/PluginLoadTriage.php @@ -31,12 +31,12 @@ final class PluginLoadTriage{ public array $plugins = []; /** * @var string[][] - * @phpstan-var array> + * @phpstan-var array> */ public array $dependencies = []; /** * @var string[][] - * @phpstan-var array> + * @phpstan-var array> */ public array $softDependencies = []; } diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index f84698aa0..a8440f04f 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -327,12 +327,12 @@ class PluginManager{ * @param string[][] $dependencyLists * @param Plugin[] $loadedPlugins * - * @phpstan-param array> $dependencyLists - * @phpstan-param-out array> $dependencyLists + * @phpstan-param array> $dependencyLists + * @phpstan-param-out array> $dependencyLists */ private function checkDepsForTriage(string $pluginName, string $dependencyType, array &$dependencyLists, array $loadedPlugins, PluginLoadTriage $triage) : void{ if(isset($dependencyLists[$pluginName])){ - foreach($dependencyLists[$pluginName] as $key => $dependency){ + foreach(Utils::promoteKeys($dependencyLists[$pluginName]) as $key => $dependency){ if(isset($loadedPlugins[$dependency]) || $this->getPlugin($dependency) instanceof Plugin){ $this->server->getLogger()->debug("Successfully resolved $dependencyType dependency \"$dependency\" for plugin \"$pluginName\""); unset($dependencyLists[$pluginName][$key]); @@ -399,7 +399,7 @@ class PluginManager{ //check for skippable soft dependencies first, in case the dependents could resolve hard dependencies foreach(Utils::stringifyKeys($triage->plugins) as $name => $file){ if(isset($triage->softDependencies[$name]) && !isset($triage->dependencies[$name])){ - foreach($triage->softDependencies[$name] as $k => $dependency){ + foreach(Utils::promoteKeys($triage->softDependencies[$name]) as $k => $dependency){ if($this->getPlugin($dependency) === null && !array_key_exists($dependency, $triage->plugins)){ $this->server->getLogger()->debug("Skipping resolution of missing soft dependency \"$dependency\" for plugin \"$name\""); unset($triage->softDependencies[$name][$k]); @@ -416,7 +416,7 @@ class PluginManager{ if(isset($triage->dependencies[$name])){ $unknownDependencies = []; - foreach($triage->dependencies[$name] as $k => $dependency){ + foreach($triage->dependencies[$name] as $dependency){ if($this->getPlugin($dependency) === null && !array_key_exists($dependency, $triage->plugins)){ //assume that the plugin is never going to be loaded //by this point all soft dependencies have been ignored if they were able to be, so From db9ba83001e2a2e6aa49e1e250d211c3f1e72fcf Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:05:06 +0000 Subject: [PATCH 197/290] Make some assumptions about proc_open() --- src/utils/Git.php | 2 +- src/utils/Process.php | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/utils/Git.php b/src/utils/Git.php index 041d795a1..2b9e242bc 100644 --- a/src/utils/Git.php +++ b/src/utils/Git.php @@ -39,7 +39,7 @@ final class Git{ * @param bool $dirty reference parameter, set to whether the repo has local changes */ public static function getRepositoryState(string $dir, bool &$dirty) : ?string{ - if(Process::execute("git -C \"$dir\" rev-parse HEAD", $out) === 0 && $out !== false && strlen($out = trim($out)) === 40){ + if(Process::execute("git -C \"$dir\" rev-parse HEAD", $out) === 0 && strlen($out = trim($out)) === 40){ if(Process::execute("git -C \"$dir\" diff --quiet") === 1 || Process::execute("git -C \"$dir\" diff --cached --quiet") === 1){ //Locally-modified $dirty = true; } diff --git a/src/utils/Process.php b/src/utils/Process.php index 1370ab27c..90149870a 100644 --- a/src/utils/Process.php +++ b/src/utils/Process.php @@ -174,8 +174,17 @@ final class Process{ return -1; } - $stdout = stream_get_contents($pipes[1]); - $stderr = stream_get_contents($pipes[2]); + $out = stream_get_contents($pipes[1]); + if($out === false){ + throw new AssumptionFailedError("Presume this can't happen for proc_open ... ???"); + } + $stdout = $out; + + $err = stream_get_contents($pipes[2]); + if($err === false){ + throw new AssumptionFailedError("Presume this can't happen for proc_open ... ???"); + } + $stderr = $err; foreach($pipes as $p){ fclose($p); From 9592f066f338c517d985ee4cf30877bbf135863d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:05:49 +0000 Subject: [PATCH 198/290] PHPDoc: Restrict ReversePriorityQueue to numeric priorities --- src/utils/ReversePriorityQueue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ReversePriorityQueue.php b/src/utils/ReversePriorityQueue.php index 53fd0f08a..03f1aea8d 100644 --- a/src/utils/ReversePriorityQueue.php +++ b/src/utils/ReversePriorityQueue.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\utils; /** - * @phpstan-template TPriority + * @phpstan-template TPriority of numeric * @phpstan-template TValue * @phpstan-extends \SplPriorityQueue */ From 73edb8799dad7d6dc8fbc029060a58da6be29bec Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:06:19 +0000 Subject: [PATCH 199/290] SignalHandler: fixed dodgy setup logic --- src/utils/SignalHandler.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/SignalHandler.php b/src/utils/SignalHandler.php index 82ae02da6..75d38ff97 100644 --- a/src/utils/SignalHandler.php +++ b/src/utils/SignalHandler.php @@ -36,14 +36,12 @@ use const SIGTERM; final class SignalHandler{ /** @phpstan-var (\Closure(int) : void)|null */ - private ?\Closure $interruptCallback; + private ?\Closure $interruptCallback = null; /** * @phpstan-param \Closure() : void $interruptCallback */ public function __construct(\Closure $interruptCallback){ - $this->interruptCallback = $interruptCallback; - if(function_exists('sapi_windows_set_ctrl_handler')){ sapi_windows_set_ctrl_handler($this->interruptCallback = function(int $signo) use ($interruptCallback) : void{ if($signo === PHP_WINDOWS_EVENT_CTRL_C || $signo === PHP_WINDOWS_EVENT_CTRL_BREAK){ From a1ba8bc3da8f726f24e43fc70059441d2f3eae05 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:07:54 +0000 Subject: [PATCH 200/290] NetworkSession: improve PHPDoc types --- src/network/mcpe/NetworkSession.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 98460bef3..21fa38ea7 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -164,7 +164,10 @@ class NetworkSession{ private ?EncryptionContext $cipher = null; - /** @var string[] */ + /** + * @var string[] + * @phpstan-var list + */ private array $sendBuffer = []; /** * @var PromiseResolver[] @@ -544,6 +547,7 @@ class NetworkSession{ * @phpstan-return Promise */ public function sendDataPacketWithReceipt(ClientboundPacket $packet, bool $immediate = false) : Promise{ + /** @phpstan-var PromiseResolver $resolver */ $resolver = new PromiseResolver(); if(!$this->sendDataPacketInternal($packet, $immediate, $resolver)){ From d1fa6edc50d53c2f2b0280580f04370ff67749bc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:08:18 +0000 Subject: [PATCH 201/290] InGamePacketHandler: fix weak comparisons --- src/network/mcpe/handler/InGamePacketHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 88b4ba1a0..dba16e1e6 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -416,7 +416,7 @@ class InGamePacketHandler extends PacketHandler{ $droppedCount = null; foreach($data->getActions() as $networkInventoryAction){ - if($networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_WORLD && $networkInventoryAction->inventorySlot == NetworkInventoryAction::ACTION_MAGIC_SLOT_DROP_ITEM){ + if($networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_WORLD && $networkInventoryAction->inventorySlot === NetworkInventoryAction::ACTION_MAGIC_SLOT_DROP_ITEM){ $droppedCount = $networkInventoryAction->newItem->getItemStack()->getCount(); if($droppedCount <= 0){ throw new PacketHandlingException("Expected positive count for dropped item"); @@ -578,7 +578,7 @@ class InGamePacketHandler extends PacketHandler{ private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{ $this->player->selectHotbarSlot($data->getHotbarSlot()); - if($data->getActionType() == ReleaseItemTransactionData::ACTION_RELEASE){ + if($data->getActionType() === ReleaseItemTransactionData::ACTION_RELEASE){ $this->player->releaseHeldItem(); return true; } From 2e32c5067029e1040ec431cd6b817470129c37ef Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:08:48 +0000 Subject: [PATCH 202/290] NetworkSession: apparently aliases are already a list at this point??? --- src/network/mcpe/NetworkSession.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 21fa38ea7..80a7e4ddf 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -1110,7 +1110,7 @@ class NetworkSession{ //work around a client bug which makes the original name not show when aliases are used $aliases[] = $lname; } - $aliasObj = new CommandEnum(ucfirst($command->getLabel()) . "Aliases", array_values($aliases)); + $aliasObj = new CommandEnum(ucfirst($command->getLabel()) . "Aliases", $aliases); } $description = $command->getDescription(); From 601be3fb3325204ea6cdf32e6007722bf6b786ba Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:09:26 +0000 Subject: [PATCH 203/290] stfu --- src/network/mcpe/NetworkSession.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 80a7e4ddf..511d0dece 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -118,7 +118,6 @@ use pocketmine\world\Position; use pocketmine\world\World; use pocketmine\YmlServerProperties; use function array_map; -use function array_values; use function base64_encode; use function bin2hex; use function count; From a17512de9388439c9dd6364e15873d4a67263e3a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 6 Jan 2025 23:26:13 +0000 Subject: [PATCH 204/290] Command: don't trust plugins not to pass junk --- src/command/Command.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/command/Command.php b/src/command/Command.php index 63276a123..aea57e6a2 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -33,6 +33,7 @@ use pocketmine\permission\PermissionManager; use pocketmine\Server; use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\TextFormat; +use function array_values; use function explode; use function implode; use function str_replace; @@ -213,6 +214,7 @@ abstract class Command{ * @phpstan-param list $aliases */ public function setAliases(array $aliases) : void{ + $aliases = array_values($aliases); //because plugins can and will pass crap $this->aliases = $aliases; if(!$this->isRegistered()){ $this->activeAliases = $aliases; From 28d31c97f81ff82481e9b4f3e6c9f8a66ac45834 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:05:01 +0000 Subject: [PATCH 205/290] Server: fixup PHPStan 2.x reported issues --- src/Server.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Server.php b/src/Server.php index 9003a7f9c..5a72ad048 100644 --- a/src/Server.php +++ b/src/Server.php @@ -138,6 +138,7 @@ use function file_put_contents; use function filemtime; use function fopen; use function get_class; +use function gettype; use function ini_set; use function is_array; use function is_dir; @@ -918,6 +919,7 @@ class Server{ TimingsHandler::getCollectCallbacks()->add(function() : array{ $promises = []; foreach($this->asyncPool->getRunningWorkers() as $workerId){ + /** @phpstan-var PromiseResolver> $resolver */ $resolver = new PromiseResolver(); $this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId); @@ -1013,7 +1015,11 @@ class Server{ copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile); } 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){ $this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage()); $this->forceShutdownExit(); @@ -1174,7 +1180,7 @@ class Server{ if($this->worldManager->getDefaultWorld() === null){ $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"); $default = "world"; $this->configGroup->setConfigString(ServerProperties::DEFAULT_WORLD_NAME, "world"); From 7b1b35ab1f0bcfb2df1acf0670d286e9a19a130f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:07:38 +0000 Subject: [PATCH 206/290] generator: fixup issues reported by PHPStan 2.0 --- src/world/generator/FlatGeneratorOptions.php | 3 +-- src/world/generator/normal/Normal.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/world/generator/FlatGeneratorOptions.php b/src/world/generator/FlatGeneratorOptions.php index ab10c8a47..ba89cefb7 100644 --- a/src/world/generator/FlatGeneratorOptions.php +++ b/src/world/generator/FlatGeneratorOptions.php @@ -75,8 +75,7 @@ final class FlatGeneratorOptions{ $y = 0; $itemParser = LegacyStringToItemParser::getInstance(); foreach($split as $line){ - preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches); - if(count($matches) !== 3){ + if(preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches) !== 1){ throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\""); } diff --git a/src/world/generator/normal/Normal.php b/src/world/generator/normal/Normal.php index 1d4805e16..a440f1e5f 100644 --- a/src/world/generator/normal/Normal.php +++ b/src/world/generator/normal/Normal.php @@ -126,10 +126,10 @@ class Normal extends Generator{ $hash = (int) $hash; $xNoise = $hash >> 20 & 3; $zNoise = $hash >> 22 & 3; - if($xNoise == 3){ + if($xNoise === 3){ $xNoise = 1; } - if($zNoise == 3){ + if($zNoise === 3){ $zNoise = 1; } From cd59e272bcabcbcf97fdd380625bcf39969043f0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:10:42 +0000 Subject: [PATCH 207/290] PHPStan 2.0 fixes --- src/crafting/CraftingManager.php | 2 -- .../block/upgrade/BlockStateUpgradeSchemaUtils.php | 7 +++---- src/data/runtime/RuntimeEnumMetadata.php | 3 +-- src/event/HandlerListManager.php | 2 +- src/inventory/transaction/InventoryTransaction.php | 2 +- src/item/WritableBookBase.php | 5 +++-- src/item/enchantment/ItemEnchantmentTagRegistry.php | 6 ++++-- src/player/Player.php | 8 +++----- src/resourcepacks/ResourcePackManager.php | 11 +++++++++-- src/scheduler/BulkCurlTask.php | 5 ++++- src/timings/TimingsHandler.php | 2 ++ src/timings/TimingsRecord.php | 2 +- src/world/World.php | 11 +++++------ src/world/format/io/region/RegionWorldProvider.php | 10 ++++++---- 14 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php index ff2be6926..895eeaccc 100644 --- a/src/crafting/CraftingManager.php +++ b/src/crafting/CraftingManager.php @@ -110,7 +110,6 @@ class CraftingManager{ /** * @param Item[] $items - * @phpstan-param list $items * * @return Item[] * @phpstan-return list @@ -135,7 +134,6 @@ class CraftingManager{ /** * @param Item[] $outputs - * @phpstan-param list $outputs */ private static function hashOutputs(array $outputs) : string{ $outputs = self::pack($outputs); diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php index 08eba8978..b4ed0dd26 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php @@ -37,7 +37,6 @@ use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function array_key_last; use function array_map; -use function array_values; use function assert; use function count; use function get_debug_type; @@ -138,8 +137,8 @@ final class BlockStateUpgradeSchemaUtils{ $convertedRemappedValuesIndex = []; foreach(Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){ - foreach($mappingValues as $k => $oldNew){ - $convertedRemappedValuesIndex[$mappingKey][$k] = new BlockStateUpgradeSchemaValueRemap( + foreach($mappingValues as $oldNew){ + $convertedRemappedValuesIndex[$mappingKey][] = new BlockStateUpgradeSchemaValueRemap( self::jsonModelToTag($oldNew->old), 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 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)){ ksort($result->remappedStates); diff --git a/src/data/runtime/RuntimeEnumMetadata.php b/src/data/runtime/RuntimeEnumMetadata.php index 261b7a1bc..45f831b19 100644 --- a/src/data/runtime/RuntimeEnumMetadata.php +++ b/src/data/runtime/RuntimeEnumMetadata.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\data\runtime; -use function array_values; use function ceil; use function count; 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) $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 = []; foreach($this->intToEnum as $int => $enum){ diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index 605a38747..9437df37f 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -119,7 +119,7 @@ class HandlerListManager{ public function getHandlersFor(string $event) : array{ $cache = $this->handlerCaches[$event] ?? null; //getListFor() will populate the cache for the next call - return $cache?->list ?? $this->getListFor($event)->getListenerList(); + return $cache->list ?? $this->getListFor($event)->getListenerList(); } /** diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index 47290e401..8f7b57610 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -232,7 +232,7 @@ class InventoryTransaction{ /** * @param SlotChangeAction[] $possibleActions - * @phpstan-param list $possibleActions + * @phpstan-param array $possibleActions */ protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{ assert(count($possibleActions) > 0); diff --git a/src/item/WritableBookBase.php b/src/item/WritableBookBase.php index 6b7e55468..d3b9b7061 100644 --- a/src/item/WritableBookBase.php +++ b/src/item/WritableBookBase.php @@ -101,8 +101,9 @@ abstract class WritableBookBase extends Item{ * @return $this */ public function deletePage(int $pageId) : self{ - unset($this->pages[$pageId]); - $this->pages = array_values($this->pages); + $newPages = $this->pages; + unset($newPages[$pageId]); + $this->pages = array_values($newPages); return $this; } diff --git a/src/item/enchantment/ItemEnchantmentTagRegistry.php b/src/item/enchantment/ItemEnchantmentTagRegistry.php index 210cd8e86..b239f18a2 100644 --- a/src/item/enchantment/ItemEnchantmentTagRegistry.php +++ b/src/item/enchantment/ItemEnchantmentTagRegistry.php @@ -32,6 +32,7 @@ use function array_merge; use function array_search; use function array_shift; use function array_unique; +use function array_values; use function count; /** @@ -103,7 +104,8 @@ final class ItemEnchantmentTagRegistry{ foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){ 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{ $this->assertNotInternalTag($tag); - $this->tagMap[$tag] = array_diff($this->tagMap[$tag], $nestedTags); + $this->tagMap[$tag] = array_values(array_diff($this->tagMap[$tag], $nestedTags)); } /** diff --git a/src/player/Player.php b/src/player/Player.php index bf911a2ff..66ba6f376 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -147,7 +147,6 @@ use function count; use function explode; use function floor; use function get_class; -use function is_int; use function max; use function mb_strlen; use function microtime; @@ -826,7 +825,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $X = null; $Z = null; World::getXZ($index, $X, $Z); - assert(is_int($X) && is_int($Z)); ++$count; @@ -1346,7 +1344,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->nextChunkOrderRun = 0; } - if(!$revert && $distanceSquared != 0){ + if(!$revert && $distanceSquared !== 0.0){ $dx = $newPos->x - $oldPos->x; $dy = $newPos->y - $oldPos->y; $dz = $newPos->z - $oldPos->z; @@ -2319,7 +2317,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason); $ev->call(); - if(($quitMessage = $ev->getQuitMessage()) != ""){ + if(($quitMessage = $ev->getQuitMessage()) !== ""){ $this->server->broadcastMessage($quitMessage); } $this->save(); @@ -2460,7 +2458,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->xpManager->setXpAndProgress(0, 0.0); } - if($ev->getDeathMessage() != ""){ + if($ev->getDeathMessage() !== ""){ $this->server->broadcastMessage($ev->getDeathMessage()); } diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index baf16ca20..c4668eb2a 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -47,10 +47,16 @@ class ResourcePackManager{ private string $path; private bool $serverForceResources = false; - /** @var ResourcePack[] */ + /** + * @var ResourcePack[] + * @phpstan-var list + */ private array $resourcePacks = []; - /** @var ResourcePack[] */ + /** + * @var ResourcePack[] + * @phpstan-var array + */ private array $uuidList = []; /** @@ -165,6 +171,7 @@ class ResourcePackManager{ /** * Returns an array of resource packs in use, sorted in order of priority. * @return ResourcePack[] + * @phpstan-return list */ public function getResourceStack() : array{ return $this->resourcePacks; diff --git a/src/scheduler/BulkCurlTask.php b/src/scheduler/BulkCurlTask.php index ccc1b2466..21f144702 100644 --- a/src/scheduler/BulkCurlTask.php +++ b/src/scheduler/BulkCurlTask.php @@ -77,7 +77,10 @@ class BulkCurlTask extends AsyncTask{ * @phpstan-var \Closure(list) : void */ $callback = $this->fetchLocal(self::TLS_KEY_COMPLETION_CALLBACK); - /** @var InternetRequestResult[]|InternetException[] $results */ + /** + * @var InternetRequestResult[]|InternetException[] $results + * @phpstan-var list $results + */ $results = $this->getResult(); $callback($results); } diff --git a/src/timings/TimingsHandler.php b/src/timings/TimingsHandler.php index 95f7dbacc..25f139d91 100644 --- a/src/timings/TimingsHandler.php +++ b/src/timings/TimingsHandler.php @@ -123,6 +123,7 @@ class TimingsHandler{ /** * @return string[] + * @phpstan-return list */ private static function printFooter() : array{ $result = []; @@ -173,6 +174,7 @@ class TimingsHandler{ } } + /** @phpstan-var PromiseResolver> $resolver */ $resolver = new PromiseResolver(); Promise::all($otherThreadRecordPromises)->onCompletion( function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{ diff --git a/src/timings/TimingsRecord.php b/src/timings/TimingsRecord.php index 2e4928d8a..390ab74e5 100644 --- a/src/timings/TimingsRecord.php +++ b/src/timings/TimingsRecord.php @@ -131,7 +131,7 @@ final class TimingsRecord{ } public function stopTiming(int $now) : void{ - if($this->start == 0){ + if($this->start === 0){ return; } if(self::$currentRecord !== $this){ diff --git a/src/world/World.php b/src/world/World.php index b6df7f2a4..7395afa78 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -113,6 +113,7 @@ use function array_keys; use function array_map; use function array_merge; use function array_sum; +use function array_values; use function assert; use function cos; use function count; @@ -678,7 +679,6 @@ class World implements ChunkManager{ * Used for broadcasting sounds and particles with specific targets. * * @param Player[] $allowed - * @phpstan-param list $allowed * * @return array */ @@ -1089,7 +1089,6 @@ class World implements ChunkManager{ /** * @param Vector3[] $blocks - * @phpstan-param list $blocks * * @return ClientboundPacket[] * @phpstan-return list @@ -1456,8 +1455,8 @@ class World implements ChunkManager{ $this->provider->saveChunk($chunkX, $chunkZ, new ChunkData( $chunk->getSubChunks(), $chunk->isPopulated(), - array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($chunkX, $chunkZ), fn(Entity $e) => $e->canSaveWithChunk())), - array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()), + 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(), array_values($chunk->getTiles())), ), $chunk->getTerrainDirtyFlags()); $chunk->clearTerrainDirtyFlags(); } @@ -3019,8 +3018,8 @@ class World implements ChunkManager{ $this->provider->saveChunk($x, $z, new ChunkData( $chunk->getSubChunks(), $chunk->isPopulated(), - array_map(fn(Entity $e) => $e->saveNBT(), array_filter($this->getChunkEntities($x, $z), fn(Entity $e) => $e->canSaveWithChunk())), - array_map(fn(Tile $t) => $t->saveNBT(), $chunk->getTiles()), + 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(), array_values($chunk->getTiles())), ), $chunk->getTerrainDirtyFlags()); }finally{ $this->timings->syncChunkSave->stopTiming(); diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index 0ab70300e..7a4463f5d 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -33,10 +33,8 @@ use pocketmine\world\format\io\exception\CorruptedChunkException; use pocketmine\world\format\io\LoadedChunkData; use pocketmine\world\format\io\WorldData; use Symfony\Component\Filesystem\Path; -use function assert; use function file_exists; use function is_dir; -use function is_int; use function morton2d_encode; use function rename; use function scandir; @@ -60,7 +58,12 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ public static function isValid(string $path) : bool{ 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, "."); 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 @@ -199,7 +202,6 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ public function loadChunk(int $chunkX, int $chunkZ) : ?LoadedChunkData{ $regionX = $regionZ = null; self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); - assert(is_int($regionX) && is_int($regionZ)); if(!file_exists($this->pathToRegion($regionX, $regionZ))){ return null; From b1c7fc017a9f68b0eefde15380ec6c67ddfa59aa Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:13:20 +0000 Subject: [PATCH 208/290] CS --- src/world/generator/FlatGeneratorOptions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/world/generator/FlatGeneratorOptions.php b/src/world/generator/FlatGeneratorOptions.php index ba89cefb7..b52d64658 100644 --- a/src/world/generator/FlatGeneratorOptions.php +++ b/src/world/generator/FlatGeneratorOptions.php @@ -27,7 +27,6 @@ use pocketmine\data\bedrock\BiomeIds; use pocketmine\item\LegacyStringToItemParser; use pocketmine\item\LegacyStringToItemParserException; use function array_map; -use function count; use function explode; use function preg_match; use function preg_match_all; From 47cb04f6a65eeed7e91e85d428f83b108bce218a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:15:50 +0000 Subject: [PATCH 209/290] tools: fix PHPStan 2.0 issues --- tools/blockstate-upgrade-schema-utils.php | 9 ++++++--- tools/compact-regions.php | 10 ++++++++-- tools/generate-bedrock-data-from-packets.php | 6 +++--- tools/generate-item-upgrade-schema.php | 15 +++++++++++---- tools/simulate-chunk-selector.php | 16 ++++++++++++++++ 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index b7a9a4169..4f5c8740c 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -523,10 +523,12 @@ function processRemappedStates(array $upgradeTable) : array{ } } } + $orderedUnchanged = []; foreach(Utils::stringifyKeys($unchangedStatesByNewName) as $newName => $unchangedStates){ - ksort($unchangedStates); - $unchangedStatesByNewName[$newName] = $unchangedStates; + sort($unchangedStates); + $orderedUnchanged[$newName] = $unchangedStates; } + $unchangedStatesByNewName = $orderedUnchanged; $notFlattenedProperties = []; @@ -656,7 +658,8 @@ function processRemappedStates(array $upgradeTable) : array{ usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{ return count($b->oldState) <=> count($a->oldState); }); - return array_values($list); + //usort discards keys, so this is already a list + return $list; } /** diff --git a/tools/compact-regions.php b/tools/compact-regions.php index 04ac3f0c9..ab80792d3 100644 --- a/tools/compact-regions.php +++ b/tools/compact-regions.php @@ -76,7 +76,12 @@ function find_regions_recursive(string $dir, array &$files) : void{ in_array(pathinfo($fullPath, PATHINFO_EXTENSION), SUPPORTED_EXTENSIONS, true) && 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)){ find_regions_recursive($fullPath, $files); } @@ -165,7 +170,8 @@ function main(array $argv) : int{ clearstatcache(); $newSize = 0; 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; $logger->info("Finished compaction of " . count($files) . " files. Freed " . number_format($diff) . " bytes of space (" . round(($diff / $currentSize) * 100, 2) . "% reduction)."); diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 2c20e6099..9aac5185e 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -454,7 +454,7 @@ class ParserPacketHandler extends PacketHandler{ //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 - foreach($recipes as $_type => $entries){ + foreach(Utils::promoteKeys($recipes) as $_type => $entries){ $_sortedRecipes = []; $_seen = []; foreach($entries as $entry){ @@ -475,10 +475,10 @@ class ParserPacketHandler extends PacketHandler{ } ksort($recipes, SORT_STRING); - foreach($recipes as $type => $entries){ + foreach(Utils::promoteKeys($recipes) as $type => $entries){ 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"); } diff --git a/tools/generate-item-upgrade-schema.php b/tools/generate-item-upgrade-schema.php index 4eee92539..7ad473b23 100644 --- a/tools/generate-item-upgrade-schema.php +++ b/tools/generate-item-upgrade-schema.php @@ -31,10 +31,12 @@ namespace pocketmine\tools\generate_item_upgrade_schema; use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\utils\Filesystem; +use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function count; use function dirname; use function file_put_contents; +use function fwrite; use function is_array; use function json_decode; use function json_encode; @@ -45,6 +47,7 @@ use const JSON_PRETTY_PRINT; use const JSON_THROW_ON_ERROR; use const SCANDIR_SORT_ASCENDING; use const SORT_STRING; +use const STDERR; require dirname(__DIR__) . '/vendor/autoload.php'; @@ -56,7 +59,7 @@ if(count($argv) !== 4){ [, $mappingTableFile, $upgradeSchemasDir, $outputFile] = $argv; $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"); exit(1); } @@ -93,7 +96,7 @@ foreach($files as $file){ $newDiff = []; -foreach($target["simple"] as $oldId => $newId){ +foreach(Utils::promoteKeys($target["simple"]) as $oldId => $newId){ $previousNewId = $merged["simple"][$oldId] ?? null; if( $previousNewId === $newId || //if previous schemas already accounted for this @@ -107,8 +110,12 @@ if(isset($newDiff["renamedIds"])){ ksort($newDiff["renamedIds"], SORT_STRING); } -foreach($target["complex"] as $oldId => $mappings){ - foreach($mappings as $meta => $newId){ +foreach(Utils::promoteKeys($target["complex"]) as $oldId => $mappings){ + 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($oldId === "minecraft:spawn_egg" && $meta === 130 && ($newId === "minecraft:axolotl_bucket" || $newId === "minecraft:axolotl_spawn_egg")){ //TODO: hack for vanilla bug workaround diff --git a/tools/simulate-chunk-selector.php b/tools/simulate-chunk-selector.php index 0b279268a..3d5e167cf 100644 --- a/tools/simulate-chunk-selector.php +++ b/tools/simulate-chunk-selector.php @@ -53,6 +53,10 @@ use const STR_PAD_LEFT; require dirname(__DIR__) . '/vendor/autoload.php'; +/** + * @phpstan-param positive-int $scale + * @phpstan-param positive-int $radius + */ function newImage(int $scale, int $radius) : \GdImage{ $image = Utils::assumeNotFalse(imagecreatetruecolor($scale * $radius * 2, $scale * $radius * 2)); imagesavealpha($image, true); @@ -149,6 +153,18 @@ if($radius === null){ fwrite(STDERR, "Please specify a radius using --radius\n"); 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; if(isset($opts["output"])){ From 38441a6ba3818b7e3b96995c048a28fb1c77dd89 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:23:16 +0000 Subject: [PATCH 210/290] build: avoid weak comparison --- build/server-phar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/server-phar.php b/build/server-phar.php index f6bb29d51..7560fa5da 100644 --- a/build/server-phar.php +++ b/build/server-phar.php @@ -129,7 +129,7 @@ function buildPhar(string $pharPath, string $basePath, array $includedPaths, arr } 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; exit(1); } From d69a887b0d40320d18fa9d0950fbeb1a54d38ec2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:24:26 +0000 Subject: [PATCH 211/290] Utils: fix parameter doc for printableExceptionInfo() --- src/utils/Utils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index cc33e60e0..37cf54390 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -406,6 +406,7 @@ final class Utils{ /** * @param mixed[][] $trace + * @phpstan-param list>|null $trace * @return string[] */ public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{ From d25ec58a6fd7151ad4a5dbf4035c9630caa1061b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:25:37 +0000 Subject: [PATCH 212/290] AsyncPoolTest: phpdoc --- tests/phpunit/scheduler/AsyncPoolTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 53ec15c12..88985cc39 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -71,6 +71,7 @@ class AsyncPoolTest extends TestCase{ } public function testThreadSafeSetResult() : void{ + /** @phpstan-var PromiseResolver> $resolver */ $resolver = new PromiseResolver(); $resolver->getPromise()->onCompletion( function(ThreadSafeArray $result) : void{ From 9633b7d8a79b90fcd7510ddb7bc765ad649361f3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:34:43 +0000 Subject: [PATCH 213/290] Update to PHPStan 2.x --- composer.json | 6 +- composer.lock | 58 +- phpstan.neon.dist | 2 + src/command/utils/CommandStringHelper.php | 3 +- src/utils/Utils.php | 9 +- .../format/io/region/RegionWorldProvider.php | 3 + src/world/generator/FlatGeneratorOptions.php | 2 +- tests/phpstan/DummyPluginOwned.php | 28 + tests/phpstan/configs/actual-problems.neon | 773 ++++++++++++------ .../phpstan/configs/dependency-problems.neon | 73 ++ .../phpstan/configs/impossible-generics.neon | 6 +- tests/phpstan/configs/phpstan-bugs.neon | 192 ++++- .../configs/spl-fixed-array-sucks.neon | 18 +- .../rules/DeprecatedLegacyEnumAccessRule.php | 27 +- .../rules/DisallowEnumComparisonRule.php | 15 +- .../rules/DisallowForeachByReferenceRule.php | 1 + .../rules/UnsafeForeachArrayOfStringRule.php | 2 +- tests/phpunit/utils/fixtures/TestTrait.php | 2 +- 18 files changed, 876 insertions(+), 344 deletions(-) create mode 100644 tests/phpstan/DummyPluginOwned.php create mode 100644 tests/phpstan/configs/dependency-problems.neon diff --git a/composer.json b/composer.json index e2ae641ca..a40f3733e 100644 --- a/composer.json +++ b/composer.json @@ -52,9 +52,9 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "1.11.11", - "phpstan/phpstan-phpunit": "^1.1.0", - "phpstan/phpstan-strict-rules": "^1.2.0", + "phpstan/phpstan": "2.1.1", + "phpstan/phpstan-phpunit": "^2.0.0", + "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" }, "autoload": { diff --git a/composer.lock b/composer.lock index 54f65014f..8df6c329d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "732102eca72dc1d29e7b67dfbce07653", + "content-hash": "994ccffe45f066768542019f6f9d237b", "packages": [ { "name": "adhocore/json-comment", @@ -1386,20 +1386,20 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.11", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" + "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", - "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", + "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -1440,34 +1440,33 @@ "type": "github" } ], - "time": "2024-08-19T14:37:29+00:00" + "time": "2025-01-05T16:43:48+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "1.4.0", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11" + "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11", - "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/e32ac656788a5bf3dedda89e6a2cad5643bf1a18", + "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.11" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" }, "conflict": { "phpunit/phpunit": "<7.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -1490,34 +1489,33 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "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", - "version": "1.6.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "363f921dd8441777d4fc137deb99beb486c77df1" + "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/363f921dd8441777d4fc137deb99beb486c77df1", - "reference": "363f921dd8441777d4fc137deb99beb486c77df1", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3", + "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.11" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0.4" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -1539,9 +1537,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "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", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 6e8578652..dfaa964e4 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,6 +1,7 @@ includes: - tests/phpstan/analyse-for-current-php-version.neon.php - tests/phpstan/configs/actual-problems.neon + - tests/phpstan/configs/dependency-problems.neon - tests/phpstan/configs/impossible-generics.neon - tests/phpstan/configs/php-bugs.neon - tests/phpstan/configs/phpstan-bugs.neon @@ -31,6 +32,7 @@ parameters: paths: - build - src + - tests/phpstan/DummyPluginOwned.php - tests/phpstan/rules - tests/phpunit - tests/plugins/TesterPlugin diff --git a/src/command/utils/CommandStringHelper.php b/src/command/utils/CommandStringHelper.php index eacc5d3d8..76d70a9bb 100644 --- a/src/command/utils/CommandStringHelper.php +++ b/src/command/utils/CommandStringHelper.php @@ -51,9 +51,8 @@ final class CommandStringHelper{ foreach($matches[0] as $k => $_){ for($i = 1; $i <= 2; ++$i){ if($matches[$i][$k] !== ""){ - /** @var string $match */ //phpstan can't understand preg_match and friends by itself :( $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; } } diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 37cf54390..f557562c9 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -167,6 +167,7 @@ final class Utils{ /** * @phpstan-return \Closure(object) : object + * @deprecated */ public static function cloneCallback() : \Closure{ return static function(object $o){ @@ -179,15 +180,13 @@ final class Utils{ * @phpstan-template TValue of object * * @param object[] $array - * @phpstan-param array $array + * @phpstan-param array|list $array * * @return object[] - * @phpstan-return array + * @phpstan-return ($array is list ? list : array) */ public static function cloneObjectArray(array $array) : array{ - /** @phpstan-var \Closure(TValue) : TValue $callback */ - $callback = self::cloneCallback(); - return array_map($callback, $array); + return array_map(fn(object $o) => clone $o, $array); } /** diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index 7a4463f5d..8fe7928b8 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -215,6 +215,9 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ return null; } + /** + * @phpstan-return \RegexIterator + */ private function createRegionIterator() : \RegexIterator{ return new \RegexIterator( new \FilesystemIterator( diff --git a/src/world/generator/FlatGeneratorOptions.php b/src/world/generator/FlatGeneratorOptions.php index b52d64658..563297b00 100644 --- a/src/world/generator/FlatGeneratorOptions.php +++ b/src/world/generator/FlatGeneratorOptions.php @@ -117,7 +117,7 @@ final class FlatGeneratorOptions{ } } } - $options[(string) $option] = $params; + $options[$option] = $params; } return new self($structure, $biomeId, $options); } diff --git a/tests/phpstan/DummyPluginOwned.php b/tests/phpstan/DummyPluginOwned.php new file mode 100644 index 000000000..b63975dcf --- /dev/null +++ b/tests/phpstan/DummyPluginOwned.php @@ -0,0 +1,28 @@ +, array\\ given\\.$#" + message: '#^Parameter \#1 \$strings of function pocketmine\\build\\server_phar\\preg_quote_array expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: ../../../build/server-phar.php - - message: "#^Do\\-while loop condition is always false\\.$#" + message: '#^Do\-while loop condition is always false\.$#' + identifier: doWhile.alwaysFalse count: 1 path: ../../../src/PocketMine.php - - message: "#^Parameter \\#1 \\$array of static method pocketmine\\\\plugin\\\\PluginGraylist\\:\\:fromArray\\(\\) expects array, mixed given\\.$#" + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: ../../../src/Server.php - - message: "#^Cannot cast mixed to int\\.$#" + message: '#^Method pocketmine\\Server\:\:getCommandAliases\(\) should return array\\> but returns array\\>\.$#' + identifier: return.type + count: 1 + path: ../../../src/Server.php + + - + message: '#^Parameter \#1 \$generatorName of closure expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/Server.php + + - + message: '#^Cannot cast mixed to int\.$#' + identifier: cast.int count: 2 path: ../../../src/ServerConfigGroup.php - - message: "#^Cannot cast mixed to string\\.$#" + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 2 path: ../../../src/ServerConfigGroup.php - - message: "#^Cannot access offset 'git' on mixed\\.$#" + message: '#^Cannot access offset ''git'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: ../../../src/VersionInfo.php - - message: "#^Method pocketmine\\\\VersionInfo\\:\\:GIT_HASH\\(\\) should return string but returns mixed\\.$#" + message: '#^Method pocketmine\\VersionInfo\:\:GIT_HASH\(\) should return string but returns mixed\.$#' + identifier: return.type count: 1 path: ../../../src/VersionInfo.php - - message: "#^Static property pocketmine\\\\VersionInfo\\:\\:\\$gitHash \\(string\\|null\\) does not accept mixed\\.$#" + message: '#^Static property pocketmine\\VersionInfo\:\:\$gitHash \(string\|null\) does not accept mixed\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/VersionInfo.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Block.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Block.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:setBlockStateId\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\format\\Chunk\:\:setBlockStateId\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Block.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Block.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Cactus.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/block/ChorusFlower.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/block/ChorusFlower.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/block/ChorusFlower.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getRealBlockSkyLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DaylightSensor.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getRealBlockSkyLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DaylightSensor.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getRealBlockSkyLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getRealBlockSkyLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DaylightSensor.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#1 \\$xDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$xDiff of class pocketmine\\world\\particle\\DragonEggTeleportParticle constructor expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int\\<\\-64, 319\\> given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int\<\-64, 319\> given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#2 \\$yDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$yDiff of class pocketmine\\world\\particle\\DragonEggTeleportParticle constructor expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#3 \\$zDiff of class pocketmine\\\\world\\\\particle\\\\DragonEggTeleportParticle constructor expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$zDiff of class pocketmine\\world\\particle\\DragonEggTeleportParticle constructor expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/DragonEgg.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Fire.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getHighestAdjacentFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getHighestAdjacentFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getHighestAdjacentFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/FrostedIce.php - - message: "#^Parameter \\#1 \\$min of function mt_rand expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$min of function mt_rand expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#2 \\$max of function mt_rand expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$max of function mt_rand expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Grass.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getHighestAdjacentBlockLight\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Ice.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getHighestAdjacentBlockLight\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Ice.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getHighestAdjacentBlockLight\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getHighestAdjacentBlockLight\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Ice.php - - message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Leaves.php - - message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Leaves.php - - message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Leaves.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Liquid.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Liquid.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Liquid.php - - message: "#^Parameter \\#1 \\$min of function mt_rand expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$min of function mt_rand expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Mycelium.php - - message: "#^Parameter \\#2 \\$max of function mt_rand expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$max of function mt_rand expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/Mycelium.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/RedMushroom.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/RedMushroom.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getFullLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getFullLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/RedMushroom.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/SnowLayer.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/SnowLayer.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/SnowLayer.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/Sugarcane.php - - message: "#^Parameter \\#1 \\$x of class pocketmine\\\\math\\\\Vector3 constructor expects float\\|int, int\\|null given\\.$#" + message: '#^Parameter \#1 \$x of class pocketmine\\math\\Vector3 constructor expects float\|int, int\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/tile/Chest.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, int\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/tile/Chest.php - - message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, int\\|null given\\.$#" + message: '#^Parameter \#2 \$value of method pocketmine\\nbt\\tag\\CompoundTag\:\:setInt\(\) expects int, int\|null given\.$#' + identifier: argument.type count: 4 path: ../../../src/block/tile/Chest.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/tile/Chest.php - - message: "#^Parameter \\#3 \\$z of class pocketmine\\\\math\\\\Vector3 constructor expects float\\|int, int\\|null given\\.$#" + message: '#^Parameter \#3 \$z of class pocketmine\\math\\Vector3 constructor expects float\|int, int\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/tile/Chest.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, int\\|null given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, int\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/tile/Chest.php - - message: "#^Property pocketmine\\\\block\\\\tile\\\\Chest\\:\\:\\$pairX \\(int\\|null\\) does not accept float\\|int\\.$#" + message: '#^Property pocketmine\\block\\tile\\Chest\:\:\$pairX \(int\|null\) does not accept float\|int\.$#' + identifier: assign.propertyType count: 2 path: ../../../src/block/tile/Chest.php - - message: "#^Property pocketmine\\\\block\\\\tile\\\\Chest\\:\\:\\$pairZ \\(int\\|null\\) does not accept float\\|int\\.$#" + message: '#^Property pocketmine\\block\\tile\\Chest\:\:\$pairZ \(int\|null\) does not accept float\|int\.$#' + identifier: assign.propertyType count: 2 path: ../../../src/block/tile/Chest.php - - message: "#^Constant pocketmine\\\\block\\\\tile\\\\MobHead\\:\\:TAG_MOUTH_MOVING is unused\\.$#" + message: '#^Constant pocketmine\\block\\tile\\MobHead\:\:TAG_MOUTH_MOVING is unused\.$#' + identifier: classConstant.unused count: 1 path: ../../../src/block/tile/MobHead.php - - message: "#^Constant pocketmine\\\\block\\\\tile\\\\MobHead\\:\\:TAG_MOUTH_TICK_COUNT is unused\\.$#" + message: '#^Constant pocketmine\\block\\tile\\MobHead\:\:TAG_MOUTH_TICK_COUNT is unused\.$#' + identifier: classConstant.unused count: 1 path: ../../../src/block/tile/MobHead.php - - message: "#^Parameter \\#2 \\$value of method pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\:\\:setInt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$value of method pocketmine\\nbt\\tag\\CompoundTag\:\:setInt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 3 path: ../../../src/block/tile/Spawnable.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getPotentialLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/utils/CropGrowthHelper.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getPotentialLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/utils/CropGrowthHelper.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getPotentialLightAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getPotentialLightAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/block/utils/CropGrowthHelper.php - - message: "#^Cannot call method addParticle\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#" + message: '#^Cannot call method addParticle\(\) on pocketmine\\world\\World\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/command/defaults/ParticleCommand.php - - message: "#^Cannot call method getSeed\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#" + message: '#^Cannot call method getSeed\(\) on pocketmine\\world\\World\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/command/defaults/SeedCommand.php - - message: "#^Cannot call method setSpawnLocation\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#" + message: '#^Cannot call method setSpawnLocation\(\) on pocketmine\\world\\World\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/command/defaults/SetWorldSpawnCommand.php - - message: "#^Cannot call method getTime\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#" + message: '#^Cannot call method getTime\(\) on pocketmine\\world\\World\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/command/defaults/TimeCommand.php - - message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$path of static method pocketmine\\utils\\Filesystem\:\:cleanPath\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/crash/CrashDump.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Binary operation "\." between ''Error\: '' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: ../../../src/crash/CrashDumpRenderer.php + + - + message: '#^Binary operation "\." between ''File\: '' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: ../../../src/crash/CrashDumpRenderer.php + + - + message: '#^Binary operation "\." between ''Line\: '' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: ../../../src/crash/CrashDumpRenderer.php + + - + message: '#^Binary operation "\." between ''Type\: '' and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: ../../../src/crash/CrashDumpRenderer.php + + - + message: '#^Parameter \#1 \$blockToItemId of class pocketmine\\data\\bedrock\\item\\BlockItemIdMap constructor expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/data/bedrock/item/BlockItemIdMap.php + + - + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/Living.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/Living.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/Living.php - - message: "#^Parameter \\#2 \\$x of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$x of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/FallingBlock.php - - message: "#^Parameter \\#3 \\$y of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$y of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/FallingBlock.php - - message: "#^Parameter \\#4 \\$z of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#4 \$z of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/FallingBlock.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/Painting.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/Painting.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/object/Painting.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/projectile/Projectile.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/projectile/Projectile.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/entity/projectile/Projectile.php - - message: "#^Parameter \\#2 \\$recipe of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects pocketmine\\\\crafting\\\\CraftingRecipe, pocketmine\\\\crafting\\\\CraftingRecipe\\|null given\\.$#" + message: '#^Parameter \#2 \$recipe of class pocketmine\\event\\inventory\\CraftItemEvent constructor expects pocketmine\\crafting\\CraftingRecipe, pocketmine\\crafting\\CraftingRecipe\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/inventory/transaction/CraftingTransaction.php - - message: "#^Parameter \\#3 \\$repetitions of class pocketmine\\\\event\\\\inventory\\\\CraftItemEvent constructor expects int, int\\|null given\\.$#" + message: '#^Parameter \#3 \$repetitions of class pocketmine\\event\\inventory\\CraftItemEvent constructor expects int, int\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/inventory/transaction/CraftingTransaction.php - - message: "#^Cannot cast mixed to int\\.$#" + message: '#^Parameter &\$haveItems @param\-out type of method pocketmine\\inventory\\transaction\\InventoryTransaction\:\:matchItems\(\) expects list\, array\, pocketmine\\item\\Item\> given\.$#' + identifier: paramOut.type + count: 1 + path: ../../../src/inventory/transaction/InventoryTransaction.php + + - + message: '#^Parameter &\$needItems @param\-out type of method pocketmine\\inventory\\transaction\\InventoryTransaction\:\:matchItems\(\) expects list\, array\, pocketmine\\item\\Item\> given\.$#' + identifier: paramOut.type + count: 1 + path: ../../../src/inventory/transaction/InventoryTransaction.php + + - + message: '#^Cannot cast mixed to int\.$#' + identifier: cast.int count: 2 path: ../../../src/item/Item.php - - message: "#^Parameter \\#1 \\$buffer of method pocketmine\\\\nbt\\\\BaseNbtSerializer\\:\\:read\\(\\) expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$buffer of method pocketmine\\nbt\\BaseNbtSerializer\:\:read\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/item/Item.php - - message: "#^Parameter \\#1 \\$string of function base64_decode expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$string of function base64_decode expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/item/Item.php - - message: "#^Parameter \\#1 \\$string of function hex2bin expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$string of function hex2bin expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/item/Item.php - - message: "#^Parameter \\#1 \\$result of method pocketmine\\\\network\\\\mcpe\\\\compression\\\\CompressBatchPromise\\:\\:resolve\\(\\) expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$result of method pocketmine\\network\\mcpe\\compression\\CompressBatchPromise\:\:resolve\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/ChunkRequestTask.php - - message: "#^Cannot call method doFirstSpawn\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method doFirstSpawn\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getAttributeMap\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method getAttributeMap\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getLanguage\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method getLanguage\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 4 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getLocation\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method getLocation\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getUsedChunkStatus\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method getUsedChunkStatus\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getUsername\\(\\) on pocketmine\\\\player\\\\PlayerInfo\\|null\\.$#" + message: '#^Cannot call method getUsername\(\) on pocketmine\\player\\PlayerInfo\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method getUuid\\(\\) on pocketmine\\\\player\\\\PlayerInfo\\|null\\.$#" + message: '#^Cannot call method getUuid\(\) on pocketmine\\player\\PlayerInfo\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method sendData\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method sendData\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method setNoClientPredictions\\(\\) on pocketmine\\\\player\\\\Player\\|null\\.$#" + message: '#^Cannot call method setNoClientPredictions\(\) on pocketmine\\player\\Player\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Cannot call method syncAll\\(\\) on pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null\\.$#" + message: '#^Cannot call method syncAll\(\) on pocketmine\\network\\mcpe\\InventoryManager\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$clientPub of class pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask constructor expects string, string\\|null given\\.$#" + message: '#^Parameter \#1 \$clientPub of class pocketmine\\network\\mcpe\\encryption\\PrepareEncryptionTask constructor expects string, string\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$for of method pocketmine\\\\network\\\\mcpe\\\\NetworkSession\\:\\:syncAbilities\\(\\) expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#1 \$for of method pocketmine\\network\\mcpe\\NetworkSession\:\:syncAbilities\(\) expects pocketmine\\player\\Player, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\DeathPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#1 \$player of class pocketmine\\network\\mcpe\\handler\\DeathPacketHandler constructor expects pocketmine\\player\\Player, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\InGamePacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#1 \$player of class pocketmine\\network\\mcpe\\handler\\InGamePacketHandler constructor expects pocketmine\\player\\Player, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$playerInfo of class pocketmine\\\\event\\\\player\\\\PlayerResourcePackOfferEvent constructor expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#" + message: '#^Parameter \#1 \$playerInfo of class pocketmine\\event\\player\\PlayerResourcePackOfferEvent constructor expects pocketmine\\player\\PlayerInfo, pocketmine\\player\\PlayerInfo\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#1 \$target of method pocketmine\\command\\Command\:\:testPermissionSilent\(\) expects pocketmine\\command\\CommandSender, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectAdded\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#2 \$entity of method pocketmine\\network\\mcpe\\EntityEventBroadcaster\:\:onEntityEffectAdded\(\) expects pocketmine\\entity\\Living, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:onEntityEffectRemoved\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#2 \$entity of method pocketmine\\network\\mcpe\\EntityEventBroadcaster\:\:onEntityEffectRemoved\(\) expects pocketmine\\entity\\Living, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#2 \\$entity of method pocketmine\\\\network\\\\mcpe\\\\EntityEventBroadcaster\\:\\:syncAttributes\\(\\) expects pocketmine\\\\entity\\\\Living, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#2 \$entity of method pocketmine\\network\\mcpe\\EntityEventBroadcaster\:\:syncAttributes\(\) expects pocketmine\\entity\\Living, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#2 \\$player of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\player\\\\Player, pocketmine\\\\player\\\\Player\\|null given\\.$#" + message: '#^Parameter \#2 \$player of class pocketmine\\network\\mcpe\\handler\\PreSpawnPacketHandler constructor expects pocketmine\\player\\Player, pocketmine\\player\\Player\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#2 \\$playerInfo of method pocketmine\\\\Server\\:\\:createPlayer\\(\\) expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#" + message: '#^Parameter \#2 \$playerInfo of method pocketmine\\Server\:\:createPlayer\(\) expects pocketmine\\player\\PlayerInfo, pocketmine\\player\\PlayerInfo\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#3 \\$inventoryManager of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\InGamePacketHandler constructor expects pocketmine\\\\network\\\\mcpe\\\\InventoryManager, pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null given\\.$#" + message: '#^Parameter \#3 \$inventoryManager of class pocketmine\\network\\mcpe\\handler\\InGamePacketHandler constructor expects pocketmine\\network\\mcpe\\InventoryManager, pocketmine\\network\\mcpe\\InventoryManager\|null given\.$#' + identifier: argument.type count: 2 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#4 \\$inventoryManager of class pocketmine\\\\network\\\\mcpe\\\\handler\\\\PreSpawnPacketHandler constructor expects pocketmine\\\\network\\\\mcpe\\\\InventoryManager, pocketmine\\\\network\\\\mcpe\\\\InventoryManager\\|null given\\.$#" + message: '#^Parameter \#4 \$inventoryManager of class pocketmine\\network\\mcpe\\handler\\PreSpawnPacketHandler constructor expects pocketmine\\network\\mcpe\\InventoryManager, pocketmine\\network\\mcpe\\InventoryManager\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Property pocketmine\\\\network\\\\mcpe\\\\auth\\\\ProcessLoginTask\\:\\:\\$chain \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\network\\mcpe\\auth\\ProcessLoginTask\:\:\$chain \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/network/mcpe/auth/ProcessLoginTask.php - - message: "#^Parameter \\#1 \\$result of method pocketmine\\\\network\\\\mcpe\\\\compression\\\\CompressBatchPromise\\:\\:resolve\\(\\) expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$result of method pocketmine\\network\\mcpe\\compression\\CompressBatchPromise\:\:resolve\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/network/mcpe/compression/CompressBatchTask.php - - message: "#^Property pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask\\:\\:\\$serverPrivateKey \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\network\\mcpe\\encryption\\PrepareEncryptionTask\:\:\$serverPrivateKey \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/network/mcpe/encryption/PrepareEncryptionTask.php - - message: "#^Method pocketmine\\\\permission\\\\DefaultPermissions\\:\\:registerPermission\\(\\) should return pocketmine\\\\permission\\\\Permission but returns pocketmine\\\\permission\\\\Permission\\|null\\.$#" + message: '#^Method pocketmine\\permission\\DefaultPermissions\:\:registerPermission\(\) should return pocketmine\\permission\\Permission but returns pocketmine\\permission\\Permission\|null\.$#' + identifier: return.type count: 1 path: ../../../src/permission/DefaultPermissions.php - - message: "#^Parameter \\#1 \\$value of static method pocketmine\\\\permission\\\\PermissionParser\\:\\:defaultFromString\\(\\) expects bool\\|string, mixed given\\.$#" + message: '#^Parameter \#1 \$value of static method pocketmine\\permission\\PermissionParser\:\:defaultFromString\(\) expects bool\|string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/permission/PermissionParser.php - - message: "#^Parameter \\#2 \\$description of class pocketmine\\\\permission\\\\Permission constructor expects pocketmine\\\\lang\\\\Translatable\\|string\\|null, mixed given\\.$#" + message: '#^Parameter \#2 \$description of class pocketmine\\permission\\Permission constructor expects pocketmine\\lang\\Translatable\|string\|null, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/permission/PermissionParser.php - - message: "#^Cannot call method getSpawnLocation\\(\\) on pocketmine\\\\world\\\\World\\|null\\.$#" + message: '#^Cannot call method getSpawnLocation\(\) on pocketmine\\world\\World\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/player/Player.php - - message: "#^Method pocketmine\\\\player\\\\Player\\:\\:getSpawn\\(\\) should return pocketmine\\\\world\\\\Position but returns pocketmine\\\\world\\\\Position\\|null\\.$#" + message: '#^Method pocketmine\\player\\Player\:\:getSpawn\(\) should return pocketmine\\world\\Position but returns pocketmine\\world\\Position\|null\.$#' + identifier: return.type count: 1 path: ../../../src/player/Player.php - - message: "#^Method pocketmine\\\\plugin\\\\PluginBase\\:\\:getConfig\\(\\) should return pocketmine\\\\utils\\\\Config but returns pocketmine\\\\utils\\\\Config\\|null\\.$#" + message: '#^Method pocketmine\\plugin\\PluginBase\:\:getConfig\(\) should return pocketmine\\utils\\Config but returns pocketmine\\utils\\Config\|null\.$#' + identifier: return.type count: 1 path: ../../../src/plugin/PluginBase.php - - message: "#^Cannot cast mixed to string\\.$#" + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Parameter \\#1 \\$haystack of function stripos expects string, mixed given\\.$#" + message: '#^Parameter \#1 \$haystack of function stripos expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Parameter \\#2 \\$subject of function preg_match expects string, mixed given\\.$#" + message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, mixed given\\.$#" + message: '#^Parameter \#3 \$subject of function str_replace expects array\\|string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Property pocketmine\\\\plugin\\\\PluginDescription\\:\\:\\$main \\(string\\) does not accept mixed\\.$#" + message: '#^Property pocketmine\\plugin\\PluginDescription\:\:\$authors \(array\\) does not accept list\\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Property pocketmine\\\\plugin\\\\PluginDescription\\:\\:\\$name \\(string\\) does not accept mixed\\.$#" + message: '#^Property pocketmine\\plugin\\PluginDescription\:\:\$main \(string\) does not accept mixed\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/plugin/PluginDescription.php - - message: "#^Cannot call method addChild\\(\\) on pocketmine\\\\permission\\\\Permission\\|null\\.$#" + message: '#^Property pocketmine\\plugin\\PluginDescription\:\:\$name \(string\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: ../../../src/plugin/PluginDescription.php + + - + message: '#^Cannot call method addChild\(\) on pocketmine\\permission\\Permission\|null\.$#' + identifier: method.nonObject count: 4 path: ../../../src/plugin/PluginManager.php - - message: "#^Method pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:getPackSize\\(\\) should return int but returns int\\<0, max\\>\\|false\\.$#" + message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getPackSize\(\) should return int but returns int\<0, max\>\|false\.$#' + identifier: return.type count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: "#^Method pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:getSha256\\(\\) should return string but returns string\\|false\\.$#" + message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getSha256\(\) should return string but returns string\|false\.$#' + identifier: return.type count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$fileResource \\(resource\\) does not accept resource\\|false\\.$#" + message: '#^Property pocketmine\\resourcepacks\\ZippedResourcePack\:\:\$fileResource \(resource\) does not accept resource\|false\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: "#^Property pocketmine\\\\resourcepacks\\\\ZippedResourcePack\\:\\:\\$sha256 \\(string\\|null\\) does not accept string\\|false\\.$#" + message: '#^Property pocketmine\\resourcepacks\\ZippedResourcePack\:\:\$sha256 \(string\|null\) does not accept string\|false\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: "#^Property pocketmine\\\\scheduler\\\\BulkCurlTask\\:\\:\\$operations \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\scheduler\\BulkCurlTask\:\:\$operations \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/scheduler/BulkCurlTask.php - - message: "#^Cannot call method getNextRun\\(\\) on array\\\\>\\|int\\|pocketmine\\\\scheduler\\\\TaskHandler\\\\.$#" + message: '#^Cannot call method getNextRun\(\) on array\\>\|int\|pocketmine\\scheduler\\TaskHandler\\.$#' + identifier: method.nonObject count: 1 path: ../../../src/scheduler/TaskScheduler.php - - message: "#^Cannot access offset string on mixed\\.$#" + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 2 path: ../../../src/utils/Config.php - - message: "#^Method pocketmine\\\\utils\\\\Config\\:\\:fixYAMLIndexes\\(\\) should return string but returns string\\|null\\.$#" + message: '#^Method pocketmine\\utils\\Config\:\:fixYAMLIndexes\(\) should return string but returns string\|null\.$#' + identifier: return.type count: 1 path: ../../../src/utils/Config.php - - message: "#^Parameter \\#1 \\$config of static method pocketmine\\\\utils\\\\Config\\:\\:writeProperties\\(\\) expects array\\, array\\ given\\.$#" + message: '#^Parameter \#1 \$config of static method pocketmine\\utils\\Config\:\:writeProperties\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: ../../../src/utils/Config.php - - message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|false given\\.$#" + message: '#^Parameter \#1 \$string of function trim expects string, string\|false given\.$#' + identifier: argument.type count: 1 path: ../../../src/utils/Timezone.php - - message: "#^Cannot cast mixed to string\\.$#" + message: '#^Binary operation "\." between mixed and ''\-\>''\|''\:\:'' results in an error\.$#' + identifier: binaryOp.invalid count: 1 path: ../../../src/utils/Utils.php - - message: "#^Method pocketmine\\\\utils\\\\Utils\\:\\:printable\\(\\) should return string but returns string\\|null\\.$#" + message: '#^Binary operation "\." between non\-falsy\-string and mixed results in an error\.$#' + identifier: binaryOp.invalid + count: 2 + path: ../../../src/utils/Utils.php + + - + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: ../../../src/utils/Utils.php - - message: "#^Parameter \\#1 \\$path of static method pocketmine\\\\utils\\\\Filesystem\\:\\:cleanPath\\(\\) expects string, mixed given\\.$#" + message: '#^Method pocketmine\\utils\\Utils\:\:printable\(\) should return string but returns string\|null\.$#' + identifier: return.type count: 1 path: ../../../src/utils/Utils.php - - message: "#^Parameter \\#2 \\$file of class pocketmine\\\\thread\\\\ThreadCrashInfoFrame constructor expects string\\|null, mixed given\\.$#" + message: '#^Parameter \#1 \$path of static method pocketmine\\utils\\Filesystem\:\:cleanPath\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/utils/Utils.php - - message: "#^Parameter \\#3 \\$line of class pocketmine\\\\thread\\\\ThreadCrashInfoFrame constructor expects int, mixed given\\.$#" + message: '#^Parameter \#2 \$file of class pocketmine\\thread\\ThreadCrashInfoFrame constructor expects string\|null, mixed given\.$#' + identifier: argument.type count: 1 path: ../../../src/utils/Utils.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$line of class pocketmine\\thread\\ThreadCrashInfoFrame constructor expects int, mixed given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/utils/Utils.php + + - + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:setBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:setBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:setBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/Explosion.php - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" + message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable count: 1 path: ../../../src/world/World.php - - message: "#^Cannot access offset 'data' on array\\{priority\\: int, data\\: pocketmine\\\\math\\\\Vector3\\}\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#" + message: '#^Cannot access offset ''data'' on array\{priority\: int, data\: pocketmine\\math\\Vector3\}\|int\|pocketmine\\math\\Vector3\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: ../../../src/world/World.php - - message: "#^Cannot access offset 'priority' on array\\{priority\\: int, data\\: pocketmine\\\\math\\\\Vector3\\}\\|int\\|pocketmine\\\\math\\\\Vector3\\.$#" + message: '#^Cannot access offset ''priority'' on array\{priority\: int, data\: pocketmine\\math\\Vector3\}\|int\|pocketmine\\math\\Vector3\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: ../../../src/world/World.php - - message: "#^Cannot cast mixed to string\\.$#" + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#2 \\$x of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$x of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#3 \\$y of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$y of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:getTileAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:getTileAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/World.php - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\world\\\\World\\:\\:isInWorld\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of method pocketmine\\world\\World\:\:isInWorld\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\World\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of static method pocketmine\\world\\World\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Parameter \\#4 \\$z of method pocketmine\\\\block\\\\Block\\:\\:position\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#4 \$z of method pocketmine\\block\\Block\:\:position\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/World.php - - message: "#^Method pocketmine\\\\world\\\\biome\\\\BiomeRegistry\\:\\:getBiome\\(\\) should return pocketmine\\\\world\\\\biome\\\\Biome but returns pocketmine\\\\world\\\\biome\\\\Biome\\|null\\.$#" + message: '#^Method pocketmine\\world\\biome\\BiomeRegistry\:\:getBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#' + identifier: return.type count: 1 path: ../../../src/world/biome/BiomeRegistry.php - - message: "#^Method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:getSubChunk\\(\\) should return pocketmine\\\\world\\\\format\\\\SubChunk but returns pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" + message: '#^Method pocketmine\\world\\format\\Chunk\:\:getSubChunk\(\) should return pocketmine\\world\\format\\SubChunk but returns pocketmine\\world\\format\\SubChunk\|null\.$#' + identifier: return.type count: 1 path: ../../../src/world/format/Chunk.php - - message: "#^Parameter \\#1 \\$x of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$x of static method pocketmine\\world\\format\\Chunk\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/format/Chunk.php - - message: "#^Parameter \\#2 \\$y of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of static method pocketmine\\world\\format\\Chunk\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/format/Chunk.php - - message: "#^Parameter \\#3 \\$z of static method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:blockHash\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#3 \$z of static method pocketmine\\world\\format\\Chunk\:\:blockHash\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/format/Chunk.php - - message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:get\\(\\) should return int but returns int\\|null\\.$#" + message: '#^Method pocketmine\\world\\format\\HeightArray\:\:get\(\) should return int but returns int\|null\.$#' + identifier: return.type count: 1 path: ../../../src/world/format/HeightArray.php - - message: "#^Only numeric types are allowed in %%, int\\<0, max\\>\\|false given on the left side\\.$#" + message: '#^Only numeric types are allowed in %%, int\<0, max\>\|false given on the left side\.$#' + identifier: mod.leftNonNumeric count: 1 path: ../../../src/world/format/io/region/RegionLoader.php - - message: "#^Parameter \\#2 \\$size of function ftruncate expects int\\<0, max\\>, int given\\.$#" + message: '#^Parameter \#2 \$size of function ftruncate expects int\<0, max\>, int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/format/io/region/RegionLoader.php - - message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../../../src/world/format/io/region/RegionWorldProvider.php - - - - message: "#^Cannot access offset 1 on mixed\\.$#" - count: 2 - path: ../../../src/world/format/io/region/RegionWorldProvider.php - - - - message: "#^Cannot access offset 2 on mixed\\.$#" - count: 2 - path: ../../../src/world/format/io/region/RegionWorldProvider.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 4 - path: ../../../src/world/format/io/region/RegionWorldProvider.php - - - - message: "#^Method pocketmine\\\\world\\\\generator\\\\biome\\\\BiomeSelector\\:\\:pickBiome\\(\\) should return pocketmine\\\\world\\\\biome\\\\Biome but returns pocketmine\\\\world\\\\biome\\\\Biome\\|null\\.$#" + message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#' + identifier: return.type count: 1 path: ../../../src/world/generator/biome/BiomeSelector.php - - message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getBiomeId\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/generator/hell/Nether.php - - message: "#^Offset int does not exist on SplFixedArray\\\\|null\\.$#" - count: 4 - path: ../../../src/world/generator/noise/Noise.php - - - - message: "#^Parameter \\$q0 of static method pocketmine\\\\world\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, float\\|null given\\.$#" + message: '#^Parameter \$q0 of static method pocketmine\\world\\generator\\noise\\Noise\:\:linearLerp\(\) expects float, float\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/generator/noise/Noise.php - - message: "#^Parameter \\$q1 of static method pocketmine\\\\world\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, float\\|null given\\.$#" + message: '#^Parameter \$q1 of static method pocketmine\\world\\generator\\noise\\Noise\:\:linearLerp\(\) expects float, float\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/generator/noise/Noise.php - - message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getBiomeId\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/generator/normal/Normal.php - - message: "#^Parameter \\#1 \\$start of method pocketmine\\\\utils\\\\Random\\:\\:nextRange\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#1 \$start of method pocketmine\\utils\\Random\:\:nextRange\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/generator/object/TallGrass.php - - message: "#^Parameter \\#2 \\$end of method pocketmine\\\\utils\\\\Random\\:\\:nextRange\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$end of method pocketmine\\utils\\Random\:\:nextRange\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/generator/object/TallGrass.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\ChunkManager\\:\\:getBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\ChunkManager\:\:getBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 2 path: ../../../src/world/generator/object/TallGrass.php - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\world\\\\ChunkManager\\:\\:setBlockAt\\(\\) expects int, float\\|int given\\.$#" + message: '#^Parameter \#2 \$y of method pocketmine\\world\\ChunkManager\:\:setBlockAt\(\) expects int, float\|int given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/generator/object/TallGrass.php - - message: "#^Cannot call method getBlockLightArray\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" + message: '#^Cannot call method getBlockLightArray\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/BlockLightUpdate.php - - message: "#^Cannot call method getBlockStateId\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" + message: '#^Cannot call method getBlockStateId\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/BlockLightUpdate.php - - message: "#^Cannot call method getSubChunks\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getSubChunks\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/BlockLightUpdate.php - - message: "#^Property pocketmine\\\\world\\\\light\\\\LightPopulationTask\\:\\:\\$resultBlockLightArrays \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\world\\light\\LightPopulationTask\:\:\$resultBlockLightArrays \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/world/light/LightPopulationTask.php - - message: "#^Property pocketmine\\\\world\\\\light\\\\LightPopulationTask\\:\\:\\$resultHeightMap \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\world\\light\\LightPopulationTask\:\:\$resultHeightMap \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/world/light/LightPopulationTask.php - - message: "#^Property pocketmine\\\\world\\\\light\\\\LightPopulationTask\\:\\:\\$resultSkyLightArrays \\(string\\) does not accept string\\|null\\.$#" + message: '#^Property pocketmine\\world\\light\\LightPopulationTask\:\:\$resultSkyLightArrays \(string\) does not accept string\|null\.$#' + identifier: assign.propertyType count: 1 path: ../../../src/world/light/LightPopulationTask.php - - message: "#^Cannot call method getBlockSkyLightArray\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" + message: '#^Cannot call method getBlockSkyLightArray\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method getBlockStateId\\(\\) on pocketmine\\\\world\\\\format\\\\SubChunk\\|null\\.$#" + message: '#^Cannot call method getBlockStateId\(\) on pocketmine\\world\\format\\SubChunk\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method getHeightMap\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getHeightMap\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 6 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method getHeightMapArray\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getHeightMapArray\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method getSubChunk\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method getSubChunk\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method setHeightMap\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method setHeightMap\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 2 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Cannot call method setHeightMapArray\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" + message: '#^Cannot call method setHeightMapArray\(\) on pocketmine\\world\\format\\Chunk\|null\.$#' + identifier: method.nonObject count: 1 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Parameter \\#1 \\$chunk of static method pocketmine\\\\world\\\\light\\\\SkyLightUpdate\\:\\:recalculateHeightMap\\(\\) expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#" + message: '#^Parameter \#1 \$chunk of static method pocketmine\\world\\light\\SkyLightUpdate\:\:recalculateHeightMap\(\) expects pocketmine\\world\\format\\Chunk, pocketmine\\world\\format\\Chunk\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/light/SkyLightUpdate.php - - message: "#^Parameter \\#1 \\$chunk of static method pocketmine\\\\world\\\\light\\\\SkyLightUpdate\\:\\:recalculateHeightMapColumn\\(\\) expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#" + message: '#^Parameter \#1 \$chunk of static method pocketmine\\world\\light\\SkyLightUpdate\:\:recalculateHeightMapColumn\(\) expects pocketmine\\world\\format\\Chunk, pocketmine\\world\\format\\Chunk\|null given\.$#' + identifier: argument.type count: 1 path: ../../../src/world/light/SkyLightUpdate.php - diff --git a/tests/phpstan/configs/dependency-problems.neon b/tests/phpstan/configs/dependency-problems.neon new file mode 100644 index 000000000..dc2a491bf --- /dev/null +++ b/tests/phpstan/configs/dependency-problems.neon @@ -0,0 +1,73 @@ +parameters: + ignoreErrors: + - + message: '#^Method pocketmine\\network\\mcpe\\convert\\BlockStateDictionary\:\:loadPaletteFromString\(\) should return list\ but returns array\\.$#' + identifier: return.type + count: 1 + path: ../../../src/network/mcpe/convert/BlockStateDictionary.php + + - + message: '#^Parameter \#3 \$entityNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/world/format/io/leveldb/LevelDB.php + + - + message: '#^Parameter \#4 \$tileNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/world/format/io/leveldb/LevelDB.php + + - + message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/world/format/io/region/Anvil.php + + - + message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/world/format/io/region/McRegion.php + + - + message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/world/format/io/region/PMAnvil.php + + - + message: '#^Parameter \#1 \$oldNewStateList of function pocketmine\\tools\\blockstate_upgrade_schema_utils\\buildUpgradeTableFromData expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/blockstate-upgrade-schema-utils.php + + - + message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#2 \$output of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#3 \$output of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#5 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/generate-bedrock-data-from-packets.php + + - + message: '#^Parameter \#6 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../tools/generate-bedrock-data-from-packets.php diff --git a/tests/phpstan/configs/impossible-generics.neon b/tests/phpstan/configs/impossible-generics.neon index b0e67d294..e0b944e69 100644 --- a/tests/phpstan/configs/impossible-generics.neon +++ b/tests/phpstan/configs/impossible-generics.neon @@ -1,12 +1,14 @@ parameters: 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 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 path: ../../../src/event/RegisteredListener.php diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 0ee2b68a0..7e3484bc5 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -1,112 +1,260 @@ parameters: 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 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 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 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 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 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 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 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 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 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 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 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 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\\> but returns array\, non\-empty\-array\, pocketmine\\crafting\\RecipeIngredient\|null\>\>\.$#' + identifier: return.type + count: 1 + path: ../../../src/crafting/ShapedRecipe.php + + - + message: '#^Property pocketmine\\crash\\CrashDumpData\:\:\$parameters \(list\\) 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 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\\) does not accept non\-empty\-array\\.$#' + identifier: assign.propertyType + count: 1 + path: ../../../src/item/WritableBookBase.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\, array\, 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 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 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 path: ../../../src/plugin/PluginManager.php - - message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\ but returns pocketmine\\\\utils\\\\ObjectSet\\\\.$#" + 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\ but returns pocketmine\\utils\\ObjectSet\\.$#' + identifier: return.type count: 1 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\, pocketmine\\utils\\ObjectSet\ 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\\) does not accept non\-empty\-array\\.$#' + identifier: assign.propertyType + count: 2 + path: ../../../src/world/format/io/region/RegionLoader.php + + - + message: '#^Property pocketmine\\world\\format\\io\\region\\RegionLoader\:\:\$locationTable \(list\\) does not accept non\-empty\-array\, 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\ but returns RegexIterator\\>\.$#' + 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 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 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 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 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 path: ../rules/UnsafeForeachArrayOfStringRule.php diff --git a/tests/phpstan/configs/spl-fixed-array-sucks.neon b/tests/phpstan/configs/spl-fixed-array-sucks.neon index 7c2b2b91a..05524fb8c 100644 --- a/tests/phpstan/configs/spl-fixed-array-sucks.neon +++ b/tests/phpstan/configs/spl-fixed-array-sucks.neon @@ -1,22 +1,32 @@ parameters: 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 path: ../../../src/world/format/Chunk.php - - message: "#^Method pocketmine\\\\world\\\\format\\\\Chunk\\:\\:getSubChunks\\(\\) should return array\\ but returns array\\\\.$#" + message: '#^Method pocketmine\\world\\format\\Chunk\:\:getSubChunks\(\) should return array\ but returns array\\.$#' + identifier: return.type count: 1 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 path: ../../../src/world/format/Chunk.php - - message: "#^Method pocketmine\\\\world\\\\format\\\\HeightArray\\:\\:getValues\\(\\) should return non\\-empty\\-array\\ but returns array\\\\.$#" + message: '#^Method pocketmine\\world\\format\\HeightArray\:\:getValues\(\) should return non\-empty\-list\ but returns array\\.$#' + identifier: return.type count: 1 path: ../../../src/world/format/HeightArray.php + - + message: '#^Offset int might not exist on SplFixedArray\\|null\.$#' + identifier: offsetAccess.notFound + count: 4 + path: ../../../src/world/generator/noise/Noise.php + diff --git a/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php b/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php index 4fa767022..5753bb628 100644 --- a/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php +++ b/tests/phpstan/rules/DeprecatedLegacyEnumAccessRule.php @@ -28,7 +28,6 @@ use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\TypeWithClassName; use pocketmine\utils\LegacyEnumShimTrait; use function sprintf; @@ -51,18 +50,15 @@ final class DeprecatedLegacyEnumAccessRule implements Rule{ $scope->resolveTypeByName($node->class) : $scope->getType($node->class); - if(!$classType instanceof TypeWithClassName){ - return []; - } + $errors = []; + $reflections = $classType->getObjectClassReflections(); + foreach($reflections as $reflection){ + if(!$reflection->hasTraitUse(LegacyEnumShimTrait::class) || !$reflection->implementsInterface(\UnitEnum::class)){ + continue; + } - $reflection = $classType->getClassReflection(); - if($reflection === null || !$reflection->hasTraitUse(LegacyEnumShimTrait::class) || !$reflection->implementsInterface(\UnitEnum::class)){ - return []; - } - - if(!$reflection->hasNativeMethod($caseName)){ - return [ - RuleErrorBuilder::message(sprintf( + if(!$reflection->hasNativeMethod($caseName)){ + $errors[] = RuleErrorBuilder::message(sprintf( 'Use of legacy enum case accessor %s::%s().', $reflection->getName(), $caseName @@ -70,10 +66,11 @@ final class DeprecatedLegacyEnumAccessRule implements Rule{ 'Access the enum constant directly instead (remove the brackets), e.g. %s::%s', $reflection->getName(), $caseName - ))->build() - ]; + ))->identifier('pocketmine.enum.deprecatedAccessor') + ->build(); + } } - return []; + return $errors; } } diff --git a/tests/phpstan/rules/DisallowEnumComparisonRule.php b/tests/phpstan/rules/DisallowEnumComparisonRule.php index fc5377173..d73cc3972 100644 --- a/tests/phpstan/rules/DisallowEnumComparisonRule.php +++ b/tests/phpstan/rules/DisallowEnumComparisonRule.php @@ -30,7 +30,6 @@ use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; @@ -61,7 +60,7 @@ class DisallowEnumComparisonRule implements Rule{ $node instanceof Identical ? '===' : '!==', $leftType->describe(VerbosityLevel::value()), $rightType->describe(VerbosityLevel::value()) - ))->build()]; + ))->identifier('pocketmine.enum.badComparison')->build()]; } return []; } @@ -69,7 +68,7 @@ class DisallowEnumComparisonRule implements Rule{ private function checkForEnumTypes(Type $comparedType) : bool{ //TODO: what we really want to do here is iterate over the contained types, but there's no universal way to //do that. This might break with other circumstances. - if($comparedType instanceof ObjectType){ + if($comparedType->isObject()->yes()){ $types = [$comparedType]; }elseif($comparedType instanceof UnionType){ $types = $comparedType->getTypes(); @@ -77,12 +76,14 @@ class DisallowEnumComparisonRule implements Rule{ return false; } foreach($types as $containedType){ - if(!($containedType instanceof ObjectType)){ + if(!($containedType->isObject()->yes())){ continue; } - $class = $containedType->getClassReflection(); - if($class !== null && $class->hasTraitUse(EnumTrait::class)){ - return true; + $classes = $containedType->getObjectClassReflections(); + foreach($classes as $class){ + if($class->hasTraitUse(EnumTrait::class)){ + return true; + } } } return false; diff --git a/tests/phpstan/rules/DisallowForeachByReferenceRule.php b/tests/phpstan/rules/DisallowForeachByReferenceRule.php index 79124d328..eb6589705 100644 --- a/tests/phpstan/rules/DisallowForeachByReferenceRule.php +++ b/tests/phpstan/rules/DisallowForeachByReferenceRule.php @@ -44,6 +44,7 @@ final class DisallowForeachByReferenceRule implements Rule{ return [ 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.") + ->identifier('pocketmine.foreach.byRef') ->build() ]; } diff --git a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php b/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php index 745cf2109..34056131b 100644 --- a/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php +++ b/tests/phpstan/rules/UnsafeForeachArrayOfStringRule.php @@ -101,7 +101,7 @@ final class UnsafeForeachArrayOfStringRule implements Rule{ RuleErrorBuilder::message(sprintf( "Unsafe foreach on array with key type %s (they might be casted to int).", $iterableType->getIterableKeyType()->describe(VerbosityLevel::value()) - ))->tip($tip)->build() + ))->tip($tip)->identifier('pocketmine.foreach.stringKeys')->build() ]; } return []; diff --git a/tests/phpunit/utils/fixtures/TestTrait.php b/tests/phpunit/utils/fixtures/TestTrait.php index bc32c0cff..3e749c0b1 100644 --- a/tests/phpunit/utils/fixtures/TestTrait.php +++ b/tests/phpunit/utils/fixtures/TestTrait.php @@ -23,6 +23,6 @@ declare(strict_types=1); namespace pocketmine\utils\fixtures; -trait TestTrait{ +trait TestTrait{ // @phpstan-ignore trait.unused } From 794641c0f80eba3bd1670e70503f1cc5e0fc18ab Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:35:19 +0000 Subject: [PATCH 214/290] Utils: split some horrifying code across multiple lines --- src/utils/Utils.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index f557562c9..b1f7e2842 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -469,7 +469,15 @@ final class Utils{ } $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; } From 689a7996b96869bdab6555b58a9e77b253d1a591 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:51:38 +0000 Subject: [PATCH 215/290] Update NBT dependency --- composer.lock | 16 +++++----- .../phpstan/configs/dependency-problems.neon | 30 ------------------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/composer.lock b/composer.lock index 8df6c329d..25c42d297 100644 --- a/composer.lock +++ b/composer.lock @@ -576,16 +576,16 @@ }, { "name": "pocketmine/nbt", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "20540271cb59e04672cb163dca73366f207974f1" + "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/20540271cb59e04672cb163dca73366f207974f1", - "reference": "20540271cb59e04672cb163dca73366f207974f1", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/53db37487bc5ddbfbd84247966e1a073bdcfdb7d", + "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d", "shasum": "" }, "require": { @@ -595,8 +595,8 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "1.10.25", - "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/phpstan": "2.1.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.5" }, "type": "library", @@ -612,9 +612,9 @@ "description": "PHP library for working with Named Binary Tags", "support": { "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", diff --git a/tests/phpstan/configs/dependency-problems.neon b/tests/phpstan/configs/dependency-problems.neon index dc2a491bf..c1c0fceea 100644 --- a/tests/phpstan/configs/dependency-problems.neon +++ b/tests/phpstan/configs/dependency-problems.neon @@ -1,11 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Method pocketmine\\network\\mcpe\\convert\\BlockStateDictionary\:\:loadPaletteFromString\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: ../../../src/network/mcpe/convert/BlockStateDictionary.php - - message: '#^Parameter \#3 \$entityNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\, array\ given\.$#' identifier: argument.type @@ -18,30 +12,6 @@ parameters: count: 1 path: ../../../src/world/format/io/leveldb/LevelDB.php - - - message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/world/format/io/region/Anvil.php - - - - message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/world/format/io/region/McRegion.php - - - - message: '#^Parameter \#1 \$array of static method pocketmine\\world\\format\\io\\ChunkUtils\:\:convertBiomeColors\(\) expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/world/format/io/region/PMAnvil.php - - - - message: '#^Parameter \#1 \$oldNewStateList of function pocketmine\\tools\\blockstate_upgrade_schema_utils\\buildUpgradeTableFromData expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/blockstate-upgrade-schema-utils.php - - message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' identifier: argument.type From e8c4b743b5e62554fd0e728d14d0c8817ee6099e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 22:54:10 +0000 Subject: [PATCH 216/290] LevelDB: stop overriding types from NBT NBT has better quality type info already --- src/world/format/io/leveldb/LevelDB.php | 2 -- tests/phpstan/configs/dependency-problems.neon | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index dda489d31..41c477867 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -711,7 +711,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $nbt = new LittleEndianNbtSerializer(); - /** @var CompoundTag[] $entities */ $entities = []; if(($entityData = $this->db->get($index . ChunkDataKey::ENTITIES)) !== false && $entityData !== ""){ try{ @@ -721,7 +720,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ } } - /** @var CompoundTag[] $tiles */ $tiles = []; if(($tileData = $this->db->get($index . ChunkDataKey::BLOCK_ENTITIES)) !== false && $tileData !== ""){ try{ diff --git a/tests/phpstan/configs/dependency-problems.neon b/tests/phpstan/configs/dependency-problems.neon index c1c0fceea..e95fcd79c 100644 --- a/tests/phpstan/configs/dependency-problems.neon +++ b/tests/phpstan/configs/dependency-problems.neon @@ -1,17 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Parameter \#3 \$entityNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/world/format/io/leveldb/LevelDB.php - - - - message: '#^Parameter \#4 \$tileNBT of class pocketmine\\world\\format\\io\\ChunkData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/world/format/io/leveldb/LevelDB.php - - message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' identifier: argument.type From e34f34f9f42aa3aaf0b145291a75e8cc4b6c879f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 7 Jan 2025 23:09:28 +0000 Subject: [PATCH 217/290] Update BedrockProtocol dependency --- composer.lock | 20 ++++++------ phpstan.neon.dist | 1 - .../phpstan/configs/dependency-problems.neon | 31 ------------------- tests/phpstan/configs/phpstan-bugs.neon | 6 ++++ 4 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 tests/phpstan/configs/dependency-problems.neon diff --git a/composer.lock b/composer.lock index 25c42d297..d59c10727 100644 --- a/composer.lock +++ b/composer.lock @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "35.0.0+bedrock-1.21.50", + "version": "35.0.3+bedrock-1.21.50", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435" + "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", - "reference": "bd1ec79bae8c88aa984e1c5f0c3313be5ae9b435", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c4d62581cb62d29ec426914c6b4d7e0ff835da9c", + "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c", "shasum": "" }, "require": { @@ -278,10 +278,10 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.11.9", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.0.0", - "phpunit/phpunit": "^9.5 || ^10.0" + "phpstan/phpstan": "2.1.0", + "phpstan/phpstan-phpunit": "^2.0.0", + "phpstan/phpstan-strict-rules": "^2.0.0", + "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/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", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index dfaa964e4..63155cbc4 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,7 +1,6 @@ includes: - tests/phpstan/analyse-for-current-php-version.neon.php - tests/phpstan/configs/actual-problems.neon - - tests/phpstan/configs/dependency-problems.neon - tests/phpstan/configs/impossible-generics.neon - tests/phpstan/configs/php-bugs.neon - tests/phpstan/configs/phpstan-bugs.neon diff --git a/tests/phpstan/configs/dependency-problems.neon b/tests/phpstan/configs/dependency-problems.neon deleted file mode 100644 index e95fcd79c..000000000 --- a/tests/phpstan/configs/dependency-problems.neon +++ /dev/null @@ -1,31 +0,0 @@ -parameters: - ignoreErrors: - - - message: '#^Parameter \#1 \$input of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/generate-bedrock-data-from-packets.php - - - - message: '#^Parameter \#2 \$output of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/generate-bedrock-data-from-packets.php - - - - message: '#^Parameter \#3 \$output of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/generate-bedrock-data-from-packets.php - - - - message: '#^Parameter \#5 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapelessRecipeData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/generate-bedrock-data-from-packets.php - - - - message: '#^Parameter \#6 \$unlockingIngredients of class pocketmine\\crafting\\json\\ShapedRecipeData constructor expects list\, array\ given\.$#' - identifier: argument.type - count: 1 - path: ../../../tools/generate-bedrock-data-from-packets.php diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 7e3484bc5..9ab125763 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -126,6 +126,12 @@ parameters: count: 1 path: ../../../src/item/WritableBookBase.php + - + message: '#^Parameter \#3 \$input of class pocketmine\\network\\mcpe\\protocol\\types\\recipe\\ShapedRecipe constructor expects list\\>, array\, non\-empty\-array\, 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 From 0a16daa61924581bec2ef35fee651e2b598a80a0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 01:45:28 +0000 Subject: [PATCH 218/290] Avoid dodgy array_flip hash building the conventional way is using array_keys and array_fill_keys. Behaviour is more predictable & also avoids benevolent union fuckery from PHPStan. --- src/item/enchantment/ProtectionEnchantment.php | 10 +++++++--- src/plugin/PluginGraylist.php | 14 ++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/item/enchantment/ProtectionEnchantment.php b/src/item/enchantment/ProtectionEnchantment.php index be78a2306..817466875 100644 --- a/src/item/enchantment/ProtectionEnchantment.php +++ b/src/item/enchantment/ProtectionEnchantment.php @@ -25,18 +25,22 @@ namespace pocketmine\item\enchantment; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\lang\Translatable; -use function array_flip; +use function array_fill_keys; use function floor; class ProtectionEnchantment extends Enchantment{ protected float $typeModifier; - /** @var int[]|null */ + /** + * @var true[]|null + * @phpstan-var array + */ protected ?array $applicableDamageTypes = null; /** * ProtectionEnchantment constructor. * * @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower + * @phpstan-param list|null $applicableDamageTypes * * @param int $primaryItemFlags @deprecated * @param int $secondaryItemFlags @deprecated @@ -48,7 +52,7 @@ class ProtectionEnchantment extends Enchantment{ $this->typeModifier = $typeModifier; if($applicableDamageTypes !== null){ - $this->applicableDamageTypes = array_flip($applicableDamageTypes); + $this->applicableDamageTypes = array_fill_keys($applicableDamageTypes, true); } } diff --git a/src/plugin/PluginGraylist.php b/src/plugin/PluginGraylist.php index ff9d71832..f3c9cf2a3 100644 --- a/src/plugin/PluginGraylist.php +++ b/src/plugin/PluginGraylist.php @@ -24,7 +24,8 @@ declare(strict_types=1); namespace pocketmine\plugin; use pocketmine\utils\Utils; -use function array_flip; +use function array_fill_keys; +use function array_keys; use function is_array; use function is_float; use function is_int; @@ -32,23 +33,28 @@ use function is_string; class PluginGraylist{ - /** @var string[] */ + /** + * @var true[] + * @phpstan-var array + */ private array $plugins; private bool $isWhitelist = false; /** * @param string[] $plugins + * @phpstan-param list $plugins */ public function __construct(array $plugins = [], bool $whitelist = false){ - $this->plugins = array_flip($plugins); + $this->plugins = array_fill_keys($plugins, true); $this->isWhitelist = $whitelist; } /** * @return string[] + * @phpstan-return list */ public function getPlugins() : array{ - return array_flip($this->plugins); + return array_keys($this->plugins); } public function isWhitelist() : bool{ From 4a83920db9a1bc5b7deef6a605b7cf5f68a17866 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 01:47:04 +0000 Subject: [PATCH 219/290] PlayerPreLoginEvent: improve array type info --- src/event/player/PlayerPreLoginEvent.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/event/player/PlayerPreLoginEvent.php b/src/event/player/PlayerPreLoginEvent.php index 5a69c0e17..4af0cdd89 100644 --- a/src/event/player/PlayerPreLoginEvent.php +++ b/src/event/player/PlayerPreLoginEvent.php @@ -52,9 +52,15 @@ class PlayerPreLoginEvent extends Event{ self::KICK_FLAG_BANNED ]; - /** @var Translatable[]|string[] reason const => associated message */ + /** + * @var Translatable[]|string[] reason const => associated message + * @phpstan-var array + */ protected array $disconnectReasons = []; - /** @var Translatable[]|string[] */ + /** + * @var Translatable[]|string[] + * @phpstan-var array + */ protected array $disconnectScreenMessages = []; public function __construct( @@ -93,6 +99,7 @@ class PlayerPreLoginEvent extends Event{ * Returns an array of kick flags currently assigned. * * @return int[] + * @phpstan-return list */ public function getKickFlags() : array{ return array_keys($this->disconnectReasons); From 5e0f03dff0e46ea5bfb54069e0f1f4ab8fcdd4cd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 01:48:15 +0000 Subject: [PATCH 220/290] Stub PalettedBlockArray functions that work with arrays and workaround PHPStan stupidity --- phpstan.neon.dist | 1 + src/world/format/io/BaseWorldProvider.php | 4 ++-- tests/phpstan/stubs/chunkutils2.stub | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/phpstan/stubs/chunkutils2.stub diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 63155cbc4..5e917dc75 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -45,6 +45,7 @@ parameters: - pocketmine\DEBUG - pocketmine\IS_DEVELOPMENT_BUILD stubFiles: + - tests/phpstan/stubs/chunkutils2.stub - tests/phpstan/stubs/JsonMapper.stub - tests/phpstan/stubs/leveldb.stub - tests/phpstan/stubs/pmmpthread.stub diff --git a/src/world/format/io/BaseWorldProvider.php b/src/world/format/io/BaseWorldProvider.php index 79f6875a4..6fcb8e10b 100644 --- a/src/world/format/io/BaseWorldProvider.php +++ b/src/world/format/io/BaseWorldProvider.php @@ -83,11 +83,11 @@ abstract class BaseWorldProvider implements WorldProvider{ } try{ - $newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData); + $newPalette[] = $this->blockStateDeserializer->deserialize($newStateData); }catch(BlockStateDeserializeException $e){ //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(); - $newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); + $newPalette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); } } diff --git a/tests/phpstan/stubs/chunkutils2.stub b/tests/phpstan/stubs/chunkutils2.stub new file mode 100644 index 000000000..b23e4a7fd --- /dev/null +++ b/tests/phpstan/stubs/chunkutils2.stub @@ -0,0 +1,15 @@ + $palette + */ + public static function fromData(int $bitsPerBlock, string $wordArray, array $palette): PalettedBlockArray {} + + /** + * @return list + */ + public function getPalette(): array {} +} From d42ec06647dbac8f8fa7634159286bceec9507ed Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 01:48:55 +0000 Subject: [PATCH 221/290] ZippedResourcePack: don't pass exception code to new exception this is a BUT (int|string) under PHPStan, and we don't need the errors. We don't care about this code anyway. --- src/resourcepacks/ZippedResourcePack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resourcepacks/ZippedResourcePack.php b/src/resourcepacks/ZippedResourcePack.php index c4daeedf7..4fcf204d9 100644 --- a/src/resourcepacks/ZippedResourcePack.php +++ b/src/resourcepacks/ZippedResourcePack.php @@ -100,7 +100,7 @@ class ZippedResourcePack implements ResourcePack{ try{ $manifest = (new CommentedJsonDecoder())->decode($manifestData); }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)){ throw new ResourcePackException("manifest.json should contain a JSON object, not " . gettype($manifest)); From 847ae26cad24df4ad3e30f911f913574684cb11d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 02:04:06 +0000 Subject: [PATCH 222/290] PHPStan: don't remember possibly-impure function return values I don't think we get much benefit from this, and the assumption that functions with a return value are pure is sketchy. In any case, it's better to avoid these repeated calls anyway. --- phpstan.neon.dist | 1 + src/entity/projectile/Projectile.php | 5 +++-- src/permission/BanEntry.php | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5e917dc75..5a816f81c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -19,6 +19,7 @@ rules: parameters: level: 9 checkMissingCallableSignature: true + rememberPossiblyImpureFunctionValues: false #risky to remember these, better for performance to avoid repeated calls anyway treatPhpDocTypesAsCertain: false bootstrapFiles: - tests/phpstan/bootstrap.php diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 0abc274b5..d6ab1c175 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -290,10 +290,11 @@ abstract class Projectile extends Entity{ $damage = $this->getResultDamage(); if($damage >= 0){ - if($this->getOwningEntity() === null){ + $owner = $this->getOwningEntity(); + if($owner === null){ $ev = new EntityDamageByEntityEvent($this, $entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); }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); diff --git a/src/permission/BanEntry.php b/src/permission/BanEntry.php index 0d5ed0c76..5f235f1a9 100644 --- a/src/permission/BanEntry.php +++ b/src/permission/BanEntry.php @@ -101,11 +101,12 @@ class BanEntry{ } public function getString() : string{ + $expires = $this->getExpires(); return implode("|", [ $this->getName(), $this->getCreated()->format(self::$format), $this->getSource(), - $this->getExpires() === null ? "Forever" : $this->getExpires()->format(self::$format), + $expires === null ? "Forever" : $expires->format(self::$format), $this->getReason() ]); } From b3f15435cc9dc50fb93c3cf2c8023c0b8a27d3db Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 8 Jan 2025 02:31:50 +0000 Subject: [PATCH 223/290] Projectile: clean up dodgy code --- src/entity/projectile/Projectile.php | 35 +++++++++------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index d6ab1c175..68b6c4763 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -44,7 +44,6 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\timings\Timings; -use function assert; use function atan2; use function ceil; use function count; @@ -170,8 +169,6 @@ abstract class Projectile extends Entity{ $start = $this->location->asVector3(); $end = $start->add($dx, $dy, $dz); - $blockHit = null; - $entityHit = null; $hitResult = null; $world = $this->getWorld(); @@ -181,8 +178,7 @@ abstract class Projectile extends Entity{ $blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end); if($blockHitResult !== null){ $end = $blockHitResult->hitVector; - $blockHit = $block; - $hitResult = $blockHitResult; + $hitResult = [$block, $blockHitResult]; break; } } @@ -206,8 +202,7 @@ abstract class Projectile extends Entity{ if($distance < $entityDistance){ $entityDistance = $distance; - $entityHit = $entity; - $hitResult = $entityHitResult; + $hitResult = [$entity, $entityHitResult]; $end = $entityHitResult->hitVector; } } @@ -223,26 +218,18 @@ abstract class Projectile extends Entity{ $this->recalculateBoundingBox(); if($hitResult !== null){ - /** @var ProjectileHitEvent|null $ev */ - $ev = null; - if($entityHit !== null){ - $ev = new ProjectileHitEntityEvent($this, $hitResult, $entityHit); - }elseif($blockHit !== null){ - $ev = new ProjectileHitBlockEvent($this, $hitResult, $blockHit); + [$objectHit, $rayTraceResult] = $hitResult; + if($objectHit instanceof Entity){ + $ev = new ProjectileHitEntityEvent($this, $rayTraceResult, $objectHit); + $specificHitFunc = fn() => $this->onHitEntity($objectHit, $rayTraceResult); }else{ - assert(false, "unknown hit type"); + $ev = new ProjectileHitBlockEvent($this, $rayTraceResult, $objectHit); + $specificHitFunc = fn() => $this->onHitBlock($objectHit, $rayTraceResult); } - if($ev !== null){ - $ev->call(); - $this->onHit($ev); - - if($ev instanceof ProjectileHitEntityEvent){ - $this->onHitEntity($ev->getEntityHit(), $ev->getRayTraceResult()); - }elseif($ev instanceof ProjectileHitBlockEvent){ - $this->onHitBlock($ev->getBlockHit(), $ev->getRayTraceResult()); - } - } + $ev->call(); + $this->onHit($ev); + $specificHitFunc(); $this->isCollided = $this->onGround = true; $this->motion = Vector3::zero(); From f349ce75e4ff0632fee57fad0a9b976b5feed26c Mon Sep 17 00:00:00 2001 From: Sergi del Olmo Date: Thu, 9 Jan 2025 21:13:46 +0100 Subject: [PATCH 224/290] Player: add ability to get & set flight speed multiplier (#6076) Since this doesn't directly correspond to flight speed (it's multiplied by different values depending on whether sprinting or not, and possibly other states), "multiplier" was preferred instead of directly calling it flight speed. Default value is 0.05. --- src/network/mcpe/NetworkSession.php | 3 +-- src/player/Player.php | 39 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 511d0dece..fdb63c467 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -1058,8 +1058,7 @@ class NetworkSession{ ]; $layers = [ - //TODO: dynamic flying speed! FINALLY!!!!!!!!!!!!!!!!! - new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, 0.05, 0.1), + new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, $for->getFlightSpeedMultiplier(), 0.1), ]; if(!$for->hasBlockCollision()){ //TODO: HACK! In 1.19.80, the client starts falling in our faux spectator mode when it clips into a diff --git a/src/player/Player.php b/src/player/Player.php index 66ba6f376..1c67b7182 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -184,6 +184,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private const MAX_REACH_DISTANCE_SURVIVAL = 7; private const MAX_REACH_DISTANCE_ENTITY_INTERACTION = 8; + public const DEFAULT_FLIGHT_SPEED_MULTIPLIER = 0.05; + public const TAG_FIRST_PLAYED = "firstPlayed"; //TAG_Long public const TAG_LAST_PLAYED = "lastPlayed"; //TAG_Long private const TAG_GAME_MODE = "playerGameType"; //TAG_Int @@ -285,6 +287,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ protected bool $blockCollision = true; protected bool $flying = false; + protected float $flightSpeedMultiplier = self::DEFAULT_FLIGHT_SPEED_MULTIPLIER; + /** @phpstan-var positive-int|null */ protected ?int $lineHeight = null; protected string $locale = "en_US"; @@ -518,6 +522,41 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return $this->flying; } + /** + * Sets the player's flight speed multiplier. + * + * Normal flying speed in blocks-per-tick is (multiplier * 10) blocks per tick. + * When sprint-flying, this is doubled to 20. + * + * If set to zero, the player will not be able to move in the xz plane when flying. + * Negative values will invert the controls. + * + * Note: Movement speed attribute does not influence flight speed. + * + * @see Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER + */ + public function setFlightSpeedMultiplier(float $flightSpeedMultiplier) : void{ + if($this->flightSpeedMultiplier !== $flightSpeedMultiplier){ + $this->flightSpeedMultiplier = $flightSpeedMultiplier; + $this->getNetworkSession()->syncAbilities($this); + } + } + + /** + * Returns the player's flight speed multiplier. + * + * Normal flying speed in blocks-per-tick is (multiplier * 10) blocks per tick. + * When sprint-flying, this is doubled to 20. + * + * If set to zero, the player will not be able to move in the xz plane when flying. + * Negative values will invert the controls. + * + * @see Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER + */ + public function getFlightSpeedMultiplier() : float{ + return $this->flightSpeedMultiplier; + } + public function setAutoJump(bool $value) : void{ if($this->autoJump !== $value){ $this->autoJump = $value; From d1066d0199ed3f7e775e3ff98b2847a57b703385 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 19:54:48 +0000 Subject: [PATCH 225/290] Bump build/php from `56cec11` to `ae94694` (#6595) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index 56cec1174..ae946949c 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 56cec11745bbd87719a303a0a1de41ee1d1d69c2 +Subproject commit ae946949c528acf8c3f05dfceadc1d66b42d1f2f From 0b60a47cdecd9ab84a4e6edcbd13b5abf634b14c Mon Sep 17 00:00:00 2001 From: Dries C Date: Fri, 17 Jan 2025 20:56:19 +0100 Subject: [PATCH 226/290] Noteblock instrument changes from 1.21.50 (#6596) Good thing this isn't saved on disk :| --- src/data/bedrock/NoteInstrumentIdMap.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php index c847ecd98..7f0c0f310 100644 --- a/src/data/bedrock/NoteInstrumentIdMap.php +++ b/src/data/bedrock/NoteInstrumentIdMap.php @@ -39,10 +39,10 @@ final class NoteInstrumentIdMap{ NoteInstrument::SNARE => 2, NoteInstrument::CLICKS_AND_STICKS => 3, NoteInstrument::DOUBLE_BASS => 4, - NoteInstrument::BELL => 5, - NoteInstrument::FLUTE => 6, - NoteInstrument::CHIME => 7, - NoteInstrument::GUITAR => 8, + NoteInstrument::FLUTE => 5, + NoteInstrument::BELL => 6, + NoteInstrument::GUITAR => 7, + NoteInstrument::CHIME => 8, NoteInstrument::XYLOPHONE => 9, NoteInstrument::IRON_XYLOPHONE => 10, NoteInstrument::COW_BELL => 11, From 04e63172c3b819bb2d01483cf9de52d9ced0b347 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 22 Jan 2025 00:08:49 +0000 Subject: [PATCH 227/290] 5.23.3 (#6597) --- changelogs/5.23.md | 10 ++++++++++ src/VersionInfo.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changelogs/5.23.md b/changelogs/5.23.md index 6e72e9403..3a287608f 100644 --- a/changelogs/5.23.md +++ b/changelogs/5.23.md @@ -127,3 +127,13 @@ Released 9th December 2024. ## Internals - Removed legacy `build/make-release.php` script. This script is no longer used, as all releases should now follow the PR workflow. + +# 5.23.3 +Released 22nd January 2025. + +## Fixes +- Fixed crashes with PHP internal stack frames being flagged as plugin crashes. +- Fixed note block instrument sounds in 1.21.50. + +## Internals +- Updated GitHub issue templates to use issue forms. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 48ce7dc9e..368328f20 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.23.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 3453ff03fd2a6c7572beafcfc26c2a902ae1e1a5 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:10:10 +0000 Subject: [PATCH 228/290] 5.23.4 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12898306655 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 368328f20..10a316835 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.23.3"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.23.4"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 406ddf3e5319eb28b2dd996962315fc59cd7dc96 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 22 Jan 2025 17:44:23 +0000 Subject: [PATCH 229/290] Revert "Internet: make postURL() error reporting behaviour more predictable" This reverts commit 97c5902ae2fea587faaee7487bbe14fa6100d67e. --- src/utils/Internet.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/utils/Internet.php b/src/utils/Internet.php index 1811ce51e..89af2a77e 100644 --- a/src/utils/Internet.php +++ b/src/utils/Internet.php @@ -157,21 +157,22 @@ class Internet{ * POSTs data to an URL * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. * + * @phpstan-template TErrorVar of mixed + * * @param string[]|string $args * @param string[] $extraHeaders * @param string|null $err reference parameter, will be set to the output of curl_error(). Use this to retrieve errors that occurred during the operation. * @phpstan-param string|array $args * @phpstan-param list $extraHeaders - * @phpstan-param-out string|null $err + * @phpstan-param TErrorVar $err + * @phpstan-param-out TErrorVar|string $err */ public static function postURL(string $page, array|string $args, int $timeout = 10, array $extraHeaders = [], &$err = null) : ?InternetRequestResult{ try{ - $result = self::simpleCurl($page, $timeout, $extraHeaders, [ + return self::simpleCurl($page, $timeout, $extraHeaders, [ CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $args ]); - $err = null; - return $result; }catch(InternetException $ex){ $err = $ex->getMessage(); return null; From 6b606dca95b413fb675562c858d0c8786dad76b1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 22 Jan 2025 17:46:43 +0000 Subject: [PATCH 230/290] UPnP: better fix for postURL error that doesn't require behavioural breaks --- src/network/upnp/UPnP.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/upnp/UPnP.php b/src/network/upnp/UPnP.php index 2d48a2db8..bd8e8376f 100644 --- a/src/network/upnp/UPnP.php +++ b/src/network/upnp/UPnP.php @@ -215,6 +215,7 @@ class UPnP{ 'SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"' ]; + $err = ""; if(Internet::postURL($serviceURL, $contents, 3, $headers, $err) === null){ throw new UPnPException("Failed to portforward using UPnP: " . $err); } From b625fee94b7e0ed3486b0c12fe32980ff5f6f7e8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 22 Jan 2025 18:00:41 +0000 Subject: [PATCH 231/290] Prepare 5.24.0 release --- changelogs/5.24.md | 108 ++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +- 2 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.24.md diff --git a/changelogs/5.24.md b/changelogs/5.24.md new file mode 100644 index 000000000..a159d0e76 --- /dev/null +++ b/changelogs/5.24.md @@ -0,0 +1,108 @@ +# 5.24.0 +Released 22nd January 2025. + +This is a minor feature release, including new gameplay features, performance improvements, and minor API additions. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## Performance +- PHP garbage collection is now managed by the server, instead of being automatically triggered by PHP. + - The mechanism for GC triggering is designed to mimic PHP's to avoid behavioural changes. Only the place it's triggered from should be significantly different. + - This change also avoids unnecessary GCs during object-heavy operations, such as encoding `CraftingDataPacket`. As such, performance during server join should see an improvement. + - Timings is now able to directly measure the impact of GC. Previously, GC would show up as random spikes under random timers, skewing timing results. +- `ChunkCache` now uses `string` for completed caches directly instead of keeping them wrapped in `CompressBatchPromise`s. This reduces memory usage, improves performance, and reduces GC workload. + +## Configuration +- The following settings have been removed from `pocketmine.yml` and will no longer have any effect: + - `memory.garbage-collection.collect-async-worker` (now always `true`) + - `memory.garbage-collection.low-memory-trigger` (now always `true`) + - `memory.max-chunks.trigger-chunk-collect` (now always `true`) + - `memory.world-caches.disable-chunk-cache` (now always `true`) + - `memory.world-caches.low-memory-trigger` (now always `true`) + +## Gameplay +- Added the following new blocks: + - All types of pale oak wood, and leaves + - Resin + - Resin Bricks, Slabs, Stairs, and Walls + - Resin Clump + - Chiseled Resin Bricks +- Some blocks have had their tool tier requirements adjusted to match latest Bedrock updates. +- Added the following new items: + - Resin Brick + - Music Disc - Creator + - Music Disc - Creator (Music Box) + - Music Disc - Precipice + - Music Disc - Relic + +## API +### General +- Many places had their PHPDoc improved to address issues highlighted by PHPStan 2.x. This may cause new, previously unreported issues to be reported in plugins using PHPStan. + +### `pocketmine` +- The following methods have been deprecated: + - `MemoryManager->canUseChunkCache()` + - `MemoryManager::dumpMemory()` - relocated to `MemoryDump` class + +### `pocketmine\item` +- The following new enum cases have been added: + - `RecordType::DISK_CREATOR` + - `RecordType::DISK_CREATOR_MUSIC_BOX` + - `RecordType::DISK_PRECIPICE` + - `RecordType::DISK_RELIC` + +### `pocketmine\player` +- The following new methods have been added: + - `public Player->getFlightSpeedMultiplier() : float` - a base multiplier for player's flight speed + - `public Player->setFlightSpeedMultiplier(float $flightSpeedMultiplier) : void` - sets the player's flight speed multiplier +- The following new constants have been added: + - `Player::DEFAULT_FLIGHT_SPEED_MULTIPLIER` + +### `pocketmine\utils` +- The following new methods have been added: + - `public static TextFormat::javaToBedrock(string $string) : string` - removes unsupported Java Edition format codes to prevent them being incorrectly displayed on Bedrock +- The following methods have behavioural changes: + - `TextFormat::toHTML()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline). This is because the codes previously used for `STRIKETHROUGH` and `UNDERLINE` conflict with the new material codes introduced by Minecraft Bedrock. + - `Terminal::toANSI()` now converts `§m` to redstone red (instead of strikethrough), and `§n` to copper orange (instead of underline), as above. However, underline and strikethrough can still be used on the terminal using `Terminal::$FORMAT_UNDERLINE` and `Terminal::$FORMAT_STRIKETHROUGH` respectively. +- The following new constants have been added: + - `TextFormat::MATERIAL_QUARTZ` + - `TextFormat::MATERIAL_IRON` + - `TextFormat::MATERIAL_NETHERITE` + - `TextFormat::MATERIAL_REDSTONE` + - `TextFormat::MATERIAL_COPPER` + - `TextFormat::MATERIAL_GOLD` + - `TextFormat::MATERIAL_EMERALD` + - `TextFormat::MATERIAL_DIAMOND` + - `TextFormat::MATERIAL_LAPIS` + - `TextFormat::MATERIAL_AMETHYST` +- The following constants have been deprecated: + - `TextFormat::STRIKETHROUGH` + - `TextFormat::UNDERLINE` +- The following static properties have been added: + - `Terminal::$COLOR_MATERIAL_QUARTZ` + - `Terminal::$COLOR_MATERIAL_IRON` + - `Terminal::$COLOR_MATERIAL_NETHERITE` + - `Terminal::$COLOR_MATERIAL_REDSTONE` + - `Terminal::$COLOR_MATERIAL_COPPER` + - `Terminal::$COLOR_MATERIAL_GOLD` + - `Terminal::$COLOR_MATERIAL_EMERALD` + - `Terminal::$COLOR_MATERIAL_DIAMOND` + - `Terminal::$COLOR_MATERIAL_LAPIS` + - `Terminal::$COLOR_MATERIAL_AMETHYST` + +## Tools +- Fixed some UI issues in `tools/convert-world.php` + +## Internals +- Block cache in `World` is now size-limited. This prevents memory exhaustion when plugins call `getBlock()` many thousands of times with cache misses. +- `RakLibServer` now disables PHP GC. As RakLib doesn't generate any unmanaged cycles, GC is just a waste of CPU time in this context. +- `MemoryManager` now has the responsibility for triggering cycle GC. It's checked every tick, but GC won't take place unless the GC threshold is exceeded, similar to PHP. + - This mechanism could probably do with alterations to better suit PocketMine-MP, but it was chosen to mimic PHP's own GC to minimize behavioural changes for now. +- `AsyncTask` now triggers cycle GC after `onRun()` completes. As with `MemoryManager`, this is based on a threshold designed to mimic PHP's own behaviour. +- `FormatConverter` now performs world provider GC periodically. This is not needed with current active providers, but was found to be a problem while developing custom providers. +- Various internal adjustments were made to avoid returning incorrectly-keyed arrays in the code. These changes shouldn't affect anything as the arrays should have been properly ordered anyway. +- Many places that previously used `==` and `!=` have been updated to use strict variants. This kind of change needs to be done carefully to avoid breaking `int|float` comparisons. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 10a316835..fcfb1c7e9 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.23.4"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.24.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From fc86d3a44e54e2720e886de83b5e6c6ee8934045 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:50:42 +0000 Subject: [PATCH 232/290] 5.24.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/12916833718 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index fcfb1c7e9..aaa357e0f 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.24.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.24.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From f60120a75ea8e80fadbedfad0148da10ea29c91f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 10:43:28 +0000 Subject: [PATCH 233/290] Bump the development-patch-updates group with 3 updates (#6603) --- composer.json | 2 +- composer.lock | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index a40f3733e..b9fb70b0e 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.1", + "phpstan/phpstan": "2.1.2", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index d59c10727..7f9055539 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "994ccffe45f066768542019f6f9d237b", + "content-hash": "5321ac37e6830cae119d03082b2668a5", "packages": [ { "name": "adhocore/json-comment", @@ -1386,16 +1386,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7" + "reference": "7d08f569e582ade182a375c366cbd896eccadd3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", - "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/7d08f569e582ade182a375c366cbd896eccadd3a", + "reference": "7d08f569e582ade182a375c366cbd896eccadd3a", "shasum": "" }, "require": { @@ -1440,20 +1440,20 @@ "type": "github" } ], - "time": "2025-01-05T16:43:48+00:00" + "time": "2025-01-21T14:54:06+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18" + "reference": "d09e152f403c843998d7a52b5d87040c937525dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/e32ac656788a5bf3dedda89e6a2cad5643bf1a18", - "reference": "e32ac656788a5bf3dedda89e6a2cad5643bf1a18", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d09e152f403c843998d7a52b5d87040c937525dd", + "reference": "d09e152f403c843998d7a52b5d87040c937525dd", "shasum": "" }, "require": { @@ -1489,22 +1489,22 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.3" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.4" }, - "time": "2024-12-19T09:14:43+00:00" + "time": "2025-01-22T13:07:38+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3" + "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3", - "reference": "ed6fea0ad4ad9c7e25f3ad2e7c4d420cf1e67fe3", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba", + "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba", "shasum": "" }, "require": { @@ -1537,9 +1537,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.1" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.3" }, - "time": "2024-12-12T20:21:10+00:00" + "time": "2025-01-21T10:52:14+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From 70fb9bbdfd06c7eda00b4cd2c2c3840755e6b8f6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 27 Jan 2025 21:28:26 +0000 Subject: [PATCH 234/290] ChorusPlant: fixed recalculateCollisionBoxes() depending on the world --- src/block/ChorusPlant.php | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/block/ChorusPlant.php b/src/block/ChorusPlant.php index 9013f6825..88cf1787c 100644 --- a/src/block/ChorusPlant.php +++ b/src/block/ChorusPlant.php @@ -34,11 +34,16 @@ use function mt_rand; final class ChorusPlant extends Flowable{ use StaticSupportTrait; + /** + * @var true[] + * @phpstan-var array + */ + protected array $connections = []; + protected function recalculateCollisionBoxes() : array{ $bb = AxisAlignedBB::one(); - foreach($this->getAllSides() as $facing => $block){ - $id = $block->getTypeId(); - if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->hasSameTypeId($this)){ + foreach(Facing::ALL as $facing){ + if(!isset($this->connections[$facing])){ $bb->trim($facing, 2 / 16); } } @@ -46,6 +51,26 @@ final class ChorusPlant extends Flowable{ return [$bb]; } + public function readStateFromWorld() : Block{ + parent::readStateFromWorld(); + + $this->collisionBoxes = null; + + foreach(Facing::ALL as $facing){ + $block = $this->getSide($facing); + if(match($block->getTypeId()){ + BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true, + default => false + }){ + $this->connections[$facing] = true; + }else{ + unset($this->connections[$facing]); + } + } + + return $this; + } + private function canBeSupportedBy(Block $block) : bool{ return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE; } From 07987890a048507d618afc69336e7144f963e607 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 13:38:25 +0000 Subject: [PATCH 235/290] Bump the github-actions group with 2 updates (#6613) --- .github/workflows/build-docker-image.yml | 8 ++++---- .github/workflows/draft-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 94856fa44..a0cb1f1d9 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.13.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.13.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.13.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.13.0 with: push: true context: ./pocketmine-mp diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 445a1f7a6..8a35e2904 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -165,7 +165,7 @@ jobs: ${{ github.workspace }}/core-permissions.rst - name: Create draft release - uses: ncipollo/release-action@v1.14.0 + uses: ncipollo/release-action@v1.15.0 id: create-draft with: artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst From 0a9a45a126ff30bf385a53d5b170b80071c6198a Mon Sep 17 00:00:00 2001 From: GameParrot <85067619+GameParrot@users.noreply.github.com> Date: Sun, 2 Feb 2025 19:30:29 +0000 Subject: [PATCH 236/290] Improve block break progress closes #6500 This fixes break time animations for mining fatigue and haste, and improves the underwater and on-ground behaviour. on-ground is still not quite right for reasons not related to this PR (see #6547). I'm also not quite sure the underwater logic is correct (in water vs underwater?) but it's definitely better than what we have currently. --- src/player/SurvivalBlockBreakHandler.php | 31 +++++++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/player/SurvivalBlockBreakHandler.php b/src/player/SurvivalBlockBreakHandler.php index e31e77ef7..57c2842ce 100644 --- a/src/player/SurvivalBlockBreakHandler.php +++ b/src/player/SurvivalBlockBreakHandler.php @@ -25,6 +25,8 @@ namespace pocketmine\player; use pocketmine\block\Block; use pocketmine\entity\animation\ArmSwingAnimation; +use pocketmine\entity\effect\VanillaEffects; +use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\network\mcpe\protocol\LevelEventPacket; @@ -65,11 +67,29 @@ final class SurvivalBlockBreakHandler{ if(!$this->block->getBreakInfo()->isBreakable()){ return 0.0; } - //TODO: improve this to take stuff like swimming, ladders, enchanted tools into account, fix wrong tool break time calculations for bad tools (pmmp/PocketMine-MP#211) $breakTimePerTick = $this->block->getBreakInfo()->getBreakTime($this->player->getInventory()->getItemInHand()) * 20; - + if(!$this->player->isOnGround() && !$this->player->isFlying()){ + $breakTimePerTick *= 5; + } + if($this->player->isUnderwater() && !$this->player->getArmorInventory()->getHelmet()->hasEnchantment(VanillaEnchantments::AQUA_AFFINITY())){ + $breakTimePerTick *= 5; + } if($breakTimePerTick > 0){ - return 1 / $breakTimePerTick; + $progressPerTick = 1 / $breakTimePerTick; + + $haste = $this->player->getEffects()->get(VanillaEffects::HASTE()); + if($haste !== null){ + $hasteLevel = $haste->getEffectLevel(); + $progressPerTick *= (1 + 0.2 * $hasteLevel) * (1.2 ** $hasteLevel); + } + + $miningFatigue = $this->player->getEffects()->get(VanillaEffects::MINING_FATIGUE()); + if($miningFatigue !== null){ + $miningFatigueLevel = $miningFatigue->getEffectLevel(); + $progressPerTick *= 0.21 ** $miningFatigueLevel; + } + + return $progressPerTick; } return 1; } @@ -82,7 +102,10 @@ final class SurvivalBlockBreakHandler{ $newBreakSpeed = $this->calculateBreakProgressPerTick(); if(abs($newBreakSpeed - $this->breakSpeed) > 0.0001){ $this->breakSpeed = $newBreakSpeed; - //TODO: sync with client + $this->player->getWorld()->broadcastPacketToViewers( + $this->blockPos, + LevelEventPacket::create(LevelEvent::BLOCK_BREAK_SPEED, (int) (65535 * $this->breakSpeed), $this->blockPos) + ); } $this->breakProgress += $this->breakSpeed; From 21ccd9014706fa9e5f7661ade27d344390224483 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 2 Feb 2025 19:43:04 +0000 Subject: [PATCH 237/290] ChunkCache: parameterize dimension ID (cc @Muqsit) --- src/network/mcpe/cache/ChunkCache.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/network/mcpe/cache/ChunkCache.php b/src/network/mcpe/cache/ChunkCache.php index 2c5f67f08..2b4265fcf 100644 --- a/src/network/mcpe/cache/ChunkCache.php +++ b/src/network/mcpe/cache/ChunkCache.php @@ -88,9 +88,13 @@ class ChunkCache implements ChunkListener{ private int $hits = 0; private int $misses = 0; + /** + * @phpstan-param DimensionIds::* $dimensionId + */ private function __construct( private World $world, - private Compressor $compressor + private Compressor $compressor, + private int $dimensionId = DimensionIds::OVERWORLD ){} private function prepareChunkAsync(int $chunkX, int $chunkZ, int $chunkHash) : CompressBatchPromise{ @@ -109,7 +113,7 @@ class ChunkCache implements ChunkListener{ new ChunkRequestTask( $chunkX, $chunkZ, - DimensionIds::OVERWORLD, //TODO: not hardcode this + $this->dimensionId, $chunk, $promise, $this->compressor From 9d6a0cc7385976fb350f6f919ecc5580b508a783 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 2 Feb 2025 19:46:54 +0000 Subject: [PATCH 238/290] Update composer dependencies --- composer.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index 7f9055539..26a3a510c 100644 --- a/composer.lock +++ b/composer.lock @@ -1013,8 +1013,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1092,8 +1092,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1210,16 +1210,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -1262,9 +1262,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -1864,16 +1864,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.38", + "version": "10.5.44", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" + "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", - "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1381c62769be4bb88fa4c5aec1366c7c66ca4f36", + "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36", "shasum": "" }, "require": { @@ -1883,7 +1883,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -1945,7 +1945,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.44" }, "funding": [ { @@ -1961,7 +1961,7 @@ "type": "tidelift" } ], - "time": "2024-10-28T13:06:21+00:00" + "time": "2025-01-31T07:00:38+00:00" }, { "name": "sebastian/cli-parser", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From 03f98ee0a9f3293b426acaa7d6763dd565d31fd6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 2 Feb 2025 19:47:47 +0000 Subject: [PATCH 239/290] New version of NBT for performance improvements --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index b9fb70b0e..9c5919027 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "pocketmine/locale-data": "~2.22.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", - "pocketmine/nbt": "~1.0.0", + "pocketmine/nbt": "~1.1.0", "pocketmine/raklib": "~1.1.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", diff --git a/composer.lock b/composer.lock index 26a3a510c..a6159bd93 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5321ac37e6830cae119d03082b2668a5", + "content-hash": "ddcce2cd4c9a4217df3c101cc1183722", "packages": [ { "name": "adhocore/json-comment", @@ -576,16 +576,16 @@ }, { "name": "pocketmine/nbt", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d" + "reference": "cfd53a86166b851786967fc560cdb372e66fde96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/53db37487bc5ddbfbd84247966e1a073bdcfdb7d", - "reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/cfd53a86166b851786967fc560cdb372e66fde96", + "reference": "cfd53a86166b851786967fc560cdb372e66fde96", "shasum": "" }, "require": { @@ -612,9 +612,9 @@ "description": "PHP library for working with Named Binary Tags", "support": { "issues": "https://github.com/pmmp/NBT/issues", - "source": "https://github.com/pmmp/NBT/tree/1.0.1" + "source": "https://github.com/pmmp/NBT/tree/1.1.0" }, - "time": "2025-01-07T22:47:46+00:00" + "time": "2025-02-01T21:20:26+00:00" }, { "name": "pocketmine/raklib", From 94797e3afab4ce153d6e430baaa0da2995a0b346 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:42:37 +0000 Subject: [PATCH 240/290] Bump build/php from `ae94694` to `1549433` (#6616) --- build/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/php b/build/php index ae946949c..154943379 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit ae946949c528acf8c3f05dfceadc1d66b42d1f2f +Subproject commit 15494337976e645499e2e3e8c8b491227522be91 From 39e69276a1bbe86ebd583e834381e5bc73948564 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:43:03 +0000 Subject: [PATCH 241/290] Bump tests/plugins/DevTools from `c6dca35` to `a030d39` (#6617) --- tests/plugins/DevTools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins/DevTools b/tests/plugins/DevTools index c6dca357c..a030d39e5 160000 --- a/tests/plugins/DevTools +++ b/tests/plugins/DevTools @@ -1 +1 @@ -Subproject commit c6dca357c7e8a37ce3479a1bedfe849451e072e3 +Subproject commit a030d39e51d267b5cd7e09069844a06910072ae7 From 9b3b45258aa33143cb23bb6e759e8e5367b5e347 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Thu, 6 Feb 2025 15:42:10 +0000 Subject: [PATCH 242/290] Optimise collision box checks by caching some basic info (#6606) This PR significantly improves performance of entity movement calculation. Previous attempts to optimise this were ineffective, as they used a cache to mitigate the cost of recomputing AABBs. Entities tend to move around randomly, so the non-cached pathway really needed to be optimized. This change improves performance on multiple fronts: 1) avoiding Block allocations for blocks with 1x1x1 AABBs and with no AABBs (the most common) 2) avoiding Block allocations and overlapping intersection checks unless a stateID is specifically known to potentially exceed its cell boundaries (like fences) 3) avoiding overlapping AABB checks when overlaps can't make any difference anyway (cubes) Together, these changes improve the performance of World->getBlockCollisionBoxes() by a factor of 5. In real-world terms, this shows up as a major performance improvement in situations with lots of entities moving in random directions. Testing with item entities showed an increase from 400 to 1200 moving items with the same CPU usage. This change is built on the assumption that `Block->recalculateCollisionBoxes()` and its overrides don't interact with any world. This is technically possible due to the crappy design of the `Block` architecture, but should be avoided. As a world is not available during `RuntimeBlockStateRegistry` initialization, attempting to interact with a world during `recalculateCollisionBoxes()` will now cause a crash. This turned out to be a problem for `ChorusPlant`, which was fixed by 70fb9bbdfd06c7eda00b4cd2c2c3840755e6b8f6. The correct solution in this case was to use dynamic states similar to how we currently deal with fence connections. --- src/block/RuntimeBlockStateRegistry.php | 79 ++++++++++++++++++++ src/world/World.php | 98 +++++++++++++++++++------ 2 files changed, 155 insertions(+), 22 deletions(-) diff --git a/src/block/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php index a8ece7722..d13b942ba 100644 --- a/src/block/RuntimeBlockStateRegistry.php +++ b/src/block/RuntimeBlockStateRegistry.php @@ -28,6 +28,7 @@ use pocketmine\block\BlockIdentifier as BID; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\light\LightUpdate; +use function count; use function min; /** @@ -40,6 +41,11 @@ use function min; class RuntimeBlockStateRegistry{ use SingletonTrait; + public const COLLISION_CUSTOM = 0; + public const COLLISION_CUBE = 1; + public const COLLISION_NONE = 2; + public const COLLISION_MAY_OVERFLOW = 3; + /** * @var Block[] * @phpstan-var array @@ -74,6 +80,13 @@ class RuntimeBlockStateRegistry{ */ public array $blastResistance = []; + /** + * Map of state ID -> useful AABB info to avoid unnecessary block allocations + * @var int[] + * @phpstan-var array + */ + public array $collisionInfo = []; + public function __construct(){ foreach(VanillaBlocks::getAll() as $block){ $this->register($block); @@ -100,6 +113,70 @@ class RuntimeBlockStateRegistry{ } } + /** + * Checks if the given class method overrides a method in Block. + * Used to determine if a block might need to disable fast path optimizations. + * + * @phpstan-param anyClosure $closure + */ + private static function overridesBlockMethod(\Closure $closure) : bool{ + $declarer = (new \ReflectionFunction($closure))->getClosureScopeClass(); + return $declarer !== null && $declarer->getName() !== Block::class; + } + + /** + * A big ugly hack to set up fast paths for handling collisions on blocks with common shapes. + * The information returned here is stored in RuntimeBlockStateRegistry->collisionInfo, and is used during entity + * collision box calculations to avoid complex logic and unnecessary block object allocations. + * This hack allows significant performance improvements. + * + * TODO: We'll want to redesign block collision box handling and block shapes in the future, but that's a job for a + * major version. For now, this hack nets major performance wins. + */ + private static function calculateCollisionInfo(Block $block) : int{ + if( + self::overridesBlockMethod($block->getModelPositionOffset(...)) || + self::overridesBlockMethod($block->readStateFromWorld(...)) + ){ + //getModelPositionOffset() might cause AABBs to shift outside the cell + //readStateFromWorld() might cause overflow in ways we can't predict just by looking at known states + //TODO: excluding overriders of readStateFromWorld() also excludes blocks with tiles that don't do anything + //weird with their AABBs, but for now this is the best we can do. + return self::COLLISION_MAY_OVERFLOW; + } + + //TODO: this could blow up if any recalculateCollisionBoxes() uses the world + //it shouldn't, but that doesn't mean that custom blocks won't... + $boxes = $block->getCollisionBoxes(); + if(count($boxes) === 0){ + return self::COLLISION_NONE; + } + + if( + count($boxes) === 1 && + $boxes[0]->minX === 0.0 && + $boxes[0]->minY === 0.0 && + $boxes[0]->minZ === 0.0 && + $boxes[0]->maxX === 1.0 && + $boxes[0]->maxY === 1.0 && + $boxes[0]->maxZ === 1.0 + ){ + return self::COLLISION_CUBE; + } + + foreach($boxes as $box){ + if( + $box->minX < 0 || $box->maxX > 1 || + $box->minY < 0 || $box->maxY > 1 || + $box->minZ < 0 || $box->maxZ > 1 + ){ + return self::COLLISION_MAY_OVERFLOW; + } + } + + return self::COLLISION_CUSTOM; + } + private function fillStaticArrays(int $index, Block $block) : void{ $fullId = $block->getStateId(); if($index !== $fullId){ @@ -112,6 +189,8 @@ class RuntimeBlockStateRegistry{ if($block->blocksDirectSkyLight()){ $this->blocksDirectSkyLight[$index] = true; } + + $this->collisionInfo[$index] = self::calculateCollisionInfo($block); } } diff --git a/src/world/World.php b/src/world/World.php index 7395afa78..86ca44848 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -375,6 +375,8 @@ class World implements ChunkManager{ private \Logger $logger; + private RuntimeBlockStateRegistry $blockStateRegistry; + /** * @phpstan-return ChunkPosHash */ @@ -488,6 +490,7 @@ class World implements ChunkManager{ $this->displayName = $this->provider->getWorldData()->getName(); $this->logger = new \PrefixedLogger($server->getLogger(), "World: $this->displayName"); + $this->blockStateRegistry = RuntimeBlockStateRegistry::getInstance(); $this->minY = $this->provider->getWorldMinY(); $this->maxY = $this->provider->getWorldMaxY(); @@ -559,7 +562,7 @@ class World implements ChunkManager{ }catch(BlockStateDeserializeException){ continue; } - $block = RuntimeBlockStateRegistry::getInstance()->fromStateId(GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData)); + $block = $this->blockStateRegistry->fromStateId(GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData)); }else{ //TODO: we probably ought to log an error here continue; @@ -570,7 +573,7 @@ class World implements ChunkManager{ } } - foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $state){ + foreach($this->blockStateRegistry->getAllKnownStates() as $state){ $dontTickName = $dontTickBlocks[$state->getTypeId()] ?? null; if($dontTickName === null && $state->ticksRandomly()){ $this->randomTickBlocks[$state->getStateId()] = true; @@ -1394,7 +1397,7 @@ class World implements ChunkManager{ $entity->onRandomUpdate(); } - $blockFactory = RuntimeBlockStateRegistry::getInstance(); + $blockFactory = $this->blockStateRegistry; foreach($chunk->getSubChunks() as $Y => $subChunk){ if(!$subChunk->isEmptyFast()){ $k = 0; @@ -1528,13 +1531,18 @@ class World implements ChunkManager{ $collides = []; + $collisionInfo = $this->blockStateRegistry->collisionInfo; if($targetFirst){ for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ - $block = $this->getBlockAt($x, $y, $z); - if($block->collidesWithBB($bb)){ - return [$block]; + $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); + if(match($stateCollisionInfo){ + RuntimeBlockStateRegistry::COLLISION_CUBE => true, + RuntimeBlockStateRegistry::COLLISION_NONE => false, + default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) + }){ + return [$this->getBlockAt($x, $y, $z)]; } } } @@ -1543,9 +1551,13 @@ class World implements ChunkManager{ for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ - $block = $this->getBlockAt($x, $y, $z); - if($block->collidesWithBB($bb)){ - $collides[] = $block; + $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); + if(match($stateCollisionInfo){ + RuntimeBlockStateRegistry::COLLISION_CUBE => true, + RuntimeBlockStateRegistry::COLLISION_NONE => false, + default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) + }){ + $collides[] = $this->getBlockAt($x, $y, $z); } } } @@ -1555,24 +1567,64 @@ class World implements ChunkManager{ return $collides; } + /** + * @param int[] $collisionInfo + * @phpstan-param array $collisionInfo + */ + private function getBlockCollisionInfo(int $x, int $y, int $z, array $collisionInfo) : int{ + if(!$this->isInWorld($x, $y, $z)){ + return RuntimeBlockStateRegistry::COLLISION_NONE; + } + $chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE); + if($chunk === null){ + return RuntimeBlockStateRegistry::COLLISION_NONE; + } + $stateId = $chunk + ->getSubChunk($y >> SubChunk::COORD_BIT_SIZE) + ->getBlockStateId( + $x & SubChunk::COORD_MASK, + $y & SubChunk::COORD_MASK, + $z & SubChunk::COORD_MASK + ); + return $collisionInfo[$stateId]; + } + /** * Returns a list of all block AABBs which overlap the full block area at the given coordinates. * This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences. * Larger AABBs (>= 2 blocks on any axis) are not accounted for. * + * @param int[] $collisionInfo + * @phpstan-param array $collisionInfo + * * @return AxisAlignedBB[] * @phpstan-return list */ - private function getBlockCollisionBoxesForCell(int $x, int $y, int $z) : array{ - $block = $this->getBlockAt($x, $y, $z); - $boxes = $block->getCollisionBoxes(); + private function getBlockCollisionBoxesForCell(int $x, int $y, int $z, array $collisionInfo) : array{ + $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); + $boxes = match($stateCollisionInfo){ + RuntimeBlockStateRegistry::COLLISION_NONE => [], + RuntimeBlockStateRegistry::COLLISION_CUBE => [AxisAlignedBB::one()->offset($x, $y, $z)], + default => $this->getBlockAt($x, $y, $z)->getCollisionBoxes() + }; - $cellBB = AxisAlignedBB::one()->offset($x, $y, $z); - foreach(Facing::OFFSET as [$dx, $dy, $dz]){ - $extraBoxes = $this->getBlockAt($x + $dx, $y + $dy, $z + $dz)->getCollisionBoxes(); - foreach($extraBoxes as $extraBox){ - if($extraBox->intersectsWith($cellBB)){ - $boxes[] = $extraBox; + //overlapping AABBs can't make any difference if this is a cube, so we can save some CPU cycles in this common case + if($stateCollisionInfo !== RuntimeBlockStateRegistry::COLLISION_CUBE){ + $cellBB = null; + foreach(Facing::OFFSET as [$dx, $dy, $dz]){ + $offsetX = $x + $dx; + $offsetY = $y + $dy; + $offsetZ = $z + $dz; + $stateCollisionInfo = $this->getBlockCollisionInfo($offsetX, $offsetY, $offsetZ, $collisionInfo); + if($stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW){ + //avoid allocating this unless it's needed + $cellBB ??= AxisAlignedBB::one()->offset($x, $y, $z); + $extraBoxes = $this->getBlockAt($offsetX, $offsetY, $offsetZ)->getCollisionBoxes(); + foreach($extraBoxes as $extraBox){ + if($extraBox->intersectsWith($cellBB)){ + $boxes[] = $extraBox; + } + } } } } @@ -1594,13 +1646,15 @@ class World implements ChunkManager{ $collides = []; + $collisionInfo = $this->blockStateRegistry->collisionInfo; + for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ $chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE); for($y = $minY; $y <= $maxY; ++$y){ $relativeBlockHash = World::chunkBlockHash($x, $y, $z); - $boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getBlockCollisionBoxesForCell($x, $y, $z); + $boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getBlockCollisionBoxesForCell($x, $y, $z, $collisionInfo); foreach($boxes as $blockBB){ if($blockBB->intersectsWith($bb)){ @@ -1795,7 +1849,7 @@ class World implements ChunkManager{ return; } - $blockFactory = RuntimeBlockStateRegistry::getInstance(); + $blockFactory = $this->blockStateRegistry; $this->timings->doBlockSkyLightUpdates->startTiming(); if($this->skyLightUpdate === null){ $this->skyLightUpdate = new SkyLightUpdate(new SubChunkExplorer($this), $blockFactory->lightFilter, $blockFactory->blocksDirectSkyLight); @@ -1914,7 +1968,7 @@ class World implements ChunkManager{ $chunk = $this->chunks[$chunkHash] ?? null; if($chunk !== null){ - $block = RuntimeBlockStateRegistry::getInstance()->fromStateId($chunk->getBlockStateId($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK)); + $block = $this->blockStateRegistry->fromStateId($chunk->getBlockStateId($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK)); }else{ $addToCache = false; $block = VanillaBlocks::AIR(); @@ -2573,7 +2627,7 @@ class World implements ChunkManager{ $localY = $tilePosition->getFloorY(); $localZ = $tilePosition->getFloorZ() & Chunk::COORD_MASK; - $newBlock = RuntimeBlockStateRegistry::getInstance()->fromStateId($chunk->getBlockStateId($localX, $localY, $localZ)); + $newBlock = $this->blockStateRegistry->fromStateId($chunk->getBlockStateId($localX, $localY, $localZ)); $expectedTileClass = $newBlock->getIdInfo()->getTileClass(); if( $expectedTileClass === null || //new block doesn't expect a tile From 5ef89200c683f3b06d54b2d1509400a0f95152f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:14:30 +0000 Subject: [PATCH 243/290] Bump phpstan/phpstan in the development-patch-updates group (#6620) --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index b9fb70b0e..bbcbe7314 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.2", + "phpstan/phpstan": "2.1.4", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index 26a3a510c..52490968f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5321ac37e6830cae119d03082b2668a5", + "content-hash": "a6aa0a34a230d325528d2e11f1439057", "packages": [ { "name": "adhocore/json-comment", @@ -1386,16 +1386,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.2", + "version": "2.1.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "7d08f569e582ade182a375c366cbd896eccadd3a" + "reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/7d08f569e582ade182a375c366cbd896eccadd3a", - "reference": "7d08f569e582ade182a375c366cbd896eccadd3a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f99e18eb775dbaf6460c95fa0b65312da9c746a", + "reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a", "shasum": "" }, "require": { @@ -1440,7 +1440,7 @@ "type": "github" } ], - "time": "2025-01-21T14:54:06+00:00" + "time": "2025-02-10T08:25:21+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From e29aa2f337f2cc8779e8b98653ca90729984c313 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 11 Feb 2025 19:15:42 +0300 Subject: [PATCH 244/290] Added resin material color (#6622) --- src/utils/Terminal.php | 6 +++++- src/utils/TextFormat.php | 9 ++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/utils/Terminal.php b/src/utils/Terminal.php index 2abdbc357..bbd232969 100644 --- a/src/utils/Terminal.php +++ b/src/utils/Terminal.php @@ -69,6 +69,7 @@ abstract class Terminal{ public static string $COLOR_MATERIAL_DIAMOND = ""; public static string $COLOR_MATERIAL_LAPIS = ""; public static string $COLOR_MATERIAL_AMETHYST = ""; + public static string $COLOR_MATERIAL_RESIN = ""; private static ?bool $formattingCodes = null; @@ -131,6 +132,7 @@ abstract class Terminal{ self::$COLOR_MATERIAL_DIAMOND = $color(37); self::$COLOR_MATERIAL_LAPIS = $color(24); self::$COLOR_MATERIAL_AMETHYST = $color(98); + self::$COLOR_MATERIAL_RESIN = $color(208); } protected static function getEscapeCodes() : void{ @@ -174,11 +176,12 @@ abstract class Terminal{ self::$COLOR_MATERIAL_DIAMOND = $colors >= 256 ? $setaf(37) : $setaf(14); self::$COLOR_MATERIAL_LAPIS = $colors >= 256 ? $setaf(24) : $setaf(12); self::$COLOR_MATERIAL_AMETHYST = $colors >= 256 ? $setaf(98) : $setaf(13); + self::$COLOR_MATERIAL_RESIN = $colors >= 256 ? $setaf(208) : $setaf(11); }else{ self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = self::$COLOR_MATERIAL_NETHERITE = $setaf(0); self::$COLOR_RED = self::$COLOR_DARK_RED = self::$COLOR_MATERIAL_REDSTONE = self::$COLOR_MATERIAL_COPPER = $setaf(1); self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = self::$COLOR_MATERIAL_EMERALD = $setaf(2); - self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = self::$COLOR_MATERIAL_GOLD = $setaf(3); + self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = self::$COLOR_MATERIAL_GOLD = self::$COLOR_MATERIAL_RESIN = $setaf(3); self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = self::$COLOR_MATERIAL_LAPIS = $setaf(4); self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = self::$COLOR_MATERIAL_AMETHYST = $setaf(5); self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = self::$COLOR_MATERIAL_DIAMOND = $setaf(6); @@ -253,6 +256,7 @@ abstract class Terminal{ TextFormat::MATERIAL_DIAMOND => Terminal::$COLOR_MATERIAL_DIAMOND, TextFormat::MATERIAL_LAPIS => Terminal::$COLOR_MATERIAL_LAPIS, TextFormat::MATERIAL_AMETHYST => Terminal::$COLOR_MATERIAL_AMETHYST, + TextFormat::MATERIAL_RESIN => Terminal::$COLOR_MATERIAL_RESIN, default => $token, }; } diff --git a/src/utils/TextFormat.php b/src/utils/TextFormat.php index fadf6ea4e..0c948592a 100644 --- a/src/utils/TextFormat.php +++ b/src/utils/TextFormat.php @@ -73,6 +73,7 @@ abstract class TextFormat{ public const MATERIAL_DIAMOND = TextFormat::ESCAPE . "s"; public const MATERIAL_LAPIS = TextFormat::ESCAPE . "t"; public const MATERIAL_AMETHYST = TextFormat::ESCAPE . "u"; + public const MATERIAL_RESIN = TextFormat::ESCAPE . "v"; public const COLORS = [ self::BLACK => self::BLACK, @@ -102,6 +103,7 @@ abstract class TextFormat{ self::MATERIAL_DIAMOND => self::MATERIAL_DIAMOND, self::MATERIAL_LAPIS => self::MATERIAL_LAPIS, self::MATERIAL_AMETHYST => self::MATERIAL_AMETHYST, + self::MATERIAL_RESIN => self::MATERIAL_RESIN, ]; public const OBFUSCATED = TextFormat::ESCAPE . "k"; @@ -150,7 +152,7 @@ abstract class TextFormat{ * @return string[] */ public static function tokenize(string $string) : array{ - $result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-u])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-v])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); if($result === false) throw self::makePcreError(); return $result; } @@ -164,7 +166,7 @@ abstract class TextFormat{ $string = mb_scrub($string, 'UTF-8'); $string = self::preg_replace("/[\x{E000}-\x{F8FF}]/u", "", $string); //remove unicode private-use-area characters (they might break the console) if($removeFormat){ - $string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-u]/u", "", $string)); + $string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-v]/u", "", $string)); } return str_replace("\x1b", "", self::preg_replace("/\x1b[\\(\\][[0-9;\\[\\(]+[Bm]/u", "", $string)); } @@ -175,7 +177,7 @@ abstract class TextFormat{ * @param string $placeholder default "&" */ public static function colorize(string $string, string $placeholder = "&") : string{ - return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-u])/u', TextFormat::ESCAPE . '$1', $string); + return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-v])/u', TextFormat::ESCAPE . '$1', $string); } /** @@ -252,6 +254,7 @@ abstract class TextFormat{ TextFormat::MATERIAL_DIAMOND => "color:#2cb9a8", TextFormat::MATERIAL_LAPIS => "color:#20487a", TextFormat::MATERIAL_AMETHYST => "color:#9a5cc5", + TextFormat::MATERIAL_RESIN => "color:#fc7812", TextFormat::BOLD => "font-weight:bold", TextFormat::ITALIC => "font-style:italic", default => null From 9402a20ee3de0232761433565fc984953939c754 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 16 Feb 2025 16:12:29 +0000 Subject: [PATCH 245/290] Update Utils::getOS() doc comment closes #6628 --- src/utils/Utils.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/utils/Utils.php b/src/utils/Utils.php index b1f7e2842..46cd49ec4 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -264,14 +264,7 @@ final class Utils{ } /** - * Returns the current Operating System - * Windows => win - * MacOS => mac - * iOS => ios - * Android => android - * Linux => Linux - * BSD => bsd - * Other => other + * @return string one of the Utils::OS_* constants */ public static function getOS(bool $recalculate = false) : string{ if(self::$os === null || $recalculate){ From 91ac64783f0e5d2cffbff8b58c4bc98d5283832a Mon Sep 17 00:00:00 2001 From: Dries C Date: Sun, 16 Feb 2025 21:51:53 +0100 Subject: [PATCH 246/290] Bedrock 1.21.60 (#6627) Co-authored-by: Dylan K. Taylor --- composer.json | 8 +- composer.lock | 52 +-- src/data/bedrock/BedrockDataFiles.php | 3 +- src/data/bedrock/block/BlockStateData.php | 4 +- src/data/bedrock/block/BlockStateNames.php | 1 + .../bedrock/block/BlockStateStringValues.php | 4 + .../convert/BlockStateDeserializerHelper.php | 4 +- .../convert/BlockStateSerializerHelper.php | 4 +- src/entity/Human.php | 1 + src/inventory/CreativeCategory.php | 34 ++ src/inventory/CreativeGroup.php | 51 +++ src/inventory/CreativeInventory.php | 69 ++-- src/inventory/CreativeInventoryEntry.php | 48 +++ src/inventory/json/CreativeGroupData.php | 38 ++ src/lang/KnownTranslationFactory.php | 324 ++++++++++++++++++ src/lang/KnownTranslationKeys.php | 81 +++++ src/network/mcpe/InventoryManager.php | 2 +- src/network/mcpe/NetworkSession.php | 4 +- .../mcpe/cache/CreativeInventoryCache.php | 106 +++++- .../cache/CreativeInventoryCacheEntry.php | 48 +++ .../ItemTypeDictionaryFromDataHelper.php | 12 +- .../mcpe/handler/PreSpawnPacketHandler.php | 5 +- src/world/format/io/data/BedrockWorldData.php | 6 +- tools/generate-bedrock-data-from-packets.php | 89 ++++- 24 files changed, 902 insertions(+), 96 deletions(-) create mode 100644 src/inventory/CreativeCategory.php create mode 100644 src/inventory/CreativeGroup.php create mode 100644 src/inventory/CreativeInventoryEntry.php create mode 100644 src/inventory/json/CreativeGroupData.php create mode 100644 src/network/mcpe/cache/CreativeInventoryCacheEntry.php diff --git a/composer.json b/composer.json index bbcbe7314..f8d060bcd 100644 --- a/composer.json +++ b/composer.json @@ -33,15 +33,15 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "netresearch/jsonmapper": "~v5.0.0", - "pocketmine/bedrock-block-upgrade-schema": "~5.0.0+bedrock-1.21.40", - "pocketmine/bedrock-data": "~2.15.0+bedrock-1.21.50", + "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", + "pocketmine/bedrock-data": "~4.0.0+bedrock-1.21.60", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~35.0.0+bedrock-1.21.50", + "pocketmine/bedrock-protocol": "~36.0.0+bedrock-1.21.60", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.7.0", - "pocketmine/locale-data": "~2.22.0", + "pocketmine/locale-data": "~2.24.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", diff --git a/composer.lock b/composer.lock index 52490968f..cc7f1f716 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a6aa0a34a230d325528d2e11f1439057", + "content-hash": "af7547291a131bfac6d7087957601325", "packages": [ { "name": "adhocore/json-comment", @@ -178,16 +178,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "5.0.0", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52" + "reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/20dd5c11e9915bacea4fe2cf649e1d23697a6e52", - "reference": "20dd5c11e9915bacea4fe2cf649e1d23697a6e52", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/2218512e4b91f5bfd09ef55f7a4c4b04e169e41a", + "reference": "2218512e4b91f5bfd09ef55f7a4c4b04e169e41a", "shasum": "" }, "type": "library", @@ -198,22 +198,22 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.0.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/5.1.0" }, - "time": "2024-11-03T14:13:50+00:00" + "time": "2025-02-11T17:41:44+00:00" }, { "name": "pocketmine/bedrock-data", - "version": "2.15.0+bedrock-1.21.50", + "version": "4.0.0+bedrock-1.21.60", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad" + "reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/6e819f36d781866ce63d2406be2ce7f2d1afd9ad", - "reference": "6e819f36d781866ce63d2406be2ce7f2d1afd9ad", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/2e5f16ec2facac653f3f894f22eb831d880ba98e", + "reference": "2e5f16ec2facac653f3f894f22eb831d880ba98e", "shasum": "" }, "type": "library", @@ -224,9 +224,9 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.50" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.21.60" }, - "time": "2024-12-04T12:59:12+00:00" + "time": "2025-02-16T15:56:56+00:00" }, { "name": "pocketmine/bedrock-item-upgrade-schema", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "35.0.3+bedrock-1.21.50", + "version": "36.0.0+bedrock-1.21.60", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c" + "reference": "2057de319c5c551001c2a544e08d1bc7727d9963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c4d62581cb62d29ec426914c6b4d7e0ff835da9c", - "reference": "c4d62581cb62d29ec426914c6b4d7e0ff835da9c", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2057de319c5c551001c2a544e08d1bc7727d9963", + "reference": "2057de319c5c551001c2a544e08d1bc7727d9963", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/35.0.3+bedrock-1.21.50" + "source": "https://github.com/pmmp/BedrockProtocol/tree/36.0.0+bedrock-1.21.60" }, - "time": "2025-01-07T23:06:29+00:00" + "time": "2025-02-16T15:59:08+00:00" }, { "name": "pocketmine/binaryutils", @@ -471,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.22.1", + "version": "2.24.0", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49" + "reference": "6ec5e92c77a2102b2692763733e4763012facae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/fa4e377c437391cfcfdedd08eea3a848eabd1b49", - "reference": "fa4e377c437391cfcfdedd08eea3a848eabd1b49", + "url": "https://api.github.com/repos/pmmp/Language/zipball/6ec5e92c77a2102b2692763733e4763012facae9", + "reference": "6ec5e92c77a2102b2692763733e4763012facae9", "shasum": "" }, "type": "library", @@ -488,9 +488,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.22.1" + "source": "https://github.com/pmmp/Language/tree/2.24.0" }, - "time": "2024-12-06T14:44:17+00:00" + "time": "2025-02-16T20:46:34+00:00" }, { "name": "pocketmine/log", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php index 3341c0503..6176eee34 100644 --- a/src/data/bedrock/BedrockDataFiles.php +++ b/src/data/bedrock/BedrockDataFiles.php @@ -39,12 +39,11 @@ final class BedrockDataFiles{ public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json'; public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt'; public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json'; - public const CREATIVEITEMS_JSON = BEDROCK_DATA_PATH . '/creativeitems.json'; public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json'; public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt'; + public const ENUMS_PY = BEDROCK_DATA_PATH . '/enums.py'; public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json'; public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json'; - public const PARTICLE_ID_MAP_JSON = BEDROCK_DATA_PATH . '/particle_id_map.json'; public const PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.json'; public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin'; public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json'; diff --git a/src/data/bedrock/block/BlockStateData.php b/src/data/bedrock/block/BlockStateData.php index 6624c4ae0..e90410ac7 100644 --- a/src/data/bedrock/block/BlockStateData.php +++ b/src/data/bedrock/block/BlockStateData.php @@ -45,8 +45,8 @@ final class BlockStateData{ public const CURRENT_VERSION = (1 << 24) | //major (21 << 16) | //minor - (40 << 8) | //patch - (1); //revision + (60 << 8) | //patch + (33); //revision public const TAG_NAME = "name"; public const TAG_STATES = "states"; diff --git a/src/data/bedrock/block/BlockStateNames.php b/src/data/bedrock/block/BlockStateNames.php index 68a3e1b1d..9fed77e4a 100644 --- a/src/data/bedrock/block/BlockStateNames.php +++ b/src/data/bedrock/block/BlockStateNames.php @@ -59,6 +59,7 @@ final class BlockStateNames{ public const COVERED_BIT = "covered_bit"; public const CRACKED_STATE = "cracked_state"; public const CRAFTING = "crafting"; + public const CREAKING_HEART_STATE = "creaking_heart_state"; public const DEAD_BIT = "dead_bit"; public const DEPRECATED = "deprecated"; public const DIRECTION = "direction"; diff --git a/src/data/bedrock/block/BlockStateStringValues.php b/src/data/bedrock/block/BlockStateStringValues.php index e8171e4da..e6ae30e64 100644 --- a/src/data/bedrock/block/BlockStateStringValues.php +++ b/src/data/bedrock/block/BlockStateStringValues.php @@ -56,6 +56,10 @@ final class BlockStateStringValues{ public const CRACKED_STATE_MAX_CRACKED = "max_cracked"; public const CRACKED_STATE_NO_CRACKS = "no_cracks"; + public const CREAKING_HEART_STATE_AWAKE = "awake"; + public const CREAKING_HEART_STATE_DORMANT = "dormant"; + public const CREAKING_HEART_STATE_UPROOTED = "uprooted"; + public const DRIPSTONE_THICKNESS_BASE = "base"; public const DRIPSTONE_THICKNESS_FRUSTUM = "frustum"; public const DRIPSTONE_THICKNESS_MERGE = "merge"; diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index fda0455aa..342f31490 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -131,7 +131,7 @@ final class BlockStateDeserializerHelper{ //TODO: check if these need any special treatment to get the appropriate data to both halves of the door return $block ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)) - ->setFacing(Facing::rotateY($in->readLegacyHorizontalFacing(), false)) + ->setFacing($in->readCardinalHorizontalFacing()) ->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } @@ -145,7 +145,7 @@ final class BlockStateDeserializerHelper{ /** @throws BlockStateDeserializeException */ public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ return $block - ->setFacing($in->readLegacyHorizontalFacing()) + ->setFacing($in->readCardinalHorizontalFacing()) ->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index 3e2215746..fa0591669 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -100,7 +100,7 @@ final class BlockStateSerializerHelper{ public static function encodeDoor(Door $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) - ->writeLegacyHorizontalFacing(Facing::rotateY($block->getFacing(), true)) + ->writeCardinalHorizontalFacing($block->getFacing()) ->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight()) ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); } @@ -112,7 +112,7 @@ final class BlockStateSerializerHelper{ public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{ return $out - ->writeLegacyHorizontalFacing($block->getFacing()) + ->writeCardinalHorizontalFacing($block->getFacing()) ->writeBool(BlockStateNames::IN_WALL_BIT, $block->isInWall()) ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); } diff --git a/src/entity/Human.php b/src/entity/Human.php index 833196651..c94b76097 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -513,6 +513,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ AbilitiesLayer::LAYER_BASE, array_fill(0, AbilitiesLayer::NUMBER_OF_ABILITIES, false), 0.0, + 0.0, 0.0 ) ])), diff --git a/src/inventory/CreativeCategory.php b/src/inventory/CreativeCategory.php new file mode 100644 index 000000000..bede48b28 --- /dev/null +++ b/src/inventory/CreativeCategory.php @@ -0,0 +1,34 @@ +getText()) : strlen($name); + if($nameLength === 0){ + throw new \InvalidArgumentException("Creative group name cannot be empty"); + } + } + + public function getName() : Translatable|string{ return $this->name; } + + public function getIcon() : Item{ return clone $this->icon; } +} diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 57e5cbb4e..77eda8138 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -24,21 +24,23 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\crafting\CraftingManagerFromDataHelper; -use pocketmine\crafting\json\ItemStackData; -use pocketmine\data\bedrock\BedrockDataFiles; +use pocketmine\inventory\json\CreativeGroupData; use pocketmine\item\Item; +use pocketmine\lang\Translatable; use pocketmine\utils\DestructorCallbackTrait; use pocketmine\utils\ObjectSet; use pocketmine\utils\SingletonTrait; -use pocketmine\utils\Utils; +use Symfony\Component\Filesystem\Path; +use function array_filter; +use function array_map; final class CreativeInventory{ use SingletonTrait; use DestructorCallbackTrait; /** - * @var Item[] - * @phpstan-var array + * @var CreativeInventoryEntry[] + * @phpstan-var array */ private array $creative = []; @@ -47,17 +49,32 @@ final class CreativeInventory{ private function __construct(){ $this->contentChangedCallbacks = new ObjectSet(); - $creativeItems = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile( - BedrockDataFiles::CREATIVEITEMS_JSON, - ItemStackData::class - ); - foreach($creativeItems as $data){ - $item = CraftingManagerFromDataHelper::deserializeItemStack($data); - if($item === null){ - //unknown item - continue; + + foreach([ + "construction" => CreativeCategory::CONSTRUCTION, + "nature" => CreativeCategory::NATURE, + "equipment" => CreativeCategory::EQUIPMENT, + "items" => CreativeCategory::ITEMS, + ] as $categoryId => $categoryEnum){ + $groups = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile( + Path::join(\pocketmine\BEDROCK_DATA_PATH, "creative", $categoryId . ".json"), + CreativeGroupData::class + ); + + foreach($groups as $groupData){ + $icon = $groupData->group_icon === null ? null : CraftingManagerFromDataHelper::deserializeItemStack($groupData->group_icon); + + $group = $icon === null ? null : new CreativeGroup( + new Translatable($groupData->group_name), + $icon + ); + + $items = array_filter(array_map(static fn($itemStack) => CraftingManagerFromDataHelper::deserializeItemStack($itemStack), $groupData->items)); + + foreach($items as $item){ + $this->add($item, $categoryEnum, $group); + } } - $this->add($item); } } @@ -75,16 +92,28 @@ final class CreativeInventory{ * @phpstan-return array */ public function getAll() : array{ - return Utils::cloneObjectArray($this->creative); + return array_map(fn(CreativeInventoryEntry $entry) => $entry->getItem(), $this->creative); + } + + /** + * @return CreativeInventoryEntry[] + * @phpstan-return array + */ + public function getAllEntries() : array{ + return $this->creative; } public function getItem(int $index) : ?Item{ - return isset($this->creative[$index]) ? clone $this->creative[$index] : null; + return $this->getEntry($index)?->getItem(); + } + + public function getEntry(int $index) : ?CreativeInventoryEntry{ + return $this->creative[$index] ?? null; } public function getItemIndex(Item $item) : int{ foreach($this->creative as $i => $d){ - if($item->equals($d, true, false)){ + if($d->matchesItem($item)){ return $i; } } @@ -96,8 +125,8 @@ final class CreativeInventory{ * Adds an item to the creative menu. * Note: Players who are already online when this is called will not see this change. */ - public function add(Item $item) : void{ - $this->creative[] = clone $item; + public function add(Item $item, CreativeCategory $category = CreativeCategory::ITEMS, ?CreativeGroup $group = null) : void{ + $this->creative[] = new CreativeInventoryEntry($item, $category, $group); $this->onContentChange(); } diff --git a/src/inventory/CreativeInventoryEntry.php b/src/inventory/CreativeInventoryEntry.php new file mode 100644 index 000000000..a5d568ee8 --- /dev/null +++ b/src/inventory/CreativeInventoryEntry.php @@ -0,0 +1,48 @@ +item = clone $item; + } + + public function getItem() : Item{ return clone $this->item; } + + public function getCategory() : CreativeCategory{ return $this->category; } + + public function getGroup() : ?CreativeGroup{ return $this->group; } + + public function matchesItem(Item $item) : bool{ + return $item->equals($this->item, checkDamage: true, checkCompound: false); + } +} diff --git a/src/inventory/json/CreativeGroupData.php b/src/inventory/json/CreativeGroupData.php new file mode 100644 index 000000000..70b73d3de --- /dev/null +++ b/src/inventory/json/CreativeGroupData.php @@ -0,0 +1,38 @@ +session->sendDataPacket(CreativeInventoryCache::getInstance()->getCache($this->player->getCreativeInventory())); + $this->session->sendDataPacket(CreativeInventoryCache::getInstance()->buildPacket($this->player->getCreativeInventory(), $this->session)); } /** diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index fdb63c467..8c457ed40 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -1058,7 +1058,7 @@ class NetworkSession{ ]; $layers = [ - new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, $for->getFlightSpeedMultiplier(), 0.1), + new AbilitiesLayer(AbilitiesLayer::LAYER_BASE, $boolAbilities, $for->getFlightSpeedMultiplier(), 1, 0.1), ]; if(!$for->hasBlockCollision()){ //TODO: HACK! In 1.19.80, the client starts falling in our faux spectator mode when it clips into a @@ -1068,7 +1068,7 @@ class NetworkSession{ $layers[] = new AbilitiesLayer(AbilitiesLayer::LAYER_SPECTATOR, [ AbilitiesLayer::ABILITY_FLYING => true, - ], null, null); + ], null, null, null); } $this->sendDataPacket(UpdateAbilitiesPacket::create(new AbilitiesData( diff --git a/src/network/mcpe/cache/CreativeInventoryCache.php b/src/network/mcpe/cache/CreativeInventoryCache.php index 04fc52604..e543f343e 100644 --- a/src/network/mcpe/cache/CreativeInventoryCache.php +++ b/src/network/mcpe/cache/CreativeInventoryCache.php @@ -23,23 +23,30 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\cache; +use pocketmine\inventory\CreativeCategory; use pocketmine\inventory\CreativeInventory; +use pocketmine\lang\Translatable; use pocketmine\network\mcpe\convert\TypeConverter; +use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\CreativeContentPacket; -use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; +use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry; +use pocketmine\network\mcpe\protocol\types\inventory\CreativeItemEntry; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\utils\SingletonTrait; +use function is_string; use function spl_object_id; +use const PHP_INT_MIN; final class CreativeInventoryCache{ use SingletonTrait; /** - * @var CreativeContentPacket[] - * @phpstan-var array + * @var CreativeInventoryCacheEntry[] + * @phpstan-var array */ private array $caches = []; - public function getCache(CreativeInventory $inventory) : CreativeContentPacket{ + private function getCacheEntry(CreativeInventory $inventory) : CreativeInventoryCacheEntry{ $id = spl_object_id($inventory); if(!isset($this->caches[$id])){ $inventory->getDestructorCallbacks()->add(function() use ($id) : void{ @@ -48,7 +55,7 @@ final class CreativeInventoryCache{ $inventory->getContentChangedCallbacks()->add(function() use ($id) : void{ unset($this->caches[$id]); }); - $this->caches[$id] = $this->buildCreativeInventoryCache($inventory); + $this->caches[$id] = $this->buildCacheEntry($inventory); } return $this->caches[$id]; } @@ -56,14 +63,91 @@ final class CreativeInventoryCache{ /** * Rebuild the cache for the given inventory. */ - private function buildCreativeInventoryCache(CreativeInventory $inventory) : CreativeContentPacket{ - $entries = []; + private function buildCacheEntry(CreativeInventory $inventory) : CreativeInventoryCacheEntry{ + $categories = []; + $groups = []; + $typeConverter = TypeConverter::getInstance(); - //creative inventory may have holes if items were unregistered - ensure network IDs used are always consistent - foreach($inventory->getAll() as $k => $item){ - $entries[] = new CreativeContentEntry($k, $typeConverter->coreItemStackToNet($item)); + + $nextIndex = 0; + $groupIndexes = []; + $itemGroupIndexes = []; + + foreach($inventory->getAllEntries() as $k => $entry){ + $group = $entry->getGroup(); + $category = $entry->getCategory(); + if($group === null){ + $groupId = PHP_INT_MIN; + }else{ + $groupId = spl_object_id($group); + unset($groupIndexes[$category->name][PHP_INT_MIN]); //start a new anonymous group for this category + } + + //group object may be reused by multiple categories + if(!isset($groupIndexes[$category->name][$groupId])){ + $groupIndexes[$category->name][$groupId] = $nextIndex++; + $categories[] = $category; + $groups[] = $group; + } + $itemGroupIndexes[$k] = $groupIndexes[$category->name][$groupId]; } - return CreativeContentPacket::create($entries); + //creative inventory may have holes if items were unregistered - ensure network IDs used are always consistent + $items = []; + foreach($inventory->getAllEntries() as $k => $entry){ + $items[] = new CreativeItemEntry( + $k, + $typeConverter->coreItemStackToNet($entry->getItem()), + $itemGroupIndexes[$k] + ); + } + + return new CreativeInventoryCacheEntry($categories, $groups, $items); + } + + public function buildPacket(CreativeInventory $inventory, NetworkSession $session) : CreativeContentPacket{ + $player = $session->getPlayer() ?? throw new \LogicException("Cannot prepare creative data for a session without a player"); + $language = $player->getLanguage(); + $forceLanguage = $player->getServer()->isLanguageForced(); + $typeConverter = $session->getTypeConverter(); + $cachedEntry = $this->getCacheEntry($inventory); + $translate = function(Translatable|string $translatable) use ($session, $language, $forceLanguage) : string{ + if(is_string($translatable)){ + $message = $translatable; + }elseif(!$forceLanguage){ + [$message,] = $session->prepareClientTranslatableMessage($translatable); + }else{ + $message = $language->translate($translatable); + } + return $message; + }; + + $groupEntries = []; + foreach($cachedEntry->categories as $index => $category){ + $group = $cachedEntry->groups[$index]; + $categoryId = match ($category) { + CreativeCategory::CONSTRUCTION => CreativeContentPacket::CATEGORY_CONSTRUCTION, + CreativeCategory::NATURE => CreativeContentPacket::CATEGORY_NATURE, + CreativeCategory::EQUIPMENT => CreativeContentPacket::CATEGORY_EQUIPMENT, + CreativeCategory::ITEMS => CreativeContentPacket::CATEGORY_ITEMS + }; + if($group === null){ + $groupEntries[] = new CreativeGroupEntry($categoryId, "", ItemStack::null()); + }else{ + $groupIcon = $group->getIcon(); + //TODO: HACK! In 1.21.60, Workaround glitchy behaviour when an item is used as an icon for a group it + //doesn't belong to. Without this hack, both instances of the item will show a +, but neither of them + //will actually expand the group work correctly. + $groupIcon->getNamedTag()->setInt("___GroupBugWorkaround___", $index); + $groupName = $group->getName(); + $groupEntries[] = new CreativeGroupEntry( + $categoryId, + $translate($groupName), + $typeConverter->coreItemStackToNet($groupIcon) + ); + } + } + + return CreativeContentPacket::create($groupEntries, $cachedEntry->items); } } diff --git a/src/network/mcpe/cache/CreativeInventoryCacheEntry.php b/src/network/mcpe/cache/CreativeInventoryCacheEntry.php new file mode 100644 index 000000000..1fc0767df --- /dev/null +++ b/src/network/mcpe/cache/CreativeInventoryCacheEntry.php @@ -0,0 +1,48 @@ + $categories + * @phpstan-param list $groups + * @phpstan-param list $items + */ + public function __construct( + public readonly array $categories, + public readonly array $groups, + public readonly array $items, + ){ + //NOOP + } +} diff --git a/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php b/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php index d962063d3..ed8ae2cc7 100644 --- a/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php +++ b/src/network/mcpe/convert/ItemTypeDictionaryFromDataHelper.php @@ -23,10 +23,15 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\convert; +use pocketmine\errorhandler\ErrorToExceptionHandler; +use pocketmine\nbt\LittleEndianNbtSerializer; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; +use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; +use function base64_decode; use function is_array; use function is_bool; use function is_int; @@ -41,12 +46,15 @@ final class ItemTypeDictionaryFromDataHelper{ throw new AssumptionFailedError("Invalid item list format"); } + $emptyNBT = new CacheableNbt(new CompoundTag()); + $nbtSerializer = new LittleEndianNbtSerializer(); + $params = []; foreach(Utils::promoteKeys($table) as $name => $entry){ - if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){ + if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"], $entry["version"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"]) || !is_int($entry["version"]) || !(is_string($componentNbt = $entry["component_nbt"] ?? null) || $componentNbt === null)){ throw new AssumptionFailedError("Invalid item list format"); } - $params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"]); + $params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"], $entry["version"], $componentNbt === null ? $emptyNBT : new CacheableNbt($nbtSerializer->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($componentNbt, true)))->mustGetCompoundTag())); } return new ItemTypeDictionary($params); } diff --git a/src/network/mcpe/handler/PreSpawnPacketHandler.php b/src/network/mcpe/handler/PreSpawnPacketHandler.php index b80874938..9aa302c0c 100644 --- a/src/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/network/mcpe/handler/PreSpawnPacketHandler.php @@ -28,6 +28,7 @@ use pocketmine\network\mcpe\cache\CraftingDataCache; use pocketmine\network\mcpe\cache\StaticPacketCache; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\ItemRegistryPacket; use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket; use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; use pocketmine\network\mcpe\protocol\StartGamePacket; @@ -110,9 +111,11 @@ class PreSpawnPacketHandler extends PacketHandler{ new NetworkPermissions(disableClientSounds: true), [], 0, - $typeConverter->getItemTypeDictionary()->getEntries(), )); + $this->session->getLogger()->debug("Sending items"); + $this->session->sendDataPacket(ItemRegistryPacket::create($typeConverter->getItemTypeDictionary()->getEntries())); + $this->session->getLogger()->debug("Sending actor identifiers"); $this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers()); diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index b2e079124..a9ca43bc3 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -51,12 +51,12 @@ use function time; class BedrockWorldData extends BaseNbtWorldData{ public const CURRENT_STORAGE_VERSION = 10; - public const CURRENT_STORAGE_NETWORK_VERSION = 748; + public const CURRENT_STORAGE_NETWORK_VERSION = 776; public const CURRENT_CLIENT_VERSION_TARGET = [ 1, //major 21, //minor - 40, //patch - 1, //revision + 60, //patch + 33, //revision 0 //is beta ]; diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 9aac5185e..c48a4f017 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -35,6 +35,7 @@ use pocketmine\crafting\json\SmithingTrimRecipeData; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\item\BlockItemIdMap; use pocketmine\data\bedrock\item\ItemTypeNames; +use pocketmine\inventory\json\CreativeGroupData; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; @@ -48,15 +49,17 @@ use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket; use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket; use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\network\mcpe\protocol\CreativeContentPacket; +use pocketmine\network\mcpe\protocol\ItemRegistryPacket; use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\StartGamePacket; use pocketmine\network\mcpe\protocol\types\CacheableNbt; -use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; +use pocketmine\network\mcpe\protocol\types\inventory\CreativeGroupEntry; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield; +use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; use pocketmine\network\mcpe\protocol\types\recipe\ComplexAliasItemDescriptor; use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe; use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor; @@ -134,6 +137,19 @@ class ParserPacketHandler extends PacketHandler{ return base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($statePropertiesTag))); } + /** + * @param ItemStackData[] $items + */ + private function creativeGroupEntryToJson(CreativeGroupEntry $entry, array $items) : CreativeGroupData{ + $data = new CreativeGroupData(); + + $data->group_name = $entry->getCategoryName(); + $data->group_icon = $entry->getIcon()->getId() === 0 ? null : $this->itemStackToJson($entry->getIcon()); + $data->items = $items; + + return $data; + } + private function itemStackToJson(ItemStack $itemStack) : ItemStackData{ if($itemStack->getId() === 0){ throw new InvalidArgumentException("Cannot serialize a null itemstack"); @@ -234,31 +250,68 @@ class ParserPacketHandler extends PacketHandler{ } public function handleStartGame(StartGamePacket $packet) : bool{ - $this->itemTypeDictionary = new ItemTypeDictionary($packet->itemTable); - - echo "updating legacy item ID mapping table\n"; - $table = []; - foreach($packet->itemTable as $entry){ - $table[$entry->getStringId()] = [ - "runtime_id" => $entry->getNumericId(), - "component_based" => $entry->isComponentBased() - ]; - } - ksort($table, SORT_STRING); - file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n"); - foreach(Utils::promoteKeys($packet->levelSettings->experiments->getExperiments()) as $name => $experiment){ echo "Experiment \"$name\" is " . ($experiment ? "" : "not ") . "active\n"; } return true; } + public function handleItemRegistry(ItemRegistryPacket $packet) : bool{ + $this->itemTypeDictionary = new ItemTypeDictionary($packet->getEntries()); + + echo "updating legacy item ID mapping table\n"; + $emptyNBT = new CompoundTag(); + $table = []; + foreach($packet->getEntries() as $entry){ + $table[$entry->getStringId()] = [ + "runtime_id" => $entry->getNumericId(), + "component_based" => $entry->isComponentBased(), + "version" => $entry->getVersion(), + ]; + + $componentNBT = $entry->getComponentNbt()->getRoot(); + if(!$componentNBT->equals($emptyNBT)){ + $table[$entry->getStringId()]["component_nbt"] = base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($componentNBT))); + } + } + ksort($table, SORT_STRING); + file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n"); + + echo "updating item registry\n"; + $items = array_map(function(ItemTypeEntry $entry) : array{ + return self::objectToOrderedArray($entry); + }, $packet->getEntries()); + file_put_contents($this->bedrockDataPath . '/item_registry.json', json_encode($items, JSON_PRETTY_PRINT) . "\n"); + return true; + } + public function handleCreativeContent(CreativeContentPacket $packet) : bool{ echo "updating creative inventory data\n"; - $items = array_map(function(CreativeContentEntry $entry) : array{ - return self::objectToOrderedArray($this->itemStackToJson($entry->getItem())); - }, $packet->getEntries()); - file_put_contents($this->bedrockDataPath . '/creativeitems.json', json_encode($items, JSON_PRETTY_PRINT) . "\n"); + + $groupItems = []; + foreach($packet->getItems() as $itemEntry){ + $groupItems[$itemEntry->getGroupId()][] = $this->itemStackToJson($itemEntry->getItem()); + } + + static $typeMap = [ + CreativeContentPacket::CATEGORY_CONSTRUCTION => "construction", + CreativeContentPacket::CATEGORY_NATURE => "nature", + CreativeContentPacket::CATEGORY_EQUIPMENT => "equipment", + CreativeContentPacket::CATEGORY_ITEMS => "items", + ]; + + $groupCategories = []; + foreach(Utils::promoteKeys($packet->getGroups()) as $groupId => $group){ + $category = $typeMap[$group->getCategoryId()] ?? throw new PacketHandlingException("Unknown creative category ID " . $group->getCategoryId()); + //FIXME: objectToOrderedArray might mess with the order of groupItems + //this isn't a problem right now because it's a list, but could cause problems in the future + $groupCategories[$category][] = self::objectToOrderedArray($this->creativeGroupEntryToJson($group, $groupItems[$groupId])); + } + + foreach(Utils::promoteKeys($groupCategories) as $category => $categoryGroups){ + file_put_contents($this->bedrockDataPath . '/creative/' . $category . '.json', json_encode($categoryGroups, JSON_PRETTY_PRINT) . "\n"); + } + return true; } From 03e4b53ac46e020ed93723e3ee2bf2b673c93d9a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Feb 2025 20:57:16 +0000 Subject: [PATCH 247/290] BedrockDataFiles: added constants for folders as well as files we probably should have it recurse too, but this is an easy win. --- build/generate-bedrockdata-path-consts.php | 4 +++- src/Server.php | 3 ++- src/data/bedrock/BedrockDataFiles.php | 3 +++ src/inventory/CreativeInventory.php | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/generate-bedrockdata-path-consts.php b/build/generate-bedrockdata-path-consts.php index 6ad6d83fd..f74137dd2 100644 --- a/build/generate-bedrockdata-path-consts.php +++ b/build/generate-bedrockdata-path-consts.php @@ -28,6 +28,7 @@ use function dirname; use function fclose; use function fopen; use function fwrite; +use function is_dir; use function is_file; use function scandir; use function str_replace; @@ -59,7 +60,7 @@ foreach($files as $file){ continue; } $path = Path::join(BEDROCK_DATA_PATH, $file); - if(!is_file($path)){ + if(!is_file($path) && !is_dir($path)){ continue; } @@ -67,6 +68,7 @@ foreach($files as $file){ 'README.md', 'LICENSE', 'composer.json', + '.github' ] as $ignored){ if($file === $ignored){ continue 2; diff --git a/src/Server.php b/src/Server.php index 5a72ad048..252964a91 100644 --- a/src/Server.php +++ b/src/Server.php @@ -36,6 +36,7 @@ use pocketmine\crafting\CraftingManager; use pocketmine\crafting\CraftingManagerFromDataHelper; use pocketmine\crash\CrashDump; use pocketmine\crash\CrashDumpRenderer; +use pocketmine\data\bedrock\BedrockDataFiles; use pocketmine\entity\EntityDataHelper; use pocketmine\entity\Location; use pocketmine\event\HandlerListManager; @@ -1005,7 +1006,7 @@ class Server{ $this->commandMap = new SimpleCommandMap($this); - $this->craftingManager = CraftingManagerFromDataHelper::make(Path::join(\pocketmine\BEDROCK_DATA_PATH, "recipes")); + $this->craftingManager = CraftingManagerFromDataHelper::make(BedrockDataFiles::RECIPES); $this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger); diff --git a/src/data/bedrock/BedrockDataFiles.php b/src/data/bedrock/BedrockDataFiles.php index 6176eee34..1ecb707cc 100644 --- a/src/data/bedrock/BedrockDataFiles.php +++ b/src/data/bedrock/BedrockDataFiles.php @@ -39,13 +39,16 @@ final class BedrockDataFiles{ public const BLOCK_STATE_META_MAP_JSON = BEDROCK_DATA_PATH . '/block_state_meta_map.json'; public const CANONICAL_BLOCK_STATES_NBT = BEDROCK_DATA_PATH . '/canonical_block_states.nbt'; public const COMMAND_ARG_TYPES_JSON = BEDROCK_DATA_PATH . '/command_arg_types.json'; + public const CREATIVE = BEDROCK_DATA_PATH . '/creative'; public const ENTITY_ID_MAP_JSON = BEDROCK_DATA_PATH . '/entity_id_map.json'; public const ENTITY_IDENTIFIERS_NBT = BEDROCK_DATA_PATH . '/entity_identifiers.nbt'; + public const ENUMS = BEDROCK_DATA_PATH . '/enums'; public const ENUMS_PY = BEDROCK_DATA_PATH . '/enums.py'; public const ITEM_TAGS_JSON = BEDROCK_DATA_PATH . '/item_tags.json'; public const LEVEL_SOUND_ID_MAP_JSON = BEDROCK_DATA_PATH . '/level_sound_id_map.json'; public const PROTOCOL_INFO_JSON = BEDROCK_DATA_PATH . '/protocol_info.json'; public const R12_TO_CURRENT_BLOCK_MAP_BIN = BEDROCK_DATA_PATH . '/r12_to_current_block_map.bin'; public const R16_TO_CURRENT_ITEM_MAP_JSON = BEDROCK_DATA_PATH . '/r16_to_current_item_map.json'; + public const RECIPES = BEDROCK_DATA_PATH . '/recipes'; public const REQUIRED_ITEM_LIST_JSON = BEDROCK_DATA_PATH . '/required_item_list.json'; } diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 77eda8138..ee27068c7 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\crafting\CraftingManagerFromDataHelper; +use pocketmine\data\bedrock\BedrockDataFiles; use pocketmine\inventory\json\CreativeGroupData; use pocketmine\item\Item; use pocketmine\lang\Translatable; @@ -57,7 +58,7 @@ final class CreativeInventory{ "items" => CreativeCategory::ITEMS, ] as $categoryId => $categoryEnum){ $groups = CraftingManagerFromDataHelper::loadJsonArrayOfObjectsFile( - Path::join(\pocketmine\BEDROCK_DATA_PATH, "creative", $categoryId . ".json"), + Path::join(BedrockDataFiles::CREATIVE, $categoryId . ".json"), CreativeGroupData::class ); From 246c1776dfb96bf49194f4fad487b84ed2e40bf4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Feb 2025 21:47:35 +0000 Subject: [PATCH 248/290] InventoryAction: avoid throwaway Item clones --- src/inventory/transaction/InventoryTransaction.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/inventory/transaction/InventoryTransaction.php b/src/inventory/transaction/InventoryTransaction.php index 8f7b57610..6e010c7b8 100644 --- a/src/inventory/transaction/InventoryTransaction.php +++ b/src/inventory/transaction/InventoryTransaction.php @@ -144,8 +144,9 @@ class InventoryTransaction{ $needItems = []; $haveItems = []; foreach($this->actions as $key => $action){ - if(!$action->getTargetItem()->isNull()){ - $needItems[] = $action->getTargetItem(); + $targetItem = $action->getTargetItem(); + if(!$targetItem->isNull()){ + $needItems[] = $targetItem; } try{ @@ -154,8 +155,9 @@ class InventoryTransaction{ throw new TransactionValidationException(get_class($action) . "#" . spl_object_id($action) . ": " . $e->getMessage(), 0, $e); } - if(!$action->getSourceItem()->isNull()){ - $haveItems[] = $action->getSourceItem(); + $sourceItem = $action->getSourceItem(); + if(!$sourceItem->isNull()){ + $haveItems[] = $sourceItem; } } From d96ef21c4d30ea60e76de7b9188fa0829e14137d Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 16 Feb 2025 22:16:47 +0000 Subject: [PATCH 249/290] Prepare 5.25.0 release (#6631) --- changelogs/5.25.md | 38 ++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.25.md diff --git a/changelogs/5.25.md b/changelogs/5.25.md new file mode 100644 index 000000000..cd3fb9365 --- /dev/null +++ b/changelogs/5.25.md @@ -0,0 +1,38 @@ +# 5.25.0 +Released 16th February 2025. + +This is a support release for Minecraft: Bedrock Edition 1.21.60. It also includes some minor API additions supporting new features in this version. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added support for Minecraft: Bedrock Edition 1.21.60. +- Removed support for earlier versions. + +## Documentation +- Fixed the documentation of `Utils::getOS()`. It now refers to the `Utils::OS_*` constants instead of a list of hardcoded strings. + +## API +### `pocketmine\inventory` +This release allows plugins to decide which creative tab they want to add their new items to. +It also allows creating new collapsible groups of items, and modifying or removing existing ones. + +- The following new methods have been added: + - `public CreativeInventory->getAllEntries() : list` - returns an array of objects, each containing a creative item and information about its category and collapsible group (if applicable). + - `public CreativeInventory->getEntry(int $index) : ?CreativeInventoryEntry` - returns the creative item with the specified identifier, or `null` if not found +- The following methods have signature changes: + - `CreativeInventory->add()` now accepts two additional optional parameters: `CreativeCategory $category, ?CreativeGroup $group`. If not specified, the item will be added to the Items tab without a group. +- The following new classes have been added: + - `CreativeCategory` - enum of possible creative inventory categories (each has its own tab on the GUI) + - `CreativeGroup` - contains information about a collapsible group of creative items, including tooltip text and icon + - `CreativeInventoryEntry` - contains information about a creative inventory item, including its category and collapsible group (if applicable) + +## Internals +- `CreativeContentPacket` is no longer fully cached due to the requirement for translation context during construction. The individual items are still cached (which is the most expensive part), but the packet itself is now constructed on demand, and group entries are constructed on the fly. This may affect performance, but this has not been investigated. +- `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files. +- The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category. +- New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index aaa357e0f..f5aa59c4f 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.24.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.25.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 34f801ee3c6d8c99dfd928b19fc58597b5b997ea Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2025 22:18:20 +0000 Subject: [PATCH 250/290] 5.25.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13359320328 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index f5aa59c4f..f8b5d7c74 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.25.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.25.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 788ee9a008e7561a112764efdea63dc9cf711fea Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Feb 2025 22:49:40 +0000 Subject: [PATCH 251/290] Allow overriding deserializers for block and item IDs there's no technical reason not to support this, since it doesn't violate any assumptions and the type returned is a base anyway. this enables implementing stuff like snow cauldrons in a plugin, which previously would require reflection due to the minecraft:cauldron deserializer being registered already. it also enables overriding IDs to map to custom blocks, which might be useful for overriding some functionality (although this is inadvisable - and won't alter the usage of stuff like VanillaBlocks::WHATEVER()). we do *not* allow overriding serializers, since type IDs are expected to be paired to block implementations, and allowing them to be reassigned could lead to crashes if the new class was incorrect. So the correct approach for overriding nether portals would be to create a custom type ID as if you were adding a fully custom item. This will also allow other plugins to distinguish between your implementation and the built-in one. --- .../convert/BlockStateToObjectDeserializer.php | 14 +++++++++++--- src/data/bedrock/item/ItemDeserializer.php | 13 ++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index cb9a6e7ae..42d8ee0cd 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -103,10 +103,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ /** @phpstan-param \Closure(Reader) : Block $c */ public function map(string $id, \Closure $c) : void{ - if(array_key_exists($id, $this->deserializeFuncs)){ - throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\""); - } $this->deserializeFuncs[$id] = $c; + $this->simpleCache = []; + } + + /** + * Returns the existing data deserializer for the given ID, or null if none exists. + * This may be useful if you need to override a deserializer, but still want to be able to fall back to the original. + * + * @phpstan-return ?\Closure(Reader) : Block + */ + public function getDeserializerForId(string $id) : ?\Closure{ + return $this->deserializeFuncs[$id] ?? null; } /** @phpstan-param \Closure() : Block $getBlock */ diff --git a/src/data/bedrock/item/ItemDeserializer.php b/src/data/bedrock/item/ItemDeserializer.php index f7854313f..da96f4ffe 100644 --- a/src/data/bedrock/item/ItemDeserializer.php +++ b/src/data/bedrock/item/ItemDeserializer.php @@ -51,12 +51,19 @@ final class ItemDeserializer{ * @phpstan-param \Closure(Data) : Item $deserializer */ public function map(string $id, \Closure $deserializer) : void{ - if(isset($this->deserializers[$id])){ - throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\""); - } $this->deserializers[$id] = $deserializer; } + /** + * Returns the existing data deserializer for the given ID, or null if none exists. + * This may be useful if you need to override a deserializer, but still want to be able to fall back to the original. + * + * @phpstan-return \Closure(Data) : Item + */ + public function getDeserializerForId(string $id) : ?\Closure{ + return $this->deserializers[$id] ?? null; + } + /** * @phpstan-param \Closure(Data) : Block $deserializer */ From d2d6a59c727967738a8b3a372d7dcc23bba69910 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Feb 2025 22:52:11 +0000 Subject: [PATCH 252/290] ItemDeserializer: fix doc comment typo --- src/data/bedrock/item/ItemDeserializer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/bedrock/item/ItemDeserializer.php b/src/data/bedrock/item/ItemDeserializer.php index da96f4ffe..ef3bee2a0 100644 --- a/src/data/bedrock/item/ItemDeserializer.php +++ b/src/data/bedrock/item/ItemDeserializer.php @@ -58,7 +58,7 @@ final class ItemDeserializer{ * Returns the existing data deserializer for the given ID, or null if none exists. * This may be useful if you need to override a deserializer, but still want to be able to fall back to the original. * - * @phpstan-return \Closure(Data) : Item + * @phpstan-return ?\Closure(Data) : Item */ public function getDeserializerForId(string $id) : ?\Closure{ return $this->deserializers[$id] ?? null; From 51cf6817b13708303e585a398b33dbf631065cb4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 16 Feb 2025 23:24:39 +0000 Subject: [PATCH 253/290] World: fixed overflow checks for getCollisionBlocks(), closes #6630 --- src/world/World.php | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 86ca44848..3a7d0c538 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1534,29 +1534,44 @@ class World implements ChunkManager{ $collisionInfo = $this->blockStateRegistry->collisionInfo; if($targetFirst){ for($z = $minZ; $z <= $maxZ; ++$z){ + $zOverflow = $z === $minZ || $z === $maxZ; for($x = $minX; $x <= $maxX; ++$x){ + $zxOverflow = $zOverflow || $x === $minX || $x === $maxX; for($y = $minY; $y <= $maxY; ++$y){ + $overflow = $zxOverflow || $y === $minY || $y === $maxY; + $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); - if(match($stateCollisionInfo){ - RuntimeBlockStateRegistry::COLLISION_CUBE => true, - RuntimeBlockStateRegistry::COLLISION_NONE => false, - default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) - }){ + if($overflow ? + $stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW && $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) : + match ($stateCollisionInfo) { + RuntimeBlockStateRegistry::COLLISION_CUBE => true, + RuntimeBlockStateRegistry::COLLISION_NONE => false, + default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) + } + ){ return [$this->getBlockAt($x, $y, $z)]; } } } } }else{ + //TODO: duplicated code :( this way is better for performance though for($z = $minZ; $z <= $maxZ; ++$z){ + $zOverflow = $z === $minZ || $z === $maxZ; for($x = $minX; $x <= $maxX; ++$x){ + $zxOverflow = $zOverflow || $x === $minX || $x === $maxX; for($y = $minY; $y <= $maxY; ++$y){ + $overflow = $zxOverflow || $y === $minY || $y === $maxY; + $stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo); - if(match($stateCollisionInfo){ - RuntimeBlockStateRegistry::COLLISION_CUBE => true, - RuntimeBlockStateRegistry::COLLISION_NONE => false, - default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) - }){ + if($overflow ? + $stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW && $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) : + match ($stateCollisionInfo) { + RuntimeBlockStateRegistry::COLLISION_CUBE => true, + RuntimeBlockStateRegistry::COLLISION_NONE => false, + default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) + } + ){ $collides[] = $this->getBlockAt($x, $y, $z); } } From 9744bd70733aa2fd7181242d9baadde0de0ec686 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 17 Feb 2025 15:35:18 +0000 Subject: [PATCH 254/290] CraftingManager: make a less dumb hash function fixes #4415 --- src/crafting/CraftingManager.php | 48 +++++++++++++------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php index 895eeaccc..93d6e1838 100644 --- a/src/crafting/CraftingManager.php +++ b/src/crafting/CraftingManager.php @@ -29,8 +29,12 @@ use pocketmine\nbt\TreeRoot; use pocketmine\utils\BinaryStream; use pocketmine\utils\DestructorCallbackTrait; use pocketmine\utils\ObjectSet; +use function array_shift; +use function count; +use function implode; +use function ksort; use function spl_object_id; -use function usort; +use const SORT_STRING; class CraftingManager{ use DestructorCallbackTrait; @@ -100,6 +104,7 @@ class CraftingManager{ /** * Function used to arrange Shapeless Recipe ingredient lists into a consistent order. + * @deprecated */ public static function sort(Item $i1, Item $i2) : int{ //Use spaceship operator to compare each property, then try the next one if they are equivalent. @@ -108,45 +113,30 @@ class CraftingManager{ return $retval; } - /** - * @param Item[] $items - * - * @return Item[] - * @phpstan-return list - */ - private static function pack(array $items) : array{ - $result = []; + private static function hashOutput(Item $output) : string{ + $write = new BinaryStream(); + $write->putVarInt($output->getStateId()); + $write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag()))); - foreach($items as $item){ - foreach($result as $otherItem){ - if($item->canStackWith($otherItem)){ - $otherItem->setCount($otherItem->getCount() + $item->getCount()); - continue 2; - } - } - - //No matching item found - $result[] = clone $item; - } - - return $result; + return $write->getBuffer(); } /** * @param Item[] $outputs */ private static function hashOutputs(array $outputs) : string{ - $outputs = self::pack($outputs); - usort($outputs, [self::class, "sort"]); - $result = new BinaryStream(); + if(count($outputs) === 1){ + return self::hashOutput(array_shift($outputs)); + } + $unique = []; foreach($outputs as $o){ //count is not written because the outputs might be from multiple repetitions of a single recipe //this reduces the accuracy of the hash, but it won't matter in most cases. - $result->putVarInt($o->getStateId()); - $result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag()))); + $hash = self::hashOutput($o); + $unique[$hash] = $hash; } - - return $result->getBuffer(); + ksort($unique, SORT_STRING); + return implode("", $unique); } /** From 77be5f8e25cb0a9f0fb7fcd79183ff63e65d4e05 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 17 Feb 2025 17:51:39 +0000 Subject: [PATCH 255/290] Update PHPStan --- composer.json | 2 +- composer.lock | 36 +++++++++++----------- src/Server.php | 2 +- src/utils/Utils.php | 2 +- tests/phpstan/configs/actual-problems.neon | 32 +------------------ tests/phpstan/configs/phpstan-bugs.neon | 6 ---- 6 files changed, 22 insertions(+), 58 deletions(-) diff --git a/composer.json b/composer.json index f8d060bcd..ac35fef77 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.4", + "phpstan/phpstan": "2.1.5", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index cc7f1f716..e5c60aa7d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "af7547291a131bfac6d7087957601325", + "content-hash": "dcf1176890f95cb24670e69c8c8f1216", "packages": [ { "name": "adhocore/json-comment", @@ -1150,16 +1150,16 @@ "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -1198,7 +1198,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -1206,7 +1206,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", @@ -1386,16 +1386,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.4", + "version": "2.1.5", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a" + "reference": "451b17f9665481ee502adc39be987cb71067ece2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f99e18eb775dbaf6460c95fa0b65312da9c746a", - "reference": "8f99e18eb775dbaf6460c95fa0b65312da9c746a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/451b17f9665481ee502adc39be987cb71067ece2", + "reference": "451b17f9665481ee502adc39be987cb71067ece2", "shasum": "" }, "require": { @@ -1440,7 +1440,7 @@ "type": "github" } ], - "time": "2025-02-10T08:25:21+00:00" + "time": "2025-02-13T12:49:56+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1864,16 +1864,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.44", + "version": "10.5.45", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36" + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1381c62769be4bb88fa4c5aec1366c7c66ca4f36", - "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", "shasum": "" }, "require": { @@ -1945,7 +1945,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.44" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" }, "funding": [ { @@ -1961,7 +1961,7 @@ "type": "tidelift" } ], - "time": "2025-01-31T07:00:38+00:00" + "time": "2025-02-06T16:08:12+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/Server.php b/src/Server.php index 252964a91..679a0ef0b 100644 --- a/src/Server.php +++ b/src/Server.php @@ -699,7 +699,7 @@ class Server{ public function removeOp(string $name) : void{ $lowercaseName = strtolower($name); - foreach($this->operators->getAll() as $operatorName => $_){ + foreach(Utils::promoteKeys($this->operators->getAll()) as $operatorName => $_){ $operatorName = (string) $operatorName; if($lowercaseName === strtolower($operatorName)){ $this->operators->remove($operatorName); diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 46cd49ec4..2a9dd65da 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -587,7 +587,7 @@ final class Utils{ * @phpstan-param \Closure(TMemberType) : void $validator */ public static function validateArrayValueType(array $array, \Closure $validator) : void{ - foreach($array as $k => $v){ + foreach(Utils::promoteKeys($array) as $k => $v){ try{ $validator($v); }catch(\TypeError $e){ diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index ef9828b37..73b7a65c1 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -48,12 +48,6 @@ parameters: count: 1 path: ../../../src/VersionInfo.php - - - message: '#^Method pocketmine\\VersionInfo\:\:GIT_HASH\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: ../../../src/VersionInfo.php - - message: '#^Static property pocketmine\\VersionInfo\:\:\$gitHash \(string\|null\) does not accept mixed\.$#' identifier: assign.propertyType @@ -918,24 +912,6 @@ parameters: count: 1 path: ../../../src/plugin/PluginDescription.php - - - message: '#^Parameter \#1 \$haystack of function stripos expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/plugin/PluginDescription.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/plugin/PluginDescription.php - - - - message: '#^Parameter \#3 \$subject of function str_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: ../../../src/plugin/PluginDescription.php - - message: '#^Property pocketmine\\plugin\\PluginDescription\:\:\$authors \(array\\) does not accept list\\.$#' identifier: assign.propertyType @@ -966,12 +942,6 @@ parameters: count: 1 path: ../../../src/resourcepacks/ZippedResourcePack.php - - - message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getSha256\(\) should return string but returns string\|false\.$#' - identifier: return.type - count: 1 - path: ../../../src/resourcepacks/ZippedResourcePack.php - - message: '#^Property pocketmine\\resourcepacks\\ZippedResourcePack\:\:\$fileResource \(resource\) does not accept resource\|false\.$#' identifier: assign.propertyType @@ -1009,7 +979,7 @@ parameters: path: ../../../src/utils/Config.php - - message: '#^Parameter \#1 \$config of static method pocketmine\\utils\\Config\:\:writeProperties\(\) expects array\, array\ given\.$#' + message: '#^Parameter \#1 \$config of static method pocketmine\\utils\\Config\:\:writeProperties\(\) expects array\, array\ given\.$#' identifier: argument.type count: 1 path: ../../../src/utils/Config.php diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index 9ab125763..aeb3fae29 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -114,12 +114,6 @@ parameters: 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 - path: ../../../src/entity/projectile/Projectile.php - - message: '#^Property pocketmine\\item\\WritableBookBase\:\:\$pages \(list\\) does not accept non\-empty\-array\\.$#' identifier: assign.propertyType From c876253f76104c56947f82aecd342951a8848a76 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 18 Feb 2025 15:30:39 +0000 Subject: [PATCH 256/290] tools/blockstate-upgrade-schema-utils: added dump-table command this command dumps a human-readable version of pmmp/mapping palette mapping files to a .txt file. may be useful for debugging issues with the schema generator or the upgrade process. --- tools/blockstate-upgrade-schema-utils.php | 42 ++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index 4f5c8740c..6bfe44a03 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -51,8 +51,10 @@ use function array_unique; use function array_values; use function count; use function dirname; +use function fclose; use function file_exists; use function file_put_contents; +use function fopen; use function fwrite; use function get_class; use function get_debug_type; @@ -885,6 +887,43 @@ function cmdUpdateAll(array $argv) : int{ return 0; } +/** + * @param string[] $argv + */ +function cmdDumpTable(array $argv) : int{ + $tableFile = $argv[2]; + $outputFile = $argv[3]; + + $output = fopen($outputFile, 'wb'); + if($output === false){ + fwrite(STDERR, "Failed to open output file: $outputFile\n"); + return 1; + } + + $table = loadUpgradeTableFromFile($tableFile, reverse: false); + + foreach(Utils::stringifyKeys($table) as $oldName => $mappings){ + fwrite($output, "---------- MAPPING LIST: $oldName ----------\n"); + foreach($mappings as $mapping){ + $oldNbt = $mapping->old->toVanillaNbt(); + $oldNbt->setInt("version", $mapping->new->getVersion()); + + //intentionally not reused result of toVanillaNbt otherwise output wouldn't include version + fwrite($output, "OLD: " . $mapping->old->toVanillaNbt() . "\n"); + if(!$oldNbt->equals($mapping->new->toVanillaNbt())){ + fwrite($output, "NEW: " . $mapping->new->toVanillaNbt() . "\n"); + }else{ + fwrite($output, "NEW: version bump only (" . $mapping->new->getVersion() . ")\n"); + } + fwrite($output, "-----\n"); + } + } + + fclose($output); + \GlobalLogger::get()->info("Table dump file $outputFile generated successfully."); + return 0; +} + /** * @param string[] $argv */ @@ -893,7 +932,8 @@ function main(array $argv) : int{ "generate" => [["palette upgrade table file", "schema output file"], cmdGenerate(...)], "test" => [["palette upgrade table file", "schema output file"], cmdTest(...)], "update" => [["schema input file", "old palette file", "updated schema output file"], cmdUpdate(...)], - "update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)] + "update-all" => [["schema folder", "path to BlockPaletteArchive"], cmdUpdateAll(...)], + "dump-table" => [["palette upgrade table file", "txt output file"], cmdDumpTable(...)] ]; $selected = $argv[1] ?? null; From a08b06d3224215e001b54ff30f3017bfba0b1b81 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 18 Feb 2025 15:34:20 +0000 Subject: [PATCH 257/290] also sort table by ID --- tools/blockstate-upgrade-schema-utils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/blockstate-upgrade-schema-utils.php b/tools/blockstate-upgrade-schema-utils.php index 6bfe44a03..7c34b7728 100644 --- a/tools/blockstate-upgrade-schema-utils.php +++ b/tools/blockstate-upgrade-schema-utils.php @@ -901,6 +901,7 @@ function cmdDumpTable(array $argv) : int{ } $table = loadUpgradeTableFromFile($tableFile, reverse: false); + ksort($table, SORT_STRING); foreach(Utils::stringifyKeys($table) as $oldName => $mappings){ fwrite($output, "---------- MAPPING LIST: $oldName ----------\n"); From 3050af0bc0c75e7d13ca0ed3c502c7c846ba2cd7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 23 Feb 2025 19:45:38 +0000 Subject: [PATCH 258/290] ResourcePackManager: validate pack UUIDs fixes CrashArchive ##12248760 --- src/resourcepacks/ResourcePackManager.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index c4668eb2a..ad4417769 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -26,6 +26,7 @@ namespace pocketmine\resourcepacks; use pocketmine\utils\Config; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; +use Ramsey\Uuid\Uuid; use Symfony\Component\Filesystem\Path; use function array_keys; use function copy; @@ -103,9 +104,14 @@ class ResourcePackManager{ try{ $newPack = $this->loadPackFromPath(Path::join($this->path, $pack)); - $this->resourcePacks[] = $newPack; $index = strtolower($newPack->getPackId()); + if(!Uuid::isValid($index)){ + //TODO: we should use Uuid in ResourcePack interface directly but that would break BC + //for now we need to validate this here to make sure it doesn't cause crashes later on + throw new ResourcePackException("Invalid UUID ($index)"); + } $this->uuidList[$index] = $newPack; + $this->resourcePacks[] = $newPack; $keyPath = Path::join($this->path, $pack . ".key"); if(file_exists($keyPath)){ @@ -190,6 +196,11 @@ class ResourcePackManager{ $resourcePacks = []; foreach($resourceStack as $pack){ $uuid = strtolower($pack->getPackId()); + if(!Uuid::isValid($uuid)){ + //TODO: we should use Uuid in ResourcePack interface directly but that would break BC + //for now we need to validate this here to make sure it doesn't cause crashes later on + throw new \InvalidArgumentException("Invalid resource pack UUID ($uuid)"); + } if(isset($uuidList[$uuid])){ throw new \InvalidArgumentException("Cannot load two resource pack with the same UUID ($uuid)"); } From 1fed9f6cb560fe3069c157736ff1c25145aef403 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 23 Feb 2025 20:02:27 +0000 Subject: [PATCH 259/290] BlockBreakInfo: fixed confusing error message --- src/block/BlockBreakInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/BlockBreakInfo.php b/src/block/BlockBreakInfo.php index 8cfa425dc..e77e06cfd 100644 --- a/src/block/BlockBreakInfo.php +++ b/src/block/BlockBreakInfo.php @@ -154,7 +154,7 @@ class BlockBreakInfo{ $efficiency = $item->getMiningEfficiency(($this->toolType & $item->getBlockToolType()) !== 0); if($efficiency <= 0){ - throw new \InvalidArgumentException(get_class($item) . " has invalid mining efficiency: expected >= 0, got $efficiency"); + throw new \InvalidArgumentException(get_class($item) . " must have a positive mining efficiency, but got $efficiency"); } $base /= $efficiency; From 3df2bdb87979aa699d17e5b82ec2368e123a1411 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 24 Feb 2025 01:04:05 +0000 Subject: [PATCH 260/290] Fixed door facing this was broken in 1.21.60 update. should've known better to expect a blockstate upgrade to mean a blockstate fix... --- .../bedrock/block/convert/BlockStateDeserializerHelper.php | 3 ++- src/data/bedrock/block/convert/BlockStateSerializerHelper.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 342f31490..1d7b4bb76 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -131,7 +131,8 @@ final class BlockStateDeserializerHelper{ //TODO: check if these need any special treatment to get the appropriate data to both halves of the door return $block ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)) - ->setFacing($in->readCardinalHorizontalFacing()) + //a door facing "east" is actually facing north - thanks mojang + ->setFacing(Facing::rotateY($in->readCardinalHorizontalFacing(), clockwise: false)) ->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index fa0591669..a25044153 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -100,7 +100,8 @@ final class BlockStateSerializerHelper{ public static function encodeDoor(Door $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) - ->writeCardinalHorizontalFacing($block->getFacing()) + //a door facing north is encoded as "east" + ->writeCardinalHorizontalFacing(Facing::rotateY($block->getFacing(), clockwise: true)) ->writeBool(BlockStateNames::DOOR_HINGE_BIT, $block->isHingeRight()) ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); } From 7c654271a85f9fad3ea549357f66d13c176d6f6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:40:11 +0000 Subject: [PATCH 261/290] Bump phpstan/phpstan in the development-patch-updates group (#6640) --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index ac35fef77..10454c560 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.5", + "phpstan/phpstan": "2.1.6", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index e5c60aa7d..dc49252d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dcf1176890f95cb24670e69c8c8f1216", + "content-hash": "bef9decc40d9f5bd82e1de2d151bd99f", "packages": [ { "name": "adhocore/json-comment", @@ -1386,16 +1386,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.5", + "version": "2.1.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "451b17f9665481ee502adc39be987cb71067ece2" + "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/451b17f9665481ee502adc39be987cb71067ece2", - "reference": "451b17f9665481ee502adc39be987cb71067ece2", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", "shasum": "" }, "require": { @@ -1440,7 +1440,7 @@ "type": "github" } ], - "time": "2025-02-13T12:49:56+00:00" + "time": "2025-02-19T15:46:42+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From 706f391068539340bbc01222e5e115a52786fd10 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 26 Feb 2025 17:13:44 +0000 Subject: [PATCH 262/290] Release 5.25.1 (#6641) --- changelogs/5.25.md | 8 ++++++++ src/VersionInfo.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changelogs/5.25.md b/changelogs/5.25.md index cd3fb9365..5fdc013a6 100644 --- a/changelogs/5.25.md +++ b/changelogs/5.25.md @@ -36,3 +36,11 @@ It also allows creating new collapsible groups of items, and modifying or removi - `BedrockDataFiles` now includes constants for folders at the top level of `BedrockData` as well as files. - The structure of creative data in `BedrockData` was changed to accommodate item category and grouping information. `creativeitems.json` has been replaced by `creative/*.json`, which contain information about item grouping and also segregates item lists per category. - New information was added to `required_item_list.json` in `BedrockData`, as the server is now required to send item component NBT data in some cases. + +# 5.25.1 +Released 26th February 2025. + +## Fixes +- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero. +- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.) +- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index f8b5d7c74..e206b09f6 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.25.1"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 092ea07d5134dbcfd282f5a534eb9e4f4392d605 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 17:14:49 +0000 Subject: [PATCH 263/290] 5.25.2 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13549549222 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index e206b09f6..712ad6e3b 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.25.1"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.25.2"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 32b98dcbdea3142ab48db3f3b1868517a8262064 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 26 Feb 2025 17:23:27 +0000 Subject: [PATCH 264/290] draft-release: add a warning about bug reporting too many people just spam on discord and expect that to somehow do something ... --- .github/workflows/draft-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 8a35e2904..db7aa3b23 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -182,6 +182,8 @@ jobs: :information_source: Download the recommended PHP binary [here](${{ steps.php-binary-url.outputs.PHP_BINARY_URL }}). + :warning: Found a bug? Report it on our [issue tracker](${{ github.server_url }}/${{ github.repository }}/issues). **We can't fix bugs if you don't report them.** + - name: Post draft release URL on PR if: github.event_name == 'pull_request_target' uses: thollander/actions-comment-pull-request@v3 From 3a2d0d77d184760dc5dd2b57c874cd3757de0d5e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 26 Feb 2025 17:30:20 +0000 Subject: [PATCH 265/290] Update composer dependencies --- composer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index dc49252d6..e224887d6 100644 --- a/composer.lock +++ b/composer.lock @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", "shasum": "" }, "require": { @@ -85,7 +85,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.2" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-26T10:21:45+00:00" }, { "name": "netresearch/jsonmapper", @@ -2967,5 +2967,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From e2f5e3e73c8abf9d06f62a0c1733ef0ff1d15a1b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 26 Feb 2025 17:31:26 +0000 Subject: [PATCH 266/290] Update composer dependencies --- composer.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 91ce96fa8..6d2981a4c 100644 --- a/composer.lock +++ b/composer.lock @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", "shasum": "" }, "require": { @@ -85,7 +85,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.2" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-26T10:21:45+00:00" }, { "name": "netresearch/jsonmapper", @@ -1150,16 +1150,16 @@ "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -1198,7 +1198,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -1206,7 +1206,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", @@ -1864,16 +1864,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.44", + "version": "10.5.45", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36" + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1381c62769be4bb88fa4c5aec1366c7c66ca4f36", - "reference": "1381c62769be4bb88fa4c5aec1366c7c66ca4f36", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", "shasum": "" }, "require": { @@ -1945,7 +1945,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.44" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" }, "funding": [ { @@ -1961,7 +1961,7 @@ "type": "tidelift" } ], - "time": "2025-01-31T07:00:38+00:00" + "time": "2025-02-06T16:08:12+00:00" }, { "name": "sebastian/cli-parser", From e3e0c14275ad715ee71dc36d8acbe1c5b00ba511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 10:04:01 +0000 Subject: [PATCH 267/290] Bump the github-actions group with 2 updates (#6644) --- .github/workflows/build-docker-image.yml | 8 ++++---- .github/workflows/draft-release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index a0cb1f1d9..6199ad7a9 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -53,7 +53,7 @@ jobs: run: echo NAME=$(echo "${GITHUB_REPOSITORY,,}") >> $GITHUB_OUTPUT - name: Build image for tag - uses: docker/build-push-action@v6.13.0 + uses: docker/build-push-action@v6.15.0 with: push: true context: ./pocketmine-mp @@ -66,7 +66,7 @@ jobs: - name: Build image for major tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.13.0 + uses: docker/build-push-action@v6.15.0 with: push: true context: ./pocketmine-mp @@ -79,7 +79,7 @@ jobs: - name: Build image for minor tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.13.0 + uses: docker/build-push-action@v6.15.0 with: push: true context: ./pocketmine-mp @@ -92,7 +92,7 @@ jobs: - name: Build image for latest tag if: steps.channel.outputs.CHANNEL == 'stable' - uses: docker/build-push-action@v6.13.0 + uses: docker/build-push-action@v6.15.0 with: push: true context: ./pocketmine-mp diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index db7aa3b23..d2e9eb0d0 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -165,7 +165,7 @@ jobs: ${{ github.workspace }}/core-permissions.rst - name: Create draft release - uses: ncipollo/release-action@v1.15.0 + uses: ncipollo/release-action@v1.16.0 id: create-draft with: artifacts: ${{ github.workspace }}/PocketMine-MP.phar,${{ github.workspace }}/start.*,${{ github.workspace }}/build_info.json,${{ github.workspace }}/core-permissions.rst From d0d84d4c5195fb0a68ea7725424fda63b85cd831 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 4 Mar 2025 20:44:01 +0000 Subject: [PATCH 268/290] New rule: explode() limit parameter must be set --- build/dump-version-info.php | 2 +- phpstan.neon.dist | 1 + src/PocketMine.php | 2 +- src/block/tile/Sign.php | 3 +- src/block/utils/SignText.php | 2 +- src/command/Command.php | 3 +- src/command/defaults/HelpCommand.php | 3 +- src/command/defaults/ParticleCommand.php | 6 +- src/console/ConsoleCommandSender.php | 2 +- src/item/LegacyStringToItemParser.php | 3 +- src/lang/Language.php | 2 +- src/network/mcpe/JwtUtils.php | 6 +- src/permission/BanEntry.php | 4 +- src/utils/Config.php | 9 +- src/utils/Internet.php | 6 +- src/utils/Utils.php | 2 +- src/world/generator/FlatGeneratorOptions.php | 11 ++- tests/phpstan/rules/ExplodeLimitRule.php | 92 ++++++++++++++++++++ tools/generate-bedrock-data-from-packets.php | 2 +- 19 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 tests/phpstan/rules/ExplodeLimitRule.php diff --git a/build/dump-version-info.php b/build/dump-version-info.php index 166264d98..e13696f3d 100644 --- a/build/dump-version-info.php +++ b/build/dump-version-info.php @@ -36,7 +36,7 @@ require dirname(__DIR__) . '/vendor/autoload.php'; */ $options = [ "base_version" => VersionInfo::BASE_VERSION, - "major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0], + "major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0], "mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK, "is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD, "changelog_file_name" => function() : string{ diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5a816f81c..48bfd4a78 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -13,6 +13,7 @@ rules: - pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule - pocketmine\phpstan\rules\DisallowEnumComparisonRule - pocketmine\phpstan\rules\DisallowForeachByReferenceRule + - pocketmine\phpstan\rules\ExplodeLimitRule - pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule # - pocketmine\phpstan\rules\ThreadedSupportedTypesRule diff --git a/src/PocketMine.php b/src/PocketMine.php index ffcfd91db..a71c9768d 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -264,7 +264,7 @@ JIT_WARNING $composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp'); if($composerGitHash !== null){ //we can't verify dependency versions if we were installed without using git - $currentGitHash = explode("-", VersionInfo::GIT_HASH())[0]; + $currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0]; if($currentGitHash !== $composerGitHash){ critical_error("Composer dependencies and/or autoloader are out of sync."); critical_error("- Current revision is $currentGitHash"); diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 2ced414ff..0bb21a6d3 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -62,9 +62,10 @@ class Sign extends Spawnable{ /** * @return string[] + * @deprecated */ public static function fixTextBlob(string $blob) : array{ - return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4); + return array_slice(array_pad(explode("\n", $blob, limit: 5), 4, ""), 0, 4); } protected SignText $text; diff --git a/src/block/utils/SignText.php b/src/block/utils/SignText.php index a7e8759b8..219899761 100644 --- a/src/block/utils/SignText.php +++ b/src/block/utils/SignText.php @@ -79,7 +79,7 @@ class SignText{ * @throws \InvalidArgumentException if the text is not valid UTF-8 */ public static function fromBlob(string $blob, ?Color $baseColor = null, bool $glowing = false) : SignText{ - return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing); + return new self(array_slice(array_pad(explode("\n", $blob, limit: self::LINE_COUNT + 1), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing); } /** diff --git a/src/command/Command.php b/src/command/Command.php index aea57e6a2..54822d80e 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -37,6 +37,7 @@ use function array_values; use function explode; use function implode; use function str_replace; +use const PHP_INT_MAX; abstract class Command{ @@ -113,7 +114,7 @@ abstract class Command{ } public function setPermission(?string $permission) : void{ - $this->setPermissions($permission === null ? [] : explode(";", $permission)); + $this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX)); } public function testPermission(CommandSender $target, ?string $permission = null) : bool{ diff --git a/src/command/defaults/HelpCommand.php b/src/command/defaults/HelpCommand.php index 487c915f2..054585455 100644 --- a/src/command/defaults/HelpCommand.php +++ b/src/command/defaults/HelpCommand.php @@ -39,6 +39,7 @@ use function ksort; use function min; use function sort; use function strtolower; +use const PHP_INT_MAX; use const SORT_FLAG_CASE; use const SORT_NATURAL; @@ -108,7 +109,7 @@ class HelpCommand extends VanillaCommand{ $usage = $cmd->getUsage(); $usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage; - $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString))) + $sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX))) ->prefix(TextFormat::GOLD)); $aliases = $cmd->getAliases(); diff --git a/src/command/defaults/ParticleCommand.php b/src/command/defaults/ParticleCommand.php index f20d47ccc..4867e3eb5 100644 --- a/src/command/defaults/ParticleCommand.php +++ b/src/command/defaults/ParticleCommand.php @@ -219,7 +219,11 @@ class ParticleCommand extends VanillaCommand{ break; case "blockdust": if($data !== null){ - $d = explode("_", $data); + //to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts + //this allows the 4th argument to be processed normally if given without forcing it to also consume + //any unexpected parts + //we probably ought to error in this case, but this will do for now + $d = explode("_", $data, limit: 5); if(count($d) >= 3){ return new DustParticle(new Color( ((int) $d[0]) & 0xff, diff --git a/src/console/ConsoleCommandSender.php b/src/console/ConsoleCommandSender.php index aa7ea6e69..a0c1c5200 100644 --- a/src/console/ConsoleCommandSender.php +++ b/src/console/ConsoleCommandSender.php @@ -62,7 +62,7 @@ class ConsoleCommandSender implements CommandSender{ $message = $this->getLanguage()->translate($message); } - foreach(explode("\n", trim($message)) as $line){ + foreach(explode("\n", trim($message), limit: PHP_INT_MAX) as $line){ Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line)); } } diff --git a/src/item/LegacyStringToItemParser.php b/src/item/LegacyStringToItemParser.php index 6969190d5..19a6d1f6c 100644 --- a/src/item/LegacyStringToItemParser.php +++ b/src/item/LegacyStringToItemParser.php @@ -111,7 +111,8 @@ final class LegacyStringToItemParser{ */ public function parse(string $input) : Item{ $key = $this->reprocess($input); - $b = explode(":", $key); + //TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given a string like 351:4:1 + $b = explode(":", $key, limit: 3); if(!isset($b[1])){ $meta = 0; diff --git a/src/lang/Language.php b/src/lang/Language.php index 29f28917d..59a309524 100644 --- a/src/lang/Language.php +++ b/src/lang/Language.php @@ -71,7 +71,7 @@ class Language{ foreach($files as $file){ try{ - $code = explode(".", $file)[0]; + $code = explode(".", $file, limit: 2)[0]; $strings = self::loadLang($path, $code); if(isset($strings[KnownTranslationKeys::LANGUAGE_NAME])){ $result[$code] = $strings[KnownTranslationKeys::LANGUAGE_NAME]; diff --git a/src/network/mcpe/JwtUtils.php b/src/network/mcpe/JwtUtils.php index 259a602d4..987ed6e61 100644 --- a/src/network/mcpe/JwtUtils.php +++ b/src/network/mcpe/JwtUtils.php @@ -72,9 +72,11 @@ final class JwtUtils{ * @throws JwtException */ public static function split(string $jwt) : array{ - $v = explode(".", $jwt); + //limit of 4 allows us to detect too many parts without having to split the string up into a potentially large + //number of parts + $v = explode(".", $jwt, limit: 4); if(count($v) !== 3){ - throw new JwtException("Expected exactly 3 JWT parts, got " . count($v)); + throw new JwtException("Expected exactly 3 JWT parts delimited by a period"); } return [$v[0], $v[1], $v[2]]; //workaround phpstan bug } diff --git a/src/permission/BanEntry.php b/src/permission/BanEntry.php index 5f235f1a9..a766a6fcc 100644 --- a/src/permission/BanEntry.php +++ b/src/permission/BanEntry.php @@ -148,7 +148,9 @@ class BanEntry{ return null; } - $parts = explode("|", trim($str)); + //we expect at most 5 parts, but accept 6 in case of an extra unexpected delimiter + //we don't want to include unexpected data into the ban reason + $parts = explode("|", trim($str), limit: 6); $entry = new BanEntry(trim(array_shift($parts))); if(count($parts) > 0){ $entry->setCreated(self::parseDate(array_shift($parts))); diff --git a/src/utils/Config.php b/src/utils/Config.php index 7b6da6389..7d0501935 100644 --- a/src/utils/Config.php +++ b/src/utils/Config.php @@ -54,6 +54,7 @@ use const CASE_LOWER; use const JSON_BIGINT_AS_STRING; use const JSON_PRETTY_PRINT; use const JSON_THROW_ON_ERROR; +use const PHP_INT_MAX; use const YAML_UTF8_ENCODING; /** @@ -339,7 +340,7 @@ class Config{ } public function setNested(string $key, mixed $value) : void{ - $vars = explode(".", $key); + $vars = explode(".", $key, limit: PHP_INT_MAX); $base = array_shift($vars); if(!isset($this->config[$base])){ @@ -366,7 +367,7 @@ class Config{ return $this->nestedCache[$key]; } - $vars = explode(".", $key); + $vars = explode(".", $key, limit: PHP_INT_MAX); $base = array_shift($vars); if(isset($this->config[$base])){ $base = $this->config[$base]; @@ -390,7 +391,7 @@ class Config{ $this->nestedCache = []; $this->changed = true; - $vars = explode(".", $key); + $vars = explode(".", $key, limit: PHP_INT_MAX); $currentNode = &$this->config; while(count($vars) > 0){ @@ -495,7 +496,7 @@ class Config{ */ public static function parseList(string $content) : array{ $result = []; - foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){ + foreach(explode("\n", trim(str_replace("\r\n", "\n", $content)), limit: PHP_INT_MAX) as $v){ $v = trim($v); if($v === ""){ continue; diff --git a/src/utils/Internet.php b/src/utils/Internet.php index 89af2a77e..4b0e00f4a 100644 --- a/src/utils/Internet.php +++ b/src/utils/Internet.php @@ -60,6 +60,7 @@ use const CURLOPT_RETURNTRANSFER; use const CURLOPT_SSL_VERIFYHOST; use const CURLOPT_SSL_VERIFYPEER; use const CURLOPT_TIMEOUT_MS; +use const PHP_INT_MAX; use const SOCK_DGRAM; use const SOL_UDP; @@ -227,9 +228,10 @@ class Internet{ $rawHeaders = substr($raw, 0, $headerSize); $body = substr($raw, $headerSize); $headers = []; - foreach(explode("\r\n\r\n", $rawHeaders) as $rawHeaderGroup){ + //TODO: explore if we can set these limits lower + foreach(explode("\r\n\r\n", $rawHeaders, limit: PHP_INT_MAX) as $rawHeaderGroup){ $headerGroup = []; - foreach(explode("\r\n", $rawHeaderGroup) as $line){ + foreach(explode("\r\n", $rawHeaderGroup, limit: PHP_INT_MAX) as $line){ $nameValue = explode(":", $line, 2); if(isset($nameValue[1])){ $headerGroup[trim(strtolower($nameValue[0]))] = trim($nameValue[1]); diff --git a/src/utils/Utils.php b/src/utils/Utils.php index 2a9dd65da..046296cf4 100644 --- a/src/utils/Utils.php +++ b/src/utils/Utils.php @@ -369,7 +369,7 @@ final class Utils{ debug_zval_dump($value); $contents = ob_get_contents(); if($contents === false) throw new AssumptionFailedError("ob_get_contents() should never return false here"); - $ret = explode("\n", $contents); + $ret = explode("\n", $contents, limit: 2); ob_end_clean(); if(preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){ diff --git a/src/world/generator/FlatGeneratorOptions.php b/src/world/generator/FlatGeneratorOptions.php index 563297b00..8271ebcbf 100644 --- a/src/world/generator/FlatGeneratorOptions.php +++ b/src/world/generator/FlatGeneratorOptions.php @@ -26,10 +26,12 @@ namespace pocketmine\world\generator; use pocketmine\data\bedrock\BiomeIds; use pocketmine\item\LegacyStringToItemParser; use pocketmine\item\LegacyStringToItemParserException; +use pocketmine\world\World; use function array_map; use function explode; use function preg_match; use function preg_match_all; +use const PHP_INT_MAX; /** * @internal @@ -70,7 +72,7 @@ final class FlatGeneratorOptions{ */ public static function parseLayers(string $layers) : array{ $result = []; - $split = array_map('\trim', explode(',', $layers)); + $split = array_map('\trim', explode(',', $layers, limit: World::Y_MAX - World::Y_MIN)); $y = 0; $itemParser = LegacyStringToItemParser::getInstance(); foreach($split as $line){ @@ -96,7 +98,7 @@ final class FlatGeneratorOptions{ * @throws InvalidGeneratorOptionsException */ public static function parsePreset(string $presetString) : self{ - $preset = explode(";", $presetString); + $preset = explode(";", $presetString, limit: 4); $blocks = $preset[1] ?? ""; $biomeId = (int) ($preset[2] ?? BiomeIds::PLAINS); $optionsString = $preset[3] ?? ""; @@ -109,9 +111,10 @@ final class FlatGeneratorOptions{ $params = true; if($matches[3][$i] !== ""){ $params = []; - $p = explode(" ", $matches[3][$i]); + $p = explode(" ", $matches[3][$i], limit: PHP_INT_MAX); foreach($p as $k){ - $k = explode("=", $k); + //TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given e.g. treecount=20=1 + $k = explode("=", $k, limit: 3); if(isset($k[1])){ $params[$k[0]] = $k[1]; } diff --git a/tests/phpstan/rules/ExplodeLimitRule.php b/tests/phpstan/rules/ExplodeLimitRule.php new file mode 100644 index 000000000..4e8a341ad --- /dev/null +++ b/tests/phpstan/rules/ExplodeLimitRule.php @@ -0,0 +1,92 @@ + + */ +final class ExplodeLimitRule implements Rule{ + private ReflectionProvider $reflectionProvider; + + public function __construct( + ReflectionProvider $reflectionProvider + ){ + $this->reflectionProvider = $reflectionProvider; + } + + public function getNodeType() : string{ + return FuncCall::class; + } + + public function processNode(Node $node, Scope $scope) : array{ + if(!$node->name instanceof Name){ + return []; + } + + if(!$this->reflectionProvider->hasFunction($node->name, $scope)){ + return []; + } + + $functionReflection = $this->reflectionProvider->getFunction($node->name, $scope); + + if($functionReflection->getName() !== 'explode'){ + return []; + } + + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + $node->getArgs(), + $functionReflection->getVariants(), + $functionReflection->getNamedArgumentsVariants(), + ); + + $normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node); + + if($normalizedFuncCall === null){ + return []; + } + + $count = count($normalizedFuncCall->getArgs()); + if($count !== 3){ + return [ + RuleErrorBuilder::message('The $limit parameter of explode() must be set to prevent malicious client data wasting resources.') + ->identifier("pocketmine.explode.limit") + ->build() + ]; + } + + return []; + } +} diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index c48a4f017..50639f51d 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -624,7 +624,7 @@ function main(array $argv) : int{ } foreach($packets as $lineNum => $line){ - $parts = explode(':', $line); + $parts = explode(':', $line, limit: 3); if(count($parts) !== 2){ fwrite(STDERR, 'Wrong packet format at line ' . ($lineNum + 1) . ', expected read:base64 or write:base64'); return 1; From 9e9f8a487084b695985302eedb1931d6353434d1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 4 Mar 2025 20:57:47 +0000 Subject: [PATCH 269/290] Prepare 5.25.2 release --- changelogs/5.25.md | 6 ++++++ src/VersionInfo.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelogs/5.25.md b/changelogs/5.25.md index 5fdc013a6..39862cbb7 100644 --- a/changelogs/5.25.md +++ b/changelogs/5.25.md @@ -44,3 +44,9 @@ Released 26th February 2025. - Fixed confusing exception message when a block-breaking tool has an efficiency value of zero. - Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.) - Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join. + +# 5.25.2 +Released 4th March 2025. + +## Fixes +- Added limits to various `explode()` calls. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 712ad6e3b..c966c03cb 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -32,7 +32,7 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; public const BASE_VERSION = "5.25.2"; - public const IS_DEVELOPMENT_BUILD = true; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From 50a1e59aa4a34580f20b1c9eec3f9b6e8ad654a9 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:05:10 +0000 Subject: [PATCH 270/290] 5.25.3 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/13662905668 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index c966c03cb..569e457da 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.25.2"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.25.3"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From aad2bce9e43c44d4e6f32ded0a5a40080caf0016 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 9 Mar 2025 00:23:59 +0000 Subject: [PATCH 271/290] readme: call out Easy tasks issues --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b9e2e1888..6f2b715ab 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ PocketMine-MP accepts community contributions! The following resources will be u * [Building and running PocketMine-MP from source](BUILDING.md) * [Contributing Guidelines](CONTRIBUTING.md) +New here? Check out [issues with the "Easy task" label](https://github.com/pmmp/PocketMine-MP/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Easy%20task%22) for things you could work to familiarise yourself with the codebase. + ## Donate PocketMine-MP is free, but it requires a lot of time and effort from unpaid volunteers to develop. Donations enable us to keep delivering support for new versions and adding features your players love. From 22915466105e9bffd8924365e184e44688f4664a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 9 Mar 2025 00:51:12 +0000 Subject: [PATCH 272/290] phpstan: added rule to ban new $class see #6635 for rationale on why we want to get rid of this for now, this rule will prevent this anti-feature from being used in new code --- phpstan.neon.dist | 1 + tests/phpstan/configs/actual-problems.neon | 30 ++++++++++ .../phpstan/rules/DisallowDynamicNewRule.php | 55 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 tests/phpstan/rules/DisallowDynamicNewRule.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 48bfd4a78..13f35c121 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,6 +11,7 @@ includes: rules: - pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule + - pocketmine\phpstan\rules\DisallowDynamicNewRule - pocketmine\phpstan\rules\DisallowEnumComparisonRule - pocketmine\phpstan\rules\DisallowForeachByReferenceRule - pocketmine\phpstan\rules\ExplodeLimitRule diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 73b7a65c1..e54bb166e 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -18,6 +18,12 @@ parameters: count: 1 path: ../../../src/Server.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.noDynamic + count: 1 + path: ../../../src/Server.php + - message: '#^Method pocketmine\\Server\:\:getCommandAliases\(\) should return array\\> but returns array\\>\.$#' identifier: return.type @@ -54,6 +60,12 @@ parameters: count: 1 path: ../../../src/VersionInfo.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.noDynamic + count: 1 + path: ../../../src/block/Block.php + - message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getBlockAt\(\) expects int, float\|int given\.$#' identifier: argument.type @@ -510,6 +522,12 @@ parameters: count: 3 path: ../../../src/block/tile/Spawnable.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.noDynamic + count: 1 + path: ../../../src/block/tile/TileFactory.php + - message: '#^Parameter \#1 \$x of method pocketmine\\world\\World\:\:getPotentialLightAt\(\) expects int, float\|int given\.$#' identifier: argument.type @@ -936,6 +954,12 @@ parameters: count: 4 path: ../../../src/plugin/PluginManager.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.noDynamic + count: 1 + path: ../../../src/plugin/PluginManager.php + - message: '#^Method pocketmine\\resourcepacks\\ZippedResourcePack\:\:getPackSize\(\) should return int but returns int\<0, max\>\|false\.$#' identifier: return.type @@ -1248,6 +1272,12 @@ parameters: count: 1 path: ../../../src/world/format/io/region/RegionLoader.php + - + message: '#^Dynamic new is not allowed\.$#' + identifier: pocketmine.new.noDynamic + count: 1 + path: ../../../src/world/generator/GeneratorRegisterTask.php + - message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#' identifier: return.type diff --git a/tests/phpstan/rules/DisallowDynamicNewRule.php b/tests/phpstan/rules/DisallowDynamicNewRule.php new file mode 100644 index 000000000..c6c15a5aa --- /dev/null +++ b/tests/phpstan/rules/DisallowDynamicNewRule.php @@ -0,0 +1,55 @@ + + */ +final class DisallowDynamicNewRule implements Rule{ + + public function getNodeType() : string{ + return New_::class; + } + + public function processNode(Node $node, Scope $scope) : array{ + /** @var New_ $node */ + if($node->class instanceof Expr){ + return [ + RuleErrorBuilder::message("Dynamic new is not allowed.") + ->tip("For factories, use closures instead. Closures can implement custom logic, are statically analyzable, and don't restrict the constructor signature.") + ->identifier("pocketmine.new.noDynamic") + ->build() + ]; + } + + return []; + } +} From 95284bc9decef6869e7d42c1d5b213a0d683562e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 9 Mar 2025 00:54:39 +0000 Subject: [PATCH 273/290] change error identifier --- tests/phpstan/configs/actual-problems.neon | 10 +++++----- tests/phpstan/rules/DisallowDynamicNewRule.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index e54bb166e..d3adde422 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -20,7 +20,7 @@ parameters: - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.noDynamic + identifier: pocketmine.new.dynamic count: 1 path: ../../../src/Server.php @@ -62,7 +62,7 @@ parameters: - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.noDynamic + identifier: pocketmine.new.dynamic count: 1 path: ../../../src/block/Block.php @@ -524,7 +524,7 @@ parameters: - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.noDynamic + identifier: pocketmine.new.dynamic count: 1 path: ../../../src/block/tile/TileFactory.php @@ -956,7 +956,7 @@ parameters: - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.noDynamic + identifier: pocketmine.new.dynamic count: 1 path: ../../../src/plugin/PluginManager.php @@ -1274,7 +1274,7 @@ parameters: - message: '#^Dynamic new is not allowed\.$#' - identifier: pocketmine.new.noDynamic + identifier: pocketmine.new.dynamic count: 1 path: ../../../src/world/generator/GeneratorRegisterTask.php diff --git a/tests/phpstan/rules/DisallowDynamicNewRule.php b/tests/phpstan/rules/DisallowDynamicNewRule.php index c6c15a5aa..c25e6a18b 100644 --- a/tests/phpstan/rules/DisallowDynamicNewRule.php +++ b/tests/phpstan/rules/DisallowDynamicNewRule.php @@ -45,7 +45,7 @@ final class DisallowDynamicNewRule implements Rule{ return [ RuleErrorBuilder::message("Dynamic new is not allowed.") ->tip("For factories, use closures instead. Closures can implement custom logic, are statically analyzable, and don't restrict the constructor signature.") - ->identifier("pocketmine.new.noDynamic") + ->identifier("pocketmine.new.dynamic") ->build() ]; } From 7af5eb3da2bb50cdb11c06622e003283fdffd0ca Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 9 Mar 2025 01:10:03 +0000 Subject: [PATCH 274/290] crafting: validate array inputs this makes sure wrong parameters don't show up as core errors, as seen in crash report 12373907 closes #6642 --- src/crafting/ShapedRecipe.php | 2 ++ src/crafting/ShapelessRecipe.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/crafting/ShapedRecipe.php b/src/crafting/ShapedRecipe.php index 4c40eb0f5..2af3f5134 100644 --- a/src/crafting/ShapedRecipe.php +++ b/src/crafting/ShapedRecipe.php @@ -97,6 +97,7 @@ class ShapedRecipe implements CraftingRecipe{ $this->shape = $shape; + Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{}); foreach(Utils::stringifyKeys($ingredients) as $char => $i){ if(!str_contains(implode($this->shape), $char)){ throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape"); @@ -105,6 +106,7 @@ class ShapedRecipe implements CraftingRecipe{ $this->ingredientList[$char] = clone $i; } + Utils::validateArrayValueType($results, function(Item $_) : void{}); $this->results = Utils::cloneObjectArray($results); } diff --git a/src/crafting/ShapelessRecipe.php b/src/crafting/ShapelessRecipe.php index 7a4a22fda..b139439ef 100644 --- a/src/crafting/ShapelessRecipe.php +++ b/src/crafting/ShapelessRecipe.php @@ -53,7 +53,9 @@ class ShapelessRecipe implements CraftingRecipe{ if(count($ingredients) > 9){ throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients"); } + Utils::validateArrayValueType($ingredients, function(RecipeIngredient $_) : void{}); $this->ingredients = $ingredients; + Utils::validateArrayValueType($results, function(Item $_) : void{}); $this->results = Utils::cloneObjectArray($results); } From afc4a3c7f18d42b41cbfde84ab6a2e4dd7c03045 Mon Sep 17 00:00:00 2001 From: Lee Siu San <15855635+leolee3914@users.noreply.github.com> Date: Sun, 9 Mar 2025 10:09:53 +0800 Subject: [PATCH 275/290] Fixed entity position offset not being included in AddActorPacket (#6643) this was causing TNT and falling blocks to briefly appear half a block lower than their true position, because their positions are measured from the center and not the base. --- src/entity/Entity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 7f0f6028b..e24c6067c 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -1495,7 +1495,7 @@ abstract class Entity{ $this->getId(), //TODO: actor unique ID $this->getId(), static::getNetworkTypeId(), - $this->location->asVector3(), + $this->getOffsetPosition($this->location->asVector3()), $this->getMotion(), $this->location->pitch, $this->location->yaw, From 00df5087279c08f079f06a74b60e8c439a3e53d4 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 12 Mar 2025 13:06:57 +0000 Subject: [PATCH 276/290] Update bug-report.yml --- .github/ISSUE_TEMPLATE/bug-report.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 3a4e49100..209ba8880 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -57,6 +57,9 @@ body: attributes: value: | ## Version, OS and game info + > [!WARNING] + > "Latest" is not a valid version. + > Failure to fill these fields with valid information may result in your issue being closed. - type: input attributes: From 73a4b076d6b62a9e325ce200f282c09cefcb27e7 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Wed, 12 Mar 2025 16:19:11 +0000 Subject: [PATCH 277/290] actions: tidy support response message --- .github/workflows/support.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/support.yml b/.github/workflows/support.yml index 68da365cb..3a65f78a4 100644 --- a/.github/workflows/support.yml +++ b/.github/workflows/support.yml @@ -20,10 +20,7 @@ jobs: - Check our [Documentation](https://doc.pmmp.io) to see if you can find answers there - - Ask the community on our [Discord server](https://discord.gg/bmSAZBG) or our [Forums](https://forums.pmmp.io) - - - [Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bmSAZBG) | [Forums](https://forums.pmmp.io) + - Ask the community on our [Discord server](https://discord.gg/bmSAZBG) close-issue: true lock-issue: false From 341c7a03a9413b5b6d48e7b9fab4ae9e3161ca4b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Mar 2025 15:40:05 +0000 Subject: [PATCH 278/290] CopperMaterial: fixed missing @return $this docs --- src/block/utils/CopperMaterial.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/block/utils/CopperMaterial.php b/src/block/utils/CopperMaterial.php index 6df22620b..ef6560620 100644 --- a/src/block/utils/CopperMaterial.php +++ b/src/block/utils/CopperMaterial.php @@ -30,9 +30,15 @@ interface CopperMaterial{ public function getOxidation() : CopperOxidation; + /** + * @return $this + */ public function setOxidation(CopperOxidation $oxidation) : CopperMaterial; public function isWaxed() : bool; + /** + * @return $this + */ public function setWaxed(bool $waxed) : CopperMaterial; } From d9e0e51e14cac88f78c7fc12bb5c371b54ad126d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Mar 2025 16:08:06 +0000 Subject: [PATCH 279/290] Reduce code duplication in copper block serialization handling --- .../convert/BlockObjectToStateSerializer.php | 342 +++++++++--------- .../convert/BlockStateDeserializerHelper.php | 40 +- .../BlockStateToObjectDeserializer.php | 251 +++++++------ 3 files changed, 356 insertions(+), 277 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index e41e82054..367d38449 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -214,6 +214,7 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->registerLeavesSerializers(); $this->registerSaplingSerializers(); $this->registerMobHeadSerializers(); + $this->registerCopperSerializers(); $this->registerSimpleSerializers(); $this->registerSerializers(); } @@ -791,6 +792,178 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ })->writeFacingWithoutDown($block->getFacing())); } + private function registerCopperSerializers() : void{ + $this->map(Blocks::COPPER(), function(Copper $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : + Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER) + ); + }); + $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_CHISELED_COPPER, + Ids::WAXED_EXPOSED_CHISELED_COPPER, + Ids::WAXED_WEATHERED_CHISELED_COPPER, + Ids::WAXED_OXIDIZED_CHISELED_COPPER + ) : + Helper::selectCopperId($oxidation, + Ids::CHISELED_COPPER, + Ids::EXPOSED_CHISELED_COPPER, + Ids::WEATHERED_CHISELED_COPPER, + Ids::OXIDIZED_CHISELED_COPPER + ) + ); + }); + $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_COPPER_GRATE, + Ids::WAXED_EXPOSED_COPPER_GRATE, + Ids::WAXED_WEATHERED_COPPER_GRATE, + Ids::WAXED_OXIDIZED_COPPER_GRATE + ) : + Helper::selectCopperId($oxidation, + Ids::COPPER_GRATE, + Ids::EXPOSED_COPPER_GRATE, + Ids::WEATHERED_COPPER_GRATE, + Ids::OXIDIZED_COPPER_GRATE + ) + ); + }); + $this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{ + $oxidation = $block->getOxidation(); + return new Writer($block->isWaxed() ? + Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : + Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER) + ); + }); + $this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeSlab( + $block, + ($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_CUT_COPPER_SLAB, + Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, + Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, + Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB + ) : + Helper::selectCopperId( + $oxidation, + Ids::CUT_COPPER_SLAB, + Ids::EXPOSED_CUT_COPPER_SLAB, + Ids::WEATHERED_CUT_COPPER_SLAB, + Ids::OXIDIZED_CUT_COPPER_SLAB + ) + ), + ($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB + ) : + Helper::selectCopperId( + $oxidation, + Ids::DOUBLE_CUT_COPPER_SLAB, + Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, + Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, + Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB + ) + ) + ); + }); + $this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeStairs( + $block, + new Writer($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_CUT_COPPER_STAIRS, + Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, + Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, + Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS + ) : + Helper::selectCopperId( + $oxidation, + Ids::CUT_COPPER_STAIRS, + Ids::EXPOSED_CUT_COPPER_STAIRS, + Ids::WEATHERED_CUT_COPPER_STAIRS, + Ids::OXIDIZED_CUT_COPPER_STAIRS + ) + ) + ); + }); + $this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{ + $oxidation = $block->getOxidation(); + return Writer::create($block->isWaxed() ? + Helper::selectCopperId($oxidation, + Ids::WAXED_COPPER_BULB, + Ids::WAXED_EXPOSED_COPPER_BULB, + Ids::WAXED_WEATHERED_COPPER_BULB, + Ids::WAXED_OXIDIZED_COPPER_BULB) : + Helper::selectCopperId($oxidation, + Ids::COPPER_BULB, + Ids::EXPOSED_COPPER_BULB, + Ids::WEATHERED_COPPER_BULB, + Ids::OXIDIZED_COPPER_BULB + )) + ->writeBool(StateNames::LIT, $block->isLit()) + ->writeBool(StateNames::POWERED_BIT, $block->isPowered()); + }); + $this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeDoor( + $block, + new Writer($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_COPPER_DOOR, + Ids::WAXED_EXPOSED_COPPER_DOOR, + Ids::WAXED_WEATHERED_COPPER_DOOR, + Ids::WAXED_OXIDIZED_COPPER_DOOR + ) : + Helper::selectCopperId( + $oxidation, + Ids::COPPER_DOOR, + Ids::EXPOSED_COPPER_DOOR, + Ids::WEATHERED_COPPER_DOOR, + Ids::OXIDIZED_COPPER_DOOR + ) + ) + ); + }); + $this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{ + $oxidation = $block->getOxidation(); + return Helper::encodeTrapdoor( + $block, + new Writer($block->isWaxed() ? + Helper::selectCopperId( + $oxidation, + Ids::WAXED_COPPER_TRAPDOOR, + Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, + Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, + Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR + ) : + Helper::selectCopperId( + $oxidation, + Ids::COPPER_TRAPDOOR, + Ids::EXPOSED_COPPER_TRAPDOOR, + Ids::WEATHERED_COPPER_TRAPDOOR, + Ids::OXIDIZED_COPPER_TRAPDOOR + ) + ) + ); + }); + } + private function registerSimpleSerializers() : void{ $this->mapSimple(Blocks::AIR(), Ids::AIR); $this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); @@ -1265,175 +1438,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ $this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB); $this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))); - $this->map(Blocks::COPPER(), function(Copper $block) : Writer{ - $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : - Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER) - ); - }); - $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : Writer{ - $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER - ) : - Helper::selectCopperId($oxidation, - Ids::CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER - ) - ); - }); - $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : Writer{ - $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE - ) : - Helper::selectCopperId($oxidation, - Ids::COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE - ) - ); - }); - $this->map(Blocks::CUT_COPPER(), function(Copper $block) : Writer{ - $oxidation = $block->getOxidation(); - return new Writer($block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : - Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER) - ); - }); - $this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeSlab( - $block, - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_SLAB, - Ids::EXPOSED_CUT_COPPER_SLAB, - Ids::WEATHERED_CUT_COPPER_SLAB, - Ids::OXIDIZED_CUT_COPPER_SLAB - ) - ), - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::DOUBLE_CUT_COPPER_SLAB, - Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) - ) - ); - }); - $this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeStairs( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_STAIRS, - Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, - Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, - Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_STAIRS, - Ids::EXPOSED_CUT_COPPER_STAIRS, - Ids::WEATHERED_CUT_COPPER_STAIRS, - Ids::OXIDIZED_CUT_COPPER_STAIRS - ) - ) - ); - }); - $this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{ - $oxidation = $block->getOxidation(); - return Writer::create($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_BULB, - Ids::WAXED_EXPOSED_COPPER_BULB, - Ids::WAXED_WEATHERED_COPPER_BULB, - Ids::WAXED_OXIDIZED_COPPER_BULB) : - Helper::selectCopperId($oxidation, - Ids::COPPER_BULB, - Ids::EXPOSED_COPPER_BULB, - Ids::WEATHERED_COPPER_BULB, - Ids::OXIDIZED_COPPER_BULB - )) - ->writeBool(StateNames::LIT, $block->isLit()) - ->writeBool(StateNames::POWERED_BIT, $block->isPowered()); - }); - $this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeDoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_DOOR, - Ids::WAXED_EXPOSED_COPPER_DOOR, - Ids::WAXED_WEATHERED_COPPER_DOOR, - Ids::WAXED_OXIDIZED_COPPER_DOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_DOOR, - Ids::EXPOSED_COPPER_DOOR, - Ids::WEATHERED_COPPER_DOOR, - Ids::OXIDIZED_COPPER_DOOR - ) - ) - ); - }); - $this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeTrapdoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_TRAPDOOR, - Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, - Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, - Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_TRAPDOOR, - Ids::EXPOSED_COPPER_TRAPDOOR, - Ids::WEATHERED_COPPER_TRAPDOOR, - Ids::OXIDIZED_COPPER_TRAPDOOR - ) - ) - ); - }); $this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{ return Writer::create(Ids::COCOA) ->writeInt(StateNames::AGE, $block->getAge()) diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 1d7b4bb76..5cf3f7f76 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -126,7 +126,13 @@ final class BlockStateDeserializerHelper{ ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TDoor of Door + * @phpstan-param TDoor $block + * @phpstan-return TDoor + * + * @throws BlockStateDeserializeException + */ public static function decodeDoor(Door $block, BlockStateReader $in) : Door{ //TODO: check if these need any special treatment to get the appropriate data to both halves of the door return $block @@ -237,18 +243,36 @@ final class BlockStateDeserializerHelper{ return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TSlab of Slab + * @phpstan-param TSlab $block + * @phpstan-return TSlab + * + * @throws BlockStateDeserializeException + */ public static function decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{ return $block->setSlabType($in->readSlabPosition()); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TSlab of Slab + * @phpstan-param TSlab $block + * @phpstan-return TSlab + * + * @throws BlockStateDeserializeException + */ public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{ $in->ignored(StateNames::MC_VERTICAL_HALF); return $block->setSlabType(SlabType::DOUBLE); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TStair of Stair + * @phpstan-param TStair $block + * @phpstan-return TStair + * + * @throws BlockStateDeserializeException + */ public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{ return $block ->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT)) @@ -265,7 +289,13 @@ final class BlockStateDeserializerHelper{ ->setFacing($facing === Facing::DOWN ? Facing::UP : $facing); } - /** @throws BlockStateDeserializeException */ + /** + * @phpstan-template TTrapdoor of Trapdoor + * @phpstan-param TTrapdoor $block + * @phpstan-return TTrapdoor + * + * @throws BlockStateDeserializeException + */ public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{ return $block ->setFacing($in->read5MinusHorizontalFacing()) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 42d8ee0cd..35f7f0df9 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -38,6 +38,7 @@ use pocketmine\block\Stair; use pocketmine\block\SweetBerryBush; use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\ChiseledBookshelfSlot; +use pocketmine\block\utils\CopperMaterial; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DirtType; @@ -59,6 +60,7 @@ use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; use pocketmine\math\Axis; use pocketmine\math\Facing; +use pocketmine\utils\Utils; use function array_key_exists; use function count; use function min; @@ -87,6 +89,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->registerSaplingDeserializers(); $this->registerLightDeserializers(); $this->registerMobHeadDeserializers(); + $this->registerCopperDeserializers(); $this->registerSimpleDeserializers(); $this->registerDeserializers(); } @@ -723,6 +726,150 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } } + /** + * @phpstan-param \Closure(Reader) : (CopperMaterial&Block) $deserializer + */ + private function mapCopper( + string $normalId, + string $waxedNormalId, + string $exposedId, + string $waxedExposedId, + string $weatheredId, + string $waxedWeatheredId, + string $oxidizedId, + string $waxedOxidizedId, + \Closure $deserializer + ) : void{ + foreach(Utils::stringifyKeys([ + $normalId => [CopperOxidation::NONE, false], + $waxedNormalId => [CopperOxidation::NONE, true], + $exposedId => [CopperOxidation::EXPOSED, false], + $waxedExposedId => [CopperOxidation::EXPOSED, true], + $weatheredId => [CopperOxidation::WEATHERED, false], + $waxedWeatheredId => [CopperOxidation::WEATHERED, true], + $oxidizedId => [CopperOxidation::OXIDIZED, false], + $waxedOxidizedId => [CopperOxidation::OXIDIZED, true], + ]) as $id => [$oxidation, $waxed]){ + $this->map($id, fn(Reader $in) => $deserializer($in)->setOxidation($oxidation)->setWaxed($waxed)); + } + } + + private function registerCopperDeserializers() : void{ + $this->mapCopper( + Ids::CUT_COPPER_SLAB, + Ids::WAXED_CUT_COPPER_SLAB, + Ids::EXPOSED_CUT_COPPER_SLAB, + Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, + Ids::WEATHERED_CUT_COPPER_SLAB, + Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, + Ids::OXIDIZED_CUT_COPPER_SLAB, + Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, + fn(Reader $in) => Helper::decodeSingleSlab(Blocks::CUT_COPPER_SLAB(), $in) + ); + $this->mapCopper( + Ids::DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, + Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, + Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, + Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, + Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, + fn(Reader $in) => Helper::decodeDoubleSlab(Blocks::CUT_COPPER_SLAB(), $in) + ); + + $this->mapCopper( + Ids::COPPER_BULB, + Ids::WAXED_COPPER_BULB, + Ids::EXPOSED_COPPER_BULB, + Ids::WAXED_EXPOSED_COPPER_BULB, + Ids::WEATHERED_COPPER_BULB, + Ids::WAXED_WEATHERED_COPPER_BULB, + Ids::OXIDIZED_COPPER_BULB, + Ids::WAXED_OXIDIZED_COPPER_BULB, + fn(Reader $in) => Blocks::COPPER_BULB() + ->setLit($in->readBool(StateNames::LIT)) + ->setPowered($in->readBool(StateNames::POWERED_BIT)) + ); + $this->mapCopper( + Ids::COPPER_DOOR, + Ids::WAXED_COPPER_DOOR, + Ids::EXPOSED_COPPER_DOOR, + Ids::WAXED_EXPOSED_COPPER_DOOR, + Ids::WEATHERED_COPPER_DOOR, + Ids::WAXED_WEATHERED_COPPER_DOOR, + Ids::OXIDIZED_COPPER_DOOR, + Ids::WAXED_OXIDIZED_COPPER_DOOR, + fn(Reader $in) => Helper::decodeDoor(Blocks::COPPER_DOOR(), $in) + ); + $this->mapCopper( + Ids::COPPER_TRAPDOOR, + Ids::WAXED_COPPER_TRAPDOOR, + Ids::EXPOSED_COPPER_TRAPDOOR, + Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, + Ids::WEATHERED_COPPER_TRAPDOOR, + Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, + Ids::OXIDIZED_COPPER_TRAPDOOR, + Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, + fn(Reader $in) => Helper::decodeTrapdoor(Blocks::COPPER_TRAPDOOR(), $in) + ); + $this->mapCopper( + Ids::COPPER_BLOCK, + Ids::WAXED_COPPER, + Ids::EXPOSED_COPPER, + Ids::WAXED_EXPOSED_COPPER, + Ids::WEATHERED_COPPER, + Ids::WAXED_WEATHERED_COPPER, + Ids::OXIDIZED_COPPER, + Ids::WAXED_OXIDIZED_COPPER, + fn(Reader $in) => Blocks::COPPER() + ); + $this->mapCopper( + Ids::CHISELED_COPPER, + Ids::WAXED_CHISELED_COPPER, + Ids::EXPOSED_CHISELED_COPPER, + Ids::WAXED_EXPOSED_CHISELED_COPPER, + Ids::WEATHERED_CHISELED_COPPER, + Ids::WAXED_WEATHERED_CHISELED_COPPER, + Ids::OXIDIZED_CHISELED_COPPER, + Ids::WAXED_OXIDIZED_CHISELED_COPPER, + Blocks::CHISELED_COPPER(...) + ); + $this->mapCopper( + Ids::COPPER_GRATE, + Ids::WAXED_COPPER_GRATE, + Ids::EXPOSED_COPPER_GRATE, + Ids::WAXED_EXPOSED_COPPER_GRATE, + Ids::WEATHERED_COPPER_GRATE, + Ids::WAXED_WEATHERED_COPPER_GRATE, + Ids::OXIDIZED_COPPER_GRATE, + Ids::WAXED_OXIDIZED_COPPER_GRATE, + Blocks::COPPER_GRATE(...) + ); + $this->mapCopper( + Ids::CUT_COPPER, + Ids::WAXED_CUT_COPPER, + Ids::EXPOSED_CUT_COPPER, + Ids::WAXED_EXPOSED_CUT_COPPER, + Ids::WEATHERED_CUT_COPPER, + Ids::WAXED_WEATHERED_CUT_COPPER, + Ids::OXIDIZED_CUT_COPPER, + Ids::WAXED_OXIDIZED_CUT_COPPER, + Blocks::CUT_COPPER(...) + ); + $this->mapCopper( + Ids::CUT_COPPER_STAIRS, + Ids::WAXED_CUT_COPPER_STAIRS, + Ids::EXPOSED_CUT_COPPER_STAIRS, + Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, + Ids::WEATHERED_CUT_COPPER_STAIRS, + Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, + Ids::OXIDIZED_CUT_COPPER_STAIRS, + Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, + fn(Reader $in) => Helper::decodeStairs(Blocks::CUT_COPPER_STAIRS(), $in) + ); + } + private function registerSimpleDeserializers() : void{ $this->mapSimple(Ids::AIR, fn() => Blocks::AIR()); $this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST()); @@ -1228,18 +1375,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR() ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) ); - $this->map(Ids::COPPER_BLOCK, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::NONE)); - $this->map(Ids::COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in)); - $this->map(Ids::COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE)); - $this->map(Ids::COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in)); - $this->map(Ids::CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); - $this->mapSlab(Ids::CUT_COPPER_SLAB, Ids::DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); - $this->mapStairs(Ids::CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE)); $this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB()); $this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB()); $this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB()); @@ -1294,19 +1429,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::ENDER_CHEST() ->setFacing($in->readCardinalHorizontalFacing()); }); - $this->map(Ids::EXPOSED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::EXPOSED)); - $this->map(Ids::EXPOSED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED)); - $this->map(Ids::EXPOSED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED)); - $this->map(Ids::EXPOSED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED)); - $this->mapSlab(Ids::EXPOSED_CUT_COPPER_SLAB, Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED)); - $this->mapStairs(Ids::EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED)); - $this->map(Ids::EXPOSED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in)); - $this->map(Ids::EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in)); $this->map(Ids::FARMLAND, function(Reader $in) : Block{ return Blocks::FARMLAND() ->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7)); @@ -1459,19 +1581,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB()); $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); $this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis())); - $this->map(Ids::OXIDIZED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED)); - $this->map(Ids::OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED)); - $this->map(Ids::OXIDIZED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED)); - $this->map(Ids::OXIDIZED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED)); - $this->mapSlab(Ids::OXIDIZED_CUT_COPPER_SLAB, Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED)); - $this->mapStairs(Ids::OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED)); - $this->map(Ids::OXIDIZED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in)); - $this->map(Ids::OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in)); $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis())); $this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB()); $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ @@ -1744,71 +1853,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ ->setFacing($in->readHorizontalFacing()); }); $this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in)); - $this->map(Ids::WAXED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::NONE)); - $this->map(Ids::WAXED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::NONE)); - $this->map(Ids::WAXED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::NONE)); - $this->map(Ids::WAXED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::NONE)); - $this->mapSlab(Ids::WAXED_CUT_COPPER_SLAB, Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::NONE)); - $this->mapStairs(Ids::WAXED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::NONE)); - $this->map(Ids::WAXED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::NONE) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::WAXED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::NONE), $in)); - $this->map(Ids::WAXED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::NONE), $in)); - $this->map(Ids::WAXED_EXPOSED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::EXPOSED)); - $this->map(Ids::WAXED_EXPOSED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::EXPOSED)); - $this->map(Ids::WAXED_EXPOSED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::EXPOSED)); - $this->map(Ids::WAXED_EXPOSED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::EXPOSED)); - $this->mapSlab(Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::EXPOSED)); - $this->mapStairs(Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::EXPOSED)); - $this->map(Ids::WAXED_EXPOSED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::EXPOSED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::WAXED_EXPOSED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::EXPOSED), $in)); - $this->map(Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::EXPOSED), $in)); - $this->map(Ids::WAXED_OXIDIZED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::OXIDIZED)); - $this->map(Ids::WAXED_OXIDIZED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::OXIDIZED)); - $this->map(Ids::WAXED_OXIDIZED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::OXIDIZED)); - $this->map(Ids::WAXED_OXIDIZED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::OXIDIZED)); - $this->mapSlab(Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::OXIDIZED)); - $this->mapStairs(Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::OXIDIZED)); - $this->map(Ids::WAXED_OXIDIZED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::OXIDIZED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::WAXED_OXIDIZED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::OXIDIZED), $in)); - $this->map(Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::OXIDIZED), $in)); - $this->map(Ids::WAXED_WEATHERED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::COPPER(), CopperOxidation::WEATHERED)); - $this->map(Ids::WAXED_WEATHERED_CHISELED_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED)); - $this->map(Ids::WAXED_WEATHERED_COPPER_GRATE, fn() => Helper::decodeWaxedCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED)); - $this->map(Ids::WAXED_WEATHERED_CUT_COPPER, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED)); - $this->mapSlab(Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED)); - $this->mapStairs(Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeWaxedCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED)); - $this->map(Ids::WAXED_WEATHERED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeWaxedCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::WAXED_WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeWaxedCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in)); - $this->map(Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeWaxedCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in)); - $this->map(Ids::WEATHERED_COPPER, fn() => Helper::decodeCopper(Blocks::COPPER(), CopperOxidation::WEATHERED)); - $this->map(Ids::WEATHERED_CHISELED_COPPER, fn() => Helper::decodeCopper(Blocks::CHISELED_COPPER(), CopperOxidation::WEATHERED)); - $this->map(Ids::WEATHERED_COPPER_GRATE, fn() => Helper::decodeCopper(Blocks::COPPER_GRATE(), CopperOxidation::WEATHERED)); - $this->map(Ids::WEATHERED_CUT_COPPER, fn() => Helper::decodeCopper(Blocks::CUT_COPPER(), CopperOxidation::WEATHERED)); - $this->mapSlab(Ids::WEATHERED_CUT_COPPER_SLAB, Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_SLAB(), CopperOxidation::WEATHERED)); - $this->mapStairs(Ids::WEATHERED_CUT_COPPER_STAIRS, fn() => Helper::decodeCopper(Blocks::CUT_COPPER_STAIRS(), CopperOxidation::WEATHERED)); - $this->map(Ids::WEATHERED_COPPER_BULB, function(Reader $in) : Block{ - return Helper::decodeCopper(Blocks::COPPER_BULB(), CopperOxidation::WEATHERED) - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::WEATHERED_COPPER_DOOR, fn(Reader $in) => Helper::decodeDoor(Helper::decodeCopper(Blocks::COPPER_DOOR(), CopperOxidation::WEATHERED), $in)); - $this->map(Ids::WEATHERED_COPPER_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Helper::decodeCopper(Blocks::COPPER_TRAPDOOR(), CopperOxidation::WEATHERED), $in)); + $this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{ return Blocks::WEEPING_VINES() ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25)); From 7cfaf04b8794cc72f186360de7778b64f757d696 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Mar 2025 16:10:56 +0000 Subject: [PATCH 280/290] stfu --- .../block/convert/BlockStateToObjectDeserializer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 35f7f0df9..2ea2ecf8c 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -833,7 +833,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ Ids::WAXED_WEATHERED_CHISELED_COPPER, Ids::OXIDIZED_CHISELED_COPPER, Ids::WAXED_OXIDIZED_CHISELED_COPPER, - Blocks::CHISELED_COPPER(...) + fn(Reader $in) => Blocks::CHISELED_COPPER() ); $this->mapCopper( Ids::COPPER_GRATE, @@ -844,7 +844,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ Ids::WAXED_WEATHERED_COPPER_GRATE, Ids::OXIDIZED_COPPER_GRATE, Ids::WAXED_OXIDIZED_COPPER_GRATE, - Blocks::COPPER_GRATE(...) + fn(Reader $in) => Blocks::COPPER_GRATE() ); $this->mapCopper( Ids::CUT_COPPER, @@ -855,7 +855,7 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ Ids::WAXED_WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER, - Blocks::CUT_COPPER(...) + fn(Reader $in) => Blocks::CUT_COPPER() ); $this->mapCopper( Ids::CUT_COPPER_STAIRS, From 09acbfab4c954edf445177389c4347725d14fb04 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 15 Mar 2025 00:03:09 +0000 Subject: [PATCH 281/290] dependabot: ignore phpstan/phpstan updates these are noisy and cause conflicts. Since they also usually cause new errors to be reported, we often can't directly update it anyway. Better to test & update this locally. --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 13721f0ba..ded96ca5d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,10 @@ updates: update-types: - "version-update:semver-major" - "version-update:semver-minor" + + #since we lock this to exact versions, it causes conflicts with minor-next & major-next in composer.lock + #better to just test updates to this locally anyway since almost every version breaks something + - dependency-name: phpstan/phpstan groups: production-patch-updates: dependency-type: production From e03c586c86801664fa484ee034f65eefee3ff894 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Mar 2025 01:29:36 +0000 Subject: [PATCH 282/290] GarbageCollectorManager: promote debug message to info this has such a big impact on performance that I think this is warranted. Should also make it more obvious what the GC is doing without needing to enable ALL debug info. --- src/GarbageCollectorManager.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/GarbageCollectorManager.php b/src/GarbageCollectorManager.php index a8912a90d..5cea2e879 100644 --- a/src/GarbageCollectorManager.php +++ b/src/GarbageCollectorManager.php @@ -48,6 +48,7 @@ final class GarbageCollectorManager{ private int $threshold = self::GC_THRESHOLD_DEFAULT; private int $collectionTimeTotalNs = 0; + private int $runs = 0; private \Logger $logger; private TimingsHandler $timings; @@ -96,7 +97,16 @@ final class GarbageCollectorManager{ $time = $end - $start; $this->collectionTimeTotalNs += $time; - $this->logger->debug("gc_collect_cycles: " . number_format($time) . " ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->collectionTimeTotalNs) . " ns"); + $this->runs++; + $this->logger->info(sprintf( + "Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms", + $this->runs, + number_format($time / 1_000_000, 2), + $rootsBefore, + $rootsAfter, + $cycles, + number_format($this->collectionTimeTotalNs / 1_000_000, 2) + )); return $cycles; } From 8b57e9007a18569e71abe8f78a3f1d6a620b2d39 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Mar 2025 01:33:29 +0000 Subject: [PATCH 283/290] :japanese_goblin: --- src/GarbageCollectorManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GarbageCollectorManager.php b/src/GarbageCollectorManager.php index 5cea2e879..294403a05 100644 --- a/src/GarbageCollectorManager.php +++ b/src/GarbageCollectorManager.php @@ -31,6 +31,7 @@ use function hrtime; use function max; use function min; use function number_format; +use function sprintf; /** * Allows threads to manually trigger the cyclic garbage collector using a threshold like PHP's own garbage collector, From 463be36b72d4f519674ec472ca492773c197dbce Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Mar 2025 20:33:47 +0000 Subject: [PATCH 284/290] Update composer dependencies --- composer.json | 2 +- composer.lock | 77 +++++++++++++++++++++------------------------------ 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index 10454c560..757ae5f57 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.6", + "phpstan/phpstan": "2.1.8", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index e224887d6..01b9ea5df 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bef9decc40d9f5bd82e1de2d151bd99f", + "content-hash": "d3a1dc398ea7b59dcb674143ac10f289", "packages": [ { "name": "adhocore/json-comment", @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.2", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.2" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2025-02-26T10:21:45+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "netresearch/jsonmapper", @@ -742,16 +742,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -759,25 +759,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -815,19 +812,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -1386,16 +1373,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.6", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f", + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f", "shasum": "" }, "require": { @@ -1440,7 +1427,7 @@ "type": "github" } ], - "time": "2025-02-19T15:46:42+00:00" + "time": "2025-03-09T09:30:48+00:00" }, { "name": "phpstan/phpstan-phpunit", From 4407e585e4fdc3c2825fbbba9b0ecfd6833b17cb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Mar 2025 20:36:39 +0000 Subject: [PATCH 285/290] Update composer dependencies (minor-next) --- composer.json | 4 +- composer.lock | 101 ++++++++++++++++++++++---------------------------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/composer.json b/composer.json index 11c9ae515..74441af76 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "pocketmine/bedrock-block-upgrade-schema": "~5.1.0+bedrock-1.21.60", "pocketmine/bedrock-data": "~4.0.0+bedrock-1.21.60", "pocketmine/bedrock-item-upgrade-schema": "~1.14.0+bedrock-1.21.50", - "pocketmine/bedrock-protocol": "~36.0.0+bedrock-1.21.60", + "pocketmine/bedrock-protocol": "~36.2.0+bedrock-1.21.60", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", @@ -52,7 +52,7 @@ "symfony/filesystem": "~6.4.0" }, "require-dev": { - "phpstan/phpstan": "2.1.6", + "phpstan/phpstan": "2.1.8", "phpstan/phpstan-phpunit": "^2.0.0", "phpstan/phpstan-strict-rules": "^2.0.0", "phpunit/phpunit": "^10.5.24" diff --git a/composer.lock b/composer.lock index 521b5b372..deb0b43b2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2a56fc6dee1dac2ade34d965aa49dc82", + "content-hash": "6314ae9a7919042f10bd17f0a3ef6e9d", "packages": [ { "name": "adhocore/json-comment", @@ -67,16 +67,16 @@ }, { "name": "brick/math", - "version": "0.12.2", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.2" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2025-02-26T10:21:45+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "netresearch/jsonmapper", @@ -256,16 +256,16 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "36.0.0+bedrock-1.21.60", + "version": "36.2.0+bedrock-1.21.60", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "2057de319c5c551001c2a544e08d1bc7727d9963" + "reference": "6830e8a9ee9ecb6984b7b02f412473136ae92d50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/2057de319c5c551001c2a544e08d1bc7727d9963", - "reference": "2057de319c5c551001c2a544e08d1bc7727d9963", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6830e8a9ee9ecb6984b7b02f412473136ae92d50", + "reference": "6830e8a9ee9ecb6984b7b02f412473136ae92d50", "shasum": "" }, "require": { @@ -296,9 +296,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/36.0.0+bedrock-1.21.60" + "source": "https://github.com/pmmp/BedrockProtocol/tree/36.2.0+bedrock-1.21.60" }, - "time": "2025-02-16T15:59:08+00:00" + "time": "2025-03-14T17:17:21+00:00" }, { "name": "pocketmine/binaryutils", @@ -576,16 +576,16 @@ }, { "name": "pocketmine/nbt", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "cfd53a86166b851786967fc560cdb372e66fde96" + "reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/cfd53a86166b851786967fc560cdb372e66fde96", - "reference": "cfd53a86166b851786967fc560cdb372e66fde96", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/c3c7b0a7295daeaf7873d90fed5c5d10381d12e1", + "reference": "c3c7b0a7295daeaf7873d90fed5c5d10381d12e1", "shasum": "" }, "require": { @@ -612,9 +612,9 @@ "description": "PHP library for working with Named Binary Tags", "support": { "issues": "https://github.com/pmmp/NBT/issues", - "source": "https://github.com/pmmp/NBT/tree/1.1.0" + "source": "https://github.com/pmmp/NBT/tree/1.1.1" }, - "time": "2025-02-01T21:20:26+00:00" + "time": "2025-03-09T01:46:03+00:00" }, { "name": "pocketmine/raklib", @@ -742,16 +742,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -759,25 +759,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -815,19 +812,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -1386,16 +1373,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.6", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f", + "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f", "shasum": "" }, "require": { @@ -1440,7 +1427,7 @@ "type": "github" } ], - "time": "2025-02-19T15:46:42+00:00" + "time": "2025-03-09T09:30:48+00:00" }, { "name": "phpstan/phpstan-phpunit", From c2f8e9365bdf359b55f91056fd158d9d2c2a9f6b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 15 Mar 2025 20:53:49 +0000 Subject: [PATCH 286/290] BlockStateToObjectDeserializer: check that the returned state is actually registered if not, this will cause random crashes in core code, which assumes that state IDs found on runtime chunk memory are valid and registered. this problem exists in other places too, and probably requires a rethink of how we're dealing with this, but for now, this will do as a band-aid. --- src/block/RuntimeBlockStateRegistry.php | 4 ++++ .../convert/BlockStateToObjectDeserializer.php | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/block/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php index d13b942ba..a458b3368 100644 --- a/src/block/RuntimeBlockStateRegistry.php +++ b/src/block/RuntimeBlockStateRegistry.php @@ -209,6 +209,10 @@ class RuntimeBlockStateRegistry{ return $block; } + public function hasStateId(int $stateId) : bool{ + return isset($this->fullList[$stateId]); + } + /** * @return Block[] * @phpstan-return array diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 2ea2ecf8c..ed45a47d3 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -33,6 +33,7 @@ use pocketmine\block\DoublePitcherCrop; use pocketmine\block\Opaque; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; +use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\Slab; use pocketmine\block\Stair; use pocketmine\block\SweetBerryBush; @@ -97,11 +98,21 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ public function deserialize(BlockStateData $stateData) : int{ if(count($stateData->getStates()) === 0){ //if a block has zero properties, we can keep a map of string ID -> internal blockstate ID - return $this->simpleCache[$stateData->getName()] ??= $this->deserializeBlock($stateData)->getStateId(); + return $this->simpleCache[$stateData->getName()] ??= $this->deserializeToStateId($stateData); } //we can't cache blocks that have properties - go ahead and deserialize the slow way - return $this->deserializeBlock($stateData)->getStateId(); + return $this->deserializeToStateId($stateData); + } + + private function deserializeToStateId(BlockStateData $stateData) : int{ + $stateId = $this->deserializeBlock($stateData)->getStateId(); + //plugin devs seem to keep missing this and causing core crashes, so we need to verify this at the earliest + //available opportunity + if(!RuntimeBlockStateRegistry::getInstance()->hasStateId($stateId)){ + throw new \LogicException("State ID $stateId returned by deserializer for " . $stateData->getName() . " is not registered in RuntimeBlockStateRegistry"); + } + return $stateId; } /** @phpstan-param \Closure(Reader) : Block $c */ From 1c6a4bde86a0083bce4c85dffd256e534c621a1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:20:59 +0000 Subject: [PATCH 287/290] Bump pocketmine/locale-data in the production-patch-updates group (#6656) --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 01b9ea5df..cd4490296 100644 --- a/composer.lock +++ b/composer.lock @@ -471,16 +471,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.24.0", + "version": "2.24.1", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "6ec5e92c77a2102b2692763733e4763012facae9" + "reference": "8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/6ec5e92c77a2102b2692763733e4763012facae9", - "reference": "6ec5e92c77a2102b2692763733e4763012facae9", + "url": "https://api.github.com/repos/pmmp/Language/zipball/8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5", + "reference": "8f48cbe1fb5835a8bb573bed00ef04c65c26c7e5", "shasum": "" }, "type": "library", @@ -488,9 +488,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.24.0" + "source": "https://github.com/pmmp/Language/tree/2.24.1" }, - "time": "2025-02-16T20:46:34+00:00" + "time": "2025-03-16T19:04:15+00:00" }, { "name": "pocketmine/log", @@ -2954,5 +2954,5 @@ "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From de26ebd12490491a0c364dd2ae1017ef6150030a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 22 Mar 2025 17:59:20 +0000 Subject: [PATCH 288/290] Prepare 5.26.0 release --- changelogs/5.26.md | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 +-- 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.26.md diff --git a/changelogs/5.26.md b/changelogs/5.26.md new file mode 100644 index 000000000..8fa5cb35d --- /dev/null +++ b/changelogs/5.26.md @@ -0,0 +1,71 @@ +# 5.26.0 +Released 22nd March 2025. + +This is a minor feature release focused on performance improvements. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## Performance +- Significantly improved performance of entity movement. Load testing with item entities showed a 3x increase in the number of entities supported without lag. +- Significantly improved performance of on-ground checks for player movement. This still needs further work, but optimisations implemented in this version should improve performance substantially. +- Updated `pocketmine/nbt` dependency with performance improvements to `TAG_Compound` and `TAG_List` comparison. This should improve performance of inventory-related actions. +- `InventoryTransaction` now avoids useless item clones when processing transactions, which should improve performance of inventory-related actions. + +## Dependencies +- `pocketmine/bedrock-protocol` has been updated to `36.2.0`, which adds new functions to access some packet fields. +- `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees. + +## Gameplay +- Block break progress now takes into account the following: jumping, being in water, haste, mining fatigue + +## Tools +- `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging. + +## API +### `pocketmine\block` +- The following methods have been added: + - `public RuntimeBlockStateRegistry->hasStateId(int $stateId) : bool` - checks whether the given state ID is registered + +### `pocketmine\crafting` +- The following methods have been deprecated: + - `CraftingManager::sort()` - this was implicitly internal anyway + +### `pocketmine\utils` +- The following constants have been added: + - `TextFormat::MATERIAL_RESIN` +- The following static properties have been added: + - `Terminal::$COLOR_MATERIAL_RESIN` + +### `pocketmine\data\bedrock\block` +- `BlockStateToObjectDeserializer` now permits overriding **deserializers** for Bedrock IDs. This may be useful to implement custom state handling, or to implement missing block variants (such as snow cauldron). + - This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**. + - Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts. + - If you want to make a custom version of a vanilla block, create a custom type ID for it, exactly as you would for a regular custom block. +- The following methods have been added: + - `public BlockStateToObjectDeserializer->getDeserializerForId(string $id) : ?(\Closure(BlockStateReader) : Block)` + +### `pocketmine\data\bedrock\item` +- `ItemDeserializer` now permits overriding **deserializers** for Bedrock IDs. As above, this may be useful to implement custom data handling, or to implement missing variants of existing items. + - This was originally prohibited since 5.0.0. However, there is no technical reason to disallow overriding **deserializers**. + - Overriding **serializers** is still **not permitted**. Reusing type IDs doesn't make any sense and would break internal design contracts. + - As above, if you want to make a custom version of a vanilla item, create a custom type ID for it, exactly as you would for a regular custom item. +- The following methods have been added: + - `public ItemDeserializer->getDeserializerForId(string $id) : ?(\Closure(SavedItemData) : Item)` + +## Internals +- `new $class` is now banned on new internals code by a PHPStan rule. Closures or factory objects should be used instead for greater flexibility and better static analysis. +- `CraftingManager` now uses a more stable hash function for recipe output filtering. +- `ChunkCache` now accepts `int $dimensionId` in the constructor. This may be useful for plugins which implement the nether. +- `RuntimeBlockStateRegistry` now precomputes basic collision info about known states for fast paths. + - This permits specialization for common shapes like cubes and collisionless blocks, which allows skipping complex logic in entity movement calculation. This vastly improves performance. + - Any block whose class overrides `readStateFromWorld()` or `getModelPositionOffset()` will *not* be optimised. + - `Block->recalculateCollisionBoxes()` now has a hard requirement not to depend on anything other than available properties. It must not use `World` or its position. + - This change was problematic for `ChorusPlant`, which used nearby blocks to calculate its collision boxes. + - Blocks which need nearby blocks should override `readStateFromWorld()` and set dynamic state properties, similar to fences. + - This design flaw will be corrected with a major change to `Block` internals currently in planning for a future major version. +- `Block->getCollisionBoxes()` may not be called at all during gameplay for blocks with shapes determined to be simple, like cubes and collisionless blocks. +- `BlockStateToObjectDeserializer` now checks if the returned blockstate is registered in `RuntimeBlockStateRegistry` to promote earlier error detection (instead of crashing in random code paths). diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 569e457da..c50af5023 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.25.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.26.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From f123df5e0d7b79fd3a239419eca8f6a9b36d1866 Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sat, 22 Mar 2025 18:16:33 +0000 Subject: [PATCH 289/290] changelog: use more accurate terminology --- changelogs/5.26.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/5.26.md b/changelogs/5.26.md index 8fa5cb35d..f8adc969f 100644 --- a/changelogs/5.26.md +++ b/changelogs/5.26.md @@ -20,7 +20,7 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if - `pocketmine/nbt` has been updated to `1.1.0`, which improves performance when comparing NBT object trees. ## Gameplay -- Block break progress now takes into account the following: jumping, being in water, haste, mining fatigue +- Block breaking animation speed now takes into account the following: jumping, being in water, haste, mining fatigue ## Tools - `blockstate-upgrade-schema-utils.php` now has a new `dump-table` command, which turns a `.bin` palette table file into human-readable text for debugging. From c80a4d5b550b6e2ecbcfecb041477309fb8a0e24 Mon Sep 17 00:00:00 2001 From: "pmmp-admin-bot[bot]" <188621379+pmmp-admin-bot[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 18:33:19 +0000 Subject: [PATCH 290/290] 5.26.1 is next Commit created by: https://github.com/pmmp/RestrictedActions/actions/runs/14011298411 --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index c50af5023..8b9b68faf 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ use function str_repeat; final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.26.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.26.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /**