Allow static properties and state masks to vary based on variant

This commit is contained in:
Dylan K. Taylor 2018-10-06 17:33:28 +01:00
parent 9338061390
commit a430f7f4f7
8 changed files with 64 additions and 102 deletions

View File

@ -35,8 +35,6 @@ use pocketmine\level\Position;
class BlockFactory{
/** @var \SplFixedArray<Block> */
private static $fullList = null;
/** @var \SplFixedArray|\Closure[] */
private static $getInterceptors = null;
/** @var \SplFixedArray<int> */
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);
}

View File

@ -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;
}

View File

@ -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 :(
}
}

View File

@ -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 :(
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);