mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 00:33:59 +00:00
Allow static properties and state masks to vary based on variant
This commit is contained in:
parent
9338061390
commit
a430f7f4f7
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 :(
|
||||
}
|
||||
}
|
||||
|
@ -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 :(
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user