RuntimeBlockMapping is now a singleton instead of static class

this prepares for a fully dynamic block mapper, as well as allowing a small performance improvement to chunk encoding by eliding the constant lazy-init checks.
This commit is contained in:
Dylan K. Taylor 2020-04-23 21:09:58 +01:00
parent f3fed60d57
commit aa1828aa98
4 changed files with 34 additions and 36 deletions

View File

@ -111,7 +111,7 @@ class Block{
* @internal
*/
public function getRuntimeId() : int{
return RuntimeBlockMapping::toStaticRuntimeId($this->getId(), $this->getMeta());
return RuntimeBlockMapping::getInstance()->toStaticRuntimeId($this->getId(), $this->getMeta());
}
public function getMeta() : int{

View File

@ -289,7 +289,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
if($this->blockTable === null){
if(self::$blockTableCache === null){
//this is a really nasty hack, but it'll do for now
self::$blockTableCache = (new NetworkNbtSerializer())->write(new TreeRoot(new ListTag(RuntimeBlockMapping::getBedrockKnownStates())));
self::$blockTableCache = (new NetworkNbtSerializer())->write(new TreeRoot(new ListTag(RuntimeBlockMapping::getInstance()->getBedrockKnownStates())));
}
$out->put(self::$blockTableCache);
}else{

View File

@ -39,19 +39,25 @@ use function shuffle;
* @internal
*/
final class RuntimeBlockMapping{
/** @var self|null */
private static $instance = null;
/** @var int[] */
private static $legacyToRuntimeMap = [];
/** @var int[] */
private static $runtimeToLegacyMap = [];
/** @var CompoundTag[]|null */
private static $bedrockKnownStates = null;
public static function getInstance() : self{
if(self::$instance === null){
self::$instance = new self;
}
private function __construct(){
//NOOP
return self::$instance;
}
public static function init() : void{
/** @var int[] */
private $legacyToRuntimeMap = [];
/** @var int[] */
private $runtimeToLegacyMap = [];
/** @var CompoundTag[]|null */
private $bedrockKnownStates = null;
private function __construct(){
$tag = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"))->getTag();
if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy
throw new \RuntimeException("Invalid blockstates table, expected TAG_List<TAG_Compound> root");
@ -59,12 +65,12 @@ final class RuntimeBlockMapping{
/** @var CompoundTag[] $list */
$list = $tag->getValue();
self::$bedrockKnownStates = self::randomizeTable($list);
$this->bedrockKnownStates = self::randomizeTable($list);
self::setupLegacyMappings();
$this->setupLegacyMappings();
}
private static function setupLegacyMappings() : void{
private function setupLegacyMappings() : void{
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
$legacyStateMap = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"))->getTag();
if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){
@ -75,7 +81,7 @@ final class RuntimeBlockMapping{
* @var int[][] $idToStatesMap string id -> int[] list of candidate state indices
*/
$idToStatesMap = [];
foreach(self::$bedrockKnownStates as $k => $state){
foreach($this->bedrockKnownStates as $k => $state){
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
}
/** @var CompoundTag $pair */
@ -93,9 +99,9 @@ final class RuntimeBlockMapping{
throw new \RuntimeException("Mapped new state does not appear in network table");
}
foreach($idToStatesMap[$mappedName] as $k){
$networkState = self::$bedrockKnownStates[$k];
$networkState = $this->bedrockKnownStates[$k];
if($mappedState->equals($networkState->getCompoundTag("block"))){
self::registerMapping($k, $id, $data);
$this->registerMapping($k, $id, $data);
continue 2;
}
}
@ -103,12 +109,6 @@ final class RuntimeBlockMapping{
}
}
private static function lazyInit() : void{
if(self::$bedrockKnownStates === null){
self::init();
}
}
/**
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
@ -126,35 +126,32 @@ final class RuntimeBlockMapping{
return $table;
}
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
self::lazyInit();
public function toStaticRuntimeId(int $id, int $meta = 0) : int{
/*
* try id+meta first
* if not found, try id+0 (strip meta)
* if still not found, return update! block
*/
return self::$legacyToRuntimeMap[($id << 4) | $meta] ?? self::$legacyToRuntimeMap[$id << 4] ?? self::$legacyToRuntimeMap[BlockLegacyIds::INFO_UPDATE << 4];
return $this->legacyToRuntimeMap[($id << 4) | $meta] ?? $this->legacyToRuntimeMap[$id << 4] ?? $this->legacyToRuntimeMap[BlockLegacyIds::INFO_UPDATE << 4];
}
/**
* @return int[] [id, meta]
*/
public static function fromStaticRuntimeId(int $runtimeId) : array{
self::lazyInit();
$v = self::$runtimeToLegacyMap[$runtimeId];
public function fromStaticRuntimeId(int $runtimeId) : array{
$v = $this->runtimeToLegacyMap[$runtimeId];
return [$v >> 4, $v & 0xf];
}
private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{
self::$legacyToRuntimeMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId;
self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
private function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{
$this->legacyToRuntimeMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId;
$this->runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
}
/**
* @return CompoundTag[]
*/
public static function getBedrockKnownStates() : array{
self::lazyInit();
return self::$bedrockKnownStates;
public function getBedrockKnownStates() : array{
return $this->bedrockKnownStates;
}
}

View File

@ -53,6 +53,7 @@ final class ChunkSerializer{
public static function serialize(Chunk $chunk, ?string $tiles = null) : string{
$stream = new NetworkBinaryStream();
$subChunkCount = self::getSubChunkCount($chunk);
$blockMapper = RuntimeBlockMapping::getInstance();
for($y = 0; $y < $subChunkCount; ++$y){
$layers = $chunk->getSubChunk($y)->getBlockLayers();
$stream->putByte(8); //version
@ -69,7 +70,7 @@ final class ChunkSerializer{
//zigzag and just shift directly.
$stream->putUnsignedVarInt(count($palette) << 1); //yes, this is intentionally zigzag
foreach($palette as $p){
$stream->putUnsignedVarInt(RuntimeBlockMapping::toStaticRuntimeId($p >> 4, $p & 0xf) << 1);
$stream->putUnsignedVarInt($blockMapper->toStaticRuntimeId($p >> 4, $p & 0xf) << 1);
}
}
}