mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-08-30 15:01:19 +00:00
Merge branch 'next-minor' into next-major
This commit is contained in:
commit
5c2ed210fc
@ -106,12 +106,12 @@ use pocketmine\utils\SignalHandler;
|
|||||||
use pocketmine\utils\Terminal;
|
use pocketmine\utils\Terminal;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use pocketmine\world\format\Chunk;
|
|
||||||
use pocketmine\world\format\io\WorldProviderManager;
|
use pocketmine\world\format\io\WorldProviderManager;
|
||||||
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
use pocketmine\world\format\io\WritableWorldProviderManagerEntry;
|
||||||
use pocketmine\world\generator\Generator;
|
use pocketmine\world\generator\Generator;
|
||||||
use pocketmine\world\generator\GeneratorManager;
|
use pocketmine\world\generator\GeneratorManager;
|
||||||
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
use pocketmine\world\generator\InvalidGeneratorOptionsException;
|
||||||
|
use pocketmine\world\Position;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use pocketmine\world\WorldCreationOptions;
|
use pocketmine\world\WorldCreationOptions;
|
||||||
use pocketmine\world\WorldManager;
|
use pocketmine\world\WorldManager;
|
||||||
@ -551,50 +551,44 @@ class Server{
|
|||||||
|
|
||||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
||||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||||
$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");
|
||||||
}
|
}
|
||||||
$playerPos = null;
|
$playerPos = null;
|
||||||
$spawn = $world->getSpawnLocation();
|
|
||||||
}
|
}
|
||||||
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
|
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
|
||||||
$playerPromiseResolver = new PromiseResolver();
|
$playerPromiseResolver = new PromiseResolver();
|
||||||
$world->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
|
||||||
function() use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $world, $playerPos, $spawn, $offlinePlayerData) : void{
|
|
||||||
if(!$session->isConnected()){
|
|
||||||
$playerPromiseResolver->reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stick with the original spawn at the time of generation request, even if it changed since then.
|
$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
|
||||||
* This is because we know for sure that that chunk will be generated, but the one at the new location
|
$player = new $class($this, $session, $playerInfo, $authenticated, $location, $offlinePlayerData);
|
||||||
* might not be, and it would be much more complex to go back and redo the whole thing.
|
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
|
||||||
* 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()
|
|
||||||
* @var Player $player
|
|
||||||
*/
|
|
||||||
$player = new $class($this, $session, $playerInfo, $authenticated, $playerPos ?? Location::fromObject($world->getSafeSpawn($spawn), $world), $offlinePlayerData);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
$playerPromiseResolver->resolve($player);
|
|
||||||
},
|
|
||||||
static function() use ($playerPromiseResolver, $session) : void{
|
|
||||||
if($session->isConnected()){
|
|
||||||
$session->getLogger()->error("Spawn terrain generation failed");
|
|
||||||
$session->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_internal());
|
|
||||||
}
|
|
||||||
$playerPromiseResolver->reject();
|
|
||||||
}
|
}
|
||||||
);
|
$playerPromiseResolver->resolve($player);
|
||||||
|
};
|
||||||
|
|
||||||
|
if($playerPos === null){ //new player or no valid position due to world not being loaded
|
||||||
|
$world->requestSafeSpawn()->onCompletion(
|
||||||
|
function(Position $spawn) use ($createPlayer, $playerPromiseResolver, $session, $world) : void{
|
||||||
|
if(!$session->isConnected()){
|
||||||
|
$playerPromiseResolver->reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$createPlayer(Location::fromObject($spawn, $world));
|
||||||
|
},
|
||||||
|
function() use ($playerPromiseResolver, $session) : void{
|
||||||
|
if($session->isConnected()){
|
||||||
|
$session->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_internal());
|
||||||
|
}
|
||||||
|
$playerPromiseResolver->reject();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}else{ //returning player with a valid position - safe spawn not required
|
||||||
|
$createPlayer($playerPos);
|
||||||
|
}
|
||||||
|
|
||||||
return $playerPromiseResolver->getPromise();
|
return $playerPromiseResolver->getPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +73,8 @@ class Language{
|
|||||||
try{
|
try{
|
||||||
$code = explode(".", $file)[0];
|
$code = explode(".", $file)[0];
|
||||||
$strings = self::loadLang($path, $code);
|
$strings = self::loadLang($path, $code);
|
||||||
if(isset($strings["language.name"])){
|
if(isset($strings[KnownTranslationKeys::LANGUAGE_NAME])){
|
||||||
$result[$code] = $strings["language.name"];
|
$result[$code] = $strings[KnownTranslationKeys::LANGUAGE_NAME];
|
||||||
}
|
}
|
||||||
}catch(LanguageNotFoundException $e){
|
}catch(LanguageNotFoundException $e){
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -2308,16 +2308,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
}
|
}
|
||||||
$this->respawnLocked = true;
|
$this->respawnLocked = true;
|
||||||
|
|
||||||
$this->logger->debug("Waiting for spawn terrain generation for respawn");
|
$this->logger->debug("Waiting for safe respawn position to be located");
|
||||||
$spawn = $this->getSpawn();
|
$spawn = $this->getSpawn();
|
||||||
$spawn->getWorld()->orderChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
$spawn->getWorld()->requestSafeSpawn($spawn)->onCompletion(
|
||||||
function() use ($spawn) : void{
|
function(Position $safeSpawn) : void{
|
||||||
if(!$this->isConnected()){
|
if(!$this->isConnected()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->logger->debug("Spawn terrain generation done, completing respawn");
|
$this->logger->debug("Respawn position located, completing respawn");
|
||||||
$spawn = $spawn->getWorld()->getSafeSpawn($spawn);
|
$ev = new PlayerRespawnEvent($this, $safeSpawn);
|
||||||
$ev = new PlayerRespawnEvent($this, $spawn);
|
|
||||||
$ev->call();
|
$ev->call();
|
||||||
|
|
||||||
$realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getWorld());
|
$realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getWorld());
|
||||||
|
@ -2846,6 +2846,36 @@ class World implements ChunkManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Requests a safe spawn position near the given position, or near the world's spawn position if not provided.
|
||||||
|
* Terrain near the position will be loaded or generated as needed.
|
||||||
|
*
|
||||||
|
* @return Promise Resolved to a Position object, or rejected if the world is unloaded.
|
||||||
|
* @phpstan-return Promise<Position>
|
||||||
|
*/
|
||||||
|
public function requestSafeSpawn(?Vector3 $spawn = null) : Promise{
|
||||||
|
$resolver = new PromiseResolver();
|
||||||
|
$spawn ??= $this->getSpawnLocation();
|
||||||
|
/*
|
||||||
|
* TODO: this relies on the assumption that getSafeSpawn() will only alter the Y coordinate of the provided
|
||||||
|
* position, which is currently OK, but might be a problem in the future.
|
||||||
|
*/
|
||||||
|
$this->requestChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion(
|
||||||
|
function() use ($spawn, $resolver) : void{
|
||||||
|
$spawn = $this->getSafeSpawn($spawn);
|
||||||
|
$resolver->resolve($spawn);
|
||||||
|
},
|
||||||
|
function() use ($resolver) : void{
|
||||||
|
$resolver->reject();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $resolver->getPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a safe spawn position near the given position, or near the world's spawn position if not provided.
|
||||||
|
* This function will throw an exception if the terrain is not already generated in advance.
|
||||||
|
*
|
||||||
* @throws WorldException if the terrain is not generated
|
* @throws WorldException if the terrain is not generated
|
||||||
*/
|
*/
|
||||||
public function getSafeSpawn(?Vector3 $spawn = null) : Position{
|
public function getSafeSpawn(?Vector3 $spawn = null) : Position{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user