From be2437ac6e6cd851f0cd282f54983cb2e9fcba6f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 5 Aug 2024 22:38:02 +0100 Subject: [PATCH] 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]); }