62 $lines[] =
"Renames:";
63 foreach($schema->renamedIds as $rename){
64 $lines[] =
"- $rename";
66 $lines[] =
"Added properties:";
67 foreach(Utils::stringifyKeys($schema->addedProperties) as $blockName => $tags){
68 foreach(Utils::stringifyKeys($tags) as $k => $v){
69 $lines[] =
"- $blockName has $k added: $v";
73 $lines[] =
"Removed properties:";
74 foreach(Utils::stringifyKeys($schema->removedProperties) as $blockName => $tagNames){
75 foreach($tagNames as $tagName){
76 $lines[] =
"- $blockName has $tagName removed";
79 $lines[] =
"Renamed properties:";
80 foreach(Utils::stringifyKeys($schema->renamedProperties) as $blockName => $tagNames){
81 foreach(Utils::stringifyKeys($tagNames) as $oldTagName => $newTagName){
82 $lines[] =
"- $blockName has $oldTagName renamed to $newTagName";
85 $lines[] =
"Remapped property values:";
86 foreach(Utils::stringifyKeys($schema->remappedPropertyValues) as $blockName => $remaps){
87 foreach(Utils::stringifyKeys($remaps) as $tagName => $oldNewList){
88 foreach($oldNewList as $oldNew){
89 $lines[] =
"- $blockName has $tagName value changed from $oldNew->old to $oldNew->new";
93 return implode(
"\n", $lines);
98 if($tag instanceof
IntTag){
102 }elseif($tag instanceof
ByteTag){
105 throw new \UnexpectedValueException(
"Unexpected value type " . get_debug_type($tag));
113 isset($model->byte) && !isset($model->int) && !isset($model->string) =>
new ByteTag($model->byte),
114 !isset($model->byte) && isset($model->int) && !isset($model->string) =>
new IntTag($model->int),
115 !isset($model->byte) && !isset($model->int) && isset($model->string) =>
new StringTag($model->string),
116 default =>
throw new \UnexpectedValueException(
"Malformed JSON model tag, expected exactly one of 'byte', 'int' or 'string' properties")
122 $model->maxVersionMajor,
123 $model->maxVersionMinor,
124 $model->maxVersionPatch,
125 $model->maxVersionRevision,
128 $result->renamedIds = $model->renamedIds ?? [];
129 $result->renamedProperties = $model->renamedProperties ?? [];
130 $result->removedProperties = $model->removedProperties ?? [];
132 foreach(Utils::stringifyKeys($model->addedProperties ?? []) as $blockName => $properties){
133 foreach(Utils::stringifyKeys($properties) as $propertyName => $propertyValue){
134 $result->addedProperties[$blockName][$propertyName] = self::jsonModelToTag($propertyValue);
138 $convertedRemappedValuesIndex = [];
139 foreach(Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){
140 foreach($mappingValues as $oldNew){
142 self::jsonModelToTag($oldNew->old),
143 self::jsonModelToTag($oldNew->new)
148 foreach(Utils::stringifyKeys($model->remappedPropertyValues ?? []) as $blockName => $properties){
149 foreach(Utils::stringifyKeys($properties) as $property => $mappedValuesKey){
150 if(!isset($convertedRemappedValuesIndex[$mappedValuesKey])){
151 throw new \UnexpectedValueException(
"Missing key from schema values index $mappedValuesKey");
153 $result->remappedPropertyValues[$blockName][$property] = $convertedRemappedValuesIndex[$mappedValuesKey];
157 foreach(Utils::stringifyKeys($model->flattenedProperties ?? []) as $blockName => $flattenRule){
158 $result->flattenedProperties[$blockName] = self::jsonModelToFlattenRule($flattenRule);
161 foreach(Utils::stringifyKeys($model->remappedStates ?? []) as $oldBlockName => $remaps){
162 foreach($remaps as $remap){
163 if(isset($remap->newName)){
164 $remapName = $remap->newName;
165 }elseif(isset($remap->newFlattenedName)){
166 $flattenRule = $remap->newFlattenedName;
167 $remapName = self::jsonModelToFlattenRule($flattenRule);
169 throw new \UnexpectedValueException(
"Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
176 $remap->copiedState ?? []
185 if(count($schema->remappedPropertyValues) === 0){
191 $orderedRemappedValues = $schema->remappedPropertyValues;
192 ksort($orderedRemappedValues);
193 foreach(Utils::stringifyKeys($orderedRemappedValues) as $blockName => $remaps){
195 foreach(Utils::stringifyKeys($remaps) as $propertyName => $remappedValues){
196 $remappedValuesMap = [];
197 foreach($remappedValues as $oldNew){
198 $remappedValuesMap[$oldNew->old->toString()] = $oldNew;
200 ksort($remappedValuesMap);
202 if(isset($dedupTableMap[$propertyName])){
203 foreach($dedupTableMap[$propertyName] as $k => $dedupValuesMap){
204 if(count($remappedValuesMap) !== count($dedupValuesMap)){
208 foreach(Utils::stringifyKeys($remappedValuesMap) as $oldHash => $remappedOldNew){
210 !isset($dedupValuesMap[$oldHash]) ||
211 !$remappedOldNew->old->equals($dedupValuesMap[$oldHash]->old) ||
212 !$remappedOldNew->new->equals($dedupValuesMap[$oldHash]->new)
219 $dedupMapping[$blockName][$propertyName] = $k;
225 $dedupTableMap[$propertyName][] = $remappedValuesMap;
226 $dedupMapping[$blockName][$propertyName] = array_key_last($dedupTableMap[$propertyName]);
231 foreach(Utils::stringifyKeys($dedupTableMap) as $propertyName => $mappingSet){
232 foreach($mappingSet as $setId => $valuePairs){
233 $newDedupName = $propertyName .
"_" . str_pad(strval($setId), 2,
"0", STR_PAD_LEFT);
234 foreach($valuePairs as $pair){
236 BlockStateUpgradeSchemaUtils::tagToJsonModel($pair->old),
237 BlockStateUpgradeSchemaUtils::tagToJsonModel($pair->new),
242 $modelDedupMapping = [];
243 foreach(Utils::stringifyKeys($dedupMapping) as $blockName => $properties){
244 foreach(Utils::stringifyKeys($properties) as $propertyName => $dedupTableIndex){
245 $modelDedupMapping[$blockName][$propertyName] = $propertyName .
"_" . str_pad(strval($dedupTableIndex), 2,
"0", STR_PAD_LEFT);
250 ksort($modelDedupMapping);
251 foreach(Utils::stringifyKeys($dedupMapping) as $blockName => $properties){
253 $dedupMapping[$blockName] = $properties;
256 $model->remappedPropertyValuesIndex = $modelTable;
257 $model->remappedPropertyValues = $modelDedupMapping;
262 $flattenRule->prefix,
263 $flattenRule->flattenedProperty,
264 $flattenRule->suffix,
265 $flattenRule->flattenedValueRemaps,
266 match($flattenRule->flattenedPropertyType){
267 StringTag::class => null,
268 ByteTag::class =>
"byte",
269 IntTag::class =>
"int",
270 default => throw new \LogicException(
"Unexpected tag type " . $flattenRule->flattenedPropertyType .
" in flattened property type")
277 $flattenRule->prefix,
278 $flattenRule->flattenedProperty,
279 $flattenRule->suffix,
280 $flattenRule->flattenedValueRemaps ?? [],
281 match ($flattenRule->flattenedPropertyType) {
282 "string", null => StringTag::class,
283 "int" => IntTag::class,
284 "byte" => ByteTag::class,
285 default => throw new \UnexpectedValueException(
"Unexpected flattened property type $flattenRule->flattenedPropertyType, expected 'string', 'int' or 'byte'")
292 $result->maxVersionMajor = $schema->maxVersionMajor;
293 $result->maxVersionMinor = $schema->maxVersionMinor;
294 $result->maxVersionPatch = $schema->maxVersionPatch;
295 $result->maxVersionRevision = $schema->maxVersionRevision;
297 $result->renamedIds = $schema->renamedIds;
298 ksort($result->renamedIds);
300 $result->renamedProperties = $schema->renamedProperties;
301 ksort($result->renamedProperties);
302 foreach(Utils::stringifyKeys($result->renamedProperties) as $blockName => $properties){
304 $result->renamedProperties[$blockName] = $properties;
307 $result->removedProperties = $schema->removedProperties;
308 ksort($result->removedProperties);
309 foreach(Utils::stringifyKeys($result->removedProperties) as $blockName => $properties){
311 $result->removedProperties[$blockName] = $properties;
314 foreach(Utils::stringifyKeys($schema->addedProperties) as $blockName => $properties){
315 $addedProperties = [];
316 foreach(Utils::stringifyKeys($properties) as $propertyName => $propertyValue){
317 $addedProperties[$propertyName] = self::tagToJsonModel($propertyValue);
319 ksort($addedProperties);
320 $result->addedProperties[$blockName] = $addedProperties;
322 if(isset($result->addedProperties)){
323 ksort($result->addedProperties);
326 self::buildRemappedValuesIndex($schema, $result);
328 foreach(Utils::stringifyKeys($schema->flattenedProperties) as $blockName => $flattenRule){
329 $result->flattenedProperties[$blockName] = self::flattenRuleToJsonModel($flattenRule);
331 if(isset($result->flattenedProperties)){
332 ksort($result->flattenedProperties);
335 foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
337 foreach($remaps as $remap){
339 array_map(fn(
Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
340 is_string($remap->newName) ? $remap->newName : self::flattenRuleToJsonModel($remap->newName),
341 array_map(fn(
Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
344 if(count($modelRemap->copiedState) === 0){
345 unset($modelRemap->copiedState);
347 $key = json_encode($modelRemap);
348 assert(!isset($keyedRemaps[$key]));
349 if(isset($keyedRemaps[$key])){
352 $keyedRemaps[$key] = $modelRemap;
356 $filterSizeCompare = count($b->oldState ?? []) <=> count($a->oldState ?? []);
357 if($filterSizeCompare !== 0){
358 return $filterSizeCompare;
361 return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
363 $result->remappedStates[$oldBlockName] = $keyedRemaps;
365 if(isset($result->remappedStates)){
366 ksort($result->remappedStates);
377 public static function loadSchemas(
string $path,
int $maxSchemaId) : array{
378 $iterator = new \RegexIterator(
379 new \FilesystemIterator(
381 \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::SKIP_DOTS
383 '/^(\d{4}).*\.json$/',
384 \RegexIterator::GET_MATCH,
385 \RegexIterator::USE_KEY
391 foreach($iterator as $matches){
392 $filename = $matches[0];
393 $schemaId = (int) $matches[1];
395 if($schemaId > $maxSchemaId){
399 $fullPath = Path::join($path, $filename);
401 $raw = Filesystem::fileGetContents($fullPath);
404 $schema = self::loadSchemaFromString($raw, $schemaId);
405 }
catch(\RuntimeException $e){
406 throw new \RuntimeException(
"Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
409 $result[$schemaId] = $schema;
412 ksort($result, SORT_NUMERIC);
416 public static function loadSchemaFromString(
string $raw,
int $schemaId) : BlockStateUpgradeSchema{
418 $json = json_decode($raw, false, flags: JSON_THROW_ON_ERROR);
419 }
catch(\JsonException $e){
420 throw new \RuntimeException($e->getMessage(), 0, $e);
422 if(!is_object($json)){
423 throw new \RuntimeException(
"Unexpected root type of schema file " . gettype($json) .
", expected object");
426 $jsonMapper = new \JsonMapper();
427 $jsonMapper->bExceptionOnMissingData =
true;
428 $jsonMapper->bExceptionOnUndefinedProperty =
true;
429 $jsonMapper->bStrictObjectTypeChecking =
true;
431 $model = $jsonMapper->map($json,
new BlockStateUpgradeSchemaModel());
432 }
catch(\JsonMapper_Exception $e){
433 throw new \RuntimeException($e->getMessage(), 0, $e);
436 return self::fromJsonModel($model, $schemaId);