mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 16:24:05 +00:00
Merge pull request #5707 from pmmp/hot-events-optimisation
Avoid unnecessary event-related work in hot paths when the events have no registered handlers
This commit is contained in:
commit
6000bcccdd
@ -27,6 +27,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event;
|
||||
|
||||
use pocketmine\timings\Timings;
|
||||
use function count;
|
||||
use function get_class;
|
||||
|
||||
abstract class Event{
|
||||
@ -54,11 +55,11 @@ abstract class Event{
|
||||
$timings = Timings::getEventTimings($this);
|
||||
$timings->startTiming();
|
||||
|
||||
$handlerList = HandlerListManager::global()->getListFor(get_class($this));
|
||||
$handlers = HandlerListManager::global()->getHandlersFor(static::class);
|
||||
|
||||
++self::$eventCallDepth;
|
||||
try{
|
||||
foreach($handlerList->getListenerList() as $registration){
|
||||
foreach($handlers as $registration){
|
||||
$registration->callEvent($this);
|
||||
}
|
||||
}finally{
|
||||
@ -66,4 +67,14 @@ abstract class Event{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current class context has any registered global handlers.
|
||||
* This can be used in hot code paths to avoid unnecessary event object creation.
|
||||
*
|
||||
* Usage: SomeEventClass::hasHandlers()
|
||||
*/
|
||||
public static function hasHandlers() : bool{
|
||||
return count(HandlerListManager::global()->getHandlersFor(static::class)) > 0;
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ class HandlerList{
|
||||
/** @var RegisteredListener[][] */
|
||||
private array $handlerSlots = [];
|
||||
|
||||
private RegisteredListenerCache $handlerCache;
|
||||
|
||||
/** @var RegisteredListenerCache[] */
|
||||
private array $affectedHandlerCaches = [];
|
||||
|
||||
@ -44,9 +42,9 @@ class HandlerList{
|
||||
*/
|
||||
public function __construct(
|
||||
private string $class,
|
||||
private ?HandlerList $parentList
|
||||
private ?HandlerList $parentList,
|
||||
private RegisteredListenerCache $handlerCache = new RegisteredListenerCache()
|
||||
){
|
||||
$this->handlerCache = new RegisteredListenerCache();
|
||||
for($list = $this; $list !== null; $list = $list->parentList){
|
||||
$list->affectedHandlerCaches[spl_object_id($this->handlerCache)] = $this->handlerCache;
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ class HandlerListManager{
|
||||
|
||||
/** @var HandlerList[] classname => HandlerList */
|
||||
private array $allLists = [];
|
||||
/**
|
||||
* @var RegisteredListenerCache[] event class name => cache
|
||||
* @phpstan-var array<class-string<Event>, RegisteredListenerCache>
|
||||
*/
|
||||
private array $handlerCaches = [];
|
||||
|
||||
/**
|
||||
* Unregisters all the listeners
|
||||
@ -98,7 +103,25 @@ class HandlerListManager{
|
||||
}
|
||||
|
||||
$parent = self::resolveNearestHandleableParent($class);
|
||||
return $this->allLists[$event] = new HandlerList($event, $parent !== null ? $this->getListFor($parent->getName()) : null);
|
||||
$cache = new RegisteredListenerCache();
|
||||
$this->handlerCaches[$event] = $cache;
|
||||
return $this->allLists[$event] = new HandlerList(
|
||||
$event,
|
||||
parentList: $parent !== null ? $this->getListFor($parent->getName()) : null,
|
||||
handlerCache: $cache
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TEvent of Event
|
||||
* @phpstan-param class-string<TEvent> $event
|
||||
*
|
||||
* @return RegisteredListener[]
|
||||
*/
|
||||
public function getHandlersFor(string $event) : array{
|
||||
$cache = $this->handlerCaches[$event] ?? null;
|
||||
//getListFor() will populate the cache for the next call
|
||||
return $cache?->list ?? $this->getListFor($event)->getListenerList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,10 +406,12 @@ class NetworkSession{
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
if(DataPacketDecodeEvent::hasHandlers()){
|
||||
$ev = new DataPacketDecodeEvent($this, $packet->pid(), $buffer);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$decodeTimings = Timings::getDecodeDataPacketTimings($packet);
|
||||
@ -429,19 +431,22 @@ class NetworkSession{
|
||||
$decodeTimings->stopTiming();
|
||||
}
|
||||
|
||||
$ev = new DataPacketReceiveEvent($this, $packet);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$handlerTimings = Timings::getHandleDataPacketTimings($packet);
|
||||
$handlerTimings->startTiming();
|
||||
try{
|
||||
if($this->handler === null || !$packet->handle($this->handler)){
|
||||
$this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer()));
|
||||
}
|
||||
}finally{
|
||||
$handlerTimings->stopTiming();
|
||||
if(DataPacketReceiveEvent::hasHandlers()){
|
||||
$ev = new DataPacketReceiveEvent($this, $packet);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
}
|
||||
$handlerTimings = Timings::getHandleDataPacketTimings($packet);
|
||||
$handlerTimings->startTiming();
|
||||
try{
|
||||
if($this->handler === null || !$packet->handle($this->handler)){
|
||||
$this->logger->debug("Unhandled " . $packet->getName() . ": " . base64_encode($stream->getBuffer()));
|
||||
}
|
||||
}finally{
|
||||
$handlerTimings->stopTiming();
|
||||
}
|
||||
}finally{
|
||||
$timings->stopTiming();
|
||||
}
|
||||
@ -459,12 +464,16 @@ class NetworkSession{
|
||||
$timings = Timings::getSendDataPacketTimings($packet);
|
||||
$timings->startTiming();
|
||||
try{
|
||||
$ev = new DataPacketSendEvent([$this], [$packet]);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
if(DataPacketSendEvent::hasHandlers()){
|
||||
$ev = new DataPacketSendEvent([$this], [$packet]);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}
|
||||
$packets = $ev->getPackets();
|
||||
}else{
|
||||
$packets = [$packet];
|
||||
}
|
||||
$packets = $ev->getPackets();
|
||||
|
||||
foreach($packets as $evPacket){
|
||||
$this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket));
|
||||
|
@ -44,12 +44,14 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
public function broadcastPackets(array $recipients, array $packets) : void{
|
||||
//TODO: this shouldn't really be called here, since the broadcaster might be replaced by an alternative
|
||||
//implementation that doesn't fire events
|
||||
$ev = new DataPacketSendEvent($recipients, $packets);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
if(DataPacketSendEvent::hasHandlers()){
|
||||
$ev = new DataPacketSendEvent($recipients, $packets);
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
return;
|
||||
}
|
||||
$packets = $ev->getPackets();
|
||||
}
|
||||
$packets = $ev->getPackets();
|
||||
|
||||
$compressors = [];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user