Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement waiting login sequence #6015

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
361f9b7
implement base async login sequence
ShockedPlot7560 Aug 23, 2023
382a5a3
fix cs
ShockedPlot7560 Aug 23, 2023
74e3b71
fix CS
ShockedPlot7560 Aug 23, 2023
22aac71
remove PlayerCreatedEvent, moove to PlayerCreationEvent
ShockedPlot7560 Aug 29, 2023
7757f4e
remove PlayerCreatedEvent
ShockedPlot7560 Aug 29, 2023
f5ad477
fix CS
ShockedPlot7560 Aug 29, 2023
76230cf
implement solution with Promise & refactor callback hell
ShockedPlot7560 Sep 7, 2023
47b86e2
fix promise rejection continue check & style
ShockedPlot7560 Sep 7, 2023
c488d67
fix PHPstan
ShockedPlot7560 Sep 7, 2023
68baf2c
rename var
ShockedPlot7560 Sep 7, 2023
64a164d
use ObjectSet instead of exposing promises array in event
ShockedPlot7560 Sep 7, 2023
66877e6
rename phpDoc tag to phpstan-
ShockedPlot7560 Sep 7, 2023
b93bae7
set promises value
ShockedPlot7560 Sep 8, 2023
316e9d5
fix cS
ShockedPlot7560 Sep 8, 2023
77f2ab8
Rename TTValue template in TValue
ShockedPlot7560 Sep 8, 2023
1b97763
remove Promise::all template
ShockedPlot7560 Sep 10, 2023
b54bc52
made Promise template covariant
ShockedPlot7560 Sep 12, 2023
e142dc1
add promise template
ShockedPlot7560 Sep 17, 2023
2169aa1
Merge branch 'feat/async-login' of github.com:ShockedPlot7560/PocketM…
ShockedPlot7560 Sep 17, 2023
2123e0d
remove system from PlayerCreationEvent
ShockedPlot7560 Oct 7, 2023
2dfd8bc
moove system to LoginPacket
ShockedPlot7560 Oct 7, 2023
3db11de
Merge remote-tracking branch 'upstream/minor-next' into feat/async-login
ShockedPlot7560 Oct 7, 2023
1eb3a4f
fix documentation
ShockedPlot7560 Oct 7, 2023
b3273b9
fix PHPStan
ShockedPlot7560 Oct 7, 2023
53aa3ac
change PlayerLoginPrepareEvent to PlayerPreCreationEvent
ShockedPlot7560 Oct 27, 2023
19fe4e9
remove Promise covariant
ShockedPlot7560 Oct 27, 2023
2504e0d
fix PHPstan & update docs
ShockedPlot7560 Oct 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -569,34 +569,42 @@ public function createPlayer(NetworkSession $session, PlayerInfo $playerInfo, bo
/** @phpstan-var PromiseResolver<Player> $playerPromiseResolver */
$playerPromiseResolver = new PromiseResolver();

$createPlayer = function(Location $location) use ($playerPromiseResolver, $class, $session, $playerInfo, $authenticated, $offlinePlayerData) : void{
$playerCreation = function(Location $location) use ($class, $session, $playerInfo, $authenticated, $offlinePlayerData, $playerPromiseResolver) : void{
if(!$session->isConnected()){
$playerPromiseResolver->reject();
return;
}

$player = new $class($this, $session, $playerInfo, $authenticated, $location, $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);
};

$ev->getWaitGroup()->wait(function() use ($playerPos, $world, $playerPromiseResolver, $session, $createPlayer) : void{
$playerCreationRejected = function (Translatable|string $message) use ($playerPromiseResolver, $session) : void {
if($session->isConnected()){
$session->disconnectWithError($message);
}
$playerPromiseResolver->reject();
};

$promise = Promise::all($ev->getPromises());
$promise->onCompletion(function () use ($playerPos, $world, $playerCreation, $playerCreationRejected) : void {
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(Position $spawn) use ($playerCreation, $world) : void{
$playerCreation(Location::fromObject($spawn, $world));
},
function() use ($playerPromiseResolver, $session) : void{
if($session->isConnected()){
$session->disconnectWithError(KnownTranslationFactory::pocketmine_disconnect_error_respawn());
}
$playerPromiseResolver->reject();
function() use ($playerCreationRejected) : void{
$playerCreationRejected(KnownTranslationFactory::pocketmine_disconnect_error_respawn());
}
);
}else{ //returning player with a valid position - safe spawn not required
$createPlayer($playerPos);
$playerCreation($playerPos);
}
}, function () use ($playerCreationRejected) : void {
$playerCreationRejected("Failed to create player");
});
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved

return $playerPromiseResolver->getPromise();
Expand Down
37 changes: 21 additions & 16 deletions src/event/player/PlayerCreationEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@
use pocketmine\event\Event;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\player\Player;
use pocketmine\promise\Promise;
use pocketmine\utils\Utils;
use pocketmine\utils\WaitGroup;
use function is_a;

/**
* Allows the use of custom Player classes. This enables overriding built-in Player methods to change behaviour that is
* not possible to alter any other way.
*
* Allows the plugins to specify tasks that need to be executed before the login sequence take place, use WaitGroup to
* manage synchronization between tasks.
* Allows the plugins to specify promise that need to be completed before the login sequence take place.
*
* You probably don't need this event, and found your way here because you looked at some code in an old plugin that
* abused it (very common). Instead of using custom player classes, you should consider making session classes instead.
Expand All @@ -55,10 +54,10 @@ class PlayerCreationEvent extends Event{
private string $baseClass = Player::class;
/** @phpstan-var class-string<Player> */
private string $playerClass = Player::class;
private WaitGroup $waitGroup;
/** @phpstan-var Promise<mixed>[] $promises */
private array $promises = [];

public function __construct(private NetworkSession $session){
$this->waitGroup = new WaitGroup();
}

public function getNetworkSession() : NetworkSession{
Expand Down Expand Up @@ -118,18 +117,24 @@ public function setPlayerClass(string $class) : void{
}

/**
* The typical use cases for this WaitGroup include:
* - Carrying out preparatory tasks such as configuring default preferences, setting
* player-specific settings, or initializing in-game assets.
* - Loading initial player data from external sources or databases before the login sequence begins.
* - Providing a tailored welcome experience, which might involve sending messages,
* granting starter items, or awarding initial rewards.
* Retrieves all promises that have been added to the event.
* These promises must be completed before the player login sequence is initiated.
*
* Note:
* After each task is completed, it's essential to call the `done` method of the WaitGroup
* to signal its completion and allow the WaitGroup to determine when to proceed.
* @return Promise[]
* @phpstan-return Promise<mixed>[]
*/
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
public function getWaitGroup() : WaitGroup{
return $this->waitGroup;
public function getPromises() : array{
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
return $this->promises;
}

/**
* Adds a promise to the waiting list for the login sequence.
* Once all the promises that have been added have been completed,
* the player login sequence will be initiated and the player will be created.
*
* @param Promise<mixed> $promise
*/
public function addPromise(Promise $promise) : void{
$this->promises[] = $promise;
}
}
48 changes: 48 additions & 0 deletions src/promise/Promise.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,52 @@
public function isResolved() : bool{
return $this->shared->resolved;
}

/**
* Returns a promise that will resolve only once all the Promises in
* `$promises` have resolved. The resolution value of the returned promise
* will be an array containing the resolution values of each Promises in
* `$promises` indexed by the respective Promises' array keys.
*
* @phpstan-param Promise<mixed>[] $promises
*
* @phpstan-return Promise<array<int, mixed>>
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
*/
public static function all(array $promises) : Promise {
/** @phpstan-var PromiseResolver<array<int, mixed>> $resolver */
$resolver = new PromiseResolver();
$values = [];
$toResolve = 0;
$continue = true;

foreach($promises as $key => $promise){
$toResolve++;
$values[$key] = null;

$promise->onCompletion(
function(mixed $value) use ($resolver, $key, &$toResolve, &$continue, &$values) : void{
$values[$key] = $value;

if(--$toResolve === 0 && $continue){

Check failure on line 81 in src/promise/Promise.php

View workflow job for this annotation

GitHub Actions / PHPStan analysis (ubuntu-20.04, 8.1)

Right side of && is always true.

Check failure on line 81 in src/promise/Promise.php

View workflow job for this annotation

GitHub Actions / PHPStan analysis (ubuntu-20.04, 8.2)

Right side of && is always true.
$resolver->resolve($values);
}
},
function() use ($resolver, &$continue) : void{
$continue = false;
$resolver->reject();
}
);

if(!$continue){
break;
}
}

if($toResolve === 0){
$continue = false;
$resolver->resolve($values);
}

return $resolver->getPromise();
}
}
130 changes: 0 additions & 130 deletions src/utils/WaitGroup.php

This file was deleted.

5 changes: 5 additions & 0 deletions tests/phpstan/configs/actual-problems.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1215,3 +1215,8 @@ parameters:
count: 1
path: ../../phpunit/scheduler/AsyncPoolTest.php

-
message: "#^Right side of && is always true\\.$#"
count: 1
path: src/promise/Promise.php

Loading