From 2f3c77c68a0d9e0fdfb0eb69bd44344f4c8d028d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 20 Aug 2017 20:31:09 +0100 Subject: [PATCH] Fixed entity move performance issue and a ton of entity movement bugs - fixed zombies and villagers movement not updating - fixed dropped items "movement" lagging the living **** out of the server when not actually moving - fixed arrows not falling when the supporting block is removed - fixed knockback - fixed zombies + villagers being un-attackable after hitting them ... the list goes on --- src/pocketmine/Player.php | 4 ++ src/pocketmine/entity/Arrow.php | 8 +-- src/pocketmine/entity/Entity.php | 80 ++++++++++++++++++++++++++- src/pocketmine/entity/FallingSand.php | 28 +--------- src/pocketmine/entity/Item.php | 49 ++++------------ src/pocketmine/entity/PrimedTNT.php | 37 +------------ src/pocketmine/entity/Projectile.php | 30 +++------- src/pocketmine/entity/Snowball.php | 8 +-- src/pocketmine/entity/Squid.php | 37 +++---------- src/pocketmine/level/Level.php | 3 +- 10 files changed, 122 insertions(+), 162 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 2502de03a..bce2aa180 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1619,6 +1619,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } + protected function tryChangeMovement(){ + + } + public function sendAttributes(bool $sendAll = false){ $entries = $sendAll ? $this->attributeMap->getAll() : $this->attributeMap->needSend(); if(count($entries) > 0){ diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index 2580c1c87..1fbba5478 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -62,14 +62,12 @@ class Arrow extends Projectile{ } } - public function onUpdate(int $currentTick) : bool{ + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - $this->timings->startTiming(); - - $hasUpdate = parent::onUpdate($currentTick); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->onGround or $this->hadCollision){ $this->setCritical(false); @@ -80,8 +78,6 @@ class Arrow extends Projectile{ $hasUpdate = true; } - $this->timings->stopTiming(); - return $hasUpdate; } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 775e58ac9..114da9a6b 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -63,6 +63,8 @@ use pocketmine\Server; abstract class Entity extends Location implements Metadatable{ + const MOTION_THRESHOLD = 0.00001; + const NETWORK_ID = -1; const DATA_TYPE_BYTE = 0; @@ -270,6 +272,8 @@ abstract class Entity extends Location implements Metadatable{ public $lastMotionY; /** @var float */ public $lastMotionZ; + /** @var bool */ + protected $forceMovementUpdate = false; /** @var float */ public $lastYaw; @@ -1198,6 +1202,35 @@ abstract class Entity extends Location implements Metadatable{ } } + protected function applyDragBeforeGravity() : bool{ + return false; + } + + protected function applyGravity(){ + $this->motionY -= $this->gravity; + } + + protected function tryChangeMovement(){ + $friction = 1 - $this->drag; + + if(!$this->onGround){ + if($this->applyDragBeforeGravity()){ + $this->motionY *= $friction; + } + + $this->applyGravity(); + + if(!$this->applyDragBeforeGravity()){ + $this->motionY *= $friction; + } + }else{ + $friction = $this->level->getBlock($this->floor()->subtract(0, 1, 0))->getFrictionFactor(); + } + + $this->motionX *= $friction; + $this->motionZ *= $friction; + } + /** * @return Vector3 */ @@ -1240,16 +1273,34 @@ abstract class Entity extends Location implements Metadatable{ $this->timings->startTiming(); + if($this->hasMovementUpdate()){ + $this->tryChangeMovement(); + $this->move($this->motionX, $this->motionY, $this->motionZ); + + if(abs($this->motionX) <= self::MOTION_THRESHOLD){ + $this->motionX = 0; + } + if(abs($this->motionY) <= self::MOTION_THRESHOLD){ + $this->motionY = 0; + } + if(abs($this->motionZ) <= self::MOTION_THRESHOLD){ + $this->motionZ = 0; + } + + $this->updateMovement(); + $this->forceMovementUpdate = false; + } + Timings::$timerEntityBaseTick->startTiming(); $hasUpdate = $this->entityBaseTick($tickDiff); Timings::$timerEntityBaseTick->stopTiming(); - $this->updateMovement(); + $this->timings->stopTiming(); //if($this->isStatic()) - return $hasUpdate; + return ($hasUpdate or $this->hasMovementUpdate()); //return !($this instanceof Player); } @@ -1257,6 +1308,31 @@ abstract class Entity extends Location implements Metadatable{ $this->level->updateEntities[$this->id] = $this; } + /** + * Flags the entity as needing a movement update on the next tick. Setting this forces a movement update even if the + * entity's motion is zero. Used to trigger movement updates when blocks change near entities. + * + * @param bool $value + */ + final public function setForceMovementUpdate(bool $value = true){ + $this->forceMovementUpdate = $value; + $this->onGround = false; + } + + /** + * Returns whether the entity needs a movement update on the next tick. + * @return bool + */ + final public function hasMovementUpdate() : bool{ + return ( + $this->forceMovementUpdate or + abs($this->motionX) > self::MOTION_THRESHOLD or + abs($this->motionY) > self::MOTION_THRESHOLD or + abs($this->motionZ) > self::MOTION_THRESHOLD or + !$this->onGround + ); + } + public function isOnFire() : bool{ return $this->fireTicks > 0; } diff --git a/src/pocketmine/entity/FallingSand.php b/src/pocketmine/entity/FallingSand.php index 4dba50075..c54298218 100644 --- a/src/pocketmine/entity/FallingSand.php +++ b/src/pocketmine/entity/FallingSand.php @@ -80,34 +80,14 @@ class FallingSand extends Entity{ } } - public function onUpdate(int $currentTick) : bool{ - + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - $this->timings->startTiming(); - - $tickDiff = $currentTick - $this->lastUpdate; - if($tickDiff <= 0 and !$this->justCreated){ - return true; - } - - $this->lastUpdate = $currentTick; - - $hasUpdate = $this->entityBaseTick($tickDiff); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->isAlive()){ - $this->motionY -= $this->gravity; - - $this->move($this->motionX, $this->motionY, $this->motionZ); - - $friction = 1 - $this->drag; - - $this->motionX *= $friction; - $this->motionY *= 1 - $this->drag; - $this->motionZ *= $friction; - $pos = (new Vector3($this->x - 0.5, $this->y, $this->z - 0.5))->floor(); if($this->onGround){ @@ -124,11 +104,9 @@ class FallingSand extends Entity{ } $hasUpdate = true; } - - $this->updateMovement(); } - return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; + return $hasUpdate; } public function getBlock(){ diff --git a/src/pocketmine/entity/Item.php b/src/pocketmine/entity/Item.php index 973a0d0a5..f45088c0c 100644 --- a/src/pocketmine/entity/Item.php +++ b/src/pocketmine/entity/Item.php @@ -98,24 +98,14 @@ class Item extends Entity{ } } - public function onUpdate(int $currentTick) : bool{ + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - $tickDiff = $currentTick - $this->lastUpdate; - if($tickDiff <= 0 and !$this->justCreated){ - return true; - } - - $this->lastUpdate = $currentTick; - - $this->timings->startTiming(); - - $hasUpdate = $this->entityBaseTick($tickDiff); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->isAlive()){ - if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay $this->pickupDelay -= $tickDiff; if($this->pickupDelay < 0){ @@ -123,30 +113,6 @@ class Item extends Entity{ } } - $this->motionY -= $this->gravity; - - if($this->checkObstruction($this->x, $this->y, $this->z)){ - $hasUpdate = true; - } - - $this->move($this->motionX, $this->motionY, $this->motionZ); - - $friction = 1 - $this->drag; - - if($this->onGround and (abs($this->motionX) > 0.00001 or abs($this->motionZ) > 0.00001)){ - $friction = $this->getLevel()->getBlock($this->temporalVector->setComponents((int) floor($this->x), (int) floor($this->y - 1), (int) floor($this->z) - 1))->getFrictionFactor() * $friction; - } - - $this->motionX *= $friction; - $this->motionY *= 1 - $this->drag; - $this->motionZ *= $friction; - - if($this->onGround){ - $this->motionY *= -0.5; - } - - $this->updateMovement(); - if($this->age > 6000){ $this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this)); if($ev->isCancelled()){ @@ -159,9 +125,16 @@ class Item extends Entity{ } - $this->timings->stopTiming(); + return $hasUpdate; + } - return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; + protected function tryChangeMovement(){ + $this->checkObstruction($this->x, $this->y, $this->z); + parent::tryChangeMovement(); + } + + protected function applyDragBeforeGravity() : bool{ + return true; } public function saveNBT(){ diff --git a/src/pocketmine/entity/PrimedTNT.php b/src/pocketmine/entity/PrimedTNT.php index b88f62234..1d248a8a4 100644 --- a/src/pocketmine/entity/PrimedTNT.php +++ b/src/pocketmine/entity/PrimedTNT.php @@ -79,58 +79,27 @@ class PrimedTNT extends Entity implements Explosive{ $this->namedtag->Fuse = new ByteTag("Fuse", $this->fuse); } - public function onUpdate(int $currentTick) : bool{ - + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - $this->timings->startTiming(); - - $tickDiff = $currentTick - $this->lastUpdate; - if($tickDiff <= 0 and !$this->justCreated){ - return true; - } + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->fuse % 5 === 0){ //don't spam it every tick, it's not necessary $this->setDataProperty(self::DATA_FUSE_LENGTH, self::DATA_TYPE_INT, $this->fuse); } - $this->lastUpdate = $currentTick; - - $hasUpdate = $this->entityBaseTick($tickDiff); - if($this->isAlive()){ - - $this->motionY -= $this->gravity; - - $this->move($this->motionX, $this->motionY, $this->motionZ); - - $friction = 1 - $this->drag; - - $this->motionX *= $friction; - $this->motionY *= $friction; - $this->motionZ *= $friction; - - $this->updateMovement(); - - if($this->onGround){ - $this->motionY *= -0.5; - $this->motionX *= 0.7; - $this->motionZ *= 0.7; - } - $this->fuse -= $tickDiff; if($this->fuse <= 0){ $this->kill(); $this->explode(); } - } - - return $hasUpdate or $this->fuse >= 0 or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; + return $hasUpdate or $this->fuse >= 0; } public function explode(){ diff --git a/src/pocketmine/entity/Projectile.php b/src/pocketmine/entity/Projectile.php index c48d9ccda..03ef87172 100644 --- a/src/pocketmine/entity/Projectile.php +++ b/src/pocketmine/entity/Projectile.php @@ -108,28 +108,20 @@ abstract class Projectile extends Entity{ $this->namedtag->Age = new ShortTag("Age", $this->age); } - public function onUpdate(int $currentTick) : bool{ + protected function applyDragBeforeGravity() : bool{ + return true; + } + + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - - $tickDiff = $currentTick - $this->lastUpdate; - if($tickDiff <= 0 and !$this->justCreated){ - return true; - } - $this->lastUpdate = $currentTick; - - $hasUpdate = $this->entityBaseTick($tickDiff); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->isAlive()){ - $movingObjectPosition = null; - if(!$this->isCollided){ - $this->motionY -= $this->gravity; - } - $moveVector = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); $list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this); @@ -170,8 +162,6 @@ abstract class Projectile extends Entity{ } } - $this->move($this->motionX, $this->motionY, $this->motionZ); - if($this->isCollided and !$this->hadCollision){ //Collided with a block $this->hadCollision = true; @@ -181,20 +171,16 @@ abstract class Projectile extends Entity{ $this->server->getPluginManager()->callEvent(new ProjectileHitEvent($this)); return false; - }elseif(!$this->isCollided and $this->hadCollision){ //Collided with block, but block later removed - //This currently doesn't work because the arrow's motion is all zeros when it's hit a block, so move() doesn't do any collision checks. - //TODO: fix this + }elseif(!$this->isCollided and $this->hadCollision){ //Previously collided with block, but block later removed $this->hadCollision = false; } - if(!$this->hadCollision or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001){ + if(!$this->hadCollision or abs($this->motionX) > self::MOTION_THRESHOLD or abs($this->motionY) > self::MOTION_THRESHOLD or abs($this->motionZ) > self::MOTION_THRESHOLD){ $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI); $this->pitch = (atan2($this->motionY, $f) * 180 / M_PI); $hasUpdate = true; } - - $this->updateMovement(); } return $hasUpdate; diff --git a/src/pocketmine/entity/Snowball.php b/src/pocketmine/entity/Snowball.php index 680353ff0..4f95eb518 100644 --- a/src/pocketmine/entity/Snowball.php +++ b/src/pocketmine/entity/Snowball.php @@ -42,22 +42,18 @@ class Snowball extends Projectile{ parent::__construct($level, $nbt, $shootingEntity); } - public function onUpdate(int $currentTick) : bool{ + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } - $this->timings->startTiming(); - - $hasUpdate = parent::onUpdate($currentTick); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; } - $this->timings->stopTiming(); - return $hasUpdate; } diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php index f6e3b7748..40826c446 100644 --- a/src/pocketmine/entity/Squid.php +++ b/src/pocketmine/entity/Squid.php @@ -78,21 +78,19 @@ class Squid extends WaterAnimal{ } - public function onUpdate(int $currentTick) : bool{ + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed !== false){ return false; } - if(++$this->switchDirectionTicker === 100){ + if(++$this->switchDirectionTicker === 100 or $this->isCollided){ $this->switchDirectionTicker = 0; if(mt_rand(0, 100) < 50){ $this->swimDirection = null; } } - $this->timings->startTiming(); - - $hasUpdate = parent::onUpdate($currentTick); + $hasUpdate = parent::entityBaseTick($tickDiff); if($this->isAlive()){ @@ -102,7 +100,6 @@ class Squid extends WaterAnimal{ $inWater = $this->isInsideOfWater(); if(!$inWater){ - $this->motionY -= $this->gravity; $this->swimDirection = null; }elseif($this->swimDirection !== null){ if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){ @@ -115,34 +112,18 @@ class Squid extends WaterAnimal{ $this->swimSpeed = mt_rand(50, 100) / 2000; } - $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); - - $this->move($this->motionX, $this->motionY, $this->motionZ); - - if($expectedPos->distanceSquared($this) > 0){ - $this->swimDirection = $this->generateRandomDirection(); - $this->swimSpeed = mt_rand(50, 100) / 2000; - } - - $friction = 1 - $this->drag; - - $this->motionX *= $friction; - $this->motionY *= 1 - $this->drag; - $this->motionZ *= $friction; - $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI); $this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); - - if($this->onGround){ - $this->motionY *= -0.5; - } - } - $this->timings->stopTiming(); + return $hasUpdate; + } - return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; + protected function applyGravity(){ + if(!$this->isInsideOfWater()){ + parent::applyGravity(); + } } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 20e39f13c..0b922b494 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1512,7 +1512,8 @@ class Level implements ChunkManager, Metadatable{ $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($block)); if(!$ev->isCancelled()){ - foreach($this->getNearbyEntities(new AxisAlignedBB($block->x - 1, $block->y - 1, $block->z - 1, $block->x + 1, $block->y + 1, $block->z + 1)) as $entity){ + foreach($this->getNearbyEntities(new AxisAlignedBB($block->x - 1, $block->y - 1, $block->z - 1, $block->x + 2, $block->y + 2, $block->z + 2)) as $entity){ + $entity->setForceMovementUpdate(); $entity->scheduleUpdate(); } $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL);