mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-07 06:19:48 +00:00
AsyncEvent: make the code easier to make sense of
This commit is contained in:
parent
cb2fadeb26
commit
409066c8f5
@ -26,7 +26,6 @@ namespace pocketmine\event;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
|
||||
@ -37,17 +36,14 @@ use function count;
|
||||
* When all the promises of a priority level have been resolved, the next priority level is called.
|
||||
*/
|
||||
abstract class AsyncEvent{
|
||||
/** @phpstan-var ObjectSet<Promise<null>> $promises */
|
||||
private ObjectSet $promises;
|
||||
/** @var array<class-string<AsyncEvent>, int> $delegatesCallDepth */
|
||||
private static array $delegatesCallDepth = [];
|
||||
private const MAX_EVENT_CALL_DEPTH = 50;
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<self>
|
||||
* @phpstan-return Promise<static>
|
||||
*/
|
||||
final public function call() : Promise{
|
||||
$this->promises = new ObjectSet();
|
||||
if(!isset(self::$delegatesCallDepth[$class = static::class])){
|
||||
self::$delegatesCallDepth[$class] = 0;
|
||||
}
|
||||
@ -62,7 +58,12 @@ abstract class AsyncEvent{
|
||||
|
||||
++self::$delegatesCallDepth[$class];
|
||||
try{
|
||||
return $this->callAsyncDepth();
|
||||
/** @phpstan-var PromiseResolver<static> $globalResolver */
|
||||
$globalResolver = new PromiseResolver();
|
||||
|
||||
$this->asyncEachPriority(HandlerListManager::global()->getAsyncListFor(static::class), EventPriority::ALL, $globalResolver);
|
||||
|
||||
return $globalResolver->getPromise();
|
||||
}finally{
|
||||
--self::$delegatesCallDepth[$class];
|
||||
$timings->stopTiming();
|
||||
@ -70,81 +71,84 @@ abstract class AsyncEvent{
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<self>
|
||||
* TODO: this should use EventPriority constants for the list type but it's inconvenient with the current design
|
||||
* @phpstan-param list<int> $remaining
|
||||
* @phpstan-param PromiseResolver<static> $globalResolver
|
||||
*/
|
||||
private function callAsyncDepth() : Promise{
|
||||
/** @phpstan-var PromiseResolver<self> $globalResolver */
|
||||
$globalResolver = new PromiseResolver();
|
||||
|
||||
$handlerList = HandlerListManager::global()->getAsyncListFor(static::class);
|
||||
$priorities = EventPriority::ALL;
|
||||
$testResolve = function () use ($handlerList, &$testResolve, &$priorities, $globalResolver){
|
||||
if(count($priorities) === 0){
|
||||
private function asyncEachPriority(AsyncHandlerList $handlerList, array $remaining, PromiseResolver $globalResolver) : void{
|
||||
while(true){
|
||||
$nextPriority = array_shift($remaining);
|
||||
if($nextPriority === null){
|
||||
$globalResolver->resolve($this);
|
||||
}else{
|
||||
$this->callPriority($handlerList, array_shift($priorities))->onCompletion(function() use ($testResolve) : void{
|
||||
$testResolve();
|
||||
}, function () use ($globalResolver) {
|
||||
$globalResolver->reject();
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$testResolve();
|
||||
|
||||
return $globalResolver->getPromise();
|
||||
$promise = $this->callPriority($handlerList, $nextPriority);
|
||||
if($promise !== null){
|
||||
$promise->onCompletion(
|
||||
onSuccess: fn() => $this->asyncEachPriority($handlerList, $remaining, $globalResolver),
|
||||
onFailure: $globalResolver->reject(...)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<null>
|
||||
*/
|
||||
private function callPriority(AsyncHandlerList $handlerList, int $priority) : Promise{
|
||||
private function callPriority(AsyncHandlerList $handlerList, int $priority) : ?Promise{
|
||||
$handlers = $handlerList->getListenersByPriority($priority);
|
||||
if(count($handlers) === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @phpstan-var PromiseResolver<null> $resolver */
|
||||
$resolver = new PromiseResolver();
|
||||
|
||||
$concurrentPromises = [];
|
||||
$nonConcurrentHandlers = [];
|
||||
foreach($handlers as $registration){
|
||||
if($registration->canBeCalledConcurrently()){
|
||||
$result = $registration->callAsync($this);
|
||||
if($result !== null) {
|
||||
$this->promises->add($result);
|
||||
$concurrentPromises[] = $result;
|
||||
}
|
||||
}else{
|
||||
$nonConcurrentHandlers[] = $registration;
|
||||
}
|
||||
}
|
||||
|
||||
$testResolve = function() use (&$nonConcurrentHandlers, &$testResolve, $resolver){
|
||||
$this->waitForPromises()->onCompletion(function() use (&$nonConcurrentHandlers, $testResolve, $resolver){
|
||||
$handler = array_shift($nonConcurrentHandlers);
|
||||
if($handler !== null){
|
||||
$result = $handler->callAsync($this);
|
||||
if($result !== null) {
|
||||
$this->promises->add($result);
|
||||
}
|
||||
$testResolve();
|
||||
}else{
|
||||
$resolver->resolve(null);
|
||||
}
|
||||
}, function() use ($resolver) {
|
||||
$resolver->reject();
|
||||
});
|
||||
};
|
||||
|
||||
$testResolve();
|
||||
Promise::all($concurrentPromises)->onCompletion(
|
||||
onSuccess: fn() => $this->processExclusiveHandlers($nonConcurrentHandlers, $resolver),
|
||||
onFailure: $resolver->reject(...)
|
||||
);
|
||||
|
||||
return $resolver->getPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return Promise<array<int, null>>
|
||||
* @param AsyncRegisteredListener[] $handlers
|
||||
* @phpstan-param PromiseResolver<null> $resolver
|
||||
*/
|
||||
private function waitForPromises() : Promise{
|
||||
$array = $this->promises->toArray();
|
||||
$this->promises->clear();
|
||||
private function processExclusiveHandlers(array $handlers, PromiseResolver $resolver) : void{
|
||||
while(true){
|
||||
$handler = array_shift($handlers);
|
||||
if($handler === null){
|
||||
$resolver->resolve(null);
|
||||
break;
|
||||
}
|
||||
$result = $handler->callAsync($this);
|
||||
if($result instanceof Promise){
|
||||
//wait for this promise to resolve before calling the next handler
|
||||
$result->onCompletion(
|
||||
onSuccess: fn() => $this->processExclusiveHandlers($handlers, $resolver),
|
||||
onFailure: $resolver->reject(...)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return Promise::all($array);
|
||||
//this handler didn't return a promise - continue directly to the next one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user