mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 20:28:31 +00:00
BlockStateData: use array<Tag> instead of CompoundTag to store state properties
this reduces the footprint of RuntimeBlockMapping by a further 1 MB, as well as simplifying various parts of the code, and solidifying the immutability guarantee of BlockStateData.
This commit is contained in:
parent
151f2c3f3a
commit
ccb3c3cb05
@ -74,7 +74,7 @@ function generateBlockPaletteReport(array $states) : BlockPaletteReport{
|
|||||||
foreach($states as $stateData){
|
foreach($states as $stateData){
|
||||||
$name = $stateData->getName();
|
$name = $stateData->getName();
|
||||||
$result->seenTypes[$name] = $name;
|
$result->seenTypes[$name] = $name;
|
||||||
foreach($stateData->getStates() as $k => $v){
|
foreach(Utils::stringifyKeys($stateData->getStates()) as $k => $v){
|
||||||
$result->seenStateValues[$k][$v->getValue()] = $v->getValue();
|
$result->seenStateValues[$k][$v->getValue()] = $v->getValue();
|
||||||
asort($result->seenStateValues[$k]);
|
asort($result->seenStateValues[$k]);
|
||||||
}
|
}
|
||||||
|
@ -121,10 +121,11 @@ final class CraftingManagerFromDataHelper{
|
|||||||
throw new SavedDataLoadingException("Meta should not be specified for blockitems");
|
throw new SavedDataLoadingException("Meta should not be specified for blockitems");
|
||||||
}
|
}
|
||||||
$blockStatesTag = $blockStatesRaw === null ?
|
$blockStatesTag = $blockStatesRaw === null ?
|
||||||
CompoundTag::create() :
|
[] :
|
||||||
(new LittleEndianNbtSerializer())
|
(new LittleEndianNbtSerializer())
|
||||||
->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($blockStatesRaw, true)))
|
->read(ErrorToExceptionHandler::trapAndRemoveFalse(fn() => base64_decode($blockStatesRaw, true)))
|
||||||
->mustGetCompoundTag();
|
->mustGetCompoundTag()
|
||||||
|
->getValue();
|
||||||
$blockStateData = new BlockStateData($blockName, $blockStatesTag, BlockStateData::CURRENT_VERSION);
|
$blockStateData = new BlockStateData($blockName, $blockStatesTag, BlockStateData::CURRENT_VERSION);
|
||||||
}else{
|
}else{
|
||||||
$blockStateData = null;
|
$blockStateData = null;
|
||||||
|
@ -25,6 +25,8 @@ namespace pocketmine\data\bedrock\block;
|
|||||||
|
|
||||||
use pocketmine\nbt\NbtException;
|
use pocketmine\nbt\NbtException;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
use pocketmine\nbt\tag\Tag;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
@ -46,15 +48,27 @@ final class BlockStateData{
|
|||||||
public const TAG_STATES = "states";
|
public const TAG_STATES = "states";
|
||||||
public const TAG_VERSION = "version";
|
public const TAG_VERSION = "version";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tag[] $states
|
||||||
|
* @phpstan-param array<string, Tag> $states
|
||||||
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $name,
|
private string $name,
|
||||||
private CompoundTag $states,
|
private array $states,
|
||||||
private int $version
|
private int $version
|
||||||
){}
|
){}
|
||||||
|
|
||||||
public function getName() : string{ return $this->name; }
|
public function getName() : string{ return $this->name; }
|
||||||
|
|
||||||
public function getStates() : CompoundTag{ return $this->states; }
|
/**
|
||||||
|
* @return Tag[]
|
||||||
|
* @phpstan-return array<string, Tag>
|
||||||
|
*/
|
||||||
|
public function getStates() : array{ return $this->states; }
|
||||||
|
|
||||||
|
public function getState(string $name) : ?Tag{
|
||||||
|
return $this->states[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getVersion() : int{ return $this->version; }
|
public function getVersion() : int{ return $this->version; }
|
||||||
|
|
||||||
@ -76,20 +90,30 @@ final class BlockStateData{
|
|||||||
throw new BlockStateDeserializeException("Unexpected extra keys: " . implode(", ", array_keys($allKeys)));
|
throw new BlockStateDeserializeException("Unexpected extra keys: " . implode(", ", array_keys($allKeys)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new self($name, $states, $version);
|
return new self($name, $states->getValue(), $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toNbt() : CompoundTag{
|
public function toNbt() : CompoundTag{
|
||||||
|
$statesTag = CompoundTag::create();
|
||||||
|
foreach(Utils::stringifyKeys($this->states) as $key => $value){
|
||||||
|
$statesTag->setTag($key, $value);
|
||||||
|
}
|
||||||
return CompoundTag::create()
|
return CompoundTag::create()
|
||||||
->setString(self::TAG_NAME, $this->name)
|
->setString(self::TAG_NAME, $this->name)
|
||||||
->setInt(self::TAG_VERSION, $this->version)
|
->setInt(self::TAG_VERSION, $this->version)
|
||||||
->setTag(self::TAG_STATES, $this->states);
|
->setTag(self::TAG_STATES, $statesTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function equals(self $that) : bool{
|
public function equals(self $that) : bool{
|
||||||
return
|
if($this->name !== $that->name || count($this->states) !== count($that->states)){
|
||||||
$this->name === $that->name &&
|
return false;
|
||||||
$this->states->equals($that->states) &&
|
}
|
||||||
$this->version === $that->version;
|
foreach(Utils::stringifyKeys($this->states) as $k => $v){
|
||||||
|
if(!isset($that->states[$k]) || !$that->states[$k]->equals($v)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\data\bedrock\block;
|
namespace pocketmine\data\bedrock\block;
|
||||||
|
|
||||||
|
use function count;
|
||||||
|
|
||||||
final class CachingBlockStateDeserializer implements DelegatingBlockStateDeserializer{
|
final class CachingBlockStateDeserializer implements DelegatingBlockStateDeserializer{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +38,7 @@ final class CachingBlockStateDeserializer implements DelegatingBlockStateDeseria
|
|||||||
){}
|
){}
|
||||||
|
|
||||||
public function deserialize(BlockStateData $stateData) : int{
|
public function deserialize(BlockStateData $stateData) : int{
|
||||||
if($stateData->getStates()->count() === 0){
|
if(count($stateData->getStates()) === 0){
|
||||||
//if a block has zero properties, we can keep a map of string ID -> internal blockstate ID
|
//if a block has zero properties, we can keep a map of string ID -> internal blockstate ID
|
||||||
return $this->simpleCache[$stateData->getName()] ??= $this->realDeserializer->deserialize($stateData);
|
return $this->simpleCache[$stateData->getName()] ??= $this->realDeserializer->deserialize($stateData);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ use pocketmine\nbt\tag\ByteTag;
|
|||||||
use pocketmine\nbt\tag\IntTag;
|
use pocketmine\nbt\tag\IntTag;
|
||||||
use pocketmine\nbt\tag\StringTag;
|
use pocketmine\nbt\tag\StringTag;
|
||||||
use pocketmine\nbt\tag\Tag;
|
use pocketmine\nbt\tag\Tag;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
|
|
||||||
final class BlockStateReader{
|
final class BlockStateReader{
|
||||||
@ -66,7 +67,7 @@ final class BlockStateReader{
|
|||||||
/** @throws BlockStateDeserializeException */
|
/** @throws BlockStateDeserializeException */
|
||||||
public function readBool(string $name) : bool{
|
public function readBool(string $name) : bool{
|
||||||
$this->usedStates[$name] = true;
|
$this->usedStates[$name] = true;
|
||||||
$tag = $this->data->getStates()->getTag($name);
|
$tag = $this->data->getState($name);
|
||||||
if($tag instanceof ByteTag){
|
if($tag instanceof ByteTag){
|
||||||
switch($tag->getValue()){
|
switch($tag->getValue()){
|
||||||
case 0: return false;
|
case 0: return false;
|
||||||
@ -80,7 +81,7 @@ final class BlockStateReader{
|
|||||||
/** @throws BlockStateDeserializeException */
|
/** @throws BlockStateDeserializeException */
|
||||||
public function readInt(string $name) : int{
|
public function readInt(string $name) : int{
|
||||||
$this->usedStates[$name] = true;
|
$this->usedStates[$name] = true;
|
||||||
$tag = $this->data->getStates()->getTag($name);
|
$tag = $this->data->getState($name);
|
||||||
if($tag instanceof IntTag){
|
if($tag instanceof IntTag){
|
||||||
return $tag->getValue();
|
return $tag->getValue();
|
||||||
}
|
}
|
||||||
@ -100,7 +101,7 @@ final class BlockStateReader{
|
|||||||
public function readString(string $name) : string{
|
public function readString(string $name) : string{
|
||||||
$this->usedStates[$name] = true;
|
$this->usedStates[$name] = true;
|
||||||
//TODO: only allow a specific set of values (strings are primarily used for enums)
|
//TODO: only allow a specific set of values (strings are primarily used for enums)
|
||||||
$tag = $this->data->getStates()->getTag($name);
|
$tag = $this->data->getState($name);
|
||||||
if($tag instanceof StringTag){
|
if($tag instanceof StringTag){
|
||||||
return $tag->getValue();
|
return $tag->getValue();
|
||||||
}
|
}
|
||||||
@ -314,7 +315,7 @@ final class BlockStateReader{
|
|||||||
* Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled
|
* Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled
|
||||||
*/
|
*/
|
||||||
public function ignored(string $name) : void{
|
public function ignored(string $name) : void{
|
||||||
if($this->data->getStates()->getTag($name) !== null){
|
if($this->data->getState($name) !== null){
|
||||||
$this->usedStates[$name] = true;
|
$this->usedStates[$name] = true;
|
||||||
}else{
|
}else{
|
||||||
throw $this->missingOrWrongTypeException($name, null);
|
throw $this->missingOrWrongTypeException($name, null);
|
||||||
@ -332,7 +333,7 @@ final class BlockStateReader{
|
|||||||
* @throws BlockStateDeserializeException
|
* @throws BlockStateDeserializeException
|
||||||
*/
|
*/
|
||||||
public function checkUnreadProperties() : void{
|
public function checkUnreadProperties() : void{
|
||||||
foreach($this->data->getStates() as $name => $tag){
|
foreach(Utils::stringifyKeys($this->data->getStates()) as $name => $tag){
|
||||||
if(!isset($this->usedStates[$name])){
|
if(!isset($this->usedStates[$name])){
|
||||||
throw new BlockStateDeserializeException("Unread property \"$name\"");
|
throw new BlockStateDeserializeException("Unread property \"$name\"");
|
||||||
}
|
}
|
||||||
|
@ -35,17 +35,22 @@ use pocketmine\data\bedrock\block\BlockStateSerializeException;
|
|||||||
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
|
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
|
||||||
use pocketmine\math\Axis;
|
use pocketmine\math\Axis;
|
||||||
use pocketmine\math\Facing;
|
use pocketmine\math\Facing;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\ByteTag;
|
||||||
|
use pocketmine\nbt\tag\IntTag;
|
||||||
|
use pocketmine\nbt\tag\StringTag;
|
||||||
|
use pocketmine\nbt\tag\Tag;
|
||||||
|
|
||||||
final class BlockStateWriter{
|
final class BlockStateWriter{
|
||||||
|
|
||||||
private CompoundTag $states;
|
/**
|
||||||
|
* @var Tag[]
|
||||||
|
* @phpstan-var array<string, Tag>
|
||||||
|
*/
|
||||||
|
private array $states = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $id
|
private string $id
|
||||||
){
|
){}
|
||||||
$this->states = CompoundTag::create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function create(string $id) : self{
|
public static function create(string $id) : self{
|
||||||
return new self($id);
|
return new self($id);
|
||||||
@ -53,19 +58,19 @@ final class BlockStateWriter{
|
|||||||
|
|
||||||
/** @return $this */
|
/** @return $this */
|
||||||
public function writeBool(string $name, bool $value) : self{
|
public function writeBool(string $name, bool $value) : self{
|
||||||
$this->states->setByte($name, $value ? 1 : 0);
|
$this->states[$name] = new ByteTag($value ? 1 : 0);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return $this */
|
/** @return $this */
|
||||||
public function writeInt(string $name, int $value) : self{
|
public function writeInt(string $name, int $value) : self{
|
||||||
$this->states->setInt($name, $value);
|
$this->states[$name] = new IntTag($value);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return $this */
|
/** @return $this */
|
||||||
public function writeString(string $name, string $value) : self{
|
public function writeString(string $name, string $value) : self{
|
||||||
$this->states->setString($name, $value);
|
$this->states[$name] = new StringTag($value);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ final class BlockDataUpgrader{
|
|||||||
$blockStateData = $this->upgradeStringIdMeta($id, $data);
|
$blockStateData = $this->upgradeStringIdMeta($id, $data);
|
||||||
if($blockStateData === null){
|
if($blockStateData === null){
|
||||||
//unknown block, invalid ID
|
//unknown block, invalid ID
|
||||||
$blockStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION);
|
$blockStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, [], BlockStateData::CURRENT_VERSION);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
//Modern (post-1.13) blockstate
|
//Modern (post-1.13) blockstate
|
||||||
|
@ -23,15 +23,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\data\bedrock\block\upgrade;
|
namespace pocketmine\data\bedrock\block\upgrade;
|
||||||
|
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
|
||||||
use pocketmine\nbt\tag\Tag;
|
use pocketmine\nbt\tag\Tag;
|
||||||
use pocketmine\utils\Utils;
|
|
||||||
|
|
||||||
final class BlockStateUpgradeSchemaBlockRemap{
|
final class BlockStateUpgradeSchemaBlockRemap{
|
||||||
|
|
||||||
public CompoundTag $oldState;
|
|
||||||
public CompoundTag $newState;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Tag[] $oldState
|
* @param Tag[] $oldState
|
||||||
* @param Tag[] $newState
|
* @param Tag[] $newState
|
||||||
@ -39,17 +33,8 @@ final class BlockStateUpgradeSchemaBlockRemap{
|
|||||||
* @phpstan-param array<string, Tag> $newState
|
* @phpstan-param array<string, Tag> $newState
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $oldState,
|
public array $oldState,
|
||||||
public string $newName,
|
public string $newName,
|
||||||
array $newState
|
public array $newState
|
||||||
){
|
){}
|
||||||
$this->oldState = CompoundTag::create();
|
|
||||||
$this->newState = CompoundTag::create();
|
|
||||||
foreach(Utils::stringifyKeys($oldState) as $k => $v){
|
|
||||||
$this->oldState->setTag($k, $v);
|
|
||||||
}
|
|
||||||
foreach(Utils::stringifyKeys($newState) as $k => $v){
|
|
||||||
$this->newState->setTag($k, $v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -240,9 +240,9 @@ final class BlockStateUpgradeSchemaUtils{
|
|||||||
foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
|
foreach(Utils::stringifyKeys($schema->remappedStates) as $oldBlockName => $remaps){
|
||||||
foreach($remaps as $remap){
|
foreach($remaps as $remap){
|
||||||
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaModelBlockRemap(
|
$result->remappedStates[$oldBlockName][] = new BlockStateUpgradeSchemaModelBlockRemap(
|
||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState->getValue()),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->oldState),
|
||||||
$remap->newName,
|
$remap->newName,
|
||||||
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState->getValue()),
|
array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,9 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\data\bedrock\block\upgrade;
|
namespace pocketmine\data\bedrock\block\upgrade;
|
||||||
|
|
||||||
use pocketmine\data\bedrock\block\BlockStateData;
|
use pocketmine\data\bedrock\block\BlockStateData;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
|
||||||
use pocketmine\nbt\tag\Tag;
|
use pocketmine\nbt\tag\Tag;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
|
use function count;
|
||||||
use function ksort;
|
use function ksort;
|
||||||
use const SORT_NUMERIC;
|
use const SORT_NUMERIC;
|
||||||
|
|
||||||
@ -68,12 +68,20 @@ final class BlockStateUpgrader{
|
|||||||
}
|
}
|
||||||
foreach($schemas as $schema){
|
foreach($schemas as $schema){
|
||||||
$oldName = $blockStateData->getName();
|
$oldName = $blockStateData->getName();
|
||||||
|
$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($blockStateData->getStates()->equals($remap->oldState)){
|
if(count($oldState) !== count($remap->oldState)){
|
||||||
$blockStateData = new BlockStateData($remap->newName, clone $remap->newState, $resultVersion);
|
continue; //try next state
|
||||||
continue 2;
|
|
||||||
}
|
}
|
||||||
|
foreach(Utils::stringifyKeys($oldState) as $k => $v){
|
||||||
|
if(!isset($remap->oldState[$k]) || !$remap->oldState[$k]->equals($v)){
|
||||||
|
continue 2; //try next state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$blockStateData = new BlockStateData($remap->newName, $remap->newState, $resultVersion);
|
||||||
|
continue 2; //try next schema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$newName = $schema->renamedIds[$oldName] ?? null;
|
$newName = $schema->renamedIds[$oldName] ?? null;
|
||||||
@ -96,42 +104,44 @@ final class BlockStateUpgrader{
|
|||||||
return $blockStateData;
|
return $blockStateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cloneIfNeeded(CompoundTag $states, int &$stateChanges) : CompoundTag{
|
/**
|
||||||
if($stateChanges === 0){
|
* @param Tag[] $states
|
||||||
$states = clone $states;
|
* @phpstan-param array<string, Tag> $states
|
||||||
}
|
*
|
||||||
|
* @return Tag[]
|
||||||
|
* @phpstan-return array<string, Tag>
|
||||||
|
*/
|
||||||
|
private function applyPropertyAdded(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{
|
||||||
|
if(isset($schema->addedProperties[$oldName])){
|
||||||
|
foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){
|
||||||
|
if(!isset($states[$propertyName])){
|
||||||
$stateChanges++;
|
$stateChanges++;
|
||||||
|
$states[$propertyName] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $states;
|
return $states;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyPropertyAdded(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{
|
/**
|
||||||
$newStates = $states;
|
* @param Tag[] $states
|
||||||
if(isset($schema->addedProperties[$oldName])){
|
* @phpstan-param array<string, Tag> $states
|
||||||
foreach(Utils::stringifyKeys($schema->addedProperties[$oldName]) as $propertyName => $value){
|
*
|
||||||
$oldValue = $states->getTag($propertyName);
|
* @return Tag[]
|
||||||
if($oldValue === null){
|
* @phpstan-return array<string, Tag>
|
||||||
$newStates = $this->cloneIfNeeded($newStates, $stateChanges);
|
*/
|
||||||
$newStates->setTag($propertyName, $value);
|
private function applyPropertyRemoved(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $newStates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applyPropertyRemoved(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{
|
|
||||||
$newStates = $states;
|
|
||||||
if(isset($schema->removedProperties[$oldName])){
|
if(isset($schema->removedProperties[$oldName])){
|
||||||
foreach($schema->removedProperties[$oldName] as $propertyName){
|
foreach($schema->removedProperties[$oldName] as $propertyName){
|
||||||
if($states->getTag($propertyName) !== null){
|
if(isset($states[$propertyName])){
|
||||||
$newStates = $this->cloneIfNeeded($newStates, $stateChanges);
|
$stateChanges++;
|
||||||
$newStates->removeTag($propertyName);
|
unset($states[$propertyName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $newStates;
|
return $states;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function locateNewPropertyValue(BlockStateUpgradeSchema $schema, string $oldName, string $oldPropertyName, Tag $oldValue) : Tag{
|
private function locateNewPropertyValue(BlockStateUpgradeSchema $schema, string $oldName, string $oldPropertyName, Tag $oldValue) : Tag{
|
||||||
@ -146,18 +156,25 @@ final class BlockStateUpgrader{
|
|||||||
return $oldValue;
|
return $oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{
|
/**
|
||||||
|
* @param Tag[] $states
|
||||||
|
* @phpstan-param array<string, Tag> $states
|
||||||
|
*
|
||||||
|
* @return Tag[]
|
||||||
|
* @phpstan-return array<string, Tag>
|
||||||
|
*/
|
||||||
|
private function applyPropertyRenamedOrValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{
|
||||||
if(isset($schema->renamedProperties[$oldName])){
|
if(isset($schema->renamedProperties[$oldName])){
|
||||||
foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){
|
foreach(Utils::stringifyKeys($schema->renamedProperties[$oldName]) as $oldPropertyName => $newPropertyName){
|
||||||
$oldValue = $states->getTag($oldPropertyName);
|
$oldValue = $states[$oldPropertyName] ?? null;
|
||||||
if($oldValue !== null){
|
if($oldValue !== null){
|
||||||
$states = $this->cloneIfNeeded($states, $stateChanges);
|
$stateChanges++;
|
||||||
$states->removeTag($oldPropertyName);
|
unset($states[$oldPropertyName]);
|
||||||
|
|
||||||
//If a value remap is needed, we need to do it here, since we won't be able to locate the property
|
//If a value remap is needed, we need to do it here, since we won't be able to locate the property
|
||||||
//after it's been renamed - value remaps are always indexed by old property name for the sake of
|
//after it's been renamed - value remaps are always indexed by old property name for the sake of
|
||||||
//being able to do changes in any order.
|
//being able to do changes in any order.
|
||||||
$states->setTag($newPropertyName, $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue));
|
$states[$newPropertyName] = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,15 +182,22 @@ final class BlockStateUpgrader{
|
|||||||
return $states;
|
return $states;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema, string $oldName, CompoundTag $states, int &$stateChanges) : CompoundTag{
|
/**
|
||||||
|
* @param Tag[] $states
|
||||||
|
* @phpstan-param array<string, Tag> $states
|
||||||
|
*
|
||||||
|
* @return Tag[]
|
||||||
|
* @phpstan-return array<string, Tag>
|
||||||
|
*/
|
||||||
|
private function applyPropertyValueChanged(BlockStateUpgradeSchema $schema, string $oldName, array $states, int &$stateChanges) : array{
|
||||||
if(isset($schema->remappedPropertyValues[$oldName])){
|
if(isset($schema->remappedPropertyValues[$oldName])){
|
||||||
foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){
|
foreach(Utils::stringifyKeys($schema->remappedPropertyValues[$oldName]) as $oldPropertyName => $remappedValues){
|
||||||
$oldValue = $states->getTag($oldPropertyName);
|
$oldValue = $states[$oldPropertyName] ?? null;
|
||||||
if($oldValue !== null){
|
if($oldValue !== null){
|
||||||
$newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
|
$newValue = $this->locateNewPropertyValue($schema, $oldName, $oldPropertyName, $oldValue);
|
||||||
if($newValue !== $oldValue){
|
if($newValue !== $oldValue){
|
||||||
$states = $this->cloneIfNeeded($states, $stateChanges);
|
$stateChanges++;
|
||||||
$states->setTag($oldPropertyName, $newValue);
|
$states[$oldPropertyName] = $newValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,8 @@ final class BlockStateLookupCache{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->nameToNetworkIdsLookup[$name])){
|
if(isset($this->nameToNetworkIdsLookup[$name])){
|
||||||
$states = $data->getStates();
|
|
||||||
foreach($this->nameToNetworkIdsLookup[$name] as $stateId => $stateNbt){
|
foreach($this->nameToNetworkIdsLookup[$name] as $stateId => $stateNbt){
|
||||||
if($stateNbt->getStates()->equals($states)){
|
if($stateNbt->equals($data)){
|
||||||
return $stateId;
|
return $stateId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ use pocketmine\data\bedrock\block\BlockStateData;
|
|||||||
use pocketmine\data\bedrock\block\BlockStateSerializeException;
|
use pocketmine\data\bedrock\block\BlockStateSerializeException;
|
||||||
use pocketmine\data\bedrock\block\BlockStateSerializer;
|
use pocketmine\data\bedrock\block\BlockStateSerializer;
|
||||||
use pocketmine\data\bedrock\block\BlockTypeNames;
|
use pocketmine\data\bedrock\block\BlockTypeNames;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
@ -67,7 +66,7 @@ final class RuntimeBlockMapping{
|
|||||||
private BlockStateDictionary $blockStateDictionary,
|
private BlockStateDictionary $blockStateDictionary,
|
||||||
private BlockStateSerializer $blockStateSerializer
|
private BlockStateSerializer $blockStateSerializer
|
||||||
){
|
){
|
||||||
$this->fallbackStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION);
|
$this->fallbackStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, [], BlockStateData::CURRENT_VERSION);
|
||||||
$this->fallbackStateId = $this->blockStateDictionary->lookupStateIdFromData($this->fallbackStateData) ?? throw new AssumptionFailedError(BlockTypeNames::INFO_UPDATE . " should always exist");
|
$this->fallbackStateId = $this->blockStateDictionary->lookupStateIdFromData($this->fallbackStateData) ?? throw new AssumptionFailedError(BlockTypeNames::INFO_UPDATE . " should always exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ namespace pocketmine\world\format\io;
|
|||||||
|
|
||||||
use pocketmine\data\bedrock\block\BlockStateData;
|
use pocketmine\data\bedrock\block\BlockStateData;
|
||||||
use pocketmine\data\bedrock\block\BlockTypeNames;
|
use pocketmine\data\bedrock\block\BlockTypeNames;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
|
||||||
use pocketmine\world\format\io\exception\CorruptedWorldException;
|
use pocketmine\world\format\io\exception\CorruptedWorldException;
|
||||||
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
|
use pocketmine\world\format\io\exception\UnsupportedWorldFormatException;
|
||||||
use pocketmine\world\format\PalettedBlockArray;
|
use pocketmine\world\format\PalettedBlockArray;
|
||||||
@ -63,7 +62,7 @@ abstract class BaseWorldProvider implements WorldProvider{
|
|||||||
$newStateData = $blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
|
$newStateData = $blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf);
|
||||||
if($newStateData === null){
|
if($newStateData === null){
|
||||||
//TODO: remember data for unknown states so we can implement them later
|
//TODO: remember data for unknown states so we can implement them later
|
||||||
$newStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION);
|
$newStateData = new BlockStateData(BlockTypeNames::INFO_UPDATE, [], BlockStateData::CURRENT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
$newPalette[$k] = $blockStateDeserializer->deserialize($newStateData);
|
$newPalette[$k] = $blockStateDeserializer->deserialize($newStateData);
|
||||||
|
@ -180,7 +180,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
//TODO: remember data for unknown states so we can implement them later
|
//TODO: remember data for unknown states so we can implement them later
|
||||||
//TODO: this is slow; we need to cache this
|
//TODO: this is slow; we need to cache this
|
||||||
//TODO: log this
|
//TODO: log this
|
||||||
$palette[] = $blockStateDeserializer->deserialize(new BlockStateData(BlockTypeNames::INFO_UPDATE, CompoundTag::create(), BlockStateData::CURRENT_VERSION));
|
$palette[] = $blockStateDeserializer->deserialize(new BlockStateData(BlockTypeNames::INFO_UPDATE, [], BlockStateData::CURRENT_VERSION));
|
||||||
}
|
}
|
||||||
}catch(NbtException | BlockStateDeserializeException $e){
|
}catch(NbtException | BlockStateDeserializeException $e){
|
||||||
throw new CorruptedChunkException("Invalid blockstate NBT at offset $i in paletted storage: " . $e->getMessage(), 0, $e);
|
throw new CorruptedChunkException("Invalid blockstate NBT at offset $i in paletted storage: " . $e->getMessage(), 0, $e);
|
||||||
|
@ -25,7 +25,6 @@ namespace pocketmine\data\bedrock\block\upgrade;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use pocketmine\data\bedrock\block\BlockStateData;
|
use pocketmine\data\bedrock\block\BlockStateData;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
|
||||||
use pocketmine\nbt\tag\IntTag;
|
use pocketmine\nbt\tag\IntTag;
|
||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
@ -81,13 +80,13 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function getEmptyPreimage() : BlockStateData{
|
private function getEmptyPreimage() : BlockStateData{
|
||||||
return new BlockStateData(self::TEST_BLOCK, CompoundTag::create(), self::TEST_VERSION);
|
return new BlockStateData(self::TEST_BLOCK, [], self::TEST_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPreimageOneProperty(string $propertyName, int $value) : BlockStateData{
|
private function getPreimageOneProperty(string $propertyName, int $value) : BlockStateData{
|
||||||
return new BlockStateData(
|
return new BlockStateData(
|
||||||
self::TEST_BLOCK,
|
self::TEST_BLOCK,
|
||||||
CompoundTag::create()->setInt($propertyName, $value),
|
[$propertyName => new IntTag($value)],
|
||||||
self::TEST_VERSION
|
self::TEST_VERSION
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -98,7 +97,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
$getStateData = fn() => $this->getEmptyPreimage();
|
$getStateData = fn() => $this->getEmptyPreimage();
|
||||||
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
||||||
|
|
||||||
self::assertSame(self::TEST_PROPERTY_VALUE_1, $upgradedStateData->getStates()->getTag(self::TEST_PROPERTY)?->getValue());
|
self::assertSame(self::TEST_PROPERTY_VALUE_1, $upgradedStateData->getState(self::TEST_PROPERTY)?->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddPropertyAlreadyExists() : void{
|
public function testAddPropertyAlreadyExists() : void{
|
||||||
@ -132,7 +131,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
|
|
||||||
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
||||||
|
|
||||||
self::assertNull($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY));
|
self::assertNull($upgradedStateData->getState(self::TEST_PROPERTY));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareRenamePropertySchema(BlockStateUpgradeSchema $schema) : void{
|
private function prepareRenamePropertySchema(BlockStateUpgradeSchema $schema) : void{
|
||||||
@ -157,7 +156,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
|
|
||||||
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
||||||
|
|
||||||
self::assertSame($valueAfter, $upgradedStateData->getStates()->getTag(self::TEST_PROPERTY_2)?->getValue());
|
self::assertSame($valueAfter, $upgradedStateData->getState(self::TEST_PROPERTY_2)?->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareRemapPropertyValueSchema(BlockStateUpgradeSchema $schema) : void{
|
private function prepareRemapPropertyValueSchema(BlockStateUpgradeSchema $schema) : void{
|
||||||
@ -193,7 +192,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
|
|
||||||
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
||||||
|
|
||||||
self::assertSame($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY)?->getValue(), $valueAfter);
|
self::assertSame($upgradedStateData->getState(self::TEST_PROPERTY)?->getValue(), $valueAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,7 +206,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
|
|
||||||
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
$upgradedStateData = $this->upgrade($getStateData(), $getStateData);
|
||||||
|
|
||||||
self::assertSame($upgradedStateData->getStates()->getTag(self::TEST_PROPERTY_2)?->getValue(), $valueAfter);
|
self::assertSame($upgradedStateData->getState(self::TEST_PROPERTY_2)?->getValue(), $valueAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,7 +227,7 @@ class BlockStateUpgraderTest extends TestCase{
|
|||||||
|
|
||||||
$getStateData = fn() => new BlockStateData(
|
$getStateData = fn() => new BlockStateData(
|
||||||
self::TEST_BLOCK,
|
self::TEST_BLOCK,
|
||||||
CompoundTag::create(),
|
[],
|
||||||
$stateVersion
|
$stateVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ $reportMap = [];
|
|||||||
foreach($states as $state){
|
foreach($states as $state){
|
||||||
$name = $state->getName();
|
$name = $state->getName();
|
||||||
$reportMap[$name] ??= [];
|
$reportMap[$name] ??= [];
|
||||||
foreach($state->getStates() as $propertyName => $value){
|
foreach(Utils::stringifyKeys($state->getStates()) as $propertyName => $value){
|
||||||
if($value instanceof IntTag || $value instanceof StringTag){
|
if($value instanceof IntTag || $value instanceof StringTag){
|
||||||
$rawValue = $value->getValue();
|
$rawValue = $value->getValue();
|
||||||
}elseif($value instanceof ByteTag){
|
}elseif($value instanceof ByteTag){
|
||||||
|
@ -97,8 +97,8 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
|
|||||||
|
|
||||||
$propertyRemoved = [];
|
$propertyRemoved = [];
|
||||||
$propertyAdded = [];
|
$propertyAdded = [];
|
||||||
foreach($oldStates as $propertyName => $oldProperty){
|
foreach(Utils::stringifyKeys($oldStates) as $propertyName => $oldProperty){
|
||||||
$newProperty = $newStates->getTag($propertyName);
|
$newProperty = $new->getState($propertyName);
|
||||||
if($newProperty === null){
|
if($newProperty === null){
|
||||||
$propertyRemoved[$propertyName] = $oldProperty;
|
$propertyRemoved[$propertyName] = $oldProperty;
|
||||||
}elseif(!$newProperty->equals($oldProperty)){
|
}elseif(!$newProperty->equals($oldProperty)){
|
||||||
@ -112,8 +112,8 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($newStates as $propertyName => $value){
|
foreach(Utils::stringifyKeys($newStates) as $propertyName => $value){
|
||||||
if($oldStates->getTag($propertyName) === null){
|
if($old->getState($propertyName) === null){
|
||||||
$propertyAdded[$propertyName] = $value;
|
$propertyAdded[$propertyName] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,9 +179,9 @@ function processState(BlockStateData $old, BlockStateData $new, BlockStateUpgrad
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
$result->remappedStates[$oldName][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||||
$oldStates->getValue(),
|
$oldStates,
|
||||||
$new->getName(),
|
$new->getName(),
|
||||||
$newStates->getValue()
|
$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");;
|
||||||
}
|
}
|
||||||
@ -231,11 +231,11 @@ function generateBlockStateUpgradeSchema(array $upgradeTable) : BlockStateUpgrad
|
|||||||
}else{
|
}else{
|
||||||
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
//block mapped to multiple different new IDs; we can't guess these, so we just do a plain old remap
|
||||||
foreach($blockStateMappings as $mapping){
|
foreach($blockStateMappings as $mapping){
|
||||||
if($mapping->old->getName() !== $mapping->new->getName() || !$mapping->old->getStates()->equals($mapping->new->getStates())){
|
if(!$mapping->old->equals($mapping->new)){
|
||||||
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
|
$result->remappedStates[$mapping->old->getName()][] = new BlockStateUpgradeSchemaBlockRemap(
|
||||||
$mapping->old->getStates()->getValue(),
|
$mapping->old->getStates(),
|
||||||
$mapping->new->getName(),
|
$mapping->new->getName(),
|
||||||
$mapping->new->getStates()->getValue()
|
$mapping->new->getStates()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user