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, 0); $this->upgrader->addSchema($schema); 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)); } }