RuntimeBlockMapping: use new data, item frames & doors fixed

floor & ceiling item frames not supported though
This commit is contained in:
Dylan K. Taylor 2019-12-06 11:31:18 +00:00
parent 124e60301a
commit 8e984a1bc3
2 changed files with 56 additions and 42 deletions

View File

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

View File

@ -24,7 +24,8 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types; namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\block\BlockIds; use pocketmine\block\BlockIds;
use pocketmine\nbt\BigEndianNBTStream; use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
@ -45,7 +46,7 @@ final class RuntimeBlockMapping{
private static $legacyToRuntimeMap = []; private static $legacyToRuntimeMap = [];
/** @var int[] */ /** @var int[] */
private static $runtimeToLegacyMap = []; private static $runtimeToLegacyMap = [];
/** @var mixed[]|null */ /** @var CompoundTag[]|null */
private static $bedrockKnownStates = null; private static $bedrockKnownStates = null;
private function __construct(){ private function __construct(){
@ -53,36 +54,57 @@ final class RuntimeBlockMapping{
} }
public static function init() : void{ public static function init() : void{
$tag = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"));
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");
}
/** @var CompoundTag[] $list */
$list = $tag->getValue();
self::$bedrockKnownStates = self::randomizeTable($list);
self::setupLegacyMappings();
}
private static function setupLegacyMappings() : void{
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
$tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); $legacyStateMap = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"));
if(!($tag instanceof CompoundTag)){ //this is a little redundant currently, but good for auto complete and makes phpstan happy if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){
throw new \RuntimeException("Invalid blockstates table, expected CompoundTag root"); throw new \RuntimeException("Invalid legacy states mapping table, expected TAG_List<TAG_Compound> root");
} }
$decompressed = []; /**
* @var int[][] $idToStatesMap string id -> int[] list of candidate state indices
$states = $tag->getListTag("Palette"); */
/** @var CompoundTag $state */ $idToStatesMap = [];
foreach($states as $state){ foreach(self::$bedrockKnownStates as $k => $state){
$block = $state->getCompoundTag("block"); $idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
$name = $block->getString("name");
$decompressed[] = [
"name" => $name,
"states" => $block->getCompoundTag("states"),
"data" => $state->getShort("meta"),
"legacy_id" => $legacyIdMap[$name]
];
} }
self::$bedrockKnownStates = self::randomizeTable($decompressed); /** @var CompoundTag $pair */
foreach($legacyStateMap as $pair){
foreach(self::$bedrockKnownStates as $k => $obj){ $oldState = $pair->getCompoundTag("old");
if($obj["data"] > 15){ $id = $legacyIdMap[$oldState->getString("name")];
//TODO: in 1.12 they started using data values bigger than 4 bits which we can't handle right now $data = $oldState->getShort("val");
if($data > 15){
//we can't handle metadata with more than 4 bits
continue; continue;
} }
//this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries $mappedState = $pair->getCompoundTag("new");
self::registerMapping($k, $obj["legacy_id"], $obj["data"]);
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
$mappedState->setName("block");
$mappedName = $mappedState->getString("name");
if(!isset($idToStatesMap[$mappedName])){
throw new \RuntimeException("Mapped new state does not appear in network table");
}
foreach($idToStatesMap[$mappedName] as $k){
$networkState = self::$bedrockKnownStates[$k];
if($mappedState->equals($networkState->getCompoundTag("block"))){
self::registerMapping($k, $id, $data);
continue 2;
}
}
throw new \RuntimeException("Mapped new state does not appear in network table");
} }
} }
@ -97,9 +119,9 @@ final class RuntimeBlockMapping{
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they * Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
* aren't supposed to do, so we have to deliberately break it to make them stop. * aren't supposed to do, so we have to deliberately break it to make them stop.
* *
* @param array $table * @param CompoundTag[] $table
* *
* @return array * @return CompoundTag[]
*/ */
private static function randomizeTable(array $table) : array{ private static function randomizeTable(array $table) : array{
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms $postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
@ -141,19 +163,11 @@ final class RuntimeBlockMapping{
self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
} }
public static function generateBlockTable() : ListTag{ /**
* @return CompoundTag[]
*/
public static function getBedrockKnownStates() : array{
self::lazyInit(); self::lazyInit();
$states = new ListTag(); return self::$bedrockKnownStates;
//TODO: this assoc array mess really doesn't make sense anymore, we can store NBT directly
foreach(self::$bedrockKnownStates as $v){
$state = new CompoundTag();
$state->setTag(new CompoundTag("block", [
new StringTag("name", $v["name"]),
$v["states"]
]));
$state->setShort("id", $v["legacy_id"]);
$states->push($state);
}
return $states;
} }
} }