88    public const MOTION_THRESHOLD = 0.00001;
 
   89    protected const STEP_CLIP_MULTIPLIER = 0.4;
 
   91    private const TAG_FIRE = 
"Fire"; 
 
   92    private const TAG_ON_GROUND = 
"OnGround"; 
 
   93    private const TAG_FALL_DISTANCE = 
"FallDistance"; 
 
   94    private const TAG_CUSTOM_NAME = 
"CustomName"; 
 
   95    private const TAG_CUSTOM_NAME_VISIBLE = 
"CustomNameVisible"; 
 
   96    public const TAG_POS = 
"Pos"; 
 
   97    public const TAG_MOTION = 
"Motion"; 
 
   98    public const TAG_ROTATION = 
"Rotation"; 
 
  100    private static int $entityCount = 1;
 
  106        return self::$entityCount++;
 
 
  113    protected array $hasSpawned = [];
 
  122    protected ?array $blocksAround = 
null;
 
  128    protected bool $forceMovementUpdate = 
false;
 
  129    private bool $checkBlockIntersectionsNextTick = 
true;
 
  132    public bool $onGround = 
false;
 
  136    private float $health = 20.0;
 
  137    private int $maxHealth = 20;
 
  139    protected float $ySize = 0.0;
 
  140    protected float $stepHeight = 0.0;
 
  141    public bool $keepMovement = 
false;
 
  143    public float $fallDistance = 0.0;
 
  144    public int $ticksLived = 0;
 
  145    public int $lastUpdate;
 
  146    protected int $fireTicks = 0;
 
  148    private bool $savedWithChunk = 
true;
 
  150    public bool $isCollided = 
false;
 
  151    public bool $isCollidedHorizontally = 
false;
 
  152    public bool $isCollidedVertically = 
false;
 
  154    public int $noDamageTicks = 0;
 
  155    protected bool $justCreated = 
true;
 
  159    protected float $gravity;
 
  160    protected float $drag;
 
  161    protected bool $gravityEnabled = 
true;
 
  165    protected bool $closed = 
false;
 
  166    private bool $closeInFlight = 
false;
 
  167    private bool $needsDespawn = 
false;
 
  171    protected bool $networkPropertiesDirty = 
false;
 
  173    protected string $nameTag = 
"";
 
  174    protected bool $nameTagVisible = 
true;
 
  175    protected bool $alwaysShowNameTag = 
false;
 
  176    protected string $scoreTag = 
"";
 
  177    protected float $scale = 1.0;
 
  179    protected bool $canClimb = 
false;
 
  180    protected bool $canClimbWalls = 
false;
 
  181    protected bool $noClientPredictions = 
false;
 
  182    protected bool $invisible = 
false;
 
  183    protected bool $silent = 
false;
 
  185    protected ?
int $ownerId = 
null;
 
  186    protected ?
int $targetId = 
null;
 
  188    private bool $constructorCalled = 
