From e9254237496fe06cc13958056a9ebd70fa39fcdd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 10 Dec 2020 18:13:18 +0000 Subject: [PATCH] Entity: fix chunk tracking consistency issues, fixed attempted chunk loading in origin world when teleporting to a different position in a different world --- src/entity/Entity.php | 52 ++++++++-------------------- src/entity/projectile/Projectile.php | 2 +- src/player/Player.php | 4 +-- src/world/World.php | 10 ++++-- 4 files changed, 25 insertions(+), 43 deletions(-) diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 1931e980c..860dec2b3 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -96,8 +96,8 @@ abstract class Entity{ /** @var EntityMetadataCollection */ private $networkProperties; - /** @var Location */ - private $worldLastKnownLocation; + /** @var Vector3 */ + public $worldLastKnownLocation; /** @var EntityDamageEvent|null */ protected $lastDamageCause = null; @@ -1210,7 +1210,7 @@ abstract class Entity{ $this->location->world ); - $this->checkChunks(); + $this->getWorld()->onEntityMoved($this); $this->checkBlockCollision(); $this->checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz); $this->updateFallState($dy, $this->onGround); @@ -1309,15 +1309,17 @@ abstract class Entity{ return false; } - if($pos instanceof Position and $pos->isValid() and $pos->getWorld() !== $this->getWorld()){ - if(!$this->switchWorld($pos->getWorld())){ - return false; - } + $oldWorld = $this->getWorld(); + //TODO: staying in the same world when the target is invalid is probably not expected behaviour... this should bail instead + $newWorld = $pos instanceof Position && $pos->isValid() ? $pos->getWorld() : $oldWorld; + if($oldWorld !== $newWorld){ + $this->despawnFromAll(); + $oldWorld->removeEntity($this); } $this->location = Location::fromObject( $pos, - $this->location->world, + $newWorld, $this->location->yaw, $this->location->pitch ); @@ -1326,7 +1328,11 @@ abstract class Entity{ $this->blocksAround = null; - $this->checkChunks(); + if($oldWorld !== $newWorld){ + $newWorld->addEntity($this); + }else{ + $newWorld->onEntityMoved($this); + } return true; } @@ -1347,11 +1353,6 @@ abstract class Entity{ return false; } - protected function checkChunks() : void{ - $this->getWorld()->onEntityMoved($this, $this->worldLastKnownLocation); - $this->worldLastKnownLocation = $this->location->asLocation(); - } - protected function resetLastMovements() : void{ $this->lastLocation = $this->location->asLocation(); $this->lastMotion = clone $this->motion; @@ -1421,29 +1422,6 @@ abstract class Entity{ return false; } - protected function switchWorld(World $targetWorld) : bool{ - if($this->closed){ - return false; - } - - if($this->location->isValid()){ - $this->getWorld()->removeEntity($this); - $this->despawnFromAll(); - } - - $this->location = new Location( - $this->location->x, - $this->location->y, - $this->location->z, - $this->location->yaw, - $this->location->pitch, - $targetWorld - ); - $this->getWorld()->addEntity($this); - - return true; - } - public function getId() : int{ return $this->id; } diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 49d386578..c95c38ff0 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -261,7 +261,7 @@ abstract class Projectile extends Entity{ $this->location->pitch = (atan2($this->motion->y, $f) * 180 / M_PI); } - $this->checkChunks(); + $this->getWorld()->onEntityMoved($this); $this->checkBlockCollision(); Timings::$entityMoveTimer->stopTiming(); diff --git a/src/player/Player.php b/src/player/Player.php index e5e2031bc..c6adef475 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -648,9 +648,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } } - protected function switchWorld(World $targetWorld) : bool{ + protected function setPosition(Vector3 $pos) : bool{ $oldWorld = $this->location->isValid() ? $this->location->getWorld() : null; - if(parent::switchWorld($targetWorld)){ + if(parent::setPosition($pos)){ if($oldWorld !== null){ foreach($this->usedChunks as $index => $status){ World::getXZ($index, $X, $Z); diff --git a/src/world/World.php b/src/world/World.php index c82ac7f1d..05513b8be 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2136,11 +2136,13 @@ class World implements ChunkManager{ if($entity->getWorld() !== $this){ throw new \InvalidArgumentException("Invalid Entity world"); } - $chunk = $this->getOrLoadChunkAtPosition($entity->getPosition()); + $pos = $entity->getPosition()->asVector3(); + $chunk = $this->getOrLoadChunkAtPosition($pos); if($chunk === null){ throw new \InvalidArgumentException("Cannot add an Entity in an ungenerated chunk"); } $chunk->addEntity($entity); + $entity->worldLastKnownLocation = $pos; if($entity instanceof Player){ $this->players[$entity->getId()] = $entity; @@ -2157,7 +2159,7 @@ class World implements ChunkManager{ if($entity->getWorld() !== $this){ throw new \InvalidArgumentException("Invalid Entity world"); } - $pos = $entity->getPosition(); + $pos = $entity->worldLastKnownLocation; $chunk = $this->getChunk($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4); if($chunk !== null){ //we don't care if the chunk already went out of scope $chunk->removeEntity($entity); @@ -2175,11 +2177,12 @@ class World implements ChunkManager{ /** * @internal */ - public function onEntityMoved(Entity $entity, Position $oldPosition) : void{ + public function onEntityMoved(Entity $entity) : void{ if(!array_key_exists($entity->getId(), $this->entities)){ //this can happen if the entity was teleported before addEntity() was called return; } + $oldPosition = $entity->worldLastKnownLocation; $newPosition = $entity->getPosition(); $oldChunkX = $oldPosition->getFloorX() >> 4; @@ -2216,6 +2219,7 @@ class World implements ChunkManager{ } $newChunk->addEntity($entity); + $entity->worldLastKnownLocation = $newPosition->asVector3(); } } }