44    public const COLLISION_CUSTOM = 0;
 
   45    public const COLLISION_CUBE = 1;
 
   46    public const COLLISION_NONE = 2;
 
   47    public const COLLISION_MAY_OVERFLOW = 3;
 
   53    private array $fullList = [];
 
   60    private array $typeIndex = [];
 
   66    public array $light = [];
 
   71    public array $lightFilter = [];
 
   76    public array $blocksDirectSkyLight = [];
 
   81    public array $blastResistance = [];
 
   88    public array $collisionInfo = [];
 
   90    public function __construct(){
 
   92            $this->
register($block);
 
  102    public function register(
Block $block) : void{
 
  103        $typeId = $block->getTypeId();
 
  105        if(isset($this->typeIndex[$typeId])){
 
  106            throw new \InvalidArgumentException(
"Block ID $typeId is already used by another block");
 
  109        $this->typeIndex[$typeId] = clone $block;
 
  111        foreach($block->generateStatePermutations() as $v){
 
  112            $this->fillStaticArrays($v->getStateId(), $v);
 
 
  122    private static function overridesBlockMethod(\Closure $closure) : bool{
 
  123        $declarer = (new \ReflectionFunction($closure))->getClosureScopeClass();
 
  124        return $declarer !== 
null && $declarer->getName() !== Block::class;
 
  136    private static function calculateCollisionInfo(Block $block) : int{
 
  138            self::overridesBlockMethod($block->getModelPositionOffset(...)) ||
 
  139            self::overridesBlockMethod($block->readStateFromWorld(...))
 
  145            return self::COLLISION_MAY_OVERFLOW;
 
  150        $boxes = $block->getCollisionBoxes();
 
  151        if(count($boxes) === 0){
 
  152            return self::COLLISION_NONE;
 
  156            count($boxes) === 1 &&
 
  157            $boxes[0]->minX === 0.0 &&
 
  158            $boxes[0]->minY === 0.0 &&
 
  159            $boxes[0]->minZ === 0.0 &&
 
  160            $boxes[0]->maxX === 1.0 &&
 
  161            $boxes[0]->maxY === 1.0 &&
 
  162            $boxes[0]->maxZ === 1.0
 
  164            return self::COLLISION_CUBE;
 
  167        foreach($boxes as $box){
 
  169                $box->minX < 0 || $box->maxX > 1 ||
 
  170                $box->minY < 0 || $box->maxY > 1 ||
 
  171                $box->minZ < 0 || $box->maxZ > 1
 
  173                return self::COLLISION_MAY_OVERFLOW;
 
  177        return self::COLLISION_CUSTOM;
 
  180    private function fillStaticArrays(
int $index, Block $block) : void{
 
  181        $fullId = $block->getStateId();
 
  182        if($index !== $fullId){
 
  183            throw new AssumptionFailedError(
"Cannot fill static arrays for an invalid blockstate");
 
  185            $this->fullList[$index] = $block;
 
  186            $this->blastResistance[$index] = $block->getBreakInfo()->getBlastResistance();
 
  187            $this->light[$index] = $block->getLightLevel();
 
  188            $this->lightFilter[$index] = min(15, $block->getLightFilter() + LightUpdate::BASE_LIGHT_FILTER);
 
  189            if($block->blocksDirectSkyLight()){
 
  190                $this->blocksDirectSkyLight[$index] = 
true;
 
  193            $this->collisionInfo[$index] = self::calculateCollisionInfo($block);
 
  197    public function fromStateId(
int $stateId) : Block{
 
  199            throw new \InvalidArgumentException(
"Block state ID cannot be negative");
 
  201        if(isset($this->fullList[$stateId])) { 
 
  202            $block = clone $this->fullList[$stateId];
 
  204            $typeId = $stateId >> Block::INTERNAL_STATE_DATA_BITS;
 
  205            $stateData = ($stateId ^ $typeId) & Block::INTERNAL_STATE_DATA_MASK;
 
  206            $block = 
new UnknownBlock(
new BID($typeId), 
new BlockTypeInfo(BreakInfo::instant()), $stateData);
 
  212    public function hasStateId(
int $stateId) : bool{
 
  213        return isset($this->fullList[$stateId]);
 
  221        return $this->fullList;