From 0cc997f531c34dc684f73527e3e48930d42cc947 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 4 Feb 2022 00:16:48 +0000 Subject: [PATCH] Checking in BlockStateUpgrader and a bunch of unit tests --- .../bedrock/blockstate/BlockStateData.php | 7 + .../upgrade/BlockStateUpgradeSchema.php | 70 +++++ .../upgrade/BlockStateUpgradeSchemaUtils.php | 221 ++++++++++++++++ .../BlockStateUpgradeSchemaValueRemap.php | 34 +++ .../blockstate/upgrade/BlockStateUpgrader.php | 163 ++++++++++++ .../LegacyIdMetaToBlockStateDataMap.php | 64 +++++ .../upgrade/BlockStateUpgraderTest.php | 240 ++++++++++++++++++ .../mcpe/convert/RuntimeBlockMappingTest.php | 39 +++ 8 files changed, 838 insertions(+) create mode 100644 src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchema.php create mode 100644 src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaUtils.php create mode 100644 src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaValueRemap.php create mode 100644 src/data/bedrock/blockstate/upgrade/BlockStateUpgrader.php create mode 100644 src/data/bedrock/blockstate/upgrade/LegacyIdMetaToBlockStateDataMap.php create mode 100644 tests/phpunit/data/bedrock/blockstate/upgrade/BlockStateUpgraderTest.php create mode 100644 tests/phpunit/network/mcpe/convert/RuntimeBlockMappingTest.php diff --git a/src/data/bedrock/blockstate/BlockStateData.php b/src/data/bedrock/blockstate/BlockStateData.php index c6485c748..5d81152ca 100644 --- a/src/data/bedrock/blockstate/BlockStateData.php +++ b/src/data/bedrock/blockstate/BlockStateData.php @@ -85,4 +85,11 @@ final class BlockStateData{ ->setInt(self::TAG_VERSION, $this->version) ->setTag(self::TAG_STATES, $this->states); } + + public function equals(self $that) : bool{ + return + $this->name === $that->name && + $this->states->equals($that->states) && + $this->version === $that->version; + } } diff --git a/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchema.php b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchema.php new file mode 100644 index 000000000..b47c9f1fe --- /dev/null +++ b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchema.php @@ -0,0 +1,70 @@ + + */ + public array $renamedIds = []; + + /** + * @var Tag[][] + * @phpstan-var array> + */ + public array $addedProperties = []; + + /** + * @var string[][] + * @phpstan-var array> + */ + public array $removedProperties = []; + + /** + * @var string[][] + * @phpstan-var array> + */ + public array $renamedProperties = []; + + /** + * @var ValueRemap[][][] + * @phpstan-var array>> + */ + public array $remappedPropertyValues = []; + + public function __construct( + public int $maxVersionMajor, + public int $maxVersionMinor, + public int $maxVersionPatch, + public int $maxVersionRevision + ){} + + public function getVersionId() : int{ + return ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision; + } +} diff --git a/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaUtils.php b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaUtils.php new file mode 100644 index 000000000..2430069cf --- /dev/null +++ b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaUtils.php @@ -0,0 +1,221 @@ +renamedIds as $rename){ + $lines[] = "- $rename"; + } + $lines[] = "Added properties:"; + foreach(Utils::stringifyKeys($schema->addedProperties) as $blockName => $tags){ + foreach(Utils::stringifyKeys($tags) as $k => $v){ + $lines[] = "- $blockName has $k added: $v"; + } + } + + $lines[] = "Removed properties:"; + foreach(Utils::stringifyKeys($schema->removedProperties) as $blockName => $tagNames){ + foreach($tagNames as $tagName){ + $lines[] = "- $blockName has $tagName removed"; + } + } + $lines[] = "Renamed properties:"; + foreach(Utils::stringifyKeys($schema->renamedProperties) as $blockName => $tagNames){ + foreach(Utils::stringifyKeys($tagNames) as $oldTagName => $newTagName){ + $lines[] = "- $blockName has $oldTagName renamed to $newTagName"; + } + } + $lines[] = "Remapped property values:"; + foreach(Utils::stringifyKeys($schema->remappedPropertyValues) as $blockName => $remaps){ + foreach(Utils::stringifyKeys($remaps) as $tagName => $oldNewList){ + foreach($oldNewList as $oldNew){ + $lines[] = "- $blockName has $tagName value changed from $oldNew->old to $oldNew->new"; + } + } + } + return implode("\n", $lines); + } + + private static function tagToJsonModel(Tag $tag) : BlockStateUpgradeSchemaModelTag{ + $type = match(get_class($tag)){ + IntTag::class => "int", + StringTag::class => "string", + ByteTag::class => "byte", + default => throw new \UnexpectedValueException() + }; + + return new BlockStateUpgradeSchemaModelTag($type, $tag->getValue()); + } + + private static function jsonModelToTag(BlockStateUpgradeSchemaModelTag $model) : Tag{ + if($model->type === "int"){ + if(!is_int($model->value)){ + throw new \UnexpectedValueException("Value for type int must be an int"); + } + return new IntTag($model->value); + }elseif($model->type === "byte"){ + if(!is_int($model->value)){ + throw new \UnexpectedValueException("Value for type byte must be an int"); + } + return new ByteTag($model->value); + }elseif($model->type === "string"){ + if(!is_string($model->value)){ + throw new \UnexpectedValueException("Value for type string must be a string"); + } + return new StringTag($model->value); + }else{ + throw new \UnexpectedValueException("Unknown blockstate value type $model->type"); + } + } + + public static function fromJsonModel(BlockStateUpgradeSchemaModel $model) : BlockStateUpgradeSchema{ + $result = new BlockStateUpgradeSchema( + $model->maxVersionMajor, + $model->maxVersionMinor, + $model->maxVersionPatch, + $model->maxVersionRevision + ); + $result->renamedIds = $model->renamedIds ?? []; + $result->renamedProperties = $model->renamedProperties ?? []; + $result->removedProperties = $model->removedProperties ?? []; + + foreach(Utils::stringifyKeys($model->addedProperties ?? []) as $blockName => $properties){ + foreach(Utils::stringifyKeys($properties) as $propertyName => $propertyValue){ + $result->addedProperties[$blockName][$propertyName] = self::jsonModelToTag($propertyValue); + } + } + + foreach(Utils::stringifyKeys($model->remappedPropertyValues ?? []) as $blockName => $properties){ + foreach(Utils::stringifyKeys($properties) as $property => $mappedValuesKey){ + foreach($mappedValuesKey as $oldNew){ + $result->remappedPropertyValues[$blockName][$property][] = new BlockStateUpgradeSchemaValueRemap( + self::jsonModelToTag($oldNew->old), + self::jsonModelToTag($oldNew->new) + ); + } + } + } + + return $result; + } + + public static function toJsonModel(BlockStateUpgradeSchema $schema) : BlockStateUpgradeSchemaModel{ + $result = new BlockStateUpgradeSchemaModel(); + $result->maxVersionMajor = $schema->maxVersionMajor; + $result->maxVersionMinor = $schema->maxVersionMinor; + $result->maxVersionPatch = $schema->maxVersionPatch; + $result->maxVersionRevision = $schema->maxVersionRevision; + $result->renamedIds = $schema->renamedIds; + $result->renamedProperties = $schema->renamedProperties; + $result->removedProperties = $schema->removedProperties; + + foreach(Utils::stringifyKeys($schema->addedProperties) as $blockName => $properties){ + foreach(Utils::stringifyKeys($properties) as $propertyName => $propertyValue){ + $result->addedProperties[$blockName][$propertyName] = self::tagToJsonModel($propertyValue); + } + } + + foreach(Utils::stringifyKeys($schema->remappedPropertyValues) as $blockName => $properties){ + foreach(Utils::stringifyKeys($properties) as $property => $propertyValues){ + foreach($propertyValues as $oldNew){ + $result->remappedPropertyValues[$blockName][$property][] = (array) new BlockStateUpgradeSchemaModelValueRemap( + self::tagToJsonModel($oldNew->old), + self::tagToJsonModel($oldNew->new) + ); + } + } + } + + return $result; + } + + /** + * Returns a list of schemas ordered by priority. Oldest schemas appear first. + * + * @return BlockStateUpgradeSchema[] + */ + public static function loadSchemas(string $path) : array{ + $iterator = new \RegexIterator( + new \FilesystemIterator( + $path, + \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS + ), + '/\/mapping_schema_(\d{4}).*\.json$/', + \RegexIterator::GET_MATCH + ); + + $result = []; + + $jsonMapper = new \JsonMapper(); + /** @var string[] $matches */ + foreach($iterator as $matches){ + $filename = $matches[0]; + $priority = (int) $matches[1]; + + var_dump($filename); + + $fullPath = Path::join($path, $filename); + + //TODO: should we bother handling exceptions in here? + $raw = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($fullPath)); + + $json = json_decode($raw, false, flags: JSON_THROW_ON_ERROR); + if(!is_object($json)){ + throw new \RuntimeException("Unexpected root type of schema file $fullPath"); + } + $model = $jsonMapper->map($json, new BlockStateUpgradeSchemaModel()); + + $result[$priority] = self::fromJsonModel($model); + } + + ksort($result, SORT_NUMERIC); + return $result; + } +} diff --git a/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaValueRemap.php b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaValueRemap.php new file mode 100644 index 000000000..38f3b1f3f --- /dev/null +++ b/src/data/bedrock/blockstate/upgrade/BlockStateUpgradeSchemaValueRemap.php @@ -0,0 +1,34 @@ +upgradeSchemas[$schema->getVersionId()][$priority])){ + throw new \InvalidArgumentException("Another schema already has this priority"); + } + $this->upgradeSchemas[$schema->getVersionId()][$priority] = $schema; + ksort($this->upgradeSchemas, SORT_NUMERIC | SORT_ASC); + ksort($this->upgradeSchemas[$schema->getVersionId()], SORT_NUMERIC | SORT_ASC); + } + + public function upgrade(BlockStateData $blockStateData) : BlockStateData{ + $oldName = $blockStateData->getName(); + + $version = $blockStateData->getVersion(); + foreach($this->upgradeSchemas as $resultVersion => $schemas){ + if($version > $resultVersion){ + //even if this is actually the same version, we have to apply it anyway because mojang are dumb and + //didn't always bump the blockstate version when changing it :( + continue; + } + foreach($schemas as $schema){ + $newName = $schema->renamedIds[$oldName] ?? null; + + $stateChanges = 0; + $states = $blockStateData->getStates(); + + $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges); + + if($newName !== null || $stateChanges > 0){ + $blockStateData = new BlockStateData($newName ?? $oldName, $states, $resultVersion); + //don't break out; we may need to further upgrade the state + } + } + } + + return $blockStateData; + } + + private function cloneIfNeeded(CompoundTag $states, int &$stateChanges) : CompoundTag{ + if($stateChanges === 0){ + $states = clone $states; + } + $stateChanges++; + + return $states; + } + + private function applyPropertyAdded(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{ + $newStates = $states; + if(isset($schema->addedProperties[$oldName])){ + foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){ + $oldValue = $states->getTag($propertyName); + if($oldValue === null){ + $newStates = $this->cloneIfNeeded($newStates, $stateChanges); + $newStates->setTag($propertyName, $value); + } + } + } + + return $newStates; + } + + private function applyPropertyRemoved(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{ + $newStates = $states; + if(isset($schema->removedProperties[$oldName])){ + foreach($schema->removedProperties[$oldName] as $propertyName){ + if($states->getTag($propertyName) !== null){ + $newStates = $this->cloneIfNeeded($newStates, $stateChanges); + $newStates->removeTag($propertyName); + } + } + } + + return $newStates; + } + + private function locateNewPropertyValue(BlockStateUpgradeSchema $schema, string $oldName, string $oldPropertyName, Tag $oldValue) : Tag{ + if(isset($schema->remappedPropertyValues[$oldName][$oldPropertyName])){ + foreach($schema->remappedPropertyValues[$oldName][$oldPropertyName] as $mappedPair){ + if($mappedPair->old->equals($oldValue)){ + return $mappedPair->new; + } + } + } + + return $oldValue; + } + + private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{ + if(isset($schema->renamedProperties[$oldName])){ + foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){ + $oldValue = $states->getTag($oldPropertyName); + if($oldValue !== null){ + $states = $this->cloneIfNeeded($states, $stateChanges); + $states->removeTag($oldPropertyName); + + //If a value remap is needed, we need to do it here, since we won't be able to locate the property + //after it's been renamed - value remaps are always indexed by old property name for the sake of + //being able to do changes in any order. + $states->setTag($newPropertyName, $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue)); + } + } + } + + return $states; + } + + private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{ + if(isset($schema->remappedPropertyValues[$oldName])){ + foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){ + $oldValue = $states->getTag($oldPropertyName); + if($oldValue !== null){ + $newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue); + if($newValue !== $oldValue){ + $states = $this->cloneIfNeeded($states, $stateChanges); + $states->setTag($oldPropertyName, $newValue); + } + } + } + } + + return $states; + } +} diff --git a/src/data/bedrock/blockstate/upgrade/LegacyIdMetaToBlockStateDataMap.php b/src/data/bedrock/blockstate/upgrade/LegacyIdMetaToBlockStateDataMap.php new file mode 100644 index 000000000..3ecf18a17 --- /dev/null +++ b/src/data/bedrock/blockstate/upgrade/LegacyIdMetaToBlockStateDataMap.php @@ -0,0 +1,64 @@ +> $mappingTable + */ + public function __construct( + private array $mappingTable + ){} + + public function getDataFromLegacyIdMeta(string $id, int $meta) : ?BlockStateData{ + return $this->mappingTable[$id][$meta] ?? null; + } + + public static function loadFromString(string $data) : self{ + $mappingTable = []; + + $legacyStateMapReader = new BinaryStream($data); + $nbtReader = new NetworkNbtSerializer(); + while(!$legacyStateMapReader->feof()){ + $id = $legacyStateMapReader->get($legacyStateMapReader->getUnsignedVarInt()); + $meta = $legacyStateMapReader->getLShort(); + + $offset = $legacyStateMapReader->getOffset(); + $state = $nbtReader->read($legacyStateMapReader->getBuffer(), $offset)->mustGetCompoundTag(); + $legacyStateMapReader->setOffset($offset); + $mappingTable[$id][$meta] = BlockStateData::fromNbt($state); + } + + return new self($mappingTable); + } +} diff --git a/tests/phpunit/data/bedrock/blockstate/upgrade/BlockStateUpgraderTest.php b/tests/phpunit/data/bedrock/blockstate/upgrade/BlockStateUpgraderTest.php new file mode 100644 index 000000000..caf917ec4 --- /dev/null +++ b/tests/phpunit/data/bedrock/blockstate/upgrade/BlockStateUpgraderTest.php @@ -0,0 +1,240 @@ +upgrader = new BlockStateUpgrader(); + } + + private function getNewSchema() : BlockStateUpgradeSchema{ + return $this->getNewSchemaVersion(PHP_INT_MAX); + } + + private function getNewSchemaVersion(int $versionId) : BlockStateUpgradeSchema{ + $schema = new BlockStateUpgradeSchema(($versionId >> 24) & 0xff, ($versionId >> 16) & 0xff, ($versionId >> 8) & 0xff, $versionId & 0xff); + $this->upgrader->addSchema($schema, 0); + return $schema; + } + + /** + * @phpstan-param \Closure() : BlockStateData $getStateData + */ + private function upgrade(BlockStateData $stateData, \Closure $getStateData) : BlockStateData{ + $result = $this->upgrader->upgrade($stateData); + self::assertTrue($stateData->equals($getStateData()), "Upgrading states must not alter the original input"); + + return $result; + } + + public function testRenameId() : void{ + $this->getNewSchema()->renamedIds[self::TEST_BLOCK] = self::TEST_BLOCK_2; + + $getStateData = fn() => $this->getEmptyPreimage(); + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertSame($upgradedStateData->getName(), self::TEST_BLOCK_2); + } + + private function prepareAddPropertySchema(BlockStateUpgradeSchema $schema) : void{ + $schema->addedProperties[self::TEST_BLOCK][self::TEST_PROPERTY] = new IntTag(self::TEST_PROPERTY_VALUE_1); + } + + private function getEmptyPreimage() : BlockStateData{ + return new BlockStateData(self::TEST_BLOCK, CompoundTag::create(), self::TEST_VERSION); + } + + private function getPreimageOneProperty(string $propertyName, int $value) : BlockStateData{ + return new BlockStateData( + self::TEST_BLOCK, + CompoundTag::create()->setInt($propertyName, $value), + self::TEST_VERSION + ); + } + + public function testAddNewProperty() : void{ + $this->prepareAddPropertySchema($this->getNewSchema()); + + $getStateData = fn() => $this->getEmptyPreimage(); + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertSame(self::TEST_PROPERTY_VALUE_1, $upgradedStateData->getStates()->getTag(self::TEST_PROPERTY)?->getValue()); + } + + public function testAddPropertyAlreadyExists() : void{ + $this->prepareAddPropertySchema($this->getNewSchema()); + + $getStateData = fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_1 + 1); + $stateData = $getStateData(); + $upgradedStateData = $this->upgrade($stateData, $getStateData); + + self::assertSame($stateData, $upgradedStateData, "Adding a property that already exists with a different value should not alter the state"); + } + + private function prepareRemovePropertySchema(BlockStateUpgradeSchema $schema) : void{ + $schema->removedProperties[self::TEST_BLOCK][] = self::TEST_PROPERTY; + } + + /** + * @phpstan-return \Generator + */ + public function removePropertyProvider() : \Generator{ + yield [fn() => $this->getEmptyPreimage()]; + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_1)]; + } + + /** + * @dataProvider removePropertyProvider + * @phpstan-param \Closure() : BlockStateData $getStateData + */ + public function testRemoveProperty(\Closure $getStateData) : void{ + $this->prepareRemovePropertySchema($this->getNewSchema()); + + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertNull($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY)); + } + + private function prepareRenamePropertySchema(BlockStateUpgradeSchema $schema) : void{ + $schema->renamedProperties[self::TEST_BLOCK][self::TEST_PROPERTY] = self::TEST_PROPERTY_2; + } + + /** + * @phpstan-return \Generator + */ + public function renamePropertyProvider() : \Generator{ + yield [fn() => $this->getEmptyPreimage(), null]; + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_1), self::TEST_PROPERTY_VALUE_1]; + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY_2, self::TEST_PROPERTY_VALUE_1), self::TEST_PROPERTY_VALUE_1]; + } + + /** + * @dataProvider renamePropertyProvider + * @phpstan-param \Closure() : BlockStateData $getStateData + */ + public function testRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ + $this->prepareRenamePropertySchema($this->getNewSchema()); + + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertSame($valueAfter, $upgradedStateData->getStates()->getTag(self::TEST_PROPERTY_2)?->getValue()); + } + + private function prepareRemapPropertyValueSchema(BlockStateUpgradeSchema $schema) : void{ + $schema->remappedPropertyValues[self::TEST_BLOCK][self::TEST_PROPERTY][] = new BlockStateUpgradeSchemaValueRemap( + new IntTag(self::TEST_PROPERTY_VALUE_1), + new IntTag(self::TEST_PROPERTY_VALUE_2) + ); + } + + /** + * @phpstan-return \Generator + */ + public function remapPropertyValueProvider() : \Generator{ + //no property to remap + yield [fn() => $this->getEmptyPreimage(), null]; + + //value that will be remapped + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_1), self::TEST_PROPERTY_VALUE_2]; + + //value that is already at the target value + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_2), self::TEST_PROPERTY_VALUE_2]; + + //value that is not remapped and is different from target value (to detect unconditional overwrite bugs) + yield [fn() => $this->getPreimageOneProperty(self::TEST_PROPERTY, self::TEST_PROPERTY_VALUE_3), self::TEST_PROPERTY_VALUE_3]; + } + + /** + * @dataProvider remapPropertyValueProvider + * @phpstan-param \Closure() : BlockStateData $getStateData + */ + public function testRemapPropertyValue(\Closure $getStateData, ?int $valueAfter) : void{ + $this->prepareRemapPropertyValueSchema($this->getNewSchema()); + + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertSame($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY)?->getValue(), $valueAfter); + } + + /** + * @dataProvider remapPropertyValueProvider + * @phpstan-param \Closure() : BlockStateData $getStateData + */ + public function testRemapAndRenameProperty(\Closure $getStateData, ?int $valueAfter) : void{ + $schema = $this->getNewSchema(); + $this->prepareRenamePropertySchema($schema); + $this->prepareRemapPropertyValueSchema($schema); + + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + + self::assertSame($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY_2)?->getValue(), $valueAfter); + } + + /** + * @phpstan-return \Generator + */ + public function upgraderVersionCompatibilityProvider() : \Generator{ + yield [0x1_00_00_00, 0x1_00_00_00, true]; //Same version: must be altered - this may be a backwards-compatible change that Mojang didn't bother to bump for + yield [0x1_00_01_00, 0x1_00_00_00, true]; //Schema newer than block: must be altered + yield [0x1_00_00_00, 0x1_00_01_00, false]; //Block newer than schema: block must NOT be altered + } + + /** + * @dataProvider upgraderVersionCompatibilityProvider + */ + public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange) : void{ + $schema = $this->getNewSchemaVersion($schemaVersion); + $schema->renamedIds[self::TEST_BLOCK] = self::TEST_BLOCK_2; + + $getStateData = fn() => new BlockStateData( + self::TEST_BLOCK, + CompoundTag::create(), + $stateVersion + ); + + $upgradedStateData = $this->upgrade($getStateData(), $getStateData); + $originalStateData = $getStateData(); + + self::assertNotSame($shouldChange, $upgradedStateData->equals($originalStateData)); + } +} diff --git a/tests/phpunit/network/mcpe/convert/RuntimeBlockMappingTest.php b/tests/phpunit/network/mcpe/convert/RuntimeBlockMappingTest.php new file mode 100644 index 000000000..cc6b43dd6 --- /dev/null +++ b/tests/phpunit/network/mcpe/convert/RuntimeBlockMappingTest.php @@ -0,0 +1,39 @@ +getAllKnownStates() as $state){ + RuntimeBlockMapping::getInstance()->toRuntimeId($state->getFullId()); + } + } +}