PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
Entity.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
25 namespace pocketmine\entity;
26 
53 use pocketmine\Network;
62 
63 abstract class Entity extends Location implements Metadatable{
64 
65 
66  const NETWORK_ID = -1;
67 
68  public static $entityCount = 1;
70  private static $knownEntities = [];
71  private static $shortNames = [];
72 
76  protected $hasSpawned = [];
77 
78  protected $id;
79 
80  public $passenger = null;
81  public $vehicle = null;
82 
84  public $chunkX;
86  public $chunkZ;
87 
89  public $chunk;
90 
91  protected $lastDamageCause = null;
92 
93  public $lastX = null;
94  public $lastY = null;
95  public $lastZ = null;
96 
97  public $motionX;
98  public $motionY;
99  public $motionZ;
100  public $lastMotionX;
101  public $lastMotionY;
102  public $lastMotionZ;
103 
104  public $lastYaw;
105  public $lastPitch;
106 
108  public $boundingBox;
109  public $onGround;
110  public $inBlock = false;
111  public $positionChanged;
112  public $motionChanged;
113  public $dead;
114  public $deadTicks = 0;
115  protected $age = 0;
116 
117  public $height;
118 
119  public $eyeHeight = null;
120 
121  public $width;
122  public $length;
123 
125  private $health = 20;
126  private $maxHealth = 20;
127 
128  protected $ySize = 0;
129  protected $stepHeight = 0;
130  public $keepMovement = false;
131 
132  public $fallDistance;
133  public $ticksLived;
134  public $lastUpdate;
135  public $maxFireTicks;
136  public $fireTicks;
137  public $airTicks;
138  public $namedtag;
139  public $canCollide = true;
140 
141  protected $isStatic = false;
142 
143  public $isCollided = false;
144  public $isCollidedHorizontally = false;
145  public $isCollidedVertically = false;
146 
147  public $noDamageTicks;
148  private $justCreated;
149  protected $fireProof;
150  private $invulnerable;
151 
152  protected $gravity;
153  protected $drag;
154 
156  protected $server;
157 
158  public $closed = false;
159 
161  protected $timings;
162 
163 
164  public function __construct(FullChunk $chunk, Compound $nbt){
165  if($chunk === null or $chunk->getProvider() === null){
166  throw new ChunkException("Invalid garbage Chunk given to Entity");
167  }
168 
169  $this->timings = Timings::getEntityTimings($this);
170 
171  if($this->eyeHeight === null){
172  $this->eyeHeight = $this->height / 2 + 0.1;
173  }
174 
175  $this->id = Entity::$entityCount++;
176  $this->justCreated = true;
177  $this->namedtag = $nbt;
178 
179  $this->chunk = $chunk;
180  $this->setLevel($chunk->getProvider()->getLevel());
181  $this->server = $chunk->getProvider()->getLevel()->getServer();
182 
183  $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
184  $this->setPositionAndRotation(
185  new Vector3(
186  $this->namedtag["Pos"][0],
187  $this->namedtag["Pos"][1],
188  $this->namedtag["Pos"][2]
189  ),
190  $this->namedtag->Rotation[0],
191  $this->namedtag->Rotation[1],
192  true
193  );
194  $this->setMotion(new Vector3($this->namedtag["Motion"][0], $this->namedtag["Motion"][1], $this->namedtag["Motion"][2]));
195 
196  if(!isset($this->namedtag->FallDistance)){
197  $this->namedtag->FallDistance = new Float("FallDistance", 0);
198  }
199  $this->fallDistance = $this->namedtag["FallDistance"];
200 
201  if(!isset($this->namedtag->Fire)){
202  $this->namedtag->Fire = new Short("Fire", 0);
203  }
204  $this->fireTicks = $this->namedtag["Fire"];
205 
206  if(!isset($this->namedtag->Air)){
207  $this->namedtag->Air = new Short("Air", 300);
208  }
209  $this->airTicks = $this->namedtag["Air"];
210 
211  if(!isset($this->namedtag->OnGround)){
212  $this->namedtag->OnGround = new Byte("OnGround", 0);
213  }
214  $this->onGround = $this->namedtag["OnGround"] > 0 ? true : false;
215 
216  if(!isset($this->namedtag->Invulnerable)){
217  $this->namedtag->Invulnerable = new Byte("Invulnerable", 0);
218  }
219  $this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false;
220 
221  $this->chunk->addEntity($this);
222  $this->level->addEntity($this);
223  $this->initEntity();
224  $this->lastUpdate = $this->server->getTick();
225  $this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
226 
227  $this->scheduleUpdate();
228 
229  }
230 
239  public static function createEntity($type, FullChunk $chunk, Compound $nbt, ...$args){
240  if(isset(self::$knownEntities[$type])){
241  $class = self::$knownEntities[$type];
242  return new $class($chunk, $nbt, ...$args);
243  }
244 
245  return null;
246  }
247 
248  public static function registerEntity($className, $force = false){
249  $class = new \ReflectionClass($className);
250  if(is_a($className, Entity::class, true) and !$class->isAbstract()){
251  if($className::NETWORK_ID !== -1){
252  self::$knownEntities[$className::NETWORK_ID] = $className;
253  }elseif(!$force){
254  return false;
255  }
256 
257  self::$knownEntities[$class->getShortName()] = $className;
258  self::$shortNames[$className] = $class->getShortName();
259  return true;
260  }
261 
262  return false;
263  }
264 
270  public function getSaveId(){
271  return self::$shortNames[static::class];
272  }
273 
274  public function saveNBT(){
275  if(!($this instanceof Player)){
276  $this->namedtag->id = new String("id", $this->getSaveId());
277  }
278 
279  $this->namedtag->Pos = new Enum("Pos", [
280  new Double(0, $this->x),
281  new Double(1, $this->y),
282  new Double(2, $this->z)
283  ]);
284 
285  $this->namedtag->Motion = new Enum("Motion", [
286  new Double(0, $this->motionX),
287  new Double(1, $this->motionY),
288  new Double(2, $this->motionZ)
289  ]);
290 
291  $this->namedtag->Rotation = new Enum("Rotation", [
292  new Float(0, $this->yaw),
293  new Float(1, $this->pitch)
294  ]);
295 
296  $this->namedtag->FallDistance = new Float("FallDistance", $this->fallDistance);
297  $this->namedtag->Fire = new Short("Fire", $this->fireTicks);
298  $this->namedtag->Air = new Short("Air", $this->airTicks);
299  $this->namedtag->OnGround = new Byte("OnGround", $this->onGround == true ? 1 : 0);
300  $this->namedtag->Invulnerable = new Byte("Invulnerable", $this->invulnerable == true ? 1 : 0);
301  }
302 
303  protected abstract function initEntity();
304 
308  public function getViewers(){
309  return $this->hasSpawned;
310  }
311 
315  public function spawnTo(Player $player){
316  if(!isset($this->hasSpawned[$player->getId()]) and isset($player->usedChunks[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())])){
317  $this->hasSpawned[$player->getId()] = $player;
318  }
319  }
320 
324  public function sendMetadata($player){
325  if($player instanceof Player){
326  $player = [$player];
327  }
328 
329  $pk = new SetEntityDataPacket();
330  $pk->eid = $this->id;
331  $pk->metadata = $this->getData();
332  $pk->encode();
333  $pk->isEncoded = true;
334  foreach($player as $p){
335  if($p === $this){
337  $pk2 = new SetEntityDataPacket();
338  $pk2->eid = 0;
339  $pk2->metadata = $this->getData();
340  $p->dataPacket($pk2);
341  }else{
342  $p->dataPacket($pk);
343  }
344  }
345  }
346 
350  public function despawnFrom(Player $player){
351  if(isset($this->hasSpawned[$player->getId()])){
352  $pk = new RemoveEntityPacket();
353  $pk->eid = $this->id;
354  $player->dataPacket($pk);
355  unset($this->hasSpawned[$player->getId()]);
356  }
357  }
358 
364  abstract function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC);
365 
371  abstract function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC);
372 
376  public function getHealth(){
377  return $this->health;
378  }
379 
385  public function setHealth($amount){
386  if($amount === $this->health){
387  return;
388  }
389 
390  if($amount <= 0){
391  $this->health = 0;
392  if($this->dead !== true){
393  $this->kill();
394  }
395  }elseif($amount > $this->getMaxHealth()){
396  $this->health = $this->getMaxHealth();
397  }else{
398  $this->health = (int) $amount;
399  }
400  }
401 
405  public function setLastDamageCause($type){
406  $this->lastDamageCause = $type instanceof EntityDamageEvent ? $type : $type;
407  }
408 
412  public function getLastDamageCause(){
413  return $this->lastDamageCause;
414  }
415 
419  public function getMaxHealth(){
420  return $this->maxHealth;
421  }
422 
426  public function setMaxHealth($amount){
427  $this->maxHealth = (int) $amount;
428  $this->health = (int) min($this->health, $this->maxHealth);
429  }
430 
431  public function canCollideWith(Entity $entity){
432  return !$this->justCreated and $entity !== $this;
433  }
434 
435  protected function checkObstruction($x, $y, $z){
436  $i = (int) $x;
437  $j = (int) $y;
438  $k = (int) $z;
439 
440  $diffX = $x - $i;
441  $diffY = $y - $j;
442  $diffZ = $z - $k;
443 
444  $list = $this->level->getCollisionBlocks($this->boundingBox);
445 
446  $v = new Vector3($i, $j, $k);
447 
448  if(count($list) === 0 and !$this->level->isFullBlock($v->setComponents($i, $j, $k))){
449  return false;
450  }else{
451  $flag = !$this->level->isFullBlock($v->setComponents($i - 1, $j, $k));
452  $flag1 = !$this->level->isFullBlock($v->setComponents($i + 1, $j, $k));
453  //$flag2 = !$this->level->isFullBlock($v->setComponents($i, $j - 1, $k));
454  $flag3 = !$this->level->isFullBlock($v->setComponents($i, $j + 1, $k));
455  $flag4 = !$this->level->isFullBlock($v->setComponents($i, $j, $k - 1));
456  $flag5 = !$this->level->isFullBlock($v->setComponents($i, $j, $k + 1));
457 
458  $direction = 3; //UP!
459  $limit = 9999;
460 
461  if($flag){
462  $limit = $diffX;
463  $direction = 0;
464  }
465 
466  if($flag1 and 1 - $diffX < $limit){
467  $limit = 1 - $diffX;
468  $direction = 1;
469  }
470 
471  if($flag3 and 1 - $diffY < $limit){
472  $limit = 1 - $diffY;
473  $direction = 3;
474  }
475 
476  if($flag4 and $diffZ < $limit){
477  $limit = $diffZ;
478  $direction = 4;
479  }
480 
481  if($flag5 and 1 - $diffZ < $limit){
482  $direction = 5;
483  }
484 
485  $force = lcg_value() * 0.2 + 0.1;
486 
487  if($direction === 0){
488  $this->motionX = -$force;
489 
490  return true;
491  }
492 
493  if($direction === 1){
494  $this->motionX = $force;
495 
496  return true;
497  }
498 
499  //No direction 2
500 
501  if($direction === 3){
502  $this->motionY = $force;
503 
504  return true;
505  }
506 
507  if($direction === 4){
508  $this->motionZ = -$force;
509 
510  return true;
511  }
512 
513  if($direction === 5){
514  $this->motionY = $force;
515  }
516 
517  return true;
518 
519  }
520  }
521 
522  public function entityBaseTick($tickDiff = 1){
523 
524  Timings::$tickEntityTimer->startTiming();
525  //TODO: check vehicles
526 
527  $this->justCreated = false;
528  $isPlayer = $this instanceof Player;
529 
530  if($this->dead === true){
531  $this->despawnFromAll();
532  if(!$isPlayer){
533  $this->close();
534  }
535 
536  Timings::$tickEntityTimer->stopTiming();
537  return false;
538  }
539 
540  $hasUpdate = false;
541 
542  $this->checkBlockCollision();
543 
544  if($this->y < 0 and $this->dead !== true){
545  $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10);
546  $this->attack($ev->getFinalDamage(), $ev);
547  $hasUpdate = true;
548  }
549 
550  if($this->fireTicks > 0){
551  if($this->fireProof){
552  $this->fireTicks -= 4 * $tickDiff;
553  if($this->fireTicks < 0){
554  $this->fireTicks = 0;
555  }
556  }else{
557  if(($this->fireTicks % 20) === 0 or $tickDiff > 20){
558  $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
559  $this->attack($ev->getFinalDamage(), $ev);
560  }
561  $this->fireTicks -= $tickDiff;
562  }
563 
564  if($this->fireTicks <= 0){
565  $this->extinguish();
566  }else{
567  $hasUpdate = true;
568  }
569  }
570 
571  if($this->noDamageTicks > 0){
572  $this->noDamageTicks -= $tickDiff;
573  if($this->noDamageTicks < 0){
574  $this->noDamageTicks = 0;
575  }
576  }
577 
578  $this->age += $tickDiff;
579  $this->ticksLived += $tickDiff;
580 
581  Timings::$tickEntityTimer->stopTiming();
582 
583  return $hasUpdate;
584  }
585 
586  public function updateMovement(){
587  if($this->x !== $this->lastX or $this->y !== $this->lastY or $this->z !== $this->lastZ or $this->yaw !== $this->lastYaw or $this->pitch !== $this->lastPitch){
588  $this->lastX = $this->x;
589  $this->lastY = $this->y;
590  $this->lastZ = $this->z;
591 
592  $this->lastYaw = $this->yaw;
593  $this->lastPitch = $this->pitch;
594 
595  if($this instanceof Human){
596  $pk = new MovePlayerPacket();
597  $pk->eid = $this->id;
598  $pk->x = $this->x;
599  $pk->y = $this->y;
600  $pk->z = $this->z;
601  $pk->yaw = $this->yaw;
602  $pk->pitch = $this->pitch;
603  $pk->bodyYaw = $this->yaw;
604  Server::broadcastPacket($this->hasSpawned, $pk);
605  }else{
606  foreach($this->hasSpawned as $player){
607  $player->addEntityMovement($this->id, $this->x, $this->y + $this->getEyeHeight(), $this->z, $this->yaw, $this->pitch);
608  }
609  }
610 
611  }
612 
613  if(($this->lastMotionX != $this->motionX or $this->lastMotionY != $this->motionY or $this->lastMotionZ != $this->motionZ)){
614  $this->lastMotionX = $this->motionX;
615  $this->lastMotionY = $this->motionY;
616  $this->lastMotionZ = $this->motionZ;
617 
618  foreach($this->hasSpawned as $player){
619  $player->addEntityMotion($this->id, $this->motionX, $this->motionY, $this->motionZ);
620  }
621 
622  if($this instanceof Player){
623  $this->motionX = 0;
624  $this->motionY = 0;
625  $this->motionZ = 0;
626  }
627  }
628  }
629 
633  public function getDirectionVector(){
634  $pitch = ($this->pitch * M_PI) / 180;
635  $yaw = (($this->yaw + 90) * M_PI) / 180;
636 
637  $y = -sin(deg2rad($this->pitch));
638  $xz = cos(deg2rad($this->pitch));
639  $x = -$xz * sin(deg2rad($this->yaw));
640  $z = $xz * cos(deg2rad($this->yaw));
641 
642  return new Vector3($x, $y, $z);
643  }
644 
645  public function onUpdate($currentTick){
646  if($this->closed){
647  return false;
648  }
649 
650  $tickDiff = max(1, $currentTick - $this->lastUpdate);
651  $this->lastUpdate = $currentTick;
652 
653  $this->timings->startTiming();
654 
655  $hasUpdate = $this->entityBaseTick($tickDiff);
656 
657  $this->updateMovement();
658 
659  $this->timings->stopTiming();
660 
661  //if($this->isStatic())
662  return $hasUpdate;
663  //return !($this instanceof Player);
664  }
665 
666  public final function scheduleUpdate(){
667  $this->level->updateEntities[$this->id] = $this;
668  }
669 
670  public function setOnFire($seconds){
671  $ticks = $seconds * 20;
672  if($ticks > $this->fireTicks){
673  $this->fireTicks = $ticks;
674  }
675 
676  $this->sendMetadata($this->hasSpawned);
677  if($this instanceof Player){
678  $this->sendMetadata($this);
679  }
680  }
681 
682  public function getDirection(){
683  $rotation = ($this->yaw - 90) % 360;
684  if($rotation < 0){
685  $rotation += 360.0;
686  }
687  if((0 <= $rotation and $rotation < 45) or (315 <= $rotation and $rotation < 360)){
688  return 2; //North
689  }elseif(45 <= $rotation and $rotation < 135){
690  return 3; //East
691  }elseif(135 <= $rotation and $rotation < 225){
692  return 0; //South
693  }elseif(225 <= $rotation and $rotation < 315){
694  return 1; //West
695  }else{
696  return null;
697  }
698  }
699 
700  public function extinguish(){
701  $this->fireTicks = 0;
702  $this->sendMetadata($this->hasSpawned);
703  if($this instanceof Player){
704  $this->sendMetadata($this);
705  }
706  }
707 
708  public function canTriggerWalking(){
709  return true;
710  }
711 
712  protected function updateFallState($distanceThisTick, $onGround){
713  if($onGround === true){
714  if($this->fallDistance > 0){
715  if($this instanceof Living){
716  //TODO
717  }
718 
719  $this->fall($this->fallDistance);
720  $this->fallDistance = 0;
721  }
722  }elseif($distanceThisTick < 0){
723  $this->fallDistance -= $distanceThisTick;
724  }
725  }
726 
727  public function getBoundingBox(){
728  return $this->boundingBox;
729  }
730 
731  public function fall($fallDistance){
732  $damage = floor($fallDistance - 3);
733  if($damage > 0){
734  $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
735  $this->attack($ev->getFinalDamage(), $ev);
736  }
737  }
738 
739  public function handleLavaMovement(){ //TODO
740 
741  }
742 
743  public function getEyeHeight(){
744  return $this->eyeHeight;
745  }
746 
747  public function moveFlying(){ //TODO
748 
749  }
750 
751  public function onCollideWithPlayer(Human $entityPlayer){
752 
753  }
754 
755  protected function switchLevel(Level $targetLevel){
756  if($this->isValid()){
757  $this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->level, $targetLevel));
758  if($ev->isCancelled()){
759  return false;
760  }
761 
762  $this->level->removeEntity($this);
763  if($this->chunk !== null){
764  $this->chunk->removeEntity($this);
765  }
766  $this->despawnFromAll();
767  if($this instanceof Player){
768  foreach($this->usedChunks as $index => $d){
769  $X = null;
770  $Z = null;
771  Level::getXZ($index, $X, $Z);
772  $this->unloadChunk($X, $Z);
773  }
774  }
775  }
776  $this->setLevel($targetLevel);
777  $this->level->addEntity($this);
778  if($this instanceof Player){
779  $this->usedChunks = [];
780  $pk = new SetTimePacket();
781  $pk->time = $this->level->getTime();
782  $pk->started = $this->level->stopTime == false;
783  $this->dataPacket($pk);
784  }
785  $this->chunk = null;
786 
787  return true;
788  }
789 
790  public function getPosition(){
791  return new Position($this->x, $this->y, $this->z, $this->level);
792  }
793 
794  public function getLocation(){
795  return new Location($this->x, $this->y, $this->z, $this->yaw, $this->pitch, $this->level);
796  }
797 
798  public function isInsideOfWater(){
799  $block = $this->level->getBlock(new Vector3(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z)));
800 
801  if($block instanceof Water){
802  $f = ($block->y + 1) - ($block->getFluidHeightPercent() - 0.1111111);
803  return $y < $f;
804  }
805 
806  return false;
807  }
808 
809  public function isInsideOfSolid(){
810  $block = $this->level->getBlock(new Vector3(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z)));
811 
812  $bb = $block->getBoundingBox();
813 
814  if($bb !== null and $block->isSolid() and !$block->isTransparent() and $bb->intersectsWith($this->getBoundingBox())){
815  return true;
816  }
817  return false;
818  }
819 
820  public function move($dx, $dy, $dz){
821 
822  if($dx == 0 and $dz == 0 and $dy == 0){
823  return true;
824  }
825 
826  if($this->keepMovement){
827  $this->boundingBox->offset($dx, $dy, $dz);
828  $this->setPosition(new Vector3(($this->boundingBox->minX + $this->boundingBox->maxX) / 2, $this->boundingBox->minY, ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2));
829  $this->onGround = $this instanceof Player ? true : false;
830  }else{
831 
832  Timings::$entityMoveTimer->startTiming();
833 
834  $this->ySize *= 0.4;
835 
836  /*
837  if($this->isColliding){ //With cobweb?
838  $this->isColliding = false;
839  $dx *= 0.25;
840  $dy *= 0.05;
841  $dz *= 0.25;
842  $this->motionX = 0;
843  $this->motionY = 0;
844  $this->motionZ = 0;
845  }
846  */
847 
848  $movX = $dx;
849  $movY = $dy;
850  $movZ = $dz;
851 
852  $axisalignedbb = clone $this->boundingBox;
853 
854  /*$sneakFlag = $this->onGround and $this instanceof Player;
855 
856  if($sneakFlag){
857  for($mov = 0.05; $dx != 0.0 and count($this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, -1, 0))) === 0; $movX = $dx){
858  if($dx < $mov and $dx >= -$mov){
859  $dx = 0;
860  }elseif($dx > 0){
861  $dx -= $mov;
862  }else{
863  $dx += $mov;
864  }
865  }
866 
867  for(; $dz != 0.0 and count($this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox(0, -1, $dz))) === 0; $movZ = $dz){
868  if($dz < $mov and $dz >= -$mov){
869  $dz = 0;
870  }elseif($dz > 0){
871  $dz -= $mov;
872  }else{
873  $dz += $mov;
874  }
875  }
876 
877  //TODO: big messy loop
878  }*/
879 
880  $list = $this->level->getCollisionCubes($this, $this->boundingBox->addCoord($dx, $dy, $dz));
881 
882 
883  foreach($list as $bb){
884  $dy = $bb->calculateYOffset($this->boundingBox, $dy);
885  }
886 
887  $this->boundingBox->offset(0, $dy, 0);
888 
889  if($movY != $dy){
890  $dx = 0;
891  $dy = 0;
892  $dz = 0;
893  }
894 
895  $fallingFlag = ($this->onGround or ($dy != $movY and $movY < 0));
896 
897  foreach($list as $bb){
898  $dx = $bb->calculateXOffset($this->boundingBox, $dx);
899  }
900 
901  $this->boundingBox->offset($dx, 0, 0);
902 
903  if($movX != $dx){
904  $dx = 0;
905  $dy = 0;
906  $dz = 0;
907  }
908 
909  foreach($list as $bb){
910  $dz = $bb->calculateZOffset($this->boundingBox, $dz);
911  }
912 
913  $this->boundingBox->offset(0, 0, $dz);
914 
915  if($movZ != $dz){
916  $dx = 0;
917  $dy = 0;
918  $dz = 0;
919  }
920 
921 
922  if($this->stepHeight > 0 and $fallingFlag and $this->ySize < 0.05 and ($movX != $dx or $movZ != $dz)){
923  $cx = $dx;
924  $cy = $dy;
925  $cz = $dz;
926  $dx = $movX;
927  $dy = $this->stepHeight;
928  $dz = $movZ;
929 
930  $axisalignedbb1 = clone $this->boundingBox;
931 
932  $this->boundingBox->setBB($axisalignedbb);
933 
934  $list = $this->level->getCollisionCubes($this, $this->boundingBox->addCoord($dx, $dy, $dz), false);
935 
936  foreach($list as $bb){
937  $dy = $bb->calculateYOffset($this->boundingBox, $dy);
938  }
939 
940  $this->boundingBox->offset(0, $dy, 0);
941 
942  foreach($list as $bb){
943  $dx = $bb->calculateXOffset($this->boundingBox, $dx);
944  }
945 
946  $this->boundingBox->offset($dx, 0, 0);
947  if($movX != $dx){
948  $dx = 0;
949  $dy = 0;
950  $dz = 0;
951  }
952 
953  foreach($list as $bb){
954  $dz = $bb->calculateZOffset($this->boundingBox, $dz);
955  }
956 
957  $this->boundingBox->offset(0, 0, $dz);
958  if($movZ != $dz){
959  $dx = 0;
960  $dy = 0;
961  $dz = 0;
962  }
963 
964  if($dy == 0){
965  $dx = 0;
966  $dy = 0;
967  $dz = 0;
968  }else{
969  $dy = -$this->stepHeight;
970  foreach($list as $bb){
971  $dy = $bb->calculateYOffset($this->boundingBox, $dy);
972  }
973  $this->boundingBox->offset(0, $dy, 0);
974  }
975 
976  if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){
977  $dx = $cx;
978  $dy = $cy;
979  $dz = $cz;
980  $this->boundingBox->setBB($axisalignedbb1);
981  }else{
982  $diff = $this->boundingBox->minY - (int) $this->boundingBox->minY;
983 
984  if($diff > 0){
985  $this->ySize += $diff + 0.01;
986  }
987  }
988 
989  }
990 
991  $pos = new Vector3(
992  ($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
993  $this->boundingBox->minY + $this->ySize,
994  ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
995  );
996 
997  $result = true;
998 
999  if(!$this->setPosition($pos)){
1000  $this->boundingBox->setBB($axisalignedbb);
1001  $result = false;
1002  }else{
1003 
1004  if($this instanceof Player){
1005  if(!$this->onGround or $movY != 0){
1006  $bb = clone $this->boundingBox;
1007  $bb->minY -= 1;
1008  if(count($this->level->getCollisionBlocks($bb->expand(0.01, 0.01, 0.01))) > 0){
1009  $this->onGround = true;
1010  }else{
1011  $this->onGround = false;
1012  }
1013  }
1014  $this->isCollided = $this->onGround;
1015  }else{
1016  $this->isCollidedVertically = $movY != $dy;
1017  $this->isCollidedHorizontally = ($movX != $dx or $movZ != $dz);
1018  $this->isCollided = ($this->isCollidedHorizontally or $this->isCollidedVertically);
1019  $this->onGround = ($movY != $dy and $movY < 0);
1020  }
1021  $this->updateFallState($dy, $this->onGround);
1022 
1023  if($movX != $dx){
1024  $this->motionX = 0;
1025  }
1026 
1027  if($movY != $dy){
1028  $this->motionY = 0;
1029  }
1030 
1031  if($movZ != $dz){
1032  $this->motionZ = 0;
1033  }
1034  }
1035 
1036  //TODO: vehicle collision events (first we need to spawn them!)
1037 
1038  Timings::$entityMoveTimer->stopTiming();
1039 
1040  return $result;
1041  }
1042  }
1043 
1044  protected function checkBlockCollision(){
1045  $minX = Math::floorFloat($this->boundingBox->minX + 0.001);
1046  $minY = Math::floorFloat($this->boundingBox->minY + 0.001);
1047  $minZ = Math::floorFloat($this->boundingBox->minZ + 0.001);
1048  $maxX = Math::floorFloat($this->boundingBox->maxX - 0.001);
1049  $maxY = Math::floorFloat($this->boundingBox->maxY - 0.001);
1050  $maxZ = Math::floorFloat($this->boundingBox->maxZ - 0.001);
1051 
1052  $vector = new Vector3(0, 0, 0);
1053  $v = new Vector3(0, 0, 0);
1054 
1055  for($v->z = $minZ; $v->z <= $maxZ; ++$v->z){
1056  for($v->x = $minX; $v->x <= $maxX; ++$v->x){
1057  for($v->y = $minY; $v->y <= $maxY; ++$v->y){
1058  $block = $this->level->getBlock($v);
1059  if($block->hasEntityCollision()){
1060  $block->onEntityCollide($this);
1061  if(!($this instanceof Player)){
1062  $block->addVelocityToEntity($this, $vector);
1063  }
1064  }
1065  }
1066  }
1067  }
1068 
1069  if(!($this instanceof Player) and $vector->length() > 0){
1070  $vector = $vector->normalize();
1071  $d = 0.014;
1072  $this->motionX += $vector->x * $d;
1073  $this->motionY += $vector->y * $d;
1074  $this->motionZ += $vector->z * $d;
1075  }
1076  }
1077 
1078  public function setPositionAndRotation(Vector3 $pos, $yaw, $pitch){
1079  if($this->setPosition($pos) === true){
1080  $this->setRotation($yaw, $pitch);
1081 
1082  return true;
1083  }
1084 
1085  return false;
1086  }
1087 
1088  public function setRotation($yaw, $pitch){
1089  $this->yaw = $yaw;
1090  $this->pitch = $pitch;
1091  $this->scheduleUpdate();
1092  }
1093 
1094  public function setPosition(Vector3 $pos){
1095  if($this->closed){
1096  return false;
1097  }
1098 
1099  if($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level){
1100  if($this->switchLevel($pos->getLevel()) === false){
1101  return false;
1102  }
1103  }
1104 
1105  $this->x = $pos->x;
1106  $this->y = $pos->y;
1107  $this->z = $pos->z;
1108 
1109  $radius = $this->width / 2;
1110  $this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius);
1111 
1112 
1113  if($this->chunk === null or ($this->chunkX !== ($this->x >> 4) and $this->chunkZ !== ($this->z >> 4))){
1114  if($this->chunk !== null){
1115  $this->chunk->removeEntity($this);
1116  }
1117  $this->level->loadChunk($this->x >> 4, $this->z >> 4);
1118  $this->chunk = $this->level->getChunk($this->x >> 4, $this->z >> 4);
1119  $this->chunkX = $this->chunk->getX();
1120  $this->chunkZ = $this->chunk->getZ();
1121 
1122  if(!$this->justCreated){
1123  $newChunk = $this->level->getUsingChunk($this->x >> 4, $this->z >> 4);
1124  foreach($this->hasSpawned as $player){
1125  if(!isset($newChunk[$player->getId()])){
1126  $this->despawnFrom($player);
1127  }else{
1128  unset($newChunk[$player->getId()]);
1129  }
1130  }
1131  foreach($newChunk as $player){
1132  $this->spawnTo($player);
1133  }
1134  }
1135 
1136  $this->chunk->addEntity($this);
1137  }
1138 
1139  return true;
1140  }
1141 
1142  public function getMotion(){
1143  return new Vector3($this->motionX, $this->motionY, $this->motionZ);
1144  }
1145 
1146  public function setMotion(Vector3 $motion){
1147  if(!$this->justCreated){
1148  $this->server->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion));
1149  if($ev->isCancelled()){
1150  return false;
1151  }
1152  }
1153 
1154  $this->motionX = $motion->x;
1155  $this->motionY = $motion->y;
1156  $this->motionZ = $motion->z;
1157 
1158  if(!$this->justCreated){
1159  if($this instanceof Player){
1160  $this->addEntityMotion(0, $this->motionX, $this->motionY, $this->motionZ);
1161  }
1162  $this->updateMovement();
1163  }
1164 
1165  return true;
1166  }
1167 
1168  public function isOnGround(){
1169  return $this->onGround === true;
1170  }
1171 
1172  public function kill(){
1173  if($this->dead){
1174  return;
1175  }
1176  $this->dead = true;
1177  $this->setHealth(0);
1178  $this->scheduleUpdate();
1179  }
1180 
1188  public function teleport(Vector3 $pos, $yaw = null, $pitch = null){
1189  if($pos instanceof Location){
1190  $yaw = $yaw === null ? $pos->yaw : $yaw;
1191  $pitch = $pitch === null ? $pos->pitch : $pitch;
1192  }
1193  $from = Position::fromObject($this, $this->level);
1194  $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level);
1195  $this->server->getPluginManager()->callEvent($ev = new EntityTeleportEvent($this, $from, $to));
1196  if($ev->isCancelled()){
1197  return false;
1198  }
1199  $this->ySize = 0;
1200  $pos = $ev->getTo();
1201 
1202  $this->setMotion(new Vector3(0, 0, 0));
1203  if($this->setPositionAndRotation($pos, $yaw === null ? $this->yaw : $yaw, $pitch === null ? $this->pitch : $pitch, true) !== false){
1204  $this->fallDistance = 0;
1205  $this->onGround = true;
1206 
1207  $this->lastX = $this->x;
1208  $this->lastY = $this->y;
1209  $this->lastZ = $this->z;
1210 
1211  $this->lastYaw = $this->yaw;
1212  $this->lastPitch = $this->pitch;
1213 
1214  foreach($this->hasSpawned as $player){
1215  $player->addEntityMovement($this->getId(), $this->x, $this->y, $this->z, $this->yaw, $this->pitch);
1216  }
1217 
1218  return true;
1219  }
1220 
1221  return false;
1222  }
1223 
1224  public function getId(){
1225  return $this->id;
1226  }
1227 
1228  public function spawnToAll(){
1229  foreach($this->level->getUsingChunk($this->chunkX, $this->chunkZ) as $player){
1230  if($player->loggedIn === true){
1231  $this->spawnTo($player);
1232  }
1233  }
1234  }
1235 
1236  public function despawnFromAll(){
1237  foreach($this->hasSpawned as $player){
1238  $this->despawnFrom($player);
1239  }
1240  }
1241 
1242  public function close(){
1243  if(!$this->closed){
1244  $this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this));
1245  $this->closed = true;
1246  unset($this->level->updateEntities[$this->id]);
1247  if($this->chunk instanceof FullChunk){
1248  $this->chunk->removeEntity($this);
1249  }
1250  if($this->level instanceof Level){
1251  $this->level->removeEntity($this);
1252  }
1253  $this->despawnFromAll();
1254  }
1255  }
1256 
1257  abstract public function getData();
1258 
1259  public function __destruct(){
1260  $this->close();
1261  }
1262 
1263  public function setMetadata($metadataKey, MetadataValue $metadataValue){
1264  $this->server->getEntityMetadata()->setMetadata($this, $metadataKey, $metadataValue);
1265  }
1266 
1267  public function getMetadata($metadataKey){
1268  return $this->server->getEntityMetadata()->getMetadata($this, $metadataKey);
1269  }
1270 
1271  public function hasMetadata($metadataKey){
1272  return $this->server->getEntityMetadata()->hasMetadata($this, $metadataKey);
1273  }
1274 
1275  public function removeMetadata($metadataKey, Plugin $plugin){
1276  $this->server->getEntityMetadata()->removeMetadata($this, $metadataKey, $plugin);
1277  }
1278 
1279  public function __toString(){
1280  return (new \ReflectionClass($this))->getShortName() . "(" . $this->getId() . ")";
1281  }
1282 
1283 }
spawnTo(Player $player)
Definition: Entity.php:315
dataPacket(DataPacket $packet, $needACK=false)
Definition: Player.php:745
hasMetadata($metadataKey)
Definition: Entity.php:1271
attack($damage, $source=EntityDamageEvent::CAUSE_MAGIC)
getMetadata($metadataKey)
Definition: Entity.php:1267
removeMetadata($metadataKey, Plugin $plugin)
Definition: Entity.php:1275
static createEntity($type, FullChunk $chunk, Compound $nbt,...$args)
Definition: Entity.php:239
heal($amount, $source=EntityRegainHealthEvent::CAUSE_MAGIC)
setMetadata($metadataKey, MetadataValue $metadataValue)
Definition: Entity.php:1263
despawnFrom(Player $player)
Definition: Entity.php:350
static broadcastPacket(array $players, DataPacket $packet)
Definition: Server.php:1725
teleport(Vector3 $pos, $yaw=null, $pitch=null)
Definition: Entity.php:1188
static getEntityTimings(Entity $entity)
Definition: Timings.php:167
static chunkHash($x, $z)
Definition: Level.php:230