22declare(strict_types=1);
24namespace pocketmine\data\bedrock\block\upgrade;
31use
function is_string;
35use
const SORT_NUMERIC;
42 private array $upgradeSchemas = [];
44 private int $outputVersion = 0;
51 foreach($upgradeSchemas as $schema){
52 $this->addSchema($schema);
57 $schemaId = $schema->getSchemaId();
59 if(isset($this->upgradeSchemas[$versionId][$schemaId])){
60 throw new \InvalidArgumentException(
"Cannot add two schemas with the same schema ID and version ID");
64 $this->upgradeSchemas[$versionId][$schemaId] = $schema;
66 ksort($this->upgradeSchemas, SORT_NUMERIC);
67 ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC);
69 $this->outputVersion = max($this->outputVersion, $schema->
getVersionId());
72 public function upgrade(BlockStateData $blockStateData) : BlockStateData{
73 $version = $blockStateData->getVersion();
74 foreach($this->upgradeSchemas as $resultVersion => $schemaList){
83 if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
87 foreach($schemaList as $schema){
88 $blockStateData = $this->applySchema($schema, $blockStateData);
92 if($this->outputVersion > $version){
95 $blockStateData =
new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion);
97 return $blockStateData;
100 private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{
101 $newStateData = $this->applyStateRemapped($schema, $blockStateData);
102 if($newStateData !==
null){
103 return $newStateData;
106 $oldName = $blockStateData->getName();
107 $newName = $schema->renamedIds[$oldName] ??
null;
110 $states = $blockStateData->getStates();
112 $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
113 $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
114 $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
115 $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
117 if($newName !==
null || $stateChanges > 0){
118 return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId());
121 return $blockStateData;
124 private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{
125 $oldName = $blockStateData->getName();
126 $oldState = $blockStateData->getStates();
128 if(isset($schema->remappedStates[$oldName])){
129 foreach($schema->remappedStates[$oldName] as $remap){
130 if(count($remap->oldState) > count($oldState)){
134 foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
135 if(!isset($oldState[$k]) || !$oldState[$k]->
equals($v)){
140 if(is_string($remap->newName)){
141 $newName = $remap->newName;
143 $flattenedValue = $oldState[$remap->newName->flattenedProperty] ??
null;
144 if($flattenedValue instanceof StringTag){
145 $embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue();
146 $newName = sprintf(
"%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix);
147 unset($oldState[$remap->newName->flattenedProperty]);
154 $newState = $remap->newState;
155 foreach($remap->copiedState as $stateName){
156 if(isset($oldState[$stateName])){
157 $newState[$stateName] = $oldState[$stateName];
161 return new BlockStateData($newName, $newState, $schema->getVersionId());
175 private function applyPropertyAdded(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
176 if(isset($schema->addedProperties[$oldName])){
177 foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){
178 if(!isset($states[$propertyName])){
180 $states[$propertyName] = $value;
195 private function applyPropertyRemoved(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
196 if(isset($schema->removedProperties[$oldName])){
197 foreach($schema->removedProperties[$oldName] as $propertyName){
198 if(isset($states[$propertyName])){
200 unset($states[$propertyName]);
208 private function locateNewPropertyValue(BlockStateUpgradeSchema $schema,
string $oldName,
string $oldPropertyName, Tag $oldValue) : Tag{
209 if(isset($schema->remappedPropertyValues[$oldName][$oldPropertyName])){
210 foreach($schema->remappedPropertyValues[$oldName][$oldPropertyName] as $mappedPair){
211 if($mappedPair->old->equals($oldValue)){
212 return $mappedPair->new;
227 private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
228 if(isset($schema->renamedProperties[$oldName])){
229 foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){
230 $oldValue = $states[$oldPropertyName] ??
null;
231 if($oldValue !==
null){
233 unset($states[$oldPropertyName]);
238 $states[$newPropertyName] = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
253 private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
254 if(isset($schema->remappedPropertyValues[$oldName])){
255 foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){
256 $oldValue = $states[$oldPropertyName] ??
null;
257 if($oldValue !==
null){
258 $newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
259 if($newValue !== $oldValue){
261 $states[$oldPropertyName] = $newValue;
__construct(array $upgradeSchemas)