Support for flattening TAG_Byte and TAG_Int properties

this allows optimisation in upcoming versions.
This commit is contained in:
Dylan K. Taylor 2024-08-05 22:38:02 +01:00
parent bdb5845cec
commit be2437ac6e
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
5 changed files with 94 additions and 24 deletions

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade; 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 function ksort;
use const SORT_STRING; use const SORT_STRING;
@ -31,12 +34,14 @@ final class BlockStateUpgradeSchemaFlattenedName{
/** /**
* @param string[] $flattenedValueRemaps * @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps * @phpstan-param array<string, string> $flattenedValueRemaps
* @phpstan-param class-string<ByteTag>|class-string<IntTag>|class-string<StringTag>|null $flattenedPropertyType
*/ */
public function __construct( public function __construct(
public string $prefix, public string $prefix,
public string $flattenedProperty, public string $flattenedProperty,
public string $suffix, public string $suffix,
public array $flattenedValueRemaps public array $flattenedValueRemaps,
public ?string $flattenedPropertyType = null
){ ){
ksort($this->flattenedValueRemaps, SORT_STRING); ksort($this->flattenedValueRemaps, SORT_STRING);
} }
@ -45,6 +50,7 @@ final class BlockStateUpgradeSchemaFlattenedName{
return $this->prefix === $that->prefix && return $this->prefix === $that->prefix &&
$this->flattenedProperty === $that->flattenedProperty && $this->flattenedProperty === $that->flattenedProperty &&
$this->suffix === $that->suffix && $this->suffix === $that->suffix &&
$this->flattenedValueRemaps === $that->flattenedValueRemaps; $this->flattenedValueRemaps === $that->flattenedValueRemaps &&
$this->flattenedPropertyType === $that->flattenedPropertyType;
} }
} }

View File

@ -157,18 +157,29 @@ final class BlockStateUpgradeSchemaUtils{
foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){ foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
foreach($remaps as $remap){ 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"); throw new \UnexpectedValueException("Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
} }
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap( $result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaBlockRemap(
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []), array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
$remap->newName ?? new BlockStateUpgradeSchemaFlattenedName( $remapName,
$remap->newFlattenedName->prefix,
$remap->newFlattenedName->flattenedProperty,
$remap->newFlattenedName->suffix,
$remap->newFlattenedName->flattenedValueRemaps ?? [],
),
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []), array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
$remap->copiedState ?? [] $remap->copiedState ?? []
); );
@ -303,7 +314,13 @@ final class BlockStateUpgradeSchemaUtils{
$remap->newName->prefix, $remap->newName->prefix,
$remap->newName->flattenedProperty, $remap->newName->flattenedProperty,
$remap->newName->suffix, $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), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
$remap->copiedState $remap->copiedState

View File

