Entity: fix chunk tracking consistency issues, fixed attempted chunk loading in origin world when teleporting to a different position in a different world

This commit is contained in:
Dylan K. Taylor 2020-12-10 18:13:18 +00:00
parent 5282ae3298
commit e925423749
4 changed files with 25 additions and 43 deletions

View File

@ -96,8 +96,8 @@ abstract class Entity{
/** @var EntityMetadataCollection */ /** @var EntityMetadataCollection */
private $networkProperties; private $networkProperties;
/** @var Location */ /** @var Vector3 */
private $worldLastKnownLocation; public $worldLastKnownLocation;
/** @var EntityDamageEvent|null */ /** @var EntityDamageEvent|null */
protected $lastDamageCause = null; protected $lastDamageCause = null;
@ -1210,7 +1210,7 @@ abstract class Entity{
$this->location->world $this->location->world
); );
$this->checkChunks(); $this->getWorld()->onEntityMoved($this);
$this->checkBlockCollision(); $this->checkBlockCollision();
$this->checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz); $this->checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz);
$this->updateFallState($dy, $this->onGround); $this->updateFallState($dy, $this->onGround);
@ -1309,15 +1309,17 @@ abstract class Entity{
return false; return false;
} }
if($pos instanceof Position and $pos->isValid() and $pos->getWorld() !== $this->getWorld()){ $oldWorld = $this->getWorld();
if(!$this->switchWorld($pos->getWorld())){ //TODO: staying in the same world when the target is invalid is probably not expected behaviour... this should bail instead
return false; $newWorld = $pos instanceof Position && $pos->isValid() ? $pos->getWorld() : $oldWorld;
} if($oldWorld !== $newWorld){
$this->despawnFromAll();
$oldWorld->removeEntity($this);
} }
$this->location = Location::fromObject( $this->location = Location::fromObject(
$pos, $pos,
$this->location->world, $newWorld,
$this->location->yaw, $this->location->yaw,
$this->location->pitch $this->location->pitch
); );
@ -1326,7 +1328,11 @@ abstract class Entity{
$this->blocksAround = null; $this->blocksAround = null;
$this->checkChunks(); if($oldWorld !== $newWorld){
$newWorld->addEntity($this);
}else{
$newWorld->onEntityMoved($this);
}
return true; return true;
} }
@ -1347,11 +1353,6 @@ abstract class Entity{
return false; return false;
} }
protected function checkChunks() : void{
$this->getWorld()->onEntityMoved($this, $this->worldLastKnownLocation);
$this->worldLastKnownLocation = $this->location->asLocation();
}
protected function resetLastMovements() : void{ protected function resetLastMovements() : void{
$this->lastLocation = $this->location->asLocation(); $this->lastLocation = $this->location->asLocation();
$this->lastMotion = clone $this->motion; $this->lastMotion = clone $this->motion;
@ -1421,29 +1422,6 @@ abstract class Entity{
return false; 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{ public function getId() : int{
return $this->id; return $this->id;
} }

View File

@ -261,7 +261,7 @@ abstract class Projectile extends Entity{
$this->location->pitch = (atan2($this->motion->y, $f) * 180 / M_PI); $this->location->pitch = (atan2($this->motion->y, $f) * 180 / M_PI);
} }
$this->checkChunks(); $this->getWorld()->onEntityMoved($this);
$this->checkBlockCollision(); $this->checkBlockCollision();
Timings::$entityMoveTimer->stopTiming(); Timings::$entityMoveTimer->stopTiming();

View File

@ -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; $oldWorld = $this->location->isValid() ? $this->location->getWorld() : null;
if(parent::switchWorld($targetWorld)){ if(parent::setPosition($pos)){
if($oldWorld !== null){ if($oldWorld !== null){
foreach($this->usedChunks as $index => $status){ foreach($this->usedChunks as $index => $status){
World::getXZ($index, $X, $Z); World::getXZ($index, $X, $Z);

View File

@ -2136,11 +2136,13 @@ class World implements ChunkManager{
if($entity->getWorld() !== $this){ if($entity->getWorld() !== $this){
throw new \InvalidArgumentException("Invalid Entity world"); throw new \InvalidArgumentException("Invalid Entity world");
} }
$chunk = $this->getOrLoadChunkAtPosition($entity->getPosition()); $pos = $entity->getPosition()->asVector3();
$chunk = $this->getOrLoadChunkAtPosition($pos);
if($chunk === null){ if($chunk === null){
throw new \InvalidArgumentException("Cannot add an Entity in an ungenerated chunk"); throw new \InvalidArgumentException("Cannot add an Entity in an ungenerated chunk");
} }
$chunk->addEntity($entity); $chunk->addEntity($entity);
$entity->worldLastKnownLocation = $pos;
if($entity instanceof Player){ if($entity instanceof Player){
$this->players[$entity->getId()] = $entity; $this->players[$entity->getId()] = $entity;
@ -2157,7 +2159,7 @@ class World implements ChunkManager{
if($entity->getWorld() !== $this){ if($entity->getWorld() !== $this){
throw new \InvalidArgumentException("Invalid Entity world"); throw new \InvalidArgumentException("Invalid Entity world");
} }
$pos = $entity->getPosition(); $pos = $entity->worldLastKnownLocation;
$chunk = $this->getChunk($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4); $chunk = $this->getChunk($pos->getFloorX() >> 4, $pos->getFloorZ() >> 4);
if($chunk !== null){ //we don't care if the chunk already went out of scope if($chunk !== null){ //we don't care if the chunk already went out of scope
$chunk->removeEntity($entity); $chunk->removeEntity($entity);
@ -2175,11 +2177,12 @@ class World implements ChunkManager{
/** /**
* @internal * @internal
*/ */
public function onEntityMoved(Entity $entity, Position $oldPosition) : void{ public function onEntityMoved(Entity $entity) : void{
if(!array_key_exists($entity->getId(), $this->entities)){ if(!array_key_exists($entity->getId(), $this->entities)){
//this can happen if the entity was teleported before addEntity() was called //this can happen if the entity was teleported before addEntity() was called
return; return;
} }
$oldPosition = $entity->worldLastKnownLocation;
$newPosition = $entity->getPosition(); $newPosition = $entity->getPosition();
$oldChunkX = $oldPosition->getFloorX() >> 4; $oldChunkX = $oldPosition->getFloorX() >> 4;
@ -2216,6 +2219,7 @@ class World implements ChunkManager{
} }
$newChunk->addEntity($entity); $newChunk->addEntity($entity);
$entity->worldLastKnownLocation = $newPosition->asVector3();
} }
} }
} }