diff --git a/src/pocketmine/block/BlockFactory.php b/src/pocketmine/block/BlockFactory.php index b105d4315..28aa85cf0 100644 --- a/src/pocketmine/block/BlockFactory.php +++ b/src/pocketmine/block/BlockFactory.php @@ -35,8 +35,6 @@ use pocketmine\level\Position; class BlockFactory{ /** @var \SplFixedArray */ private static $fullList = null; - /** @var \SplFixedArray|\Closure[] */ - private static $getInterceptors = null; /** @var \SplFixedArray */ public static $lightFilter = null; @@ -63,13 +61,12 @@ class BlockFactory{ */ public static function init() : void{ self::$fullList = new \SplFixedArray(8192); - self::$getInterceptors = new \SplFixedArray(8192); - self::$lightFilter = \SplFixedArray::fromArray(array_fill(0, 512, 1)); - self::$diffusesSkyLight = \SplFixedArray::fromArray(array_fill(0, 512, false)); - self::$blastResistance = \SplFixedArray::fromArray(array_fill(0, 512, 0)); + self::$lightFilter = \SplFixedArray::fromArray(array_fill(0, 8192, 1)); + self::$diffusesSkyLight = \SplFixedArray::fromArray(array_fill(0, 8192, false)); + self::$blastResistance = \SplFixedArray::fromArray(array_fill(0, 8192, 0)); - self::$stateMasks = new \SplFixedArray(512); + self::$stateMasks = new \SplFixedArray(8192); self::registerBlock(new Air()); @@ -212,13 +209,10 @@ class BlockFactory{ self::registerBlock(new Farmland()); self::registerBlock(new Furnace()); - self::addGetInterceptor(Block::BURNING_FURNACE, 0, function() : Block{ - $block = self::get(Block::FURNACE); - if($block instanceof Furnace){ - $block->setLit(); - } - return $block; - }); + + $furnace = new Furnace(); + $furnace->setLit(); + self::registerBlock($furnace); //flattening hack self::registerBlock(new SignPost()); self::registerBlock(new WoodenDoor(Block::OAK_DOOR_BLOCK, 0, "Oak Door", Item::OAK_DOOR)); @@ -231,22 +225,15 @@ class BlockFactory{ self::registerBlock(new IronDoor()); self::registerBlock(new WoodenPressurePlate()); self::registerBlock(new RedstoneOre()); - self::addGetInterceptor(Block::GLOWING_REDSTONE_ORE, 0, function() : Block{ - $block = self::get(Block::REDSTONE_ORE); - if($block instanceof RedstoneOre){ - $block->setLit(); - } - return $block; - }); + + $litRedstone = new RedstoneOre(); + $litRedstone->setLit(); + self::registerBlock($litRedstone); //flattening hack self::registerBlock(new RedstoneTorch()); - self::addGetInterceptor(Block::UNLIT_REDSTONE_TORCH, 0, function() : Block{ - $block = self::get(Block::REDSTONE_TORCH); - if($block instanceof RedstoneTorch){ - $block->setLit(false); //default state is lit - } - return $block; - }); + $unlitRedstoneTorch = new RedstoneTorch(); + $unlitRedstoneTorch->setLit(false); + self::registerBlock($unlitRedstoneTorch); //flattening hack self::registerBlock(new StoneButton()); self::registerBlock(new SnowLayer()); @@ -300,13 +287,9 @@ class BlockFactory{ self::registerBlock(new EndStone()); //TODO: DRAGON_EGG self::registerBlock(new RedstoneLamp()); - self::addGetInterceptor(Block::LIT_REDSTONE_LAMP, 0, function() : Block{ - $block = self::get(Block::REDSTONE_LAMP); - if($block instanceof RedstoneLamp){ - $block->setLit(); - } - return $block; - }); + $litLamp = new RedstoneLamp(); + $litLamp->setLit(); + self::registerBlock($litLamp); //flattening hack //TODO: DROPPER self::registerBlock(new ActivatorRail()); @@ -342,13 +325,9 @@ class BlockFactory{ //TODO: COMPARATOR_BLOCK //TODO: POWERED_COMPARATOR self::registerBlock(new DaylightSensor()); - self::addGetInterceptor(Block::DAYLIGHT_SENSOR_INVERTED, 0, function() : Block{ - $block = self::get(Block::DAYLIGHT_SENSOR); - if($block instanceof DaylightSensor){ - $block->setInverted(); - } - return $block; - }); + $invertedSensor = new DaylightSensor(); + $invertedSensor->setInverted(); + self::registerBlock($invertedSensor); //flattening hack self::registerBlock(new Redstone()); self::registerBlock(new NetherQuartzOre()); @@ -479,18 +458,31 @@ class BlockFactory{ $id = $block->getId(); $variant = $block->getVariant(); - if(!$override and self::isRegistered($id, $variant)){ - throw new \RuntimeException("Trying to overwrite an already registered block"); + + $stateMask = $block->getStateBitmask(); + if(($variant & $stateMask) !== 0){ + throw new \InvalidArgumentException("Block variant collides with state bitmask"); } - if(self::$stateMasks[$id] !== null and self::$stateMasks[$id] !== $block->getStateBitmask()){ - throw new \InvalidArgumentException("Blocks with the same ID must have the same state bitmask"); - } + for($m = $variant; $m <= ($variant | $stateMask); ++$m){ + if(($m & ~$stateMask) !== $variant){ + continue; + } - self::$fullList[($id << 4) | $variant] = clone $block; - if($variant === 0){ - //TODO: allow these to differ for different variants - self::fillStaticArrays($id, $block); + if(!$override and self::isRegistered($id, $m)){ + throw new \InvalidArgumentException("Block registration " . get_class($block) . " has states which conflict with other blocks"); + } + + $index = ($id << 4) | $m; + + $v = clone $block; + $v->readStateFromMeta($m & $stateMask); + + self::$fullList[$index] = $v; + self::$stateMasks[$index] = $stateMask; + self::$lightFilter[$index] = min(15, $v->getLightFilter() + 1); //opacity plus 1 standard light filter + self::$diffusesSkyLight[$index] = $v->diffusesSkyLight(); + self::$blastResistance[$index] = $v->getBlastResistance(); } } @@ -508,27 +500,18 @@ class BlockFactory{ throw new \InvalidArgumentException("Block meta value $meta is out of bounds"); } - $stateMask = self::getStateMask($id); - $variant = $meta & ~$stateMask; - $state = $meta & $stateMask; - - $index = ($id << 4) | $variant; - /** @var Block|null $block */ $block = null; try{ - if(self::$getInterceptors[$index] !== null){ - $block = (self::$getInterceptors[$index])(); - }elseif(self::$fullList[$index] !== null){ + $index = ($id << 4) | $meta; + if(self::$fullList[$index] !== null){ $block = clone self::$fullList[$index]; } }catch(\RuntimeException $e){ throw new \InvalidArgumentException("Block ID $id is out of bounds"); } - if($block !== null){ - $block->readStateFromMeta($state); - }else{ + if($block === null){ $block = new UnknownBlock($id, $meta); } @@ -542,20 +525,8 @@ class BlockFactory{ return $block; } - public static function addGetInterceptor(int $id, int $variant, \Closure $interceptor) : void{ - $block = $interceptor(); - if(!($block instanceof Block)){ - throw new \InvalidArgumentException("Interceptor must return an instance of " . Block::class); - } - self::$getInterceptors[($id << 4) | $variant] = $interceptor; - self::fillStaticArrays($id, $block); - } - - private static function fillStaticArrays(int $id, Block $block) : void{ - self::$lightFilter[$id] = min(15, $block->getLightFilter() + 1); //opacity plus 1 standard light filter - self::$diffusesSkyLight[$id] = $block->diffusesSkyLight(); - self::$blastResistance[$id] = $block->getBlastResistance(); - self::$stateMasks[$id] = $block->getStateBitmask(); + public static function fromFullBlock(int $fullState, ?Position $pos = null) : Block{ + return self::get($fullState >> 4, $fullState & 0xf, $pos); } public static function getStateMask(int $id) : int{ @@ -563,15 +534,15 @@ class BlockFactory{ } /** - * Returns whether a specified block ID is already registered in the block factory. + * Returns whether a specified block state is already registered in the block factory. * * @param int $id - * @param int $variant + * @param int $meta * * @return bool */ - public static function isRegistered(int $id, int $variant = 0) : bool{ - $b = self::$fullList[($id << 4) | $variant]; + public static function isRegistered(int $id, int $meta = 0) : bool{ + $b = self::$fullList[($id << 4) | $meta]; return $b !== null and !($b instanceof UnknownBlock); } diff --git a/src/pocketmine/block/Grass.php b/src/pocketmine/block/Grass.php index 4332cc0d5..fd1f47b9e 100644 --- a/src/pocketmine/block/Grass.php +++ b/src/pocketmine/block/Grass.php @@ -65,7 +65,7 @@ class Grass extends Solid{ public function onRandomTick() : void{ $lightAbove = $this->level->getFullLightAt($this->x, $this->y + 1, $this->z); - if($lightAbove < 4 and BlockFactory::$lightFilter[$this->level->getBlockIdAt($this->x, $this->y + 1, $this->z)] >= 3){ //2 plus 1 standard filter amount + if($lightAbove < 4 and BlockFactory::$lightFilter[$this->level->getFullBlock($this->x, $this->y + 1, $this->z)] >= 3){ //2 plus 1 standard filter amount //grass dies $ev = new BlockSpreadEvent($this, $this, BlockFactory::get(Block::DIRT)); $ev->call(); @@ -82,7 +82,7 @@ class Grass extends Solid{ $this->level->getBlockIdAt($x, $y, $z) !== Block::DIRT or $this->level->getBlockDataAt($x, $y, $z) === 1 or $this->level->getFullLightAt($x, $y + 1, $z) < 4 or - BlockFactory::$lightFilter[$this->level->getBlockIdAt($x, $y + 1, $z)] >= 3 + BlockFactory::$lightFilter[$this->level->getFullBlock($x, $y + 1, $z)] >= 3 ){ continue; } diff --git a/src/pocketmine/block/Purpur.php b/src/pocketmine/block/Purpur.php index 5c6687aed..43db8c9aa 100644 --- a/src/pocketmine/block/Purpur.php +++ b/src/pocketmine/block/Purpur.php @@ -42,8 +42,4 @@ class Purpur extends Solid{ public function getBlastResistance() : float{ return 30; } - - public function getStateBitmask() : int{ - return 0b1100; //HACK: needs to be consistent for blocks with the same ID :( - } } diff --git a/src/pocketmine/block/Quartz.php b/src/pocketmine/block/Quartz.php index 7c5e3c4e5..926a20a35 100644 --- a/src/pocketmine/block/Quartz.php +++ b/src/pocketmine/block/Quartz.php @@ -42,8 +42,4 @@ class Quartz extends Solid{ public function getToolHarvestLevel() : int{ return TieredTool::TIER_WOODEN; } - - public function getStateBitmask() : int{ - return 0b1100; //HACK: needs to be consistent for blocks with the same ID :( - } } diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 3eef24d07..5c8338986 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -123,13 +123,13 @@ class Explosion{ continue; } - $blockId = $this->subChunkHandler->currentSubChunk->getBlockId($vBlock->x & 0x0f, $vBlock->y & 0x0f, $vBlock->z & 0x0f); + $state = $this->subChunkHandler->currentSubChunk->getFullBlock($vBlock->x & 0x0f, $vBlock->y & 0x0f, $vBlock->z & 0x0f); - if($blockId !== 0){ - $blastForce -= (BlockFactory::$blastResistance[$blockId] / 5 + 0.3) * $this->stepLen; + if($state !== 0){ + $blastForce -= (BlockFactory::$blastResistance[$state] / 5 + 0.3) * $this->stepLen; if($blastForce > 0){ if(!isset($this->affectedBlocks[$index = Level::blockHash($vBlock->x, $vBlock->y, $vBlock->z)])){ - $this->affectedBlocks[$index] = BlockFactory::get($blockId, $this->subChunkHandler->currentSubChunk->getBlockData($vBlock->x & 0x0f, $vBlock->y & 0x0f, $vBlock->z & 0x0f), $vBlock); + $this->affectedBlocks[$index] = BlockFactory::get($state >> 4, $state & 0xf, $vBlock); } } } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 2dd638d9f..d040eba90 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -983,12 +983,11 @@ class Level implements ChunkManager, Metadatable{ $y = ($k >> 4) & 0x0f; $z = ($k >> 8) & 0x0f; - $blockId = $subChunk->getBlockId($x, $y, $z); - $meta = $subChunk->getBlockData($x, $y, $z); + $state = $subChunk->getFullBlock($x, $y, $z); - if($this->randomTickBlocks[($blockId << 4) | ($meta & ~BlockFactory::getStateMask($blockId))]){ + if($this->randomTickBlocks[$state & ~BlockFactory::getStateMask($state >> 4)]){ /** @var Block $block */ - $block = BlockFactory::get($blockId, $meta); + $block = BlockFactory::fromFullBlock($state); $block->x = $chunkX * 16 + $x; $block->y = ($Y << 4) + $y; diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 01835e32d..398229cf6 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -394,7 +394,7 @@ class Chunk{ public function recalculateHeightMapColumn(int $x, int $z) : int{ $max = $this->getHighestBlockAt($x, $z); for($y = $max; $y >= 0; --$y){ - if(BlockFactory::$lightFilter[$id = $this->getBlockId($x, $y, $z)] > 1 or BlockFactory::$diffusesSkyLight[$id]){ + if(BlockFactory::$lightFilter[$state = $this->getFullBlock($x, $y, $z)] > 1 or BlockFactory::$diffusesSkyLight[$state]){ break; } } @@ -426,7 +426,7 @@ class Chunk{ $light = 15; for(; $y >= 0; --$y){ if($light > 0){ - $light -= BlockFactory::$lightFilter[$this->getBlockId($x, $y, $z)]; + $light -= BlockFactory::$lightFilter[$this->getFullBlock($x, $y, $z)]; if($light <= 0){ break; } diff --git a/src/pocketmine/level/light/LightUpdate.php b/src/pocketmine/level/light/LightUpdate.php index 2ee6d3479..7dec14ccd 100644 --- a/src/pocketmine/level/light/LightUpdate.php +++ b/src/pocketmine/level/light/LightUpdate.php @@ -157,7 +157,7 @@ abstract class LightUpdate{ protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel){ $current = $this->getLight($x, $y, $z); - $potentialLight = $newAdjacentLevel - BlockFactory::$lightFilter[$this->subChunkHandler->currentSubChunk->getBlockId($x & 0x0f, $y & 0x0f, $z & 0x0f)]; + $potentialLight = $newAdjacentLevel - BlockFactory::$lightFilter[$this->subChunkHandler->currentSubChunk->getFullBlock($x & 0x0f, $y & 0x0f, $z & 0x0f)]; if($current < $potentialLight){ $this->setLight($x, $y, $z, $potentialLight);