@ -24,10 +24,14 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\block\upgrade; namespace pocketmine\data\bedrock\block\upgrade;
use pocketmine\data\bedrock\block\BlockStateData; 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\StringTag;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use function count; use function count;
use function get_class;
use function is_string; use function is_string;
use function ksort; use function ksort;
use function max; use function max;
@ -141,14 +145,21 @@ final class BlockStateUpgrader{
$newName = $remap->newName; $newName = $remap->newName;
}else{ }else{
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
if($flattenedValue instanceof StringTag){ $expectedType = $remap->newName->flattenedPropertyType;
$embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue(); if(!$flattenedValue instanceof $expectedType){
$newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix); //flattened property is not of the expected type, so this transformation is not applicable
unset($oldState[$remap->newName->flattenedProperty]);
}else{
//flattened property is not a TAG_String, so this transformation is not applicable
continue; 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; $newState = $remap->newState;

View File

@ -31,6 +31,7 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
public string $prefix; public string $prefix;
/** @required */ /** @required */
public string $flattenedProperty; public string $flattenedProperty;
public ?string $flattenedPropertyType = null;
/** @required */ /** @required */
public string $suffix; public string $suffix;
/** /**
@ -43,11 +44,12 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
* @param string[] $flattenedValueRemaps * @param string[] $flattenedValueRemaps
* @phpstan-param array<string, string> $flattenedValueRemaps * @phpstan-param array<string, string> $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->prefix = $prefix;
$this->flattenedProperty = $flattenedProperty; $this->flattenedProperty = $flattenedProperty;
$this->suffix = $suffix; $this->suffix = $suffix;
$this->flattenedValueRemaps = $flattenedValueRemaps; $this->flattenedValueRemaps = $flattenedValueRemaps;
$this->flattenedPropertyType = $flattenedPropertyType;
} }
/** /**
@ -58,6 +60,9 @@ final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializab
if(count($this->flattenedValueRemaps) === 0){ if(count($this->flattenedValueRemaps) === 0){
unset($result["flattenedValueRemaps"]); unset($result["flattenedValueRemaps"]);
} }
if($this->flattenedPropertyType === null){
unset($result["flattenedPropertyType"]);
}
return $result; return $result;
} }
} }

View File

@ -30,6 +30,8 @@ use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaFlattenedName;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\Tag; use pocketmine\nbt\tag\Tag;
use pocketmine\nbt\TreeRoot; use pocketmine\nbt\TreeRoot;
@ -48,7 +50,10 @@ use function count;
use function dirname; use function dirname;
use function file_put_contents; use function file_put_contents;
use function fwrite; use function fwrite;
use function get_class;
use function get_debug_type;
use function implode; use function implode;
use function is_numeric;
use function json_encode; use function json_encode;
use function ksort; use function ksort;
use function min; use function min;
@ -312,11 +317,13 @@ function findCommonSuffix(array $strings) : string{
/** /**
* @param string[][][] $candidateFlattenedValues * @param string[][][] $candidateFlattenedValues
* @phpstan-param array<string, array<string, array<string, string>>> $candidateFlattenedValues * @phpstan-param array<string, array<string, array<string, string>>> $candidateFlattenedValues
* @param string[] $candidateFlattenPropertyTypes
* @phpstan-param array<string, class-string<ByteTag>|class-string<IntTag>|class-string<StringTag>> $candidateFlattenPropertyTypes
* *
* @return BlockStateUpgradeSchemaFlattenedName[][] * @return BlockStateUpgradeSchemaFlattenedName[][]
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenedName>> * @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenedName>>
*/ */
function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{ function buildFlattenPropertyRules(array $candidateFlattenedValues, array $candidateFlattenPropertyTypes) : array{
$flattenPropertyRules = []; $flattenPropertyRules = [];
foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){
foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){ 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( $flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName(
$idPrefix, $idPrefix,
$propertyName, $propertyName,
$idSuffix, $idSuffix,
$valueMap $valueMap,
$candidateFlattenPropertyTypes[$propertyName],
); );
} }
} }
@ -407,16 +429,25 @@ function processRemappedStates(array $upgradeTable) : array{
$notFlattenedProperties = []; $notFlattenedProperties = [];
$candidateFlattenedValues = []; $candidateFlattenedValues = [];
$candidateFlattenedPropertyTypes = [];
foreach($upgradeTable as $pair){ foreach($upgradeTable as $pair){
foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){ foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){
if(isset($notFlattenedProperties[$propertyName])){ if(isset($notFlattenedProperties[$propertyName])){
continue; continue;
} }
if(!$propertyValue instanceof StringTag){ if(!$propertyValue instanceof StringTag && !$propertyValue instanceof IntTag && !$propertyValue instanceof ByteTag){
$notFlattenedProperties[$propertyName] = true; $notFlattenedProperties[$propertyName] = true;
continue; 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 === ""){ if($rawValue === ""){
$notFlattenedProperties[$propertyName] = true; $notFlattenedProperties[$propertyName] = true;
continue; continue;
@ -458,7 +489,7 @@ function processRemappedStates(array $upgradeTable) : array{
unset($candidateFlattenedValues[$propertyName]); unset($candidateFlattenedValues[$propertyName]);
} }
$flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues); $flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues, $candidateFlattenedPropertyTypes);
$flattenProperty = array_key_first($flattenedProperties); $flattenProperty = array_key_first($flattenedProperties);
//Properties with fewer rules take up less space for the same result //Properties with fewer rules take up less space for the same result
foreach(Utils::stringifyKeys($flattenedProperties) as $propertyName => $rules){ foreach(Utils::stringifyKeys($flattenedProperties) as $propertyName => $rules){
@ -485,8 +516,8 @@ function processRemappedStates(array $upgradeTable) : array{
ksort($cleanedNewState); ksort($cleanedNewState);
if($flattenProperty !== null){ if($flattenProperty !== null){
$flattenedValue = $cleanedOldState[$flattenProperty] ?? null; $flattenedValue = $cleanedOldState[$flattenProperty] ?? null;
if(!$flattenedValue instanceof StringTag){ if(!$flattenedValue instanceof StringTag && !$flattenedValue instanceof IntTag && !$flattenedValue instanceof ByteTag){
throw new AssumptionFailedError("This should always be a TAG_String ($newName $flattenProperty)"); throw new AssumptionFailedError("Non-flattenable type of tag ($newName $flattenProperty) but have " . get_debug_type($flattenedValue));
} }
unset($cleanedOldState[$flattenProperty]); unset($cleanedOldState[$flattenProperty]);
} }