46 private array $upgradeSchemas = [];
48 private int $outputVersion = 0;
55 foreach($upgradeSchemas as $schema){
56 $this->addSchema($schema);
61 $schemaId = $schema->getSchemaId();
63 if(isset($this->upgradeSchemas[$versionId][$schemaId])){
64 throw new \InvalidArgumentException(
"Cannot add two schemas with the same schema ID and version ID");
68 $this->upgradeSchemas[$versionId][$schemaId] = $schema;
70 ksort($this->upgradeSchemas, SORT_NUMERIC);
71 ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC);
73 $this->outputVersion = max($this->outputVersion, $schema->
getVersionId());
76 public function upgrade(BlockStateData $blockStateData) : BlockStateData{
77 $version = $blockStateData->getVersion();
78 foreach($this->upgradeSchemas as $resultVersion => $schemaList){
89 if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){
93 foreach($schemaList as $schema){
94 $blockStateData = $this->applySchema($schema, $blockStateData);
98 if($this->outputVersion > $version){
101 $blockStateData =
new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion);
103 return $blockStateData;
106 private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{
107 $newStateData = $this->applyStateRemapped($schema, $blockStateData);
108 if($newStateData !==
null){
109 return $newStateData;
112 $oldName = $blockStateData->getName();
113 $states = $blockStateData->getStates();
115 if(isset($schema->renamedIds[$oldName]) && isset($schema->flattenedProperties[$oldName])){
117 throw new AssumptionFailedError(
"Both renamedIds and flattenedProperties are set for the same block ID \"$oldName\" - don't know what to do");
119 if(isset($schema->renamedIds[$oldName])){
120 $newName = $schema->renamedIds[$oldName] ?? null;
121 }elseif(isset($schema->flattenedProperties[$oldName])){
122 [$newName, $states] = $this->applyPropertyFlattened($schema->flattenedProperties[$oldName], $oldName, $states);
129 $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges);
130 $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges);
131 $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges);
132 $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges);
134 if($newName !==
null || $stateChanges > 0){
135 return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId());
138 return $blockStateData;
141 private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{
142 $oldName = $blockStateData->getName();
143 $oldState = $blockStateData->getStates();
145 if(isset($schema->remappedStates[$oldName])){
146 foreach($schema->remappedStates[$oldName] as $remap){
147 if(count($remap->oldState) > count($oldState)){
151 foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
152 if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
157 if(is_string($remap->newName)){
158 $newName = $remap->newName;
161 [$newName, ] = $this->applyPropertyFlattened($remap->newName, $oldName, $oldState);
164 $newState = $remap->newState;
165 foreach($remap->copiedState as $stateName){
166 if(isset($oldState[$stateName])){
167 $newState[$stateName] = $oldState[$stateName];
171 return new BlockStateData($newName, $newState, $schema->getVersionId());
185 private function applyPropertyAdded(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
186 if(isset($schema->addedProperties[$oldName])){
187 foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){
188 if(!isset($states[$propertyName])){
190 $states[$propertyName] = $value;
205 private function applyPropertyRemoved(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
206 if(isset($schema->removedProperties[$oldName])){
207 foreach($schema->removedProperties[$oldName] as $propertyName){
208 if(isset($states[$propertyName])){
210 unset($states[$propertyName]);
218 private function locateNewPropertyValue(BlockStateUpgradeSchema $schema,
string $oldName,
string $oldPropertyName, Tag $oldValue) : Tag{
219 if(isset($schema->remappedPropertyValues[$oldName][$oldPropertyName])){
220 foreach($schema->remappedPropertyValues[$oldName][$oldPropertyName] as $mappedPair){
221 if($mappedPair->old->equals($oldValue)){
222 return $mappedPair->new;
237 private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
238 if(isset($schema->renamedProperties[$oldName])){
239 foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){
240 $oldValue = $states[$oldPropertyName] ?? null;
241 if($oldValue !== null){
243 unset($states[$oldPropertyName]);
248 $states[$newPropertyName] = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
263 private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema,
string $oldName, array $states,
int &$stateChanges) : array{
264 if(isset($schema->remappedPropertyValues[$oldName])){
265 foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){
266 $oldValue = $states[$oldPropertyName] ?? null;
267 if($oldValue !== null){
268 $newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
269 if($newValue !== $oldValue){
271 $states[$oldPropertyName] = $newValue;
287 private function applyPropertyFlattened(BlockStateUpgradeSchemaFlattenInfo $flattenInfo,
string $oldName, array $states) : array{
288 $flattenedValue = $states[$flattenInfo->flattenedProperty] ?? null;
289 $expectedType = $flattenInfo->flattenedPropertyType;
290 if(!$flattenedValue instanceof $expectedType){
292 return [$oldName, $states];
294 $embedKey = match(get_class($flattenedValue)){
295 StringTag::class => $flattenedValue->getValue(),
296 ByteTag::class => (string) $flattenedValue->getValue(),
297 IntTag::class => (string) $flattenedValue->getValue(),
299 default =>
throw new AssumptionFailedError(
"flattenedPropertyType should be one of these three types, but have " . get_class($flattenedValue)),
301 $embedValue = $flattenInfo->flattenedValueRemaps[$embedKey] ?? $embedKey;
302 $newName = sprintf(
"%s%s%s", $flattenInfo->prefix, $embedValue, $flattenInfo->suffix);
303 unset($states[$flattenInfo->flattenedProperty]);
305 return [$newName, $states];