mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-09 11:31:49 +00:00
Added support for compressing blockstate remaps using copiedState
this significantly reduces the size of schemas when state remaps are used (see pmmp/BedrockBlockUpgradeSchema@85b83b360e). in addition, this will likely offer a substantial performance and memory saving when walls get flattened, which will eventually happen.
This commit is contained in:
parent
263e1e9950
commit
a1d44de487
@ -29,12 +29,16 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
|||||||
/**
|
/**
|
||||||
* @param Tag[] $oldState
|
* @param Tag[] $oldState
|
||||||
* @param Tag[] $newState
|
* @param Tag[] $newState
|
||||||
|
* @param string[] $copiedState
|
||||||
|
*
|
||||||
* @phpstan-param array<string, Tag> $oldState
|
* @phpstan-param array<string, Tag> $oldState
|
||||||
* @phpstan-param array<string, Tag> $newState
|
* @phpstan-param array<string, Tag> $newState
|
||||||
|
* @phpstan-param list<string> $copiedState
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public array $oldState,
|
public array $oldState,
|
||||||
public string $newName,
|
public string $newName,
|
||||||
public array $newState
|
public array $newState,
|
||||||
|
public array $copiedState
|
||||||
){}
|
){}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,7 @@ final class BlockStateUpgradeSchemaUtils{
|
|||||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
|
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->oldState ?? []),
|
||||||
$remap->newName,
|
$remap->newName,
|
||||||
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
|
array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []),
|
||||||
|
$remap->copiedState ?? []
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,7 +278,11 @@ final class BlockStateUpgradeSchemaUtils{
|
|||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
||||||
$remap->newName,
|
$remap->newName,
|
||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||||
|
$remap->copiedState
|
||||||
);
|
);
|
||||||
|
if(count($modelRemap->copiedState) === 0){
|
||||||
|
unset($modelRemap->copiedState); //avoid polluting the JSON
|
||||||
|
}
|
||||||
$key = json_encode($modelRemap);
|
$key = json_encode($modelRemap);
|
||||||
assert(!isset($keyedRemaps[$key]));
|
assert(!isset($keyedRemaps[$key]));
|
||||||
if(isset($keyedRemaps[$key])){
|
if(isset($keyedRemaps[$key])){
|
||||||
|
@ -70,16 +70,23 @@ final class BlockStateUpgrader{
|
|||||||
$oldState = $blockStateData->getStates();
|
$oldState = $blockStateData->getStates();
|
||||||
if(isset($schema->remappedStates[$oldName])){
|
if(isset($schema->remappedStates[$oldName])){
|
||||||
foreach($schema->remappedStates[$oldName] as $remap){
|
foreach($schema->remappedStates[$oldName] as $remap){
|
||||||
if(count($oldState) !== count($remap->oldState)){
|
if(count($remap->oldState) > count($oldState)){
|
||||||
|
//match criteria has more requirements than we have state properties
|
||||||
continue; //try next state
|
continue; //try next state
|
||||||
}
|
}
|
||||||
foreach(Utils::stringifyKeys($oldState) as $k => $v){
|
foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){
|
||||||
if(!isset($remap->oldState[$k]) || !$remap->oldState[$k]->equals($v)){
|
if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){
|
||||||
continue 2; //try next state
|
continue 2; //try next state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$newState = $remap->newState;
|
||||||
|
foreach($remap->copiedState as $stateName){
|
||||||
|
if(isset($oldState[$stateName])){
|
||||||
|
$newState[$stateName] = $oldState[$stateName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$blockStateData = new BlockStateData($remap->newName, $remap->newState, $resultVersion);
|
$blockStateData = new BlockStateData($remap->newName, $newState, $resultVersion);
|
||||||
continue 2; //try next schema
|
continue 2; //try next schema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,25 @@ final class BlockStateUpgradeSchemaModelBlockRemap{
|
|||||||
*/
|
*/
|
||||||
public ?array $newState;
|
public ?array $newState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
* @phpstan-var list<string>
|
||||||
|
* May not be present in older schemas
|
||||||
|
*/
|
||||||
|
public array $copiedState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BlockStateUpgradeSchemaModelTag[] $oldState
|
* @param BlockStateUpgradeSchemaModelTag[] $oldState
|
||||||
* @param BlockStateUpgradeSchemaModelTag[] $newState
|
* @param BlockStateUpgradeSchemaModelTag[] $newState
|
||||||
|
* @param string[] $copiedState
|
||||||
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $oldState
|
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $oldState
|
||||||
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
* @phpstan-param array<string, BlockStateUpgradeSchemaModelTag> $newState
|
||||||
|
* @phpstan-param list<string> $copiedState
|
||||||
*/
|
*/
|
||||||
public function __construct(array $oldState, string $newName, array $newState){
|
public function __construct(array $oldState, string $newName, array $newState, array $copiedState){
|
||||||
$this->oldState = count($oldState) === 0 ? null : $oldState;
|
$this->oldState = count($oldState) === 0 ? null : $oldState;
|
||||||
$this->newName = $newName;
|
$this->newName = $newName;
|
||||||
$this->newState = count($newState) === 0 ? null : $newState;
|
$this->newState = count($newState) === 0 ? null : $newState;
|
||||||
|
$this->copiedState = $copiedState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,16 @@ use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer;
|
|||||||
use pocketmine\utils\Filesystem;
|
use pocketmine\utils\Filesystem;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use function array_key_first;
|
use function array_key_first;
|
||||||
|
use function array_merge;
|
||||||
|
use function array_values;
|
||||||
|
use function assert;
|
||||||
use function count;
|
use function count;
|
||||||
use function dirname;
|
use function dirname;
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
use function fwrite;
|
use function fwrite;
|
||||||
use function json_encode;
|
use function json_encode;
|
||||||
use function ksort;
|
use function ksort;
|
||||||
|
use function usort;
|
||||||
use const JSON_PRETTY_PRINT;
|
use const JSON_PRETTY_PRINT;
|
||||||
use const SORT_STRING;
|
use const SORT_STRING;
|
||||||
use const STDERR;
|
use const STDERR;
|
||||||
@ -177,13 +181,134 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
|
|||||||
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||||
$oldStates,
|
$oldStates,
|
||||||
$new->getName(),
|
$new->getName(),
|
||||||
$newStates
|
$newStates,
|
||||||
|
[]
|
||||||
);
|
);
|
||||||
\GlobalLogger::get()->warning("warning: multiple properties added and removed for $oldName; added full state remap");;
|
\GlobalLogger::get()->warning("warning: multiple properties added and removed for $oldName; added full state remap");;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to compress a list of remapped states by looking at which state properties were consistently unchanged.
|
||||||
|
* This significantly reduces the output size during flattening when the flattened block has many permutations
|
||||||
|
* (e.g. walls).
|
||||||
|
*
|
||||||
|
* @param BlockStateUpgradeSchemaBlockRemap[] $stateRemaps
|
||||||
|
* @param BlockStateMapping[] $upgradeTable
|
||||||
|
*
|
||||||
|
* @return BlockStateUpgradeSchemaBlockRemap[]
|
||||||
|
*/
|
||||||
|
function compressRemappedStates(array $upgradeTable, array $stateRemaps) : array{
|
||||||
|
$unchangedStatesByNewName = [];
|
||||||
|
|
||||||
|
foreach($upgradeTable as $pair){
|
||||||
|
if(count($pair->old->getStates()) === 0 || count($pair->new->getStates()) === 0){
|
||||||
|
//all states have changed in some way - compression not possible
|
||||||
|
$unchangedStatesByNewName[$pair->new->getName()] = [];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldStates = $pair->old->getStates();
|
||||||
|
$newStates = $pair->new->getStates();
|
||||||
|
if(!isset($unchangedStatesByNewName[$pair->new->getName()])){
|
||||||
|
//build list of unchanged states for this new ID
|
||||||
|
$unchangedStatesByNewName[$pair->new->getName()] = [];
|
||||||
|
foreach(Utils::stringifyKeys($oldStates) as $propertyName => $propertyValue){
|
||||||
|
if(isset($newStates[$propertyName]) && $newStates[$propertyName]->equals($propertyValue)){
|
||||||
|
$unchangedStatesByNewName[$pair->new->getName()][] = $propertyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//we already have a list of stuff that probably didn't change - verify that this is the case, and remove
|
||||||
|
//any that changed in later states with the same ID
|
||||||
|
foreach($unchangedStatesByNewName[$pair->new->getName()] as $k => $propertyName){
|
||||||
|
if(
|
||||||
|
!isset($oldStates[$propertyName]) ||
|
||||||
|
!isset($newStates[$propertyName]) ||
|
||||||
|
!$oldStates[$propertyName]->equals($newStates[$propertyName])
|
||||||
|
){
|
||||||
|
//this property disappeared or changed its value in another state with the same ID - we can't
|
||||||
|
//compress this state
|
||||||
|
unset($unchangedStatesByNewName[$pair->new->getName()][$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(Utils::stringifyKeys($unchangedStatesByNewName) as $newName => $unchangedStates){
|
||||||
|
ksort($unchangedStates);
|
||||||
|
$unchangedStatesByNewName[$newName] = $unchangedStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
$compressedRemaps = [];
|
||||||
|
|
||||||
|
foreach($stateRemaps as $remap){
|
||||||
|
$oldState = $remap->oldState;
|
||||||
|
$newState = $remap->newState;
|
||||||
|
|
||||||
|
if($oldState === null || $newState === null){
|
||||||
|
//no unchanged states - no compression possible
|
||||||
|
assert(!isset($unchangedStatesByNewName[$remap->newName]));
|
||||||
|
$compressedRemaps[$remap->newName][] = $remap;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanedOldState = $oldState;
|
||||||
|
$cleanedNewState = $newState;
|
||||||
|
|
||||||
|
foreach($unchangedStatesByNewName[$remap->newName] as $propertyName){
|
||||||
|
unset($cleanedOldState[$propertyName]);
|
||||||
|
unset($cleanedNewState[$propertyName]);
|
||||||
|
}
|
||||||
|
ksort($cleanedOldState);
|
||||||
|
ksort($cleanedNewState);
|
||||||
|
|
||||||
|
$duplicate = false;
|
||||||
|
$compressedRemaps[$remap->newName] ??= [];
|
||||||
|
foreach($compressedRemaps[$remap->newName] as $k => $compressedRemap){
|
||||||
|
assert($compressedRemap->oldState !== null && $compressedRemap->newState !== null);
|
||||||
|
|
||||||
|
if(
|
||||||
|
count($compressedRemap->oldState) !== count($cleanedOldState) ||
|
||||||
|
count($compressedRemap->newState) !== count($cleanedNewState)
|
||||||
|
){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach(Utils::stringifyKeys($cleanedOldState) as $propertyName => $propertyValue){
|
||||||
|
if(!isset($compressedRemap->oldState[$propertyName]) || !$compressedRemap->oldState[$propertyName]->equals($propertyValue)){
|
||||||
|
//different filter value
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(Utils::stringifyKeys($cleanedNewState) as $propertyName => $propertyValue){
|
||||||
|
if(!isset($compressedRemap->newState[$propertyName]) || !$compressedRemap->newState[$propertyName]->equals($propertyValue)){
|
||||||
|
//different replacement value
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$duplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!$duplicate){
|
||||||
|
$compressedRemaps[$remap->newName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||||
|
$cleanedOldState,
|
||||||
|
$remap->newName,
|
||||||
|
$cleanedNewState,
|
||||||
|
$unchangedStatesByNewName[$remap->newName]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = array_merge(...array_values($compressedRemaps));
|
||||||
|
|
||||||
|
//more specific filters must come before less specific ones, in case of a remap on a certain value which is
|
||||||
|
//otherwise unchanged
|
||||||
|
usort($list, function(BlockStateUpgradeSchemaBlockRemap $a, BlockStateUpgradeSchemaBlockRemap $b) : int{
|
||||||
|
return count($b->oldState) <=> count($a->oldState);
|
||||||
|
});
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BlockStateMapping[][] $upgradeTable
|
* @param BlockStateMapping[][] $upgradeTable
|
||||||
* @phpstan-param array<string, list<BlockStateMapping>> $upgradeTable
|
* @phpstan-param array<string, list<BlockStateMapping>> $upgradeTable
|
||||||
@ -197,6 +322,7 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
|||||||
}else{
|
}else{
|
||||||
$logger = \GlobalLogger::get();
|
$logger = \GlobalLogger::get();
|
||||||
$logger->emergency("Mismatched upgraded versions found: $foundVersion and " . $mapping->new->getVersion());
|
$logger->emergency("Mismatched upgraded versions found: $foundVersion and " . $mapping->new->getVersion());
|
||||||
|
$logger->emergency("Mismatched old state: " . $mapping->old->toNbt());
|
||||||
$logger->emergency("Mismatched new state: " . $mapping->new->toNbt());
|
$logger->emergency("Mismatched new state: " . $mapping->new->toNbt());
|
||||||
$logger->emergency("This is probably because the game didn't recognize the input blockstate, so it was returned unchanged.");
|
$logger->emergency("This is probably because the game didn't recognize the input blockstate, so it was returned unchanged.");
|
||||||
$logger->emergency("This is usually because the block is locked behind an experimental toggle that isn't enabled on the world you used when generating this upgrade table.");
|
$logger->emergency("This is usually because the block is locked behind an experimental toggle that isn't enabled on the world you used when generating this upgrade table.");
|
||||||
@ -238,12 +364,16 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
|||||||
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
|
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||||
$mapping->old->getStates(),
|
$mapping->old->getStates(),
|
||||||
$mapping->new->getName(),
|
$mapping->new->getName(),
|
||||||
$mapping->new->getStates()
|
$mapping->new->getStates(),
|
||||||
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach(Utils::stringifyKeys($result->remappedStates) as $oldName => $remap){
|
||||||
|
$result->remappedStates[$oldName] = compressRemappedStates($upgradeTable[$oldName], $remap);
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user