22declare(strict_types=1);
24namespace pocketmine\data\bedrock\block\upgrade;
37use Symfony\Component\Filesystem\Path;
38use
function array_key_last;
39use
function array_map;
40use
function array_values;
43use
function get_debug_type;
46use
function is_object;
47use
function is_string;
48use
function json_decode;
49use
function json_encode;
55use
const JSON_THROW_ON_ERROR;
56use
const SORT_NUMERIC;
57use
const STR_PAD_LEFT;
63 $lines[] =
"Renames:";
64 foreach($schema->renamedIds as $rename){
65 $lines[] =
"- $rename";
67 $lines[] =
"Added properties:";
70 $lines[] =
"- $blockName has $k added: $v";
74 $lines[] =
"Removed properties:";
76 foreach($tagNames as $tagName){
77 $lines[] =
"- $blockName has $tagName removed";
80 $lines[] =
"Renamed properties:";
83 $lines[] =
"- $blockName has $oldTagName renamed to $newTagName";
86 $lines[] =
"Remapped property values:";
89 foreach($oldNewList as $oldNew){
90 $lines[] =
"- $blockName has $tagName value changed from $oldNew->old to $oldNew->new";
94 return implode(
"\n", $lines);
99 if($tag instanceof
IntTag){
103 }elseif($tag instanceof
ByteTag){
106 throw new \UnexpectedValueException(
"Unexpected value type " . get_debug_type($tag));
114 isset($model->byte) && !isset($model->int) && !isset($model->string) =>
new ByteTag($model->byte),
115 !isset($model->byte) && isset($model->int) && !isset($model->string) =>
new IntTag($model->int),
116 !isset($model->byte) && !isset($model->int) && isset($model->string) =>
new StringTag($model->string),
117 default =>
throw new \UnexpectedValueException(
"Malformed JSON model tag, expected exactly one of 'byte', 'int' or 'string' properties")
123 $model->maxVersionMajor,
124 $model->maxVersionMinor,
125 $model->maxVersionPatch,
126 $model->maxVersionRevision,
129 $result->renamedIds = $model->renamedIds ?? [];
130 $result->renamedProperties = $model->renamedProperties ?? [];
131 $result->removedProperties = $model->removedProperties ?? [];
135 $result->addedProperties[$blockName][$propertyName] = self::jsonModelToTag($propertyValue);
139 $convertedRemappedValuesIndex = [];
140 foreach(
Utils::stringifyKeys($model->remappedPropertyValuesIndex ?? []) as $mappingKey => $mappingValues){
141 foreach($mappingValues as $k => $oldNew){
143 self::jsonModelToTag($oldNew->old),
144 self::jsonModelToTag($oldNew->new)
149 foreach(
Utils::stringifyKeys($model->remappedPropertyValues ?? []) as $blockName => $properties){
151 if(!isset($convertedRemappedValuesIndex[$mappedValuesKey])){
152 throw new \UnexpectedValueException(
"Missing key from schema values index $mappedValuesKey");
154 $result->remappedPropertyValues[$blockName][$property] = $convertedRemappedValuesIndex[$mappedValuesKey];
159 foreach($remaps as $remap){
160 if(isset($remap->newName) === isset($remap->newFlattenedName)){
161 throw new \UnexpectedValueException(
"Expected exactly one of 'newName' or 'newFlattenedName' properties to be set");
167 $remap->newFlattenedName->prefix,
168 $remap->newFlattenedName->flattenedProperty,
169 $remap->newFlattenedName->suffix,
170 $remap->newFlattenedName->flattenedValueRemaps ?? [],
173 $remap->copiedState ?? []
182 if(count($schema->remappedPropertyValues) === 0){
188 $orderedRemappedValues = $schema->remappedPropertyValues;
189 ksort($orderedRemappedValues);
193 $remappedValuesMap = [];
194 foreach($remappedValues as $oldNew){
195 $remappedValuesMap[$oldNew->old->toString()] = $oldNew;
197 ksort($remappedValuesMap);
199 if(isset($dedupTableMap[$propertyName])){
200 foreach($dedupTableMap[$propertyName] as $k => $dedupValuesMap){
201 if(count($remappedValuesMap) !== count($dedupValuesMap)){
207 !isset($dedupValuesMap[$oldHash]) ||
208 !$remappedOldNew->old->equals($dedupValuesMap[$oldHash]->old) ||
209 !$remappedOldNew->new->equals($dedupValuesMap[$oldHash]->new)
216 $dedupMapping[$blockName][$propertyName] = $k;
222 $dedupTableMap[$propertyName][] = $remappedValuesMap;
223 $dedupMapping[$blockName][$propertyName] = array_key_last($dedupTableMap[$propertyName]);
229 foreach($mappingSet as $setId => $valuePairs){
230 $newDedupName = $propertyName .
"_" . str_pad(strval($setId), 2,
"0", STR_PAD_LEFT);
231 foreach($valuePairs as $pair){
233 BlockStateUpgradeSchemaUtils::tagToJsonModel($pair->old),
234 BlockStateUpgradeSchemaUtils::tagToJsonModel($pair->new),
239 $modelDedupMapping = [];
242 $modelDedupMapping[$blockName][$propertyName] = $propertyName .
"_" . str_pad(strval($dedupTableIndex), 2,
"0", STR_PAD_LEFT);
247 ksort($modelDedupMapping);
250 $dedupMapping[$blockName] = $properties;
253 $model->remappedPropertyValuesIndex = $modelTable;
254 $model->remappedPropertyValues = $modelDedupMapping;
259 $result->maxVersionMajor = $schema->maxVersionMajor;
260 $result->maxVersionMinor = $schema->maxVersionMinor;
261 $result->maxVersionPatch = $schema->maxVersionPatch;
262 $result->maxVersionRevision = $schema->maxVersionRevision;
264 $result->renamedIds = $schema->renamedIds;
265 ksort($result->renamedIds);
267 $result->renamedProperties = $schema->renamedProperties;
268 ksort($result->renamedProperties);
271 $result->renamedProperties[$blockName] = $properties;
274 $result->removedProperties = $schema->removedProperties;
275 ksort($result->removedProperties);
278 $result->removedProperties[$blockName] = $properties;
282 $addedProperties = [];
284 $addedProperties[$propertyName] = self::tagToJsonModel($propertyValue);
286 ksort($addedProperties);
287 $result->addedProperties[$blockName] = $addedProperties;
289 if(isset($result->addedProperties)){
290 ksort($result->addedProperties);
293 self::buildRemappedValuesIndex($schema, $result);
297 foreach($remaps as $remap){
299 array_map(fn(
Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
300 is_string($remap->newName) ?
303 $remap->newName->prefix,
304 $remap->newName->flattenedProperty,
305 $remap->newName->suffix,
306 $remap->newName->flattenedValueRemaps
308 array_map(fn(
Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
311 if(count($modelRemap->copiedState) === 0){
312 unset($modelRemap->copiedState);
314 $key = json_encode($modelRemap);
315 assert(!isset($keyedRemaps[$key]));
316 if(isset($keyedRemaps[$key])){
319 $keyedRemaps[$key] = $modelRemap;
323 $filterSizeCompare = count($b->oldState ?? []) <=> count($a->oldState ?? []);
324 if($filterSizeCompare !== 0){
325 return $filterSizeCompare;
328 return json_encode($a->oldState ?? []) <=> json_encode($b->oldState ?? []);
330 $result->remappedStates[$oldBlockName] = array_values($keyedRemaps);
332 if(isset($result->remappedStates)){
333 ksort($result->remappedStates);
344 public static function loadSchemas(
string $path,
int $maxSchemaId) : array{
345 $iterator = new \RegexIterator(
346 new \FilesystemIterator(
348 \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::SKIP_DOTS
350 '/^(\d{4}).*\.json$/',
351 \RegexIterator::GET_MATCH,
352 \RegexIterator::USE_KEY
358 foreach($iterator as $matches){
359 $filename = $matches[0];
360 $schemaId = (int) $matches[1];
362 if($schemaId > $maxSchemaId){
366 $fullPath = Path::join($path, $filename);
371 $schema = self::loadSchemaFromString($raw, $schemaId);
372 }
catch(\RuntimeException $e){
373 throw new \RuntimeException(
"Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
376 $result[$schemaId] = $schema;
379 ksort($result, SORT_NUMERIC);
383 public static function loadSchemaFromString(
string $raw,
int $schemaId) : BlockStateUpgradeSchema{
385 $json = json_decode($raw, false, flags: JSON_THROW_ON_ERROR);
386 }
catch(\JsonException $e){
387 throw new \RuntimeException($e->getMessage(), 0, $e);
389 if(!is_object($json)){
390 throw new \RuntimeException(
"Unexpected root type of schema file " . gettype($json) .
", expected object");
393 $jsonMapper = new \JsonMapper();
394 $jsonMapper->bExceptionOnMissingData =
true;
395 $jsonMapper->bExceptionOnUndefinedProperty =
true;
396 $jsonMapper->bStrictObjectTypeChecking =
true;
398 $model = $jsonMapper->map($json,
new BlockStateUpgradeSchemaModel());
400 throw new \RuntimeException($e->getMessage(), 0, $e);
403 return self::fromJsonModel($model, $schemaId);
static loadSchemas(string $path, int $maxSchemaId)
static fileGetContents(string $fileName, bool $useIncludePath=false, $context=null, int $offset=0, ?int $length=null)
static stringifyKeys(array $array)