false;
 
  191        if($this->constructorCalled){
 
  192            throw new \LogicException(
"Attempted to call constructor for an Entity multiple times");
 
  194        $this->constructorCalled = 
true;
 
  195        Utils::checkLocationNotInfOrNaN($location);
 
  197        $this->timings = Timings::getEntityTimings($this);
 
  199        $this->size = $this->getInitialSizeInfo();
 
  200        $this->drag = $this->getInitialDragMultiplier();
 
  201        $this->gravity = $this->getInitialGravity();
 
  203        $this->
id = self::nextRuntimeId();
 
  208        $this->boundingBox = 
new AxisAlignedBB(0, 0, 0, 0, 0, 0);
 
  209        $this->recalculateBoundingBox();
 
  214            $this->motion = Vector3::zero();
 
  217        $this->resetLastMovements();
 
  219        $this->networkProperties = 
new EntityMetadataCollection();
 
  221        $this->attributeMap = 
new AttributeMap();
 
  222        $this->addAttributes();
 
  224        $this->initEntity($nbt ?? 
new CompoundTag());
 
  226        $this->getWorld()->addEntity($this);
 
  228        $this->lastUpdate = $this->
server->getTick();
 
  230        $this->scheduleUpdate();
 
  233    abstract protected function getInitialSizeInfo() : EntitySizeInfo;
 
  250    public function getNameTag() : string{
 
  251        return $this->nameTag;
 
  254    public function isNameTagVisible() : bool{
 
  255        return $this->nameTagVisible;
 
  258    public function isNameTagAlwaysVisible() : bool{
 
  259        return $this->alwaysShowNameTag;
 
  270    public function setNameTag(
string $name) : void{
 
  271        $this->nameTag = $name;
 
  272        $this->networkPropertiesDirty = 
true;
 
  275    public function setNameTagVisible(
bool $value = 
true) : void{
 
  276        $this->nameTagVisible = $value;
 
  277        $this->networkPropertiesDirty = 
true;
 
  280    public function setNameTagAlwaysVisible(
bool $value = 
true) : void{
 
  281        $this->alwaysShowNameTag = $value;
 
  282        $this->networkPropertiesDirty = 
true;
 
  285    public function getScoreTag() : ?string{
 
  286        return $this->scoreTag; 
 
  289    public function setScoreTag(
string $score) : void{
 
  290        $this->scoreTag = $score;
 
  291        $this->networkPropertiesDirty = 
true;
 
  294    public function getScale() : float{
 
  298    public function setScale(
float $value) : void{
 
  300            throw new \InvalidArgumentException(
"Scale must be greater than 0");
 
  302        $this->scale = $value;
 
  303        $this->setSize($this->getInitialSizeInfo()->scale($value));
 
  306    public function getBoundingBox() : AxisAlignedBB{
 
  307        return $this->boundingBox;
 
  310    protected function recalculateBoundingBox() : void{
 
  311        $halfWidth = $this->size->getWidth() / 2;
 
  313        $this->boundingBox = 
new AxisAlignedBB(
 
  314            $this->location->x - $halfWidth,
 
  315            $this->location->y + $this->ySize,
 
  316            $this->location->z - $halfWidth,
 
  317            $this->location->x + $halfWidth,
 
  318            $this->location->y + $this->size->getHeight() + $this->ySize,
 
  319            $this->location->z + $halfWidth
 
  323    public function getSize() : EntitySizeInfo{
 
  327    protected function setSize(EntitySizeInfo $size) : void{
 
  329        $this->recalculateBoundingBox();
 
  330        $this->networkPropertiesDirty = 
true;
 
  338        return $this->noClientPredictions;
 
 
  350        $this->noClientPredictions = $value;
 
  351        $this->networkPropertiesDirty = 
true;
 
 
  354    public function isInvisible() : bool{
 
  355        return $this->invisible;
 
  358    public function setInvisible(
bool $value = 
true) : void{
 
  359        $this->invisible = $value;
 
  360        $this->networkPropertiesDirty = 
true;
 
  363    public function isSilent() : bool{
 
  364        return $this->silent;
 
  367    public function setSilent(
bool $value = 
true) : void{
 
  368        $this->silent = $value;
 
  369        $this->networkPropertiesDirty = 
true;
 
  376        return $this->canClimb;
 
 
  383        $this->canClimb = $value;
 
  384        $this->networkPropertiesDirty = 
true;
 
 
  391        return $this->canClimbWalls;
 
 
  398        $this->canClimbWalls = $value;
 
  399        $this->networkPropertiesDirty = 
true;
 
 
  406        return $this->ownerId;
 
 
  413        return $this->ownerId !== null ? $this->
server->getWorldManager()->findEntity($this->ownerId) : null;
 
 
  423            $this->ownerId = 
null;
 
  424        }elseif($owner->closed){
 
  425            throw new \InvalidArgumentException(
"Supplied owning entity is garbage and cannot be used");
 
  427            $this->ownerId = $owner->getId();
 
  429        $this->networkPropertiesDirty = 
true;
 
 
  436        return $this->targetId;
 
 
  444        return $this->targetId !== null ? $this->
server->getWorldManager()->findEntity($this->targetId) : null;
 
 
  453        if($target === null){
 
  454            $this->targetId = 
null;
 
  455        }elseif($target->closed){
 
  456            throw new \InvalidArgumentException(
"Supplied target entity is garbage and cannot be used");
 
  458            $this->targetId = $target->getId();
 
  460        $this->networkPropertiesDirty = 
true;
 
 
  467        return $this->savedWithChunk;
 
 
  475        $this->savedWithChunk = $value;
 
 
  480            ->setTag(self::TAG_POS, new 
ListTag([
 
  485            ->setTag(self::TAG_MOTION, new 
ListTag([
 
  490            ->setTag(self::TAG_ROTATION, new 
ListTag([
 
  492                new 
FloatTag($this->location->pitch)
 
  496            EntityFactory::getInstance()->injectSaveId(get_class($this), $nbt);
 
  498            if($this->getNameTag() !== 
""){
 
  499                $nbt->setString(self::TAG_CUSTOM_NAME, $this->getNameTag());
 
  500                $nbt->setByte(self::TAG_CUSTOM_NAME_VISIBLE, $this->isNameTagVisible() ? 1 : 0);
 
  504        $nbt->
setFloat(self::TAG_FALL_DISTANCE, $this->fallDistance);
 
  505        $nbt->setShort(self::TAG_FIRE, $this->fireTicks);
 
  506        $nbt->setByte(self::TAG_ON_GROUND, $this->onGround ? 1 : 0);
 
  508        $nbt->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
 
  513    protected function initEntity(CompoundTag $nbt) : void{
 
  514        $this->fireTicks = $nbt->getShort(self::TAG_FIRE, 0);
 
  516        $this->onGround = $nbt->getByte(self::TAG_ON_GROUND, 0) !== 0;
 
  518        $this->fallDistance = $nbt->getFloat(self::TAG_FALL_DISTANCE, 0.0);
 
  520        if(($customNameTag = $nbt->getTag(self::TAG_CUSTOM_NAME)) instanceof StringTag){
 
  521            $this->setNameTag($customNameTag->getValue());
 
  523            if(($customNameVisibleTag = $nbt->getTag(self::TAG_CUSTOM_NAME_VISIBLE)) instanceof StringTag){
 
  525                $this->setNameTagVisible($customNameVisibleTag->getValue() !== 
"");
 
  527                $this->setNameTagVisible($nbt->getByte(self::TAG_CUSTOM_NAME_VISIBLE, 1) !== 0);
 
  532    protected function addAttributes() : void{
 
  536    public function attack(EntityDamageEvent $source) : void{
 
  537        if($this->isFireProof() && (
 
  538                $source->getCause() === EntityDamageEvent::CAUSE_FIRE ||
 
  539                $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK ||
 
  540                $source->getCause() === EntityDamageEvent::CAUSE_LAVA
 
  546        if($source->isCancelled()){
 
  550        $this->setLastDamageCause($source);
 
  552        $this->setHealth($this->getHealth() - $source->getFinalDamage());
 
  555    public function heal(EntityRegainHealthEvent $source) : void{
 
  557        if($source->isCancelled()){
 
  561        $this->setHealth($this->getHealth() + $source->getAmount());
 
  564    public function kill() : void{
 
  565        if($this->isAlive()){
 
  568            $this->scheduleUpdate();
 
  582    protected function onDeathUpdate(int $tickDiff) : bool{
 
 
  586    public function isAlive() : bool{
 
  587        return $this->health > 0;
 
  590    public function getHealth() : float{
 
  591        return $this->health;
 
  598        if($amount === $this->health){
 
  603            if($this->isAlive()){
 
  604                if(!$this->justCreated){
 
  610        }elseif($amount <= $this->getMaxHealth() || $amount < $this->health){
 
  611            $this->health = $amount;
 
  613            $this->health = $this->getMaxHealth();
 
 
  617    public function getMaxHealth() : int{
 
  618        return $this->maxHealth;
 
  621    public function setMaxHealth(
int $amount) : void{
 
  622        $this->maxHealth = $amount;
 
  625    public function setLastDamageCause(EntityDamageEvent $type) : void{
 
  626        $this->lastDamageCause = $type;
 
  629    public function getLastDamageCause() : ?EntityDamageEvent{
 
  630        return $this->lastDamageCause;
 
  633    public function getAttributeMap() : AttributeMap{
 
  634        return $this->attributeMap;
 
  637    public function getNetworkProperties() : EntityMetadataCollection{
 
  638        return $this->networkProperties;
 
  641    protected function entityBaseTick(
int $tickDiff = 1) : bool{
 
  644        if($this->justCreated){
 
  645            $this->justCreated = 
false;
 
  646            if(!$this->isAlive()){
 
  651        $changedProperties = $this->getDirtyNetworkData();
 
  652        if(count($changedProperties) > 0){
 
  653            $this->sendData(
null, $changedProperties);
 
  654            $this->networkProperties->clearDirtyProperties();
 
  659        if($this->checkBlockIntersectionsNextTick){
 
  660            $this->checkBlockIntersections();
 
  662        $this->checkBlockIntersectionsNextTick = 
true;
 
  664        if($this->location->y <= World::Y_MIN - 16 && $this->isAlive()){
 
  665            $ev = 
new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
 
  670        if($this->isOnFire() && $this->doOnFireTick($tickDiff)){
 
  674        if($this->noDamageTicks > 0){
 
  675            $this->noDamageTicks -= $tickDiff;
 
  676            if($this->noDamageTicks < 0){
 
  677                $this->noDamageTicks = 0;
 
  681        $this->ticksLived += $tickDiff;
 
  686    public function isOnFire() : bool{
 
  687        return $this->fireTicks > 0;
 
  690    public function setOnFire(
int $seconds) : void{
 
  691        $ticks = $seconds * 20;
 
  692        if($ticks > $this->getFireTicks()){
 
  693            $this->setFireTicks($ticks);
 
  695        $this->networkPropertiesDirty = 
true;
 
  698    public function getFireTicks() : int{
 
  699        return $this->fireTicks;
 
  707            throw new \InvalidArgumentException(
"Fire ticks cannot be negative");
 
  714        $fireTicks = min($fireTicks, Limits::INT16_MAX);
 
  716        if(!$this->isFireProof()){
 
  717            $this->fireTicks = $fireTicks;
 
  718            $this->networkPropertiesDirty = 
true;
 
 
  722    public function extinguish(
int $cause = EntityExtinguishEvent::CAUSE_CUSTOM) : void{
 
  723        $ev = new EntityExtinguishEvent($this, $cause);
 
  726        $this->fireTicks = 0;
 
  727        $this->networkPropertiesDirty = 
true;
 
  730    public function isFireProof() : bool{
 
  734    protected function doOnFireTick(
int $tickDiff = 1) : bool{
 
  735        if($this->isFireProof() && $this->isOnFire()){
 
  736            $this->extinguish(EntityExtinguishEvent::CAUSE_FIRE_PROOF);
 
  740        $this->fireTicks -= $tickDiff;
 
  742        if(($this->fireTicks % 20 === 0) || $tickDiff > 20){
 
  743            $this->dealFireDamage();
 
  746        if(!$this->isOnFire()){
 
  747            $this->extinguish(EntityExtinguishEvent::CAUSE_TICKING);
 
  763    public function canCollideWith(
Entity $entity) : bool{
 
  764        return !$this->justCreated && $entity !== $this;
 
  767    public function canBeCollidedWith() : bool{
 
  768        return $this->isAlive();
 
  771    protected function updateMovement(
bool $teleport = 
false) : void{
 
  772        $diffPosition = $this->location->distanceSquared($this->lastLocation);
 
  773        $diffRotation = ($this->location->yaw - $this->lastLocation->yaw) ** 2 + ($this->location->pitch - $this->lastLocation->pitch) ** 2;
 
  775        $diffMotion = $this->motion->subtractVector($this->lastMotion)->lengthSquared();
 
  777        $still = $this->motion->lengthSquared() === 0.0;
 
  778        $wasStill = $this->lastMotion->lengthSquared() === 0.0;
 
  779        if($wasStill !== $still){
 
  781            $this->setNoClientPredictions($still);
 
  784        if($teleport || $diffPosition > 0.0001 || $diffRotation > 1.0 || (!$wasStill && $still)){
 
  785            $this->lastLocation = $this->location->asLocation();
 
  787            $this->broadcastMovement($teleport);
 
  790        if($diffMotion > 0.0025 || $wasStill !== $still){ 
 
  791            $this->lastMotion = clone $this->motion;
 
  793            $this->broadcastMotion();
 
  797    public function getOffsetPosition(Vector3 $vector3) : Vector3{
 
  801    protected function broadcastMovement(
bool $teleport = 
false) : void{
 
  802        NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
 
  804            $this->getOffsetPosition($this->location),
 
  805            $this->location->pitch,
 
  806            $this->location->yaw,
 
  807            $this->location->yaw,
 
  814                ($this->onGround ? MoveActorAbsolutePacket::FLAG_GROUND : 0)
 
  819    protected function broadcastMotion() : void{
 
  820        NetworkBroadcastUtils::broadcastPackets($this->hasSpawned, [SetActorMotionPacket::create($this->
id, $this->getMotion(), tick: 0)]);
 
  823    public function getGravity() : float{
 
  824        return $this->gravity;
 
  827    public function setGravity(
float $gravity) : void{
 
  828        Utils::checkFloatNotInfOrNaN(
"gravity", $gravity);
 
  829        $this->gravity = $gravity;
 
  832    public function hasGravity() : bool{
 
  833        return $this->gravityEnabled;
 
  836    public function setHasGravity(
bool $v = 
true) : void{
 
  837        $this->gravityEnabled = $v;
 
  840    protected function applyDragBeforeGravity() : bool{
 
  844    protected function tryChangeMovement() : void{
 
  845        $friction = 1 - $this->drag;
 
  847        $mY = $this->motion->y;
 
  849        if($this->applyDragBeforeGravity()){
 
  853        if($this->gravityEnabled){
 
  854            $mY -= $this->gravity;
 
  857        if(!$this->applyDragBeforeGravity()){
 
  862            $friction *= $this->getWorld()->getBlockAt((
int) floor($this->location->x), (
int) floor($this->location->y - 1), (
int) floor($this->location->z))->getFrictionFactor();
 
  865        $this->motion = 
new Vector3($this->motion->x * $friction, $mY, $this->motion->z * $friction);
 
  868    protected function checkObstruction(
float $x, 
float $y, 
float $z) : bool{
 
  869        $world = $this->getWorld();
 
  870        if(count($world->getBlockCollisionBoxes($this->boundingBox)) === 0){
 
  874        $floorX = (
int) floor($x);
 
  875        $floorY = (int) floor($y);
 
  876        $floorZ = (int) floor($z);
 
  878        $diffX = $x - $floorX;
 
  879        $diffY = $y - $floorY;
 
  880        $diffZ = $z - $floorZ;
 
  882        if($world->getBlockAt($floorX, $floorY, $floorZ)->isSolid()){
 
  883            $westNonSolid = !$world->getBlockAt($floorX - 1, $floorY, $floorZ)->isSolid();
 
  884            $eastNonSolid = !$world->getBlockAt($floorX + 1, $floorY, $floorZ)->isSolid();
 
  885            $downNonSolid = !$world->getBlockAt($floorX, $floorY - 1, $floorZ)->isSolid();
 
  886            $upNonSolid = !$world->getBlockAt($floorX, $floorY + 1, $floorZ)->isSolid();
 
  887            $northNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ - 1)->isSolid();
 
  888            $southNonSolid = !$world->getBlockAt($floorX, $floorY, $floorZ + 1)->isSolid();
 
  895                $direction = Facing::WEST;
 
  898            if($eastNonSolid && 1 - $diffX < $limit){
 
  900                $direction = Facing::EAST;
 
  903            if($downNonSolid && $diffY < $limit){
 
  905                $direction = Facing::DOWN;
 
  908            if($upNonSolid && 1 - $diffY < $limit){
 
  910                $direction = Facing::UP;
 
  913            if($northNonSolid && $diffZ < $limit){
 
  915                $direction = Facing::NORTH;
 
  918            if($southNonSolid && 1 - $diffZ < $limit){
 
  919                $direction = Facing::SOUTH;
 
  922            if($direction === -1){
 
  926            $force = Utils::getRandomFloat() * 0.2 + 0.1;
 
  928            $this->motion = match($direction){
 
  929                Facing::WEST => $this->motion->withComponents(-$force, 
null, 
null),
 
  930                Facing::EAST => $this->motion->withComponents($force, 
null, 
null),
 
  931                Facing::DOWN => $this->motion->withComponents(
null, -$force, 
null),
 
  932                Facing::UP => $this->motion->withComponents(
null, $force, 
null),
 
  933                Facing::NORTH => $this->motion->withComponents(
null, 
null, -$force),
 
  934                Facing::SOUTH => $this->motion->withComponents(
null, 
null, $force),
 
  942    public function getHorizontalFacing() : int{
 
  943        $angle = fmod($this->location->yaw, 360);
 
  948        if((0 <= $angle && $angle < 45) || (315 <= $angle && $angle < 360)){
 
  949            return Facing::SOUTH;
 
  951        if(45 <= $angle && $angle < 135){
 
  954        if(135 <= $angle && $angle < 225){
 
  955            return Facing::NORTH;
 
  961    public function getDirectionVector() : Vector3{
 
  962        $y = -sin(deg2rad($this->location->pitch));
 
  963        $xz = cos(deg2rad($this->location->pitch));
 
  964        $x = -$xz * sin(deg2rad($this->location->yaw));
 
  965        $z = $xz * cos(deg2rad($this->location->yaw));
 
  967        return (
new Vector3($x, $y, $z))->normalize();
 
  970    public function getDirectionPlane() : Vector2{
 
  971        return (new Vector2(-cos(deg2rad($this->location->yaw) - M_PI_2), -sin(deg2rad($this->location->yaw) - M_PI_2)))->normalize();
 
  982    public function onUpdate(
int $currentTick) : bool{
 
  987        $tickDiff = $currentTick - $this->lastUpdate;
 
  989            if(!$this->justCreated){
 
  990                $this->
server->getLogger()->debug(
"Expected tick difference of at least 1, got $tickDiff for " . get_class($this));
 
  996        $this->lastUpdate = $currentTick;
 
  998        if($this->justCreated){
 
  999            $this->onFirstUpdate($currentTick);
 
 1002        if(!$this->isAlive()){
 
 1003            if($this->onDeathUpdate($tickDiff)){
 
 1004                $this->flagForDespawn();
 
 1010        $this->timings->startTiming();
 
 1012        if($this->hasMovementUpdate()){
 
 1013            $this->tryChangeMovement();
 
 1015            $this->motion = $this->motion->withComponents(
 
 1016                abs($this->motion->x) <= self::MOTION_THRESHOLD ? 0 : 
null,
 
 1017                abs($this->motion->y) <= self::MOTION_THRESHOLD ? 0 : 
null,
 
 1018                abs($this->motion->z) <= self::MOTION_THRESHOLD ? 0 : 
null 
 1021            if(floatval($this->motion->x) !== 0.0 || floatval($this->motion->y) !== 0.0 || floatval($this->motion->z) !== 0.0){
 
 1022                $this->move($this->motion->x, $this->motion->y, $this->motion->z);
 
 1025            $this->forceMovementUpdate = 
false;
 
 1028        $this->updateMovement();
 
 1030        Timings::$entityBaseTick->startTiming();
 
 1031        $hasUpdate = $this->entityBaseTick($tickDiff);
 
 1032        Timings::$entityBaseTick->stopTiming();
 
 1034        $this->timings->stopTiming();
 
 1036        return ($hasUpdate || $this->hasMovementUpdate());
 
 1039    final public function scheduleUpdate() : void{
 
 1041            throw new \LogicException(
"Cannot schedule update on garbage entity " . get_class($this));
 
 1043        $this->getWorld()->updateEntities[$this->id] = $this;
 
 1046    public function onNearbyBlockChange() : void{
 
 1047        $this->setForceMovementUpdate();
 
 1048        $this->scheduleUpdate();
 
 1056        $this->scheduleUpdate();
 
 
 1064        $this->forceMovementUpdate = $value;
 
 1066        $this->blocksAround = 
null;
 
 
 1074            $this->forceMovementUpdate ||
 
 1075            floatval($this->motion->x) !== 0.0 ||
 
 1076            floatval($this->motion->y) !== 0.0 ||
 
 1077            floatval($this->motion->z) !== 0.0 ||
 
 
 1082    public function getFallDistance() : float{ return $this->fallDistance; }
 
 1084    public function setFallDistance(
float $fallDistance) : void{
 
 1085        $this->fallDistance = $fallDistance;
 
 1088    public function resetFallDistance() : void{
 
 1089        $this->fallDistance = 0.0;
 
 1092    protected function updateFallState(
float $distanceThisTick, 
bool $onGround) : ?float{
 
 1093        if($distanceThisTick < $this->fallDistance){
 
 1096            $this->fallDistance -= $distanceThisTick;
 
 1100            $this->fallDistance = 0;
 
 1102        if($onGround && $this->fallDistance > 0){
 
 1103            $newVerticalVelocity = $this->onHitGround();
 
 1104            $this->resetFallDistance();
 
 1105            return $newVerticalVelocity;
 
 1117    public function getEyeHeight() : float{
 
 1118        return $this->size->getEyeHeight();
 
 1121    public function getEyePos() : Vector3{
 
 1122        return new Vector3($this->location->x, $this->location->y + $this->getEyeHeight(), $this->location->z);
 
 1125    public function onCollideWithPlayer(Player $player) : void{
 
 1136    public function isUnderwater() : bool{
 
 1137        $block = $this->getWorld()->getBlockAt((int) floor($this->location->x), $blockY = (int) floor($y = ($this->location->y + $this->getEyeHeight())), (int) floor($this->location->z));
 
 1139        if($block instanceof 
Water){
 
 1140            $f = ($blockY + 1) - ($block->getFluidHeightPercent() - 0.1111111);
 
 1147    public function isInsideOfSolid() : bool{
 
 1148        $block = $this->getWorld()->getBlockAt((int) floor($this->location->x), (int) floor($y = ($this->location->y + $this->getEyeHeight())), (int) floor($this->location->z));
 
 1150        return $block->isSolid() && !$block->isTransparent() && $block->collidesWithBB($this->getBoundingBox());
 
 1153    protected function move(
float $dx, 
float $dy, 
float $dz) : void{
 
 1154        $this->blocksAround = null;
 
 1156        Timings::$entityMove->startTiming();
 
 1157        Timings::$entityMoveCollision->startTiming();
 
 1163        if($this->keepMovement){
 
 1164            $this->boundingBox->offset($dx, $dy, $dz);
 
 1166            $this->ySize *= self::STEP_CLIP_MULTIPLIER;
 
 1168            $moveBB = clone $this->boundingBox;
 
 1170            assert(abs($dx) <= 20 && abs($dy) <= 20 && abs($dz) <= 20, 
"Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz");
 
 1172            $list = $this->getWorld()->getBlockCollisionBoxes($moveBB->addCoord($dx, $dy, $dz));
 
 1174            foreach($list as $bb){
 
 1175                $dy = $bb->calculateYOffset($moveBB, $dy);
 
 1178            $moveBB->offset(0, $dy, 0);
 
 1180            $fallingFlag = ($this->onGround || ($dy !== $wantedY && $wantedY < 0));
 
 1182            foreach($list as $bb){
 
 1183                $dx = $bb->calculateXOffset($moveBB, $dx);
 
 1186            $moveBB->offset($dx, 0, 0);
 
 1188            foreach($list as $bb){
 
 1189                $dz = $bb->calculateZOffset($moveBB, $dz);
 
 1192            $moveBB->offset(0, 0, $dz);
 
 1194            $stepHeight = $this->getStepHeight();
 
 1196            if($stepHeight > 0 && $fallingFlag && ($wantedX !== $dx || $wantedZ !== $dz)){
 
 1204                $stepBB = clone $this->boundingBox;
 
 1206                $list = $this->getWorld()->getBlockCollisionBoxes($stepBB->addCoord($dx, $dy, $dz));
 
 1207                foreach($list as $bb){
 
 1208                    $dy = $bb->calculateYOffset($stepBB, $dy);
 
 1211                $stepBB->offset(0, $dy, 0);
 
 1213                foreach($list as $bb){
 
 1214                    $dx = $bb->calculateXOffset($stepBB, $dx);
 
 1217                $stepBB->offset($dx, 0, 0);
 
 1219                foreach($list as $bb){
 
 1220                    $dz = $bb->calculateZOffset($stepBB, $dz);
 
 1223                $stepBB->offset(0, 0, $dz);
 
 1226                foreach($list as $bb){
 
 1227                    $reverseDY = $bb->calculateYOffset($stepBB, $reverseDY);
 
 1230                $stepBB->offset(0, $reverseDY, 0);
 
 1232                if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){
 
 1238                    $this->ySize += $dy;
 
 1242            $this->boundingBox = $moveBB;
 
 1244        Timings::$entityMoveCollision->stopTiming();
 
 1246        $this->location = 
new Location(
 
 1247            ($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
 
 1248            $this->boundingBox->minY - $this->ySize,
 
 1249            ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2,
 
 1250            $this->location->world,
 
 1251            $this->location->yaw,
 
 1252            $this->location->pitch
 
 1255        $this->getWorld()->onEntityMoved($this);
 
 1256        $this->checkBlockIntersections();
 
 1257        $this->checkGroundState($wantedX, $wantedY, $wantedZ, $dx, $dy, $dz);
 
 1258        $postFallVerticalVelocity = $this->updateFallState($dy, $this->onGround);
 
 1260        $this->motion = $this->motion->withComponents(
 
 1261            $wantedX !== $dx ? 0 : 
null,
 
 1262            $postFallVerticalVelocity ?? ($wantedY !== $dy ? 0 : 
null),
 
 1263            $wantedZ !== $dz ? 0 : 
null 
 1268        Timings::$entityMove->stopTiming();
 
 1271    public function setStepHeight(
float $stepHeight) : void{
 
 1272        $this->stepHeight = $stepHeight;
 
 1275    public function getStepHeight() : float{
 
 1276        return $this->stepHeight;
 
 1279    protected function checkGroundState(
float $wantedX, 
float $wantedY, 
float $wantedZ, 
float $dx, 
float $dy, 
float $dz) : void{
 
 1280        $this->isCollidedVertically = $wantedY !== $dy;
 
 1281        $this->isCollidedHorizontally = ($wantedX !== $dx || $wantedZ !== $dz);
 
 1282        $this->isCollided = ($this->isCollidedHorizontally || $this->isCollidedVertically);
 
 1283        $this->onGround = ($wantedY !== $dy && $wantedY < 0);
 
 1292        $minX = (int) floor($this->boundingBox->minX + $inset);
 
 1293        $minY = (int) floor($this->boundingBox->minY + $inset);
 
 1294        $minZ = (int) floor($this->boundingBox->minZ + $inset);
 
 1295        $maxX = (int) floor($this->boundingBox->maxX - $inset);
 
 1296        $maxY = (int) floor($this->boundingBox->maxY - $inset);
 
 1297        $maxZ = (int) floor($this->boundingBox->maxZ - $inset);
 
 1299        $world = $this->getWorld();
 
 1301        for($z = $minZ; $z <= $maxZ; ++$z){
 
 1302            for($x = $minX; $x <= $maxX; ++$x){
 
 1303                for($y = $minY; $y <= $maxY; ++$y){
 
 1304                    yield $world->getBlockAt($x, $y, $z);
 
 
 1314        if($this->blocksAround === null){
 
 1315            $this->blocksAround = [];
 
 1318            foreach($this->getBlocksIntersected($inset) as $block){
 
 1319                if($block->hasEntityCollision()){
 
 1320                    $this->blocksAround[] = $block;
 
 1325        return $this->blocksAround;
 
 
 1335    protected function checkBlockIntersections() : void{
 
 1336        $this->checkBlockIntersectionsNextTick = false;
 
 1339        foreach($this->getBlocksAroundWithEntityInsideActions() as $block){
 
 1340            if(!$block->onEntityInside($this)){
 
 1341                $this->blocksAround = 
null;
 
 1343            if(($v = $block->addVelocityToEntity($this)) !== 
null){
 
 1348        if(count($vectors) > 0){
 
 1349            $vector = Vector3::sum(...$vectors);
 
 1350            if($vector->lengthSquared() > 0){
 
 1352                $this->motion = $this->motion->addVector($vector->normalize()->multiply($d));
 
 1358        return $this->location->asPosition();
 
 1361    public function getLocation() : Location{
 
 1362        return $this->location->asLocation();
 
 1365    public function getWorld() : World{
 
 1366        return $this->location->getWorld();
 
 1369    protected function setPosition(Vector3 $pos) : bool{
 
 1374        $oldWorld = $this->getWorld();
 
 1375        $newWorld = $pos instanceof Position ? $pos->getWorld() : $oldWorld;
 
 1376        if($oldWorld !== $newWorld){
 
 1377            $this->despawnFromAll();
 
 1378            $oldWorld->removeEntity($this);
 
 1381        $this->location = Location::fromObject(
 
 1384            $this->location->yaw,
 
 1385            $this->location->pitch
 
 1388        $this->recalculateBoundingBox();
 
 1390        $this->blocksAround = 
null;
 
 1392        if($oldWorld !== $newWorld){
 
 1393            $newWorld->addEntity($this);
 
 1395            $newWorld->onEntityMoved($this);
 
 1401    public function setRotation(
float $yaw, 
float $pitch) : void{
 
 1402        Utils::checkFloatNotInfOrNaN(
"yaw", $yaw);
 
 1403        Utils::checkFloatNotInfOrNaN(
"pitch", $pitch);
 
 1404        $this->location->yaw = $yaw;
 
 1405        $this->location->pitch = $pitch;
 
 1406        $this->scheduleUpdate();
 
 1409    protected function setPositionAndRotation(Vector3 $pos, 
float $yaw, 
float $pitch) : bool{
 
 1410        if($this->setPosition($pos)){
 
 1411            $this->setRotation($yaw, $pitch);
 
 1419    protected function resetLastMovements() : void{
 
 1420        $this->lastLocation = $this->location->asLocation();
 
 1421        $this->lastMotion = clone $this->motion;
 
 1424    public function getMotion() : Vector3{
 
 1425        return clone $this->motion;
 
 1428    public function setMotion(Vector3 $motion) : bool{
 
 1429        Utils::checkVector3NotInfOrNaN($motion);
 
 1430        if(!$this->justCreated){
 
 1431            $ev = 
new EntityMotionEvent($this, $motion);
 
 1433            if($ev->isCancelled()){
 
 1438        $this->motion = clone $motion;
 
 1440        if(!$this->justCreated){
 
 1441            $this->updateMovement();
 
 1450    public function addMotion(
float $x, 
float $y, 
float $z) : void{
 
 1451        Utils::checkFloatNotInfOrNaN(
"x", $x);
 
 1452        Utils::checkFloatNotInfOrNaN(
"y", $y);
 
 1453        Utils::checkFloatNotInfOrNaN(
"z", $z);
 
 1454        $this->motion = $this->motion->add($x, $y, $z);
 
 
 1457    public function isOnGround() : bool{
 
 1458        return $this->onGround;
 
 1465        Utils::checkVector3NotInfOrNaN($pos);
 
 1467            $yaw = $yaw ?? $pos->yaw;
 
 1468            $pitch = $pitch ?? $pos->pitch;
 
 1471            Utils::checkFloatNotInfOrNaN(
"yaw", $yaw);
 
 1473        if($pitch !== 
null){
 
 1474            Utils::checkFloatNotInfOrNaN(
"pitch", $pitch);
 
 1477        $from = $this->location->asPosition();
 
 1478        $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getWorld() : $this->getWorld());
 
 1479        $ev = 
new EntityTeleportEvent($this, $from, $to);
 
 1481        if($ev->isCancelled()){
 
 1485        $pos = $ev->getTo();
 
 1487        $this->setMotion(
new Vector3(0, 0, 0));
 
 1488        if($this->setPositionAndRotation($pos, $yaw ?? $this->location->yaw, $pitch ?? $this->location->pitch)){
 
 1489            $this->resetFallDistance();
 
 1490            $this->setForceMovementUpdate();
 
 1492            $this->updateMovement(
true);
 
 
 1500    public function getId() : int{
 
 1508        return $this->hasSpawned;
 
 
 1511    abstract public static function getNetworkTypeId() : string;
 
 1517        $player->getNetworkSession()->sendDataPacket(
AddActorPacket::create(
 
 1520            static::getNetworkTypeId(),
 
 1521            $this->getOffsetPosition($this->location->asVector3()),
 
 1523            $this->location->pitch,
 
 1524            $this->location->yaw,
 
 1525            $this->location->yaw, 
 
 1526            $this->location->yaw, 
 
 1527            array_map(function(
Attribute $attr) : NetworkAttribute{
 
 1528                return new NetworkAttribute($attr->getId(), $attr->getMinValue(), $attr->getMaxValue(), $attr->getValue(), $attr->getDefaultValue(), []);
 
 1529            }, $this->attributeMap->getAll()),
 
 1530            $this->getAllNetworkData(),
 
 
 1536    public function spawnTo(
Player $player) : void{
 
 1537        $id = spl_object_id($player);
 
 1541        if(!isset($this->hasSpawned[$id]) && $player->getWorld() === $this->getWorld() && $player->
hasReceivedChunk($this->location->getFloorX() >> Chunk::COORD_BIT_SIZE, $this->location->getFloorZ() >> Chunk::COORD_BIT_SIZE)){
 
 1542            $this->hasSpawned[$id] = $player;
 
 1544            $this->sendSpawnPacket($player);
 
 1548    public function spawnToAll() : void{
 
 1552        foreach($this->getWorld()->getViewersForPosition($this->location) as $player){
 
 1553            $this->spawnTo($player);
 
 1557    public function respawnToAll() : void{
 
 1558        foreach($this->hasSpawned as $key => $player){
 
 1559            unset($this->hasSpawned[$key]);
 
 1560            $this->spawnTo($player);
 
 1569        $id = spl_object_id($player);
 
 1570        if(isset($this->hasSpawned[$id])){
 
 1572                $player->getNetworkSession()->getEntityEventBroadcaster()->onEntityRemoved([$player->getNetworkSession()], $this);
 
 1574            unset($this->hasSpawned[$id]);
 
 
 1585            fn(
EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onEntityRemoved($recipients, $this)
 
 1587        $this->hasSpawned = [];
 
 
 1601        $this->needsDespawn = true;
 
 1602        $this->scheduleUpdate();
 
 
 1605    public function isFlaggedForDespawn() : bool{
 
 1606        return $this->needsDespawn;
 
 1613        return $this->closed;
 
 
 1622        if($this->closeInFlight){
 
 1627            $this->closeInFlight = 
true;
 
 1631            $this->closed = 
true;
 
 1632            $this->destroyCycles();
 
 1633            $this->closeInFlight = 
false;
 
 
 1642        $this->despawnFromAll();
 
 1643        if($this->location->isValid()){
 
 1644            $this->getWorld()->removeEntity($this);
 
 
 1655        $this->lastDamageCause = null;
 
 
 1664    public function sendData(?array $targets, ?array $data = 
null) : void{
 
 1665        $targets = $targets ?? $this->hasSpawned;
 
 1666        $data = $data ?? $this->getAllNetworkData();
 
 1668        NetworkBroadcastUtils::broadcastEntityEvent($targets, fn(
EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->
syncActorData($recipients, $this, $data));
 
 
 1676        if($this->networkPropertiesDirty){
 
 1677            $this->syncNetworkData($this->networkProperties);
 
 1678            $this->networkPropertiesDirty = 
false;
 
 1680        return $this->networkProperties->getDirty();
 
 
 1688        if($this->networkPropertiesDirty){
 
 1689            $this->syncNetworkData($this->networkProperties);
 
 1690            $this->networkPropertiesDirty = 
false;
 
 1692        return $this->networkProperties->getAll();
 
 
 1695    protected function syncNetworkData(EntityMetadataCollection $properties) : void{
 
 1696        $properties->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $this->alwaysShowNameTag ? 1 : 0);
 
 1697        $properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->size->getHeight() / $this->scale);
 
 1698        $properties->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->size->getWidth() / $this->scale);
 
 1699        $properties->setFloat(EntityMetadataProperties::SCALE, $this->scale);
 
 1700        $properties->setLong(EntityMetadataProperties::LEAD_HOLDER_EID, -1);
 
 1701        $properties->setLong(EntityMetadataProperties::OWNER_EID, $this->ownerId ?? -1);
 
 1702        $properties->setLong(EntityMetadataProperties::TARGET_EID, $this->targetId ?? 0);
 
 1703        $properties->setString(EntityMetadataProperties::NAMETAG, $this->nameTag);
 
 1704        $properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
 
 1705        $properties->setByte(EntityMetadataProperties::COLOR, 0);
 
 1707        $properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, $this->gravityEnabled);
 
 1708        $properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
 
 1709        $properties->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $this->nameTagVisible);
 
 1710        $properties->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, 
true);
 
 1711        $properties->setGenericFlag(EntityMetadataFlags::NO_AI, $this->noClientPredictions);
 
 1712        $properties->setGenericFlag(EntityMetadataFlags::INVISIBLE, $this->invisible);
 
 1713        $properties->setGenericFlag(EntityMetadataFlags::SILENT, $this->silent);
 
 1714        $properties->setGenericFlag(EntityMetadataFlags::ONFIRE, $this->isOnFire());
 
 1715        $properties->setGenericFlag(EntityMetadataFlags::WALLCLIMBING, $this->canClimbWalls);
 
 1731            $this->getWorld()->addSound($this->location->asVector3(), $sound, $targets ?? $this->getViewers());
 
 
 1735    public function __destruct(){
 
 1739    public function __toString(){
 
 1740        return (
new \ReflectionClass($this))->getShortName() . 
"(" . $this->getId() . 
")";