mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 00:07:30 +00:00
Revert "tools/generate-blockstate-upgrade-schema: improve property remapping checks"
This reverts commit b8788c55c58effd04f5d8774ae04dd1656813abb. This changes behaviour, so it needs to target minor-next.
This commit is contained in:
parent
74d219dcb6
commit
2b40c1a5be
@ -28,26 +28,18 @@ use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchema;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaBlockRemap;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaUtils;
|
||||
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgradeSchemaValueRemap;
|
||||
use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_filter;
|
||||
use function array_key_first;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_shift;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function fwrite;
|
||||
use function implode;
|
||||
use function json_encode;
|
||||
use function ksort;
|
||||
use function usort;
|
||||
@ -64,22 +56,9 @@ class BlockStateMapping{
|
||||
){}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag[] $properties
|
||||
* @phpstan-param array<string, Tag> $properties
|
||||
*/
|
||||
function encodeOrderedProperties(array $properties) : string{
|
||||
ksort($properties, SORT_STRING);
|
||||
return implode("", array_map(fn(Tag $tag) => encodeProperty($tag), array_values($properties)));
|
||||
}
|
||||
|
||||
function encodeProperty(Tag $tag) : string{
|
||||
return (new LittleEndianNbtSerializer())->write(new TreeRoot($tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BlockStateMapping[][]
|
||||
* @phpstan-return array<string, array<string, BlockStateMapping>>
|
||||
* @phpstan-return array<string, list<BlockStateMapping>>
|
||||
*/
|
||||
function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
$contents = Filesystem::fileGetContents($file);
|
||||
@ -93,7 +72,7 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
$old = BlockStateData::fromNbt($reverse ? $newTag : $oldTag);
|
||||
$new = BlockStateData::fromNbt($reverse ? $oldTag : $newTag);
|
||||
|
||||
$result[$old->getName()][encodeOrderedProperties($old->getStates())] = new BlockStateMapping(
|
||||
$result[$old->getName()][] = new BlockStateMapping(
|
||||
$old,
|
||||
$new
|
||||
);
|
||||
@ -103,176 +82,111 @@ function loadUpgradeTable(string $file, bool $reverse) : array{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockStateData[] $states
|
||||
* @phpstan-param array<string, BlockStateData> $states
|
||||
*
|
||||
* @return Tag[][]
|
||||
* @phpstan-return array<string, array<string, Tag>>
|
||||
* @param true[] $removedPropertiesCache
|
||||
* @param Tag[][] $remappedPropertyValuesCache
|
||||
* @phpstan-param array<string, true> $removedPropertiesCache
|
||||
* @phpstan-param array<string, array<string, Tag>> $remappedPropertyValuesCache
|
||||
*/
|
||||
function buildStateGroupSchema(array $states) : ?array{
|
||||
$first = $states[array_key_first($states)];
|
||||
function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgradeSchema $result, array &$removedPropertiesCache, array &$remappedPropertyValuesCache) : void{
|
||||
|
||||
$properties = [];
|
||||
foreach(Utils::stringifyKeys($first->getStates()) as $propertyName => $propertyValue){
|
||||
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
|
||||
}
|
||||
foreach($states as $state){
|
||||
if(count($state->getStates()) !== count($properties)){
|
||||
return null;
|
||||
}
|
||||
foreach(Utils::stringifyKeys($state->getStates()) as $propertyName => $propertyValue){
|
||||
if(!isset($properties[$propertyName])){
|
||||
return null;
|
||||
}
|
||||
$properties[$propertyName][encodeProperty($propertyValue)] = $propertyValue;
|
||||
}
|
||||
}
|
||||
//new and old IDs are the same; compare states
|
||||
$oldName = $old->getName();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
$oldStates = $old->getStates();
|
||||
$newStates = $new->getStates();
|
||||
|
||||
/**
|
||||
* @param BlockStateMapping[] $upgradeTable
|
||||
* @phpstan-param array<string, BlockStateMapping> $upgradeTable
|
||||
*/
|
||||
function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgradeSchema $result) : bool{
|
||||
$newProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->new, $upgradeTable));
|
||||
if($newProperties === null){
|
||||
\GlobalLogger::get()->warning("New states for $oldName don't all have the same set of properties - processing as remaps instead");
|
||||
return false;
|
||||
}
|
||||
$oldProperties = buildStateGroupSchema(array_map(fn(BlockStateMapping $m) => $m->old, $upgradeTable));
|
||||
if($oldProperties === null){
|
||||
//TODO: not sure if this is actually required - we may be able to apply some transformations even if the states are not consistent
|
||||
//however, this should never normally occur anyway
|
||||
\GlobalLogger::get()->warning("Old states for $oldName don't all have the same set of properties - processing as remaps instead");
|
||||
return false;
|
||||
}
|
||||
|
||||
$remappedPropertyValues = [];
|
||||
$addedProperties = [];
|
||||
$removedProperties = [];
|
||||
$renamedProperties = [];
|
||||
|
||||
foreach(Utils::stringifyKeys($newProperties) as $newPropertyName => $newPropertyValues){
|
||||
if(count($newPropertyValues) === 1){
|
||||
$newPropertyValue = $newPropertyValues[array_key_first($newPropertyValues)];
|
||||
if(isset($oldProperties[$newPropertyName])){
|
||||
//all the old values for this property were mapped to the same new value
|
||||
//it would be more space-efficient to represent this as a remove+add, but we can't guarantee that the
|
||||
//removal of the old value will be done before the addition of the new value
|
||||
foreach($oldProperties[$newPropertyName] as $oldPropertyValue){
|
||||
$remappedPropertyValues[$newPropertyName][encodeProperty($oldPropertyValue)] = $newPropertyValue;
|
||||
}
|
||||
}else{
|
||||
//this property has no relation to any property value in any of the old states - it's a new property
|
||||
$addedProperties[$newPropertyName] = $newPropertyValue;
|
||||
$propertyRemoved = [];
|
||||
$propertyAdded = [];
|
||||
foreach(Utils::stringifyKeys($oldStates) as $propertyName => $oldProperty){
|
||||
$newProperty = $new->getState($propertyName);
|
||||
if($newProperty === null){
|
||||
$propertyRemoved[$propertyName] = $oldProperty;
|
||||
}elseif(!$newProperty->equals($oldProperty)){
|
||||
if(!isset($remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()])){
|
||||
$result->remappedPropertyValues[$oldName][$propertyName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$oldProperty,
|
||||
$newProperty
|
||||
);
|
||||
$remappedPropertyValuesCache[$propertyName][$oldProperty->getValue()] = $newProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($oldProperties) as $oldPropertyName => $oldPropertyValues){
|
||||
$mappingsContainingOldValue = [];
|
||||
foreach($upgradeTable as $mapping){
|
||||
$mappingOldValue = $mapping->old->getState($oldPropertyName) ?? throw new AssumptionFailedError("This should never happen");
|
||||
foreach($oldPropertyValues as $oldPropertyValue){
|
||||
if($mappingOldValue->equals($oldPropertyValue)){
|
||||
$mappingsContainingOldValue[encodeProperty($oldPropertyValue)][] = $mapping;
|
||||
break;
|
||||
foreach(Utils::stringifyKeys($newStates) as $propertyName => $value){
|
||||
if($old->getState($propertyName) === null){
|
||||
$propertyAdded[$propertyName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($propertyAdded) === 0 && count($propertyRemoved) === 0){
|
||||
return;
|
||||
}
|
||||
if(count($propertyAdded) === 1 && count($propertyRemoved) === 1){
|
||||
$propertyOldName = array_key_first($propertyRemoved);
|
||||
$propertyNewName = array_key_first($propertyAdded);
|
||||
|
||||
$propertyOldValue = $propertyRemoved[$propertyOldName];
|
||||
$propertyNewValue = $propertyAdded[$propertyNewName];
|
||||
|
||||
$existingPropertyValueMap = $remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] ?? null;
|
||||
if($propertyOldName !== $propertyNewName){
|
||||
if(!$propertyOldValue->equals($propertyNewValue) && $existingPropertyValueMap === null){
|
||||
\GlobalLogger::get()->warning("warning: guessing that $oldName has $propertyOldName renamed to $propertyNewName with a value map of $propertyOldValue mapped to $propertyNewValue");;
|
||||
}
|
||||
//this is a guess; it might not be reliable if the value changed as well
|
||||
//this will probably never be an issue, but it might rear its ugly head in the future
|
||||
$result->renamedProperties[$oldName][$propertyOldName] = $propertyNewName;
|
||||
}
|
||||
if(!$propertyOldValue->equals($propertyNewValue)){
|
||||
$mapped = true;
|
||||
if($existingPropertyValueMap !== null && !$existingPropertyValueMap->equals($propertyNewValue)){
|
||||
if($existingPropertyValueMap->equals($propertyOldValue)){
|
||||
\GlobalLogger::get()->warning("warning: guessing that the value $propertyOldValue of $propertyNewValue did not change");;
|
||||
$mapped = false;
|
||||
}else{
|
||||
\GlobalLogger::get()->warning("warning: mismatch of new value for $propertyNewName for $oldName: $propertyOldValue seen mapped to $propertyNewValue and $existingPropertyValueMap");;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$candidateNewPropertyNames = [];
|
||||
//foreach mappings by unique value, compute the diff across all the states in the list
|
||||
foreach(Utils::stringifyKeys($mappingsContainingOldValue) as $rawOldValue => $mappingList){
|
||||
$first = array_shift($mappingList);
|
||||
foreach(Utils::stringifyKeys($first->new->getStates()) as $newPropertyName => $newPropertyValue){
|
||||
if(isset($addedProperties[$newPropertyName])){
|
||||
//this property was already determined to be unrelated to any old property
|
||||
continue;
|
||||
}
|
||||
foreach($mappingList as $pair){
|
||||
if(!($pair->new->getState($newPropertyName)?->equals($newPropertyValue) ?? false)){
|
||||
//if the new property is different with an unchanged old value,
|
||||
//the property may be influenced by multiple old properties, or be unrelated entirely
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$candidateNewPropertyNames[$newPropertyName][$rawOldValue] = $newPropertyValue;
|
||||
if($mapped && !isset($remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()])){
|
||||
//value remap
|
||||
$result->remappedPropertyValues[$oldName][$propertyOldName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$propertyRemoved[$propertyOldName],
|
||||
$propertyAdded[$propertyNewName]
|
||||
);
|
||||
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
|
||||
}
|
||||
}elseif($existingPropertyValueMap !== null){
|
||||
\GlobalLogger::get()->warning("warning: multiple values found for value $propertyOldValue of $propertyNewName on block $oldName, guessing it did not change");;
|
||||
$remappedPropertyValuesCache[$propertyOldName][$propertyOldValue->getValue()] = $propertyNewValue;
|
||||
}
|
||||
}else{
|
||||
if(count($propertyAdded) !== 0 && count($propertyRemoved) === 0){
|
||||
foreach(Utils::stringifyKeys($propertyAdded) as $propertyAddedName => $propertyAddedValue){
|
||||
$existingDefault = $result->addedProperties[$oldName][$propertyAddedName] ?? null;
|
||||
if($existingDefault !== null && !$existingDefault->equals($propertyAddedValue)){
|
||||
throw new \UnexpectedValueException("Ambiguous default value for added property $propertyAddedName on block $oldName");
|
||||
}
|
||||
|
||||
if(count($candidateNewPropertyNames) === 0){
|
||||
$removedProperties[$oldPropertyName] = $oldPropertyName;
|
||||
}elseif(count($candidateNewPropertyNames) === 1){
|
||||
$newPropertyName = array_key_first($candidateNewPropertyNames);
|
||||
$newPropertyValues = $candidateNewPropertyNames[$newPropertyName];
|
||||
|
||||
if($oldPropertyName !== $newPropertyName){
|
||||
$renamedProperties[$oldPropertyName] = $newPropertyName;
|
||||
$result->addedProperties[$oldName][$propertyAddedName] = $propertyAddedValue;
|
||||
}
|
||||
|
||||
foreach(Utils::stringifyKeys($newPropertyValues) as $rawOldValue => $newPropertyValue){
|
||||
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
|
||||
$remappedPropertyValues[$oldPropertyName][$rawOldValue] = $newPropertyValue;
|
||||
}elseif(count($propertyRemoved) !== 0 && count($propertyAdded) === 0){
|
||||
foreach(Utils::stringifyKeys($propertyRemoved) as $propertyRemovedName => $propertyRemovedValue){
|
||||
if(!isset($removedPropertiesCache[$propertyRemovedName])){
|
||||
//to avoid having useless keys in the output
|
||||
$result->removedProperties[$oldName][] = $propertyRemovedName;
|
||||
$removedPropertiesCache[$propertyRemovedName] = $propertyRemovedName;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$split = true;
|
||||
if(isset($candidateNewPropertyNames[$oldPropertyName])){
|
||||
//In 1.10, direction wasn't changed at all, but not all state permutations were present in the palette,
|
||||
//making it appear that door_hinge_bit was correlated with direction.
|
||||
//If a new property is present with the same name and values as an old property, we can assume that
|
||||
//the property was unchanged, and that any extra matches properties are probably unrelated.
|
||||
$changedValues = false;
|
||||
foreach(Utils::stringifyKeys($candidateNewPropertyNames[$oldPropertyName]) as $rawOldValue => $newPropertyValue){
|
||||
if(!$newPropertyValue->equals($oldPropertyValues[$rawOldValue])){
|
||||
//if any of the new values are different, we may be dealing with a property being split into
|
||||
//multiple new properties - hand this off to the remap handler
|
||||
$changedValues = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$changedValues){
|
||||
$split = false;
|
||||
}
|
||||
}
|
||||
if($split){
|
||||
\GlobalLogger::get()->warning(
|
||||
"Multiple new properties (" . (implode(", ", array_keys($candidateNewPropertyNames))) . ") are correlated with $oldName property $oldPropertyName, processing as remaps instead"
|
||||
);
|
||||
return false;
|
||||
}else{
|
||||
//is it safe to ignore the rest?
|
||||
}
|
||||
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||
$oldStates,
|
||||
$new->getName(),
|
||||
$newStates,
|
||||
[]
|
||||
);
|
||||
\GlobalLogger::get()->warning("warning: multiple properties added and removed for $oldName; added full state remap");;
|
||||
}
|
||||
}
|
||||
|
||||
//finally, write the results to the schema
|
||||
|
||||
if(count($remappedPropertyValues) !== 0){
|
||||
foreach(Utils::stringifyKeys($remappedPropertyValues) as $oldPropertyName => $propertyValues){
|
||||
foreach(Utils::stringifyKeys($propertyValues) as $rawOldValue => $newPropertyValue){
|
||||
$oldPropertyValue = $oldProperties[$oldPropertyName][$rawOldValue];
|
||||
$result->remappedPropertyValues[$oldName][$oldPropertyName][] = new BlockStateUpgradeSchemaValueRemap(
|
||||
$oldPropertyValue,
|
||||
$newPropertyValue
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(count($addedProperties) !== 0){
|
||||
$result->addedProperties[$oldName] = $addedProperties;
|
||||
}
|
||||
if(count($removedProperties) !== 0){
|
||||
$result->removedProperties[$oldName] = array_values($removedProperties);
|
||||
}
|
||||
if(count($renamedProperties) !== 0){
|
||||
$result->renamedProperties[$oldName] = $renamedProperties;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -282,11 +196,8 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra
|
||||
*
|
||||
* @param BlockStateUpgradeSchemaBlockRemap[] $stateRemaps
|
||||
* @param BlockStateMapping[] $upgradeTable
|
||||
* @phpstan-param list<BlockStateUpgradeSchemaBlockRemap> $stateRemaps
|
||||
* @phpstan-param array<string, BlockStateMapping> $upgradeTable
|
||||
*
|
||||
* @return BlockStateUpgradeSchemaBlockRemap[]
|
||||
* @phpstan-return list<BlockStateUpgradeSchemaBlockRemap>
|
||||
*/
|
||||
function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array{
|
||||
$unchangedStatesByNewName = [];
|
||||
@ -400,7 +311,7 @@ function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array
|
||||
|
||||
/**
|
||||
* @param BlockStateMapping[][] $upgradeTable
|
||||
* @phpstan-param array<string, array<string, BlockStateMapping>> $upgradeTable
|
||||
* @phpstan-param array<string, list<BlockStateMapping>> $upgradeTable
|
||||
*/
|
||||
function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgradeSchema{
|
||||
$foundVersion = -1;
|
||||
@ -432,6 +343,8 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
foreach(Utils::stringifyKeys($upgradeTable) as $oldName => $blockStateMappings){
|
||||
$newNameFound = [];
|
||||
|
||||
$removedPropertiesCache = [];
|
||||
$remappedPropertyValuesCache = [];
|
||||
foreach($blockStateMappings as $mapping){
|
||||
$newName = $mapping->new->getName();
|
||||
$newNameFound[$newName] = true;
|
||||
@ -441,15 +354,15 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
||||
if($newName !== $oldName){
|
||||
$result->renamedIds[$oldName] = array_key_first($newNameFound);
|
||||
}
|
||||
if(!processStateGroup($oldName, $blockStateMappings, $result)){
|
||||
throw new \RuntimeException("States with the same ID should be fully consistent");
|
||||
foreach($blockStateMappings as $mapping){
|
||||
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache);
|
||||
}
|
||||
}else{
|
||||
if(isset($newNameFound[$oldName])){
|
||||
//some of the states stayed under the same ID - we can process these as normal states
|
||||
$stateGroup = array_filter($blockStateMappings, fn(BlockStateMapping $m) => $m->new->getName() === $oldName);
|
||||
if(processStateGroup($oldName, $stateGroup, $result)){
|
||||
foreach(Utils::stringifyKeys($stateGroup) as $k => $mapping){
|
||||
foreach($blockStateMappings as $k => $mapping){
|
||||
if($mapping->new->getName() === $oldName){
|
||||
processState($mapping->old, $mapping->new, $result, $removedPropertiesCache, $remappedPropertyValuesCache);
|
||||
unset($blockStateMappings[$k]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user