mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-06 11:57:10 +00:00
Add support for specialized flattenedProperties in schema format
This commit is contained in:
parent
acbfb0a3e9
commit
22718c4971
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\data\bedrock\block\upgrade;
|
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\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap as ValueRemap;
|
||||||
use pocketmine\nbt\tag\Tag;
|
use pocketmine\nbt\tag\Tag;
|
||||||
use function count;
|
use function count;
|
||||||
@ -58,6 +59,12 @@ final class BlockStateUpgradeSchema{
|
|||||||
*/
|
*/
|
||||||
public array $remappedPropertyValues = [];
|
public array $remappedPropertyValues = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FlattenInfo[]
|
||||||
|
* @phpstan-var array<string, FlattenInfo>
|
||||||
|
*/
|
||||||
|
public array $flattenedProperties = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var BlockStateUpgradeSchemaBlockRemap[][]
|
* @var BlockStateUpgradeSchemaBlockRemap[][]
|
||||||
* @phpstan-var array<string, list<BlockStateUpgradeSchemaBlockRemap>>
|
* @phpstan-var array<string, list<BlockStateUpgradeSchemaBlockRemap>>
|
||||||
@ -93,6 +100,7 @@ final class BlockStateUpgradeSchema{
|
|||||||
$this->removedProperties,
|
$this->removedProperties,
|
||||||
$this->renamedProperties,
|
$this->renamedProperties,
|
||||||
$this->remappedPropertyValues,
|
$this->remappedPropertyValues,
|
||||||
|
$this->flattenedProperties,
|
||||||
$this->remappedStates,
|
$this->remappedStates,
|
||||||
] as $list){
|
] as $list){
|
||||||
if(count($list) !== 0){
|
if(count($list) !== 0){
|
||||||
|
@ -40,7 +40,7 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public array $oldState,
|
public array $oldState,
|
||||||
public string|BlockStateUpgradeSchemaFlattenedName $newName,
|
public string|BlockStateUpgradeSchemaFlattenInfo $newName,
|
||||||
public array $newState,
|
public array $newState,
|
||||||
public array $copiedState
|
public array $copiedState
|
||||||
){}
|
){}
|
||||||
@ -48,8 +48,8 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
|||||||
public function equals(self $that) : bool{
|
public function equals(self $that) : bool{
|
||||||
$sameName = $this->newName === $that->newName ||
|
$sameName = $this->newName === $that->newName ||
|
||||||
(
|
(
|
||||||
$this->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
$this->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
|
||||||
$that->newName instanceof BlockStateUpgradeSchemaFlattenedName &&
|
$that->newName instanceof BlockStateUpgradeSchemaFlattenInfo &&
|
||||||
$this->newName->equals($that->newName)
|
$this->newName->equals($that->newName)
|
||||||
);
|
);
|
||||||
if(!$sameName){
|
if(!$sameName){
|
||||||
|
@ -29,7 +29,7 @@ use pocketmine\nbt\tag\StringTag;
|
|||||||
use function ksort;
|
use function ksort;
|
||||||
use const SORT_STRING;
|
use const SORT_STRING;
|
||||||
|
|
||||||
final class BlockStateUpgradeSchemaFlattenedName{
|
final class BlockStateUpgradeSchemaFlattenInfo{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $flattenedValueRemaps
|
* @param string[] $flattenedValueRemaps
|
@ -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\BlockStateUpgradeSchemaModel;
|
||||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelBlockRemap;
|
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\BlockStateUpgradeSchemaModelTag;
|
||||||
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
|
use pocketmine\data\bedrock\block\upgrade\model\BlockStateUpgradeSchemaModelValueRemap;
|
||||||
use pocketmine\nbt\tag\ByteTag;
|
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(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
|
||||||
foreach($remaps as $remap){
|
foreach($remaps as $remap){
|
||||||
if(isset($remap->newName)){
|
if(isset($remap->newName)){
|
||||||
$remapName = $remap->newName;
|
$remapName = $remap->newName;
|
||||||
}elseif(isset($remap->newFlattenedName)){
|
}elseif(isset($remap->newFlattenedName)){
|
||||||
$flattenRule = $remap->newFlattenedName;
|
$flattenRule = $remap->newFlattenedName;
|
||||||
$remapName = new BlockStateUpgradeSchemaFlattenedName(
|
$remapName = self::jsonModelToFlattenRule($flattenRule);
|
||||||
$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{
|
}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");
|
||||||
}
|
}
|
||||||
@ -265,6 +258,36 @@ final class BlockStateUpgradeSchemaUtils{
|
|||||||
$model->remappedPropertyValues = $modelDedupMapping;
|
$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{
|
public static function toJsonModel(BlockStateUpgradeSchema $schema) : BlockStateUpgradeSchemaModel{
|
||||||
$result = new BlockStateUpgradeSchemaModel();
|
$result = new BlockStateUpgradeSchemaModel();
|
||||||
$result->maxVersionMajor = $schema->maxVersionMajor;
|
$result->maxVersionMajor = $schema->maxVersionMajor;
|
||||||
@ -303,25 +326,19 @@ final class BlockStateUpgradeSchemaUtils{
|
|||||||
|
|
||||||
self::buildRemappedValuesIndex($schema, $result);
|
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){
|
foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
|
||||||
$keyedRemaps = [];
|
$keyedRemaps = [];
|
||||||
foreach($remaps as $remap){
|
foreach($remaps as $remap){
|
||||||
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
|
$modelRemap = new BlockStateUpgradeSchemaModelBlockRemap(
|
||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
||||||
is_string($remap->newName) ?
|
is_string($remap->newName) ? $remap->newName : self::flattenRuleToJsonModel($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")
|
|
||||||
}
|
|
||||||
),
|
|
||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||||
$remap->copiedState
|
$remap->copiedState
|
||||||
);
|
);
|
||||||
|
@ -110,10 +110,21 @@ final class BlockStateUpgrader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$oldName = $blockStateData->getName();
|
$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;
|
$stateChanges = 0;
|
||||||
$states = $blockStateData->getStates();
|
|
||||||
|
|
||||||
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
|
$states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
|
||||||
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
|
$states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
|
||||||
@ -146,22 +157,9 @@ final class BlockStateUpgrader{
|
|||||||
if(is_string($remap->newName)){
|
if(is_string($remap->newName)){
|
||||||
$newName = $remap->newName;
|
$newName = $remap->newName;
|
||||||
}else{
|
}else{
|
||||||
$flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null;
|
//yes, overwriting $oldState here is intentional, although we probably don't actually need it anyway
|
||||||
$expectedType = $remap->newName->flattenedPropertyType;
|
//it shouldn't make any difference unless the flattened property appears in copiedState for some reason
|
||||||
if(!$flattenedValue instanceof $expectedType){
|
[$newName, $oldState] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState);
|
||||||
//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;
|
$newState = $remap->newState;
|
||||||
@ -279,4 +277,32 @@ final class BlockStateUpgrader{
|
|||||||
|
|
||||||
return $states;
|
return $states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tag[] $states
|
||||||
|
* @phpstan-param array<string, Tag> $states
|
||||||
|
*
|
||||||
|
* @return (string|Tag[])[]
|
||||||
|
* @phpstan-return array{0: string, 1: array<string, Tag>}
|
||||||
|
*/
|
||||||
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,12 @@ final class BlockStateUpgradeSchemaModel implements \JsonSerializable{
|
|||||||
*/
|
*/
|
||||||
public array $remappedPropertyValuesIndex;
|
public array $remappedPropertyValuesIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var BlockStateUpgradeSchemaModelFlattenInfo[]
|
||||||
|
* @phpstan-var array<string, BlockStateUpgradeSchemaModelFlattenInfo>
|
||||||
|
*/
|
||||||
|
public array $flattenedProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var BlockStateUpgradeSchemaModelBlockRemap[][]
|
* @var BlockStateUpgradeSchemaModelBlockRemap[][]
|
||||||
* @phpstan-var array<string, list<BlockStateUpgradeSchemaModelBlockRemap>>
|
* @phpstan-var array<string, list<BlockStateUpgradeSchemaModelBlockRemap>>
|
||||||
|
@ -43,7 +43,7 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
|||||||
* Either this or newName must be present
|
* Either this or newName must be present
|
||||||
* Due to technical limitations of jsonmapper, we can't use a union type here
|
* Due to technical limitations of jsonmapper, we can't use a union type here
|
||||||
*/
|
*/
|
||||||
public BlockStateUpgradeSchemaModelFlattenedName $newFlattenedName;
|
public BlockStateUpgradeSchemaModelFlattenInfo $newFlattenedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var BlockStateUpgradeSchemaModelTag[]|null
|
* @var BlockStateUpgradeSchemaModelTag[]|null
|
||||||
@ -67,9 +67,9 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
|||||||
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
||||||
* @phpstan-param list<string> $copiedState
|
* @phpstan-param list<string> $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;
|
$this->oldState = count($oldState) === 0 ? null : $oldState;
|
||||||
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenedName){
|
if($newNameRule instanceof BlockStateUpgradeSchemaModelFlattenInfo){
|
||||||
$this->newFlattenedName = $newNameRule;
|
$this->newFlattenedName = $newNameRule;
|
||||||
}else{
|
}else{
|
||||||
$this->newName = $newNameRule;
|
$this->newName = $newNameRule;
|
||||||
|
@ -25,7 +25,7 @@ namespace pocketmine\data\bedrock\block\upgrade\model;
|
|||||||
|
|
||||||
use function count;
|
use function count;
|
||||||
|
|
||||||
final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{
|
final class BlockStateUpgradeSchemaModelFlattenInfo implements \JsonSerializable{
|
||||||
|
|
||||||
/** @required */
|
/** @required */
|
||||||
public string $prefix;
|
public string $prefix;
|
@ -24,8 +24,10 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\data\bedrock\block\upgrade;
|
namespace pocketmine\data\bedrock\block\upgrade;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use pocketmine\block\Block;
|
||||||
use pocketmine\data\bedrock\block\BlockStateData;
|
use pocketmine\data\bedrock\block\BlockStateData;
|
||||||
use pocketmine\nbt\tag\IntTag;
|
use pocketmine\nbt\tag\IntTag;
|
||||||
|
use pocketmine\nbt\tag\StringTag;
|
||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
class BlockStateUpgraderTest extends TestCase{
|
class BlockStateUpgraderTest extends TestCase{
|
||||||
@ -210,6 +212,23 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
self::assertSame($upgradedStateData->getState(self::TEST_PROPERTY_2)?->getValue(), $valueAfter);
|
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<int, array{int, int, bool, int}, void, void>
|
* @phpstan-return \Generator<int, array{int, int, bool, int}, void, void>
|
||||||
*/
|
*/
|
||||||
|
@ -27,7 +27,7 @@ use pocketmine\data\bedrock\block\BlockStateData;
|
|||||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
|
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
|
||||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
|
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
|
||||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap;
|
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\BlockStateUpgradeSchemaUtils;
|
||||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
|
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
|
||||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||||
@ -183,6 +183,11 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra
|
|||||||
$removedProperties = [];
|
$removedProperties = [];
|
||||||
$renamedProperties = [];
|
$renamedProperties = [];
|
||||||
|
|
||||||
|
$uniqueNewIds = [];
|
||||||
|
foreach($upgradeTable as $pair){
|
||||||
|
$uniqueNewIds[$pair->new->getName()] = $pair->new->getName();
|
||||||
|
}
|
||||||
|
|
||||||
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
|
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
|
||||||
if(count($newPropertyValues) === 1){
|
if(count($newPropertyValues) === 1){
|
||||||
$newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)];
|
$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
|
//finally, write the results to the schema
|
||||||
|
|
||||||
if(count($remappedPropertyValues) !== 0){
|
if(count($remappedPropertyValues) !== 0){
|
||||||
@ -332,60 +392,69 @@ function findCommonSuffix(array $strings) : string{
|
|||||||
return strrev(findCommonPrefix($reversed));
|
return strrev(findCommonPrefix($reversed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $valueToId
|
||||||
|
* @phpstan-param array<string, string> $valueToId
|
||||||
|
* @phpstan-param class-string<ByteTag|IntTag|StringTag> $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
|
* @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
|
* @param string[] $candidateFlattenPropertyTypes
|
||||||
* @phpstan-param array<string, class-string<ByteTag|IntTag|StringTag>> $candidateFlattenPropertyTypes
|
* @phpstan-param array<string, class-string<ByteTag|IntTag|StringTag>> $candidateFlattenPropertyTypes
|
||||||
*
|
*
|
||||||
* @return BlockStateUpgradeSchemaFlattenedName[][]
|
* @return BlockStateUpgradeSchemaFlattenInfo[][]
|
||||||
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenedName>>
|
* @phpstan-return array<string, array<string, BlockStateUpgradeSchemaFlattenInfo>>
|
||||||
*/
|
*/
|
||||||
function buildFlattenPropertyRules(array $candidateFlattenedValues, array $candidateFlattenPropertyTypes) : 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){
|
||||||
$ids = array_values($valueToId);
|
$flattenPropertyRules[$propertyName][$filter] = buildFlattenPropertyRule($valueToId, $propertyName, $candidateFlattenPropertyTypes[$propertyName]);
|
||||||
|
|
||||||
//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],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ksort($flattenPropertyRules, SORT_STRING);
|
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");
|
throw new \RuntimeException("States with the same ID should be fully consistent");
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
//try processing this as a regular state group first
|
||||||
//even if some of the states stay under the same ID, the compression techniques used by this function
|
//if a property was flattened into the ID, the remaining states will normally be consistent
|
||||||
//implicitly rely on knowing the full set of old states and their new transformations
|
//if not we fall back to remap states and state filters
|
||||||
$result->remappedStates[$oldName] = processRemappedStates($blockStateMappings);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user