diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 235f33739..93f325f7b 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -271,10 +271,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade protected $randomClientId; - /** @var Vector3 */ - protected $forceMovement = null; - /** @var Vector3 */ - protected $teleportPosition = null; protected $connected = true; protected $ip; protected $removeFormat = true; @@ -660,7 +656,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->spawnPosition = null; $this->gamemode = $this->server->getGamemode(); $this->setLevel($this->server->getDefaultLevel()); - $this->newPosition = new Vector3(0, 0, 0); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->uuid = null; @@ -827,7 +822,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - if($this->chunkLoadCount >= $this->spawnThreshold and $this->spawned === false and $this->teleportPosition === null){ + if($this->chunkLoadCount >= $this->spawnThreshold and $this->spawned === false){ $this->doFirstSpawn(); } } @@ -877,14 +872,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->inventory->sendArmorContents($this); $this->inventory->sendHeldItem($this); - $pos = $this->level->getSafeSpawn($this); - - $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); - - $pos = $ev->getRespawnPosition(); - - $this->sendRespawnPacket($pos); - $this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN); if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ @@ -914,8 +901,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - $this->teleport($pos); - $this->spawnToAll(); if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE) and $this->server->getProperty("auto-updater.on-update.warn-ops", true)){ @@ -1413,8 +1398,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade protected function checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz){ if(!$this->onGround or $movY != 0){ $bb = clone $this->boundingBox; - $bb->maxY = $bb->minY + 0.5; - $bb->minY -= 1; + $bb->minY = $this->y - 0.01; + $bb->maxY = $this->y + 0.01; + if(count($this->level->getCollisionBlocks($bb, true)) > 0){ $this->onGround = true; }else{ @@ -1493,10 +1479,13 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } protected function processMovement($tickDiff){ - if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->teleportPosition !== null or $this->isSleeping()){ + if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->isSleeping()){ return; } + assert($this->x !== null and $this->y !== null and $this->z !== null); + assert($this->newPosition->x !== null and $this->newPosition->y !== null and $this->newPosition->z !== null); + $newPos = $this->newPosition; $distanceSquared = $newPos->distanceSquared($this); @@ -1504,6 +1493,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if(($distanceSquared / ($tickDiff ** 2)) > 100 and !$this->allowMovementCheats){ $this->server->getLogger()->warning($this->getName() . " moved too fast, reverting movement"); + $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition); $revert = true; }else{ if($this->chunk === null or !$this->chunk->isGenerated()){ @@ -1542,6 +1532,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if(!$ev->isCancelled()){ $revert = true; $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidMove", [$this->getName()])); + $this->server->getLogger()->debug("Old position: " . $this->asVector3() . ", new position: " . $this->newPosition); } } @@ -1608,9 +1599,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->lastPitch = $from->pitch; $this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET); - $this->forceMovement = new Vector3($from->x, $from->y, $from->z); }else{ - $this->forceMovement = null; if($distanceSquared != 0 and $this->nextChunkOrderRun > 20){ $this->nextChunkOrderRun = 20; } @@ -1713,8 +1702,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - $this->checkTeleportPosition(); - $this->timings->stopTiming(); return true; @@ -1861,7 +1848,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $pk->entityRuntimeId = $this->id; $pk->playerGamemode = Player::getClientFriendlyGamemode($this->gamemode); $pk->x = $this->x; - $pk->y = $this->y; + $pk->y = $this->y + $this->baseOffset; $pk->z = $this->z; $pk->pitch = $this->pitch; $pk->yaw = $this->yaw; @@ -1917,8 +1904,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->inventory->sendCreativeContents(); } - $this->forceMovement = $this->teleportPosition = $this->getPosition(); - $this->server->addOnlinePlayer($this); $this->server->onPlayerLogin($this); @@ -2141,14 +2126,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade public function handleMovePlayer(MovePlayerPacket $packet) : bool{ $newPos = new Vector3($packet->x, $packet->y - $this->baseOffset, $packet->z); - $revert = false; - if(!$this->isAlive() or $this->spawned !== true){ - $revert = true; - $this->forceMovement = new Vector3($this->x, $this->y, $this->z); - } - - if($this->teleportPosition !== null or ($this->forceMovement instanceof Vector3 and ($newPos->distanceSquared($this->forceMovement) > 0.1 or $revert))){ - $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch, MovePlayerPacket::MODE_RESET); + if((!$this->isAlive() or $this->spawned !== true) and $newPos->distanceSquared($this) > 0.01){ + $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET); + $this->server->getLogger()->debug("Reverted movement of " . $this->getName() . " due to not alive or not spawned, received " . $newPos . ", locked at " . $this->asVector3()); }else{ $packet->yaw %= 360; $packet->pitch %= 360; @@ -2159,7 +2139,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->setRotation($packet->yaw, $packet->pitch); $this->newPosition = $newPos; - $this->forceMovement = null; } return true; @@ -2726,7 +2705,16 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); - $this->teleport($ev->getRespawnPosition()); + $realSpawn = $ev->getRespawnPosition()->add(0.5, 0, 0.5); + + if($realSpawn->distanceSquared($this->getSpawn()->add(0.5, 0, 0.5)) > 0.01){ + $this->teleport($realSpawn); //If the destination was modified by plugins + }else{ + $this->setPosition($realSpawn); //The client will move to the position of its own accord once chunks are sent + $this->nextChunkOrderRun = 0; + } + + $this->resetLastMovements(); $this->setSprinting(false); $this->setSneaking(false); @@ -3966,14 +3954,14 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - public function sendPosition(Vector3 $pos, $yaw = null, $pitch = null, $mode = MovePlayerPacket::MODE_NORMAL, array $targets = null){ + public function sendPosition(Vector3 $pos, float $yaw = null, float $pitch = null, int $mode = MovePlayerPacket::MODE_NORMAL, array $targets = null, float $baseOffsetOverride = null){ $yaw = $yaw === null ? $this->yaw : $yaw; $pitch = $pitch === null ? $this->pitch : $pitch; $pk = new MovePlayerPacket(); $pk->entityRuntimeId = $this->getId(); $pk->x = $pos->x; - $pk->y = $pos->y + $this->baseOffset; + $pk->y = $pos->y + ($baseOffsetOverride ?? $this->baseOffset); $pk->z = $pos->z; $pk->bodyYaw = $yaw; $pk->pitch = $pitch; @@ -3986,18 +3974,6 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->dataPacket($pk); } - //TODO: Remove this hack for client bug - $pk = new MovePlayerPacket(); - $pk->entityRuntimeId = $this->getId(); - $pk->x = $pos->x; - $pk->y = $pos->y + $this->baseOffset; - $pk->z = $pos->z; - $pk->bodyYaw = $yaw; - $pk->pitch = $pitch; - $pk->yaw = $yaw; - $pk->mode = MovePlayerPacket::MODE_PITCH; - $this->dataPacket($pk); - $this->newPosition = null; } @@ -4036,46 +4012,10 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } } - protected function checkTeleportPosition(){ - if($this->teleportPosition !== null){ - $chunkX = $this->teleportPosition->x >> 4; - $chunkZ = $this->teleportPosition->z >> 4; - - for($X = -1; $X <= 1; ++$X){ - for($Z = -1; $Z <= 1; ++$Z){ - if(!isset($this->usedChunks[$index = Level::chunkHash($chunkX + $X, $chunkZ + $Z)]) or $this->usedChunks[$index] === false){ - return false; - } - } - } - - $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET); - //This only needs to be sent to players who could see us before the teleport. - $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET, $this->getViewers()); - - $this->spawnToAll(); - $this->forceMovement = $this->teleportPosition; - $this->teleportPosition = null; - - return true; - } - - return true; - } - /** - * @param Vector3|Position|Location $pos - * @param float $yaw - * @param float $pitch - * - * @return bool + * {@inheritdoc} */ - public function teleport(Vector3 $pos, $yaw = null, $pitch = null){ - if(!$this->isOnline()){ - return false; - } - - $oldPos = $this->getPosition(); + public function teleport(Vector3 $pos, float $yaw = null, float $pitch = null) : bool{ if(parent::teleport($pos, $yaw, $pitch)){ foreach($this->windowIndex as $window){ @@ -4085,49 +4025,31 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->removeWindow($window); } - $this->teleportPosition = new Vector3($this->x, $this->y, $this->z); + $this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_TELEPORT, null, 0.0); + $this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_TELEPORT, $this->getViewers(), 0.0); - if(!$this->checkTeleportPosition()){ - $this->forceMovement = $oldPos; - }else{ - $this->spawnToAll(); - } + $this->spawnToAll(); $this->resetFallDistance(); $this->nextChunkOrderRun = 0; $this->newPosition = null; $this->stopSleep(); + return true; } + return false; } /** - * This method may not be reliable. Clients don't like to be moved into unloaded chunks. - * Use teleport() for a delayed teleport after chunks have been sent. + * @deprecated This functionality is now performed in {@link Player#teleport}. * - * @param Vector3 $pos - * @param float $yaw - * @param float $pitch + * @param Vector3 $pos + * @param float|null $yaw + * @param float|null $pitch */ - public function teleportImmediate(Vector3 $pos, $yaw = null, $pitch = null){ - if(parent::teleport($pos, $yaw, $pitch)){ - - foreach($this->windowIndex as $window){ - if($window === $this->inventory){ - continue; - } - $this->removeWindow($window); - } - - $this->forceMovement = new Vector3($this->x, $this->y, $this->z); - $this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_RESET); - - $this->resetFallDistance(); - $this->orderChunks(); - $this->nextChunkOrderRun = 0; - $this->newPosition = null; - } + public function teleportImmediate(Vector3 $pos, float $yaw = null, float $pitch = null) : bool{ + return $this->teleport($pos, $yaw, $pitch); } /** diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 9465fb514..6abb5944e 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -365,6 +365,8 @@ abstract class Entity extends Location implements Metadatable{ $this->setMotion($this->temporalVector->setComponents(0, 0, 0)); } + $this->resetLastMovements(); + assert(!is_nan($this->x) and !is_infinite($this->x) and !is_nan($this->y) and !is_infinite($this->y) and !is_nan($this->z) and !is_infinite($this->z)); if(!isset($this->namedtag->FallDistance)){ @@ -1744,6 +1746,12 @@ abstract class Entity extends Location implements Metadatable{ return true; } + protected function resetLastMovements(){ + list($this->lastX, $this->lastY, $this->lastZ) = [$this->x, $this->y, $this->z]; + list($this->lastYaw, $this->lastPitch) = [$this->yaw, $this->pitch]; + list($this->lastMotionX, $this->lastMotionY, $this->lastMotionZ) = [$this->motionX, $this->motionY, $this->motionZ]; + } + public function getMotion(){ return new Vector3($this->motionX, $this->motionY, $this->motionZ); } @@ -1778,12 +1786,12 @@ abstract class Entity extends Location implements Metadatable{ /** * @param Vector3|Position|Location $pos - * @param float $yaw - * @param float $pitch + * @param float|null $yaw + * @param float|null $pitch * * @return bool */ - public function teleport(Vector3 $pos, $yaw = null, $pitch = null){ + public function teleport(Vector3 $pos, float $yaw = null, float $pitch = null) : bool{ if($pos instanceof Location){ $yaw = $yaw === null ? $pos->yaw : $yaw; $pitch = $pitch === null ? $pos->pitch : $pitch; diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 6e2bf3d98..8c14e7cc9 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -54,8 +54,10 @@ abstract class Living extends Entity implements Damageable{ if(isset($this->namedtag->HealF)){ $this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag["HealF"]); unset($this->namedtag->HealF); - }elseif(isset($this->namedtag->Health) and !($this->namedtag->Health instanceof FloatTag)){ - $this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag->Health->getValue()); + }elseif(isset($this->namedtag->Health)){ + if(!($this->namedtag->Health instanceof FloatTag)){ + $this->namedtag->Health = new FloatTag("Health", (float) $this->namedtag->Health->getValue()); + } }else{ $this->namedtag->Health = new FloatTag("Health", (float) $this->getMaxHealth()); }