mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 01:09:44 +00:00
Remove EventExecutor, event handlers now use closures (#2525)
This cleans up some cargo-cult code poorly copied from Bukkit, which has negative performance effects and also makes internal event handling more complex than necessary. ## API changes - Removed `EventExecutor` and `MethodEventExecutor`. - A listener is no longer required for an event handler to be registered. Closure objects can now be used directly provided that they meet the conditions for registration. - `PluginManager->registerEvent()` signature has changed: the `Listener` and `EventExecutor` parameters have been removed and a `\Closure $handler` has been added in its place. - `RegisteredListener` now requires a `Closure` parameter instead of `Listener, EventExecutor`. ## Behavioural changes These changes reduce the execution complexity involved with calling an event handler. Since event calls can happen in hot paths, this may have visible positive effects on performance. Initial testing reveals a performance improvement of ~15% per event handler call compared to the old method.
This commit is contained in:
parent
ddef7bb09b
commit
5d7feaaf21
@ -140,7 +140,7 @@ class HandlerList{
|
||||
foreach($this->handlerSlots as $priority => $list){
|
||||
foreach($list as $hash => $listener){
|
||||
if(($object instanceof Plugin and $listener->getPlugin() === $object)
|
||||
or ($object instanceof Listener and $listener->getListener() === $object)
|
||||
or ($object instanceof Listener and (new \ReflectionFunction($listener->getHandler()))->getClosureThis() === $object) //this doesn't even need to be a listener :D
|
||||
){
|
||||
unset($this->handlerSlots[$priority][$hash]);
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\event\Listener;
|
||||
|
||||
interface EventExecutor{
|
||||
|
||||
/**
|
||||
* @param Listener $listener
|
||||
* @param Event $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute(Listener $listener, Event $event);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\event\Listener;
|
||||
|
||||
class MethodEventExecutor implements EventExecutor{
|
||||
|
||||
private $method;
|
||||
|
||||
public function __construct($method){
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function execute(Listener $listener, Event $event){
|
||||
$listener->{$this->getMethod()}($event);
|
||||
}
|
||||
|
||||
public function getMethod(){
|
||||
return $this->method;
|
||||
}
|
||||
}
|
@ -519,43 +519,44 @@ class PluginManager{
|
||||
}
|
||||
}
|
||||
|
||||
$this->registerEvent($eventClass->getName(), $listener, $priority, new MethodEventExecutor($method->getName()), $plugin, $ignoreCancelled);
|
||||
$this->registerEvent($eventClass->getName(), $handlerClosure, $priority, $plugin, $ignoreCancelled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $event Class name that extends Event
|
||||
* @param Listener $listener
|
||||
* @param int $priority
|
||||
* @param EventExecutor $executor
|
||||
* @param Plugin $plugin
|
||||
* @param bool $ignoreCancelled
|
||||
* @param string $event Class name that extends Event
|
||||
* @param \Closure $handler
|
||||
* @param int $priority
|
||||
* @param Plugin $plugin
|
||||
* @param bool $ignoreCancelled
|
||||
*
|
||||
* @throws PluginException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function registerEvent(string $event, Listener $listener, int $priority, EventExecutor $executor, Plugin $plugin, bool $ignoreCancelled = false) : void{
|
||||
public function registerEvent(string $event, \Closure $handler, int $priority, Plugin $plugin, bool $ignoreCancelled = false) : void{
|
||||
if(!is_subclass_of($event, Event::class)){
|
||||
throw new PluginException($event . " is not an Event");
|
||||
}
|
||||
|
||||
$handlerName = Utils::getNiceClosureName($handler);
|
||||
|
||||
$tags = Utils::parseDocComment((string) (new \ReflectionClass($event))->getDocComment());
|
||||
if(isset($tags["deprecated"]) and $this->server->getProperty("settings.deprecated-verbose", true)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.deprecatedEvent", [
|
||||
$plugin->getName(),
|
||||
$event,
|
||||
get_class($listener) . "->" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "<unknown>")
|
||||
$handlerName
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
if(!$plugin->isEnabled()){
|
||||
throw new PluginException("Plugin attempted to register " . $event . " while not enabled");
|
||||
throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled");
|
||||
}
|
||||
|
||||
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")");
|
||||
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . $handlerName . "(" . (new \ReflectionClass($event))->getShortName() . ")");
|
||||
|
||||
$this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled, $timings));
|
||||
$this->getEventListeners($event)->register(new RegisteredListener($handler, $priority, $plugin, $ignoreCancelled, $timings));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,13 +25,12 @@ namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\Event;
|
||||
use pocketmine\event\Listener;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
class RegisteredListener{
|
||||
|
||||
/** @var Listener */
|
||||
private $listener;
|
||||
/** @var \Closure */
|
||||
private $handler;
|
||||
|
||||
/** @var int */
|
||||
private $priority;
|
||||
@ -39,9 +38,6 @@ class RegisteredListener{
|
||||
/** @var Plugin */
|
||||
private $plugin;
|
||||
|
||||
/** @var EventExecutor */
|
||||
private $executor;
|
||||
|
||||
/** @var bool */
|
||||
private $ignoreCancelled;
|
||||
|
||||
@ -50,27 +46,22 @@ class RegisteredListener{
|
||||
|
||||
|
||||
/**
|
||||
* @param Listener $listener
|
||||
* @param EventExecutor $executor
|
||||
* @param \Closure $handler
|
||||
* @param int $priority
|
||||
* @param Plugin $plugin
|
||||
* @param bool $ignoreCancelled
|
||||
* @param TimingsHandler $timings
|
||||
*/
|
||||
public function __construct(Listener $listener, EventExecutor $executor, int $priority, Plugin $plugin, bool $ignoreCancelled, TimingsHandler $timings){
|
||||
$this->listener = $listener;
|
||||
public function __construct(\Closure $handler, int $priority, Plugin $plugin, bool $ignoreCancelled, TimingsHandler $timings){
|
||||
$this->handler = $handler;
|
||||
$this->priority = $priority;
|
||||
$this->plugin = $plugin;
|
||||
$this->executor = $executor;
|
||||
$this->ignoreCancelled = $ignoreCancelled;
|
||||
$this->timings = $timings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Listener
|
||||
*/
|
||||
public function getListener() : Listener{
|
||||
return $this->listener;
|
||||
public function getHandler() : \Closure{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +86,7 @@ class RegisteredListener{
|
||||
return;
|
||||
}
|
||||
$this->timings->startTiming();
|
||||
$this->executor->execute($this->listener, $event);
|
||||
($this->handler)($event);
|
||||
$this->timings->stopTiming();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user