Fixed corner case with player spawning in ungenerated terrain

we can't get the safe spawn location of a set of coordinates if the coordinates are in an ungenerated chunk. This can happen if doing /setworldspawn <somewhere ungenerated> and then having a new player join the server.
This commit is contained in:
Dylan K. Taylor 2021-04-03 22:20:00 +01:00
parent 4eaa600f35
commit 1898d4b42c
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -586,29 +586,40 @@ class Server{
$class = $ev->getPlayerClass(); $class = $ev->getPlayerClass();
if($offlinePlayerData !== null and ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){ if($offlinePlayerData !== null and ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
$spawn = EntityDataHelper::parseLocation($offlinePlayerData, $world); $playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
$onGround = $offlinePlayerData->getByte("OnGround", 1) === 1; $spawn = $playerPos->asVector3();
}else{ }else{
$world = $this->worldManager->getDefaultWorld(); $world = $this->worldManager->getDefaultWorld();
if($world === null){ if($world === null){
throw new AssumptionFailedError("Default world should always be loaded"); throw new AssumptionFailedError("Default world should always be loaded");
} }
$spawn = Location::fromObject($world->getSafeSpawn(), $world); $playerPos = null;
$onGround = true; $spawn = $world->getSpawnLocation();
} }
$playerPromise = new PlayerCreationPromise(); $playerPromise = new PlayerCreationPromise();
$world->requestChunkPopulation($spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4, null)->onCompletion( $world->requestChunkPopulation($spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4, null)->onCompletion(
function() use ($playerPromise, $class, $session, $playerInfo, $authenticated, $spawn, $offlinePlayerData, $onGround) : void{ function() use ($playerPromise, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
if(!$session->isConnected()){ if(!$session->isConnected()){
$playerPromise->reject(); $playerPromise->reject();
return; return;
} }
/* Stick with the original spawn at the time of generation request, even if it changed since then.
* This is because we know for sure that that chunk will be generated, but the one at the new location
* might not be, and it would be much more complex to go back and redo the whole thing.
*
* TODO: this relies on the assumption that getSafeSpawn() will only alter the Y coordinate of the
* provided position. If this assumption is broken, we'll start seeing crashes in here.
*/
/** /**
* @see Player::__construct() * @see Player::__construct()
* @var Player $player * @var Player $player
*/ */
$player = new $class($this, $session, $playerInfo, $authenticated, $spawn, $offlinePlayerData); $player = new $class($this, $session, $playerInfo, $authenticated, $playerPos ?? Location::fromObject($world->getSafeSpawn($spawn), $world), $offlinePlayerData);
$player->onGround = $onGround; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move if(!$player->hasPlayedBefore()){
$player->onGround = true; //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move
}
$playerPromise->resolve($player); $playerPromise->resolve($player);
}, },
static function() use ($playerPromise, $session) : void{ static function() use ($playerPromise, $session) : void{