64    public const INTERNAL_STATE_DATA_BITS = 11;
 
   65    public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
 
   72    public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (-7482769108513497636 & self::INTERNAL_STATE_DATA_MASK);
 
   75    protected string $fallbackName;
 
   83    protected ?array $collisionBoxes = 
null;
 
   85    private int $requiredBlockItemStateDataBits;
 
   86    private int $requiredBlockOnlyStateDataBits;
 
   88    private Block $defaultState;
 
   90    private int $stateIdXorMask;
 
  101    private static function computeStateIdXorMask(
int $typeId) : 
int{
 
  104            $typeId << self::INTERNAL_STATE_DATA_BITS |
 
  105            (BE::unpackSignedLong(hash(
'xxh3', LE::packSignedLong($typeId), binary: 
true)) & self::INTERNAL_STATE_DATA_MASK);
 
  112        $this->idInfo = $idInfo;
 
  113        $this->fallbackName = $name;
 
  114        $this->typeInfo = $typeInfo;
 
  115        $this->position = 
new Position(0, 0, 0, 
null);
 
  119        $this->requiredBlockItemStateDataBits = $calculator->getBitsUsed();
 
  123        $this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
 
  125        $this->stateIdXorMask = self::computeStateIdXorMask($idInfo->getBlockTypeId());
 
  128        $defaultState = clone $this;
 
  129        $this->defaultState = $defaultState;
 
  130        $defaultState->defaultState = $defaultState;
 
 
  133    public function __clone(){
 
  134        $this->position = clone $this->position;
 
  142        return $this->idInfo;
 
 
  149        return $this->fallbackName;
 
 
  162        return $this->idInfo->getBlockTypeId();
 
 
  182    public function getStateId() : int{
 
  183        return $this->encodeFullState() ^ $this->stateIdXorMask;
 
  190        return $this->getTypeId() === $other->getTypeId();
 
 
  199        return $this->getStateId() === $other->getStateId();
 
 
  206        return $this->typeInfo->getTypeTags();
 
 
  218        return $this->typeInfo->hasTypeTag($tag);
 
 
  228        $normalized = clone $this->defaultState;
 
  229        $normalized->decodeBlockItemState($this->encodeBlockItemState());
 
 
  233    private function decodeBlockItemState(
int $data) : void{
 
  236        $this->describeBlockItemState($reader);
 
  237        $readBits = $reader->getOffset();
 
  238        if($this->requiredBlockItemStateDataBits !== $readBits){
 
  239            throw new \LogicException(get_class($this) . 
": Exactly $this->requiredBlockItemStateDataBits bits of block-item state data were provided, but $readBits were read");
 
  243    private function decodeBlockOnlyState(
int $data) : void{
 
  244        $reader = new RuntimeDataReader($this->requiredBlockOnlyStateDataBits, $data);
 
  246        $this->describeBlockOnlyState($reader);
 
  247        $readBits = $reader->getOffset();
 
  248        if($this->requiredBlockOnlyStateDataBits !== $readBits){
 
  249            throw new \LogicException(get_class($this) . 
": Exactly $this->requiredBlockOnlyStateDataBits bits of block-only state data were provided, but $readBits were read");
 
  253    private function encodeBlockItemState() : int{
 
  254        $writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
 
  256        $this->describeBlockItemState($writer);
 
  257        $writtenBits = $writer->getOffset();
 
  258        if($this->requiredBlockItemStateDataBits !== $writtenBits){
 
  259            throw new \LogicException(get_class($this) . 
": Exactly $this->requiredBlockItemStateDataBits bits of block-item state data were expected, but $writtenBits were written");
 
  262        return $writer->getValue();
 
  265    private function encodeBlockOnlyState() : int{
 
  266        $writer = new RuntimeDataWriter($this->requiredBlockOnlyStateDataBits);
 
  268        $this->describeBlockOnlyState($writer);
 
  269        $writtenBits = $writer->getOffset();
 
  270        if($this->requiredBlockOnlyStateDataBits !== $writtenBits){
 
  271            throw new \LogicException(get_class($this) . 
": Exactly $this->requiredBlockOnlyStateDataBits bits of block-only state data were expected, but $writtenBits were written");
 
  274        return $writer->getValue();
 
  277    private function encodeFullState() : int{
 
  278        $blockItemBits = $this->requiredBlockItemStateDataBits;
 
  279        $blockOnlyBits = $this->requiredBlockOnlyStateDataBits;
 
  281        if($blockOnlyBits === 0 && $blockItemBits === 0){
 
  286        if($blockItemBits > 0){
 
  287            $result |= $this->encodeBlockItemState();
 
  289        if($blockOnlyBits > 0){
 
  290            $result |= $this->encodeBlockOnlyState() << $blockItemBits;
 
  327    public function generateStatePermutations() : \
Generator{
 
  330        $bits = $this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits;
 
  331        if($bits > Block::INTERNAL_STATE_DATA_BITS){
 
  332            throw new \LogicException(
"Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . 
" bits");
 
  334        for($blockItemStateData = 0; $blockItemStateData < (1 << $this->requiredBlockItemStateDataBits); ++$blockItemStateData){
 
  335            $withType = clone $this;
 
  337                $withType->decodeBlockItemState($blockItemStateData);
 
  338                $encoded = $withType->encodeBlockItemState();
 
  339                if($encoded !== $blockItemStateData){
 
  340                    throw new \LogicException(static::class . 
"::decodeBlockItemState() accepts invalid inputs (returned $encoded for input $blockItemStateData)");
 
  342            }
catch(InvalidSerializedRuntimeDataException){ 
 
  346            for($blockOnlyStateData = 0; $blockOnlyStateData < (1 << $this->requiredBlockOnlyStateDataBits); ++$blockOnlyStateData){
 
  347                $withState = clone $withType;
 
  349                    $withState->decodeBlockOnlyState($blockOnlyStateData);
 
  350                    $encoded = $withState->encodeBlockOnlyState();
 
  351                    if($encoded !== $blockOnlyStateData){
 
  352                        throw new \LogicException(static::class . 
"::decodeBlockOnlyState() accepts invalid inputs (returned $encoded for input $blockOnlyStateData)");
 
  354                }
catch(InvalidSerializedRuntimeDataException){ 
 
 
  384        $world = $this->position->getWorld();
 
  385        $chunk = $world->getOrLoadChunkAtPosition($this->position);
 
  387            throw new AssumptionFailedError(
"World::setBlock() should have loaded the chunk before calling this method");
 
  389        $chunk->setBlockStateId($this->position->x & Chunk::COORD_MASK, $this->position->y, $this->position->z & Chunk::COORD_MASK, $this->getStateId());
 
  391        $tileType = $this->idInfo->getTileClass();
 
  392        $oldTile = $world->getTile($this->position);
 
  393        if($oldTile !== 
null){
 
  394            if($tileType === null || !($oldTile instanceof $tileType)){
 
  398                $oldTile->clearSpawnCompoundCache(); 
 
  401        if($oldTile === 
null && $tileType !== 
null){
 
  406            $tile = 
new $tileType($world, $this->position->asVector3());
 
  407            $world->addTile($tile);
 
 
  430        return $blockReplace->canBeReplaced();
 
 
  448        $tx->addBlock($blockReplace->position, $this);
 
 
  464        return $this->typeInfo->getBreakInfo();
 
 
  477        return $this->typeInfo->getEnchantmentTags();
 
 
  486        $world = $this->position->getWorld();
 
  487        if(($t = $world->getTile($this->position)) !== 
null){
 
  488            $t->onBlockDestroyed();
 
  490        $world->setBlock($this->position, VanillaBlocks::AIR());
 
 
  504    public function ticksRandomly() : bool{
 
 
  519    public function onScheduledUpdate() : void{
 
 
  529    public function onInteract(
Item $item, int $face, 
Vector3 $clickVector, ?
Player $player = null, array &$returnedItems = []) : bool{
 
 
  568        return $this->isTransparent() ? 0 : 15;
 
 
  579        return $this->getLightFilter() > 0;
 
 
  616    final public function getPosition() : 
Position{
 
  617        return $this->position;
 
  623    final public function position(
World $world, 
int $x, 
int $y, 
int $z) : void{
 
  624        $this->position = new 
Position($x, $y, $z, $world);
 
  625        $this->collisionBoxes = 
null;
 
  634        if($this->getBreakInfo()->isToolCompatible($item)){
 
  635            if($this->isAffectedBySilkTouch() && $item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
 
  636                return $this->getSilkTouchDrops($item);
 
  639            return $this->getDropsForCompatibleTool($item);
 
  642        return $this->getDropsForIncompatibleTool($item);
 
 
  651        return [$this->asItem()];
 
 
  669        return [$this->asItem()];
 
 
  676        if($item->hasEnchantment(
VanillaEnchantments::SILK_TOUCH()) || !$this->getBreakInfo()->isToolCompatible($item)){
 
  680        return $this->getXpDropAmount();
 
 
  702        $item = $this->asItem();
 
  704            $tile = $this->position->getWorld()->getTile($this->position);
 
  705            if($tile instanceof 
Tile){
 
  706                $nbt = $tile->getCleanedNBT();
 
  708                    $item->setCustomBlockData($nbt);
 
  709                    $item->setLore([
"+(DATA)"]);
 
 
  730    public function isFireProofAsItem() : bool{
 
  760        return $this->getFlammability() > 0;
 
 
  775    public function getSide(int $side, int $step = 1){
 
  776        $position = $this->position;
 
  778            [$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0];
 
  779            return $position->getWorld()->getBlockAt(
 
  780                $position->x + ($dx * $step),
 
  781                $position->y + ($dy * $step),
 
  782                $position->z + ($dz * $step)
 
  786        throw new \LogicException(
"Block does not have a valid world");
 
 
  796        $world = $this->position->getWorld();
 
  797        foreach(Facing::HORIZONTAL as $facing){
 
  798            [$dx, $dy, $dz] = Facing::OFFSET[$facing];
 
  800            yield $world->getBlockAt(
 
  801                $this->position->x + $dx,
 
  802                $this->position->y + $dy,
 
  803                $this->position->z + $dz
 
 
  815        $world = $this->position->getWorld();
 
  816        foreach(Facing::OFFSET as [$dx, $dy, $dz]){
 
  818            yield $world->getBlockAt(
 
  819                $this->position->x + $dx,
 
  820                $this->position->y + $dy,
 
  821                $this->position->z + $dz
 
 
  840        return "Block[" . $this->getName() . 
"] (" . $this->getTypeId() . 
":" . $this->encodeFullState() . 
")";
 
 
  847        foreach($this->getCollisionBoxes() as $bb2){
 
 
  917    final public function getCollisionBoxes() : array{
 
  918        if($this->collisionBoxes === null){
 
  919            $this->collisionBoxes = $this->recalculateCollisionBoxes();
 
  920            $extraOffset = $this->getModelPositionOffset();
 
  921            $offset = $extraOffset !== 
null ? $this->position->addVector($extraOffset) : $this->position;
 
  922            foreach($this->collisionBoxes as $bb){
 
  923                $bb->offset($offset->x, $offset->y, $offset->z);
 
  927        return $this->collisionBoxes;
 
 
  951        return SupportType::FULL;
 
 
  954    protected function getAdjacentSupportType(
int $facing) : SupportType{
 
  955        return $this->getSide($facing)->getSupportType(
Facing::opposite($facing));
 
  958    public function isFullCube() : bool{
 
  959        $bb = $this->getCollisionBoxes();
 
  961        return count($bb) === 1 && $bb[0]->getAverageEdgeLength() >= 1 && $bb[0]->isCube();
 
  969        $bbs = $this->getCollisionBoxes();
 
  970        if(count($bbs) === 0){
 
  977        $currentDistance = PHP_INT_MAX;
 
  979        foreach($bbs as $bb){
 
  980            $nextHit = $bb->calculateIntercept($pos1, $pos2);
 
  981            if($nextHit === 
null){
 
  985            $nextDistance = $nextHit->hitVector->distanceSquared($pos1);
 
  986            if($nextDistance < $currentDistance){
 
  987                $currentHit = $nextHit;
 
  988                $currentDistance = $nextDistance;
 
 
 
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player=null)