mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-10-19 04:15:04 +00:00
Implemented event handler inheritance, allow registering handlers for any valid event (#1792)
* Event handlers always handle subclass events. public static $handlerList no longer required. * Removed $handlerList declarations * HandlerList cleanup: Removed HandlerList->handlers and related bake methods * Removed obsolete Event->getHandlers() * EventPriority: Added fromString() * PluginManager: throw exceptions on registering handlers with invalid priorities This allows specifying a handler of `EntityDamageEvent` which will handle any instanceof it (as per current behaviour), AND also now allows specifying a handler specifically for `EntityDamageByEntityEvent`, which only handles `EntityDamageEvent`. This was not previously possible due to limitations in the way handlers were registered. Abstract events may not be handled unless they declare the `@allowHandle` PhpDoc tag.
This commit is contained in:
@@ -24,38 +24,22 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event;
|
||||
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\plugin\RegisteredListener;
|
||||
|
||||
class HandlerList{
|
||||
|
||||
/**
|
||||
* @var RegisteredListener[]
|
||||
*/
|
||||
private $handlers = null;
|
||||
|
||||
/**
|
||||
* @var RegisteredListener[][]
|
||||
*/
|
||||
private $handlerSlots = [];
|
||||
|
||||
/**
|
||||
* @var HandlerList[]
|
||||
* @var HandlerList[] classname => HandlerList
|
||||
*/
|
||||
private static $allLists = [];
|
||||
|
||||
public static function bakeAll(){
|
||||
foreach(self::$allLists as $h){
|
||||
$h->bake();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters all the listeners
|
||||
* If a Plugin or Listener is passed, all the listeners with that object will be removed
|
||||
*
|
||||
* @param Plugin|Listener|null $object
|
||||
*/
|
||||
public static function unregisterAll($object = null){
|
||||
public static function unregisterAll($object = null) : void{
|
||||
if($object instanceof Listener or $object instanceof Plugin){
|
||||
foreach(self::$allLists as $h){
|
||||
$h->unregister($object);
|
||||
@@ -65,110 +49,41 @@ class HandlerList{
|
||||
foreach($h->handlerSlots as $key => $list){
|
||||
$h->handlerSlots[$key] = [];
|
||||
}
|
||||
$h->handlers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(){
|
||||
$this->handlerSlots = [
|
||||
EventPriority::LOWEST => [],
|
||||
EventPriority::LOW => [],
|
||||
EventPriority::NORMAL => [],
|
||||
EventPriority::HIGH => [],
|
||||
EventPriority::HIGHEST => [],
|
||||
EventPriority::MONITOR => []
|
||||
];
|
||||
self::$allLists[] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener $listener
|
||||
* Returns the HandlerList for listeners that explicitly handle this event.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function register(RegisteredListener $listener){
|
||||
if($listener->getPriority() < EventPriority::MONITOR or $listener->getPriority() > EventPriority::LOWEST){
|
||||
return;
|
||||
}
|
||||
if(isset($this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)])){
|
||||
throw new \InvalidStateException("This listener is already registered to priority " . $listener->getPriority());
|
||||
}
|
||||
$this->handlers = null;
|
||||
$this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener[] $listeners
|
||||
*/
|
||||
public function registerAll(array $listeners){
|
||||
foreach($listeners as $listener){
|
||||
$this->register($listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener|Listener|Plugin $object
|
||||
*/
|
||||
public function unregister($object){
|
||||
if($object instanceof Plugin or $object instanceof Listener){
|
||||
$changed = false;
|
||||
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)
|
||||
){
|
||||
unset($this->handlerSlots[$priority][$hash]);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($changed){
|
||||
$this->handlers = null;
|
||||
}
|
||||
}elseif($object instanceof RegisteredListener){
|
||||
if(isset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)])){
|
||||
unset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)]);
|
||||
$this->handlers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function bake(){
|
||||
if($this->handlers !== null){
|
||||
return;
|
||||
}
|
||||
$entries = [];
|
||||
foreach($this->handlerSlots as $list){
|
||||
foreach($list as $hash => $listener){
|
||||
$entries[$hash] = $listener;
|
||||
}
|
||||
}
|
||||
$this->handlers = $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|Plugin $plugin
|
||||
* Calling this method also lazily initializes the $classMap inheritance tree of handler lists.
|
||||
*
|
||||
* @return RegisteredListener[]
|
||||
* @param string $event
|
||||
*
|
||||
* @return null|HandlerList
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getRegisteredListeners($plugin = null) : array{
|
||||
if($plugin !== null){
|
||||
$listeners = [];
|
||||
foreach($this->getRegisteredListeners(null) as $hash => $listener){
|
||||
if($listener->getPlugin() === $plugin){
|
||||
$listeners[$hash] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return $listeners;
|
||||
}else{
|
||||
while(($handlers = $this->handlers) === null){
|
||||
$this->bake();
|
||||
}
|
||||
|
||||
return $handlers;
|
||||
public static function getHandlerListFor(string $event) : ?HandlerList{
|
||||
if(isset(self::$allLists[$event])){
|
||||
return self::$allLists[$event];
|
||||
}
|
||||
|
||||
$class = new \ReflectionClass($event);
|
||||
$tags = PluginManager::parseDocComment((string) $class->getDocComment());
|
||||
|
||||
if($class->isAbstract() && !isset($tags["allowHandle"])){
|
||||
return null;
|
||||
}
|
||||
|
||||
$super = $class;
|
||||
$parentList = null;
|
||||
while($parentList === null && ($super = $super->getParentClass()) !== false){
|
||||
// skip $noHandle events in the inheritance tree to go to the nearest ancestor
|
||||
// while loop to allow skipping $noHandle events in the inheritance tree
|
||||
$parentList = self::getHandlerListFor($super->getName());
|
||||
}
|
||||
|
||||
return new HandlerList($event, $parentList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,4 +93,79 @@ class HandlerList{
|
||||
return self::$allLists;
|
||||
}
|
||||
|
||||
|
||||
/** @var string */
|
||||
private $class;
|
||||
/** @var RegisteredListener[][] */
|
||||
private $handlerSlots = [];
|
||||
/** @var HandlerList|null */
|
||||
private $parentList;
|
||||
|
||||
public function __construct(string $class, ?HandlerList $parentList){
|
||||
$this->class = $class;
|
||||
$this->handlerSlots = array_fill_keys(EventPriority::ALL, []);
|
||||
$this->parentList = $parentList;
|
||||
self::$allLists[$this->class] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener $listener
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function register(RegisteredListener $listener) : void{
|
||||
if(!in_array($listener->getPriority(), EventPriority::ALL, true)){
|
||||
return;
|
||||
}
|
||||
if(isset($this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)])){
|
||||
throw new \InvalidStateException("This listener is already registered to priority {$listener->getPriority()} of event {$this->class}");
|
||||
}
|
||||
$this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener[] $listeners
|
||||
*/
|
||||
public function registerAll(array $listeners) : void{
|
||||
foreach($listeners as $listener){
|
||||
$this->register($listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisteredListener|Listener|Plugin $object
|
||||
*/
|
||||
public function unregister($object) : void{
|
||||
if($object instanceof Plugin or $object instanceof Listener){
|
||||
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)
|
||||
){
|
||||
unset($this->handlerSlots[$priority][$hash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}elseif($object instanceof RegisteredListener){
|
||||
if(isset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)])){
|
||||
unset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $priority
|
||||
*
|
||||
* @return RegisteredListener[]
|
||||
*/
|
||||
public function getListenersByPriority(int $priority) : array{
|
||||
return $this->handlerSlots[$priority];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|HandlerList
|
||||
*/
|
||||
public function getParent() : ?HandlerList{
|
||||
return $this->parentList;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user