From 520f79638fc4650c3f2689fd9c7364a843d3669c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 18 Oct 2022 20:28:52 +0100 Subject: [PATCH 1/6] First look at dedicated broadcast subscriber interface this allows removing some specialized hacks, such as that for the coloured italic audit messages. --- src/BroadcastSubscriber.php | 39 ++++++++++++++++++++++++++ src/Server.php | 22 +++++++-------- src/command/Command.php | 15 ++++------ src/event/player/PlayerChatEvent.php | 10 +++---- src/player/Player.php | 14 ++++++++- src/utils/BroadcastLoggerForwarder.php | 39 +++----------------------- 6 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 src/BroadcastSubscriber.php diff --git a/src/BroadcastSubscriber.php b/src/BroadcastSubscriber.php new file mode 100644 index 00000000000..c9abb1e8d15 --- /dev/null +++ b/src/BroadcastSubscriber.php @@ -0,0 +1,39 @@ +> + * @var BroadcastSubscriber[][] + * @phpstan-var array> */ private array $broadcastSubscribers = []; @@ -1049,7 +1049,7 @@ public function __construct( $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET))); $this->logger->info($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3))))); - $forwarder = new BroadcastLoggerForwarder($this, $this->logger, $this->language); + $forwarder = new BroadcastLoggerForwarder($this->logger, $this->language); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); @@ -1232,14 +1232,14 @@ private function startupPrepareNetworkInterfaces() : bool{ * Subscribes to a particular message broadcast channel. * The channel ID can be any arbitrary string. */ - public function subscribeToBroadcastChannel(string $channelId, CommandSender $subscriber) : void{ + public function subscribeToBroadcastChannel(string $channelId, BroadcastSubscriber $subscriber) : void{ $this->broadcastSubscribers[$channelId][spl_object_id($subscriber)] = $subscriber; } /** * Unsubscribes from a particular message broadcast channel. */ - public function unsubscribeFromBroadcastChannel(string $channelId, CommandSender $subscriber) : void{ + public function unsubscribeFromBroadcastChannel(string $channelId, BroadcastSubscriber $subscriber) : void{ if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){ unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]); if(count($this->broadcastSubscribers[$channelId]) === 0){ @@ -1251,30 +1251,30 @@ public function unsubscribeFromBroadcastChannel(string $channelId, CommandSender /** * Unsubscribes from all broadcast channels. */ - public function unsubscribeFromAllBroadcastChannels(CommandSender $subscriber) : void{ + public function unsubscribeFromAllBroadcastChannels(BroadcastSubscriber $subscriber) : void{ foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){ $this->unsubscribeFromBroadcastChannel($channelId, $subscriber); } } /** - * Returns a list of all the CommandSenders subscribed to the given broadcast channel. + * Returns a list of all the broadcast subscribers subscribed to the given broadcast channel. * - * @return CommandSender[] - * @phpstan-return array + * @return BroadcastSubscriber[] + * @phpstan-return array */ public function getBroadcastChannelSubscribers(string $channelId) : array{ return $this->broadcastSubscribers[$channelId] ?? []; } /** - * @param CommandSender[]|null $recipients + * @param BroadcastSubscriber[]|null $recipients */ public function broadcastMessage(Translatable|string $message, ?array $recipients = null) : int{ $recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS); foreach($recipients as $recipient){ - $recipient->sendMessage($message); + $recipient->onBroadcast(self::BROADCAST_CHANNEL_USERS, $message); } return count($recipients); diff --git a/src/command/Command.php b/src/command/Command.php index 7ae9ae7c1b4..4d1bd40726e 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -31,7 +31,6 @@ use pocketmine\lang\Translatable; use pocketmine\permission\PermissionManager; use pocketmine\Server; -use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\TextFormat; use function explode; use function str_replace; @@ -215,19 +214,17 @@ public function setUsage(Translatable|string $usage) : void{ } public static function broadcastCommandMessage(CommandSender $source, Translatable|string $message, bool $sendToSource = true) : void{ - $users = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); - $result = KnownTranslationFactory::chat_type_admin($source->getName(), $message); - $colored = $result->prefix(TextFormat::GRAY . TextFormat::ITALIC); + $subscribers = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); + $broadcast = KnownTranslationFactory::chat_type_admin($source->getName(), $message); if($sendToSource){ $source->sendMessage($message); } - foreach($users as $user){ - if($user instanceof BroadcastLoggerForwarder){ - $user->sendMessage($result); - }elseif($user !== $source){ - $user->sendMessage($colored); + foreach($subscribers as $user){ + //TODO: this check is flaky, since the sender might have broadcast subscribers that we don't know about + if($user !== $source){ + $user->onBroadcast(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $broadcast); } } } diff --git a/src/event/player/PlayerChatEvent.php b/src/event/player/PlayerChatEvent.php index b345c5c78be..e958ecbb082 100644 --- a/src/event/player/PlayerChatEvent.php +++ b/src/event/player/PlayerChatEvent.php @@ -23,7 +23,7 @@ namespace pocketmine\event\player; -use pocketmine\command\CommandSender; +use pocketmine\BroadcastSubscriber; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\player\Player; @@ -36,7 +36,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; /** - * @param CommandSender[] $recipients + * @param BroadcastSubscriber[] $recipients */ public function __construct( Player $player, @@ -71,17 +71,17 @@ public function setFormat(string $format) : void{ } /** - * @return CommandSender[] + * @return BroadcastSubscriber[] */ public function getRecipients() : array{ return $this->recipients; } /** - * @param CommandSender[] $recipients + * @param BroadcastSubscriber[] $recipients */ public function setRecipients(array $recipients) : void{ - Utils::validateArrayValueType($recipients, function(CommandSender $_) : void{}); + Utils::validateArrayValueType($recipients, function(BroadcastSubscriber $_) : void{}); $this->recipients = $recipients; } } diff --git a/src/player/Player.php b/src/player/Player.php index 74616b23ff2..dc03566b967 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -27,6 +27,7 @@ use pocketmine\block\BlockTypeIds; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; +use pocketmine\BroadcastSubscriber; use pocketmine\command\CommandSender; use pocketmine\crafting\CraftingGrid; use pocketmine\data\java\GameModeIdMap; @@ -159,7 +160,7 @@ /** * Main class that handles networking, recovery, and packet sending to the server part */ -class Player extends Human implements CommandSender, ChunkListener, IPlayer{ +class Player extends Human implements BroadcastSubscriber, CommandSender, ChunkListener, IPlayer{ use PermissibleDelegateTrait; private const MOVES_PER_TICK = 2; @@ -1987,6 +1988,17 @@ public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut) : void{ } } + public function onBroadcast(string $channelId, Translatable|string $message) : void{ + if($channelId === Server::BROADCAST_CHANNEL_ADMINISTRATIVE){ + if($message instanceof Translatable){ + $message = $message->prefix(TextFormat::GRAY . TextFormat::ITALIC); + }else{ + $message = TextFormat::GRAY . TextFormat::ITALIC . $message; + } + } + $this->sendMessage($message); + } + /** * Sends a direct chat message to a player */ diff --git a/src/utils/BroadcastLoggerForwarder.php b/src/utils/BroadcastLoggerForwarder.php index a015615b590..e0357cd2b5d 100644 --- a/src/utils/BroadcastLoggerForwarder.php +++ b/src/utils/BroadcastLoggerForwarder.php @@ -23,57 +23,26 @@ namespace pocketmine\utils; -use pocketmine\command\CommandSender; +use pocketmine\BroadcastSubscriber; use pocketmine\lang\Language; use pocketmine\lang\Translatable; -use pocketmine\permission\PermissibleBase; -use pocketmine\permission\PermissibleDelegateTrait; -use pocketmine\Server; /** * Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and * command audit log messages to the server log file. - * - * Unfortunately, broadcast subscribers are currently required to implement CommandSender, so this class has to include - * a lot of useless methods. */ -final class BroadcastLoggerForwarder implements CommandSender{ - use PermissibleDelegateTrait; +final class BroadcastLoggerForwarder implements BroadcastSubscriber{ public function __construct( - private Server $server, //annoying useless dependency private \Logger $logger, private Language $language - ){ - //this doesn't need any permissions - $this->perm = new PermissibleBase([]); - } - - public function getLanguage() : Language{ - return $this->language; - } + ){} - public function sendMessage(Translatable|string $message) : void{ + public function onBroadcast(string $channelId, Translatable|string $message) : void{ if($message instanceof Translatable){ $this->logger->info($this->language->translate($message)); }else{ $this->logger->info($message); } } - - public function getServer() : Server{ - return $this->server; - } - - public function getName() : string{ - return "Broadcast Logger Forwarder"; - } - - public function getScreenLineHeight() : int{ - return PHP_INT_MAX; - } - - public function setScreenLineHeight(?int $height) : void{ - //NOOP - } } From 6f6f2fb4a76bc05ec792370c4ed6d40a4ac19e20 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 1 Dec 2022 20:55:47 +0000 Subject: [PATCH 2/6] Rename BroadcastSubscriber -> ChatBroadcastSubscriber --- ...ubscriber.php => ChatBroadcastSubscriber.php} | 2 +- src/Server.php | 16 ++++++++-------- src/event/player/PlayerChatEvent.php | 10 +++++----- src/player/Player.php | 4 ++-- src/utils/BroadcastLoggerForwarder.php | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) rename src/{BroadcastSubscriber.php => ChatBroadcastSubscriber.php} (97%) diff --git a/src/BroadcastSubscriber.php b/src/ChatBroadcastSubscriber.php similarity index 97% rename from src/BroadcastSubscriber.php rename to src/ChatBroadcastSubscriber.php index c9abb1e8d15..0a51f0279e9 100644 --- a/src/BroadcastSubscriber.php +++ b/src/ChatBroadcastSubscriber.php @@ -28,7 +28,7 @@ /** * This interface can be implemented in order to receive messages from the server's global broadcast channels. */ -interface BroadcastSubscriber{ +interface ChatBroadcastSubscriber{ /** * Called when a message is broadcasted on any channel that this receiver is subscribed to. diff --git a/src/Server.php b/src/Server.php index 8ac8f8e1a96..b25515a4028 100644 --- a/src/Server.php +++ b/src/Server.php @@ -267,8 +267,8 @@ class Server{ private SignalHandler $signalHandler; /** - * @var BroadcastSubscriber[][] - * @phpstan-var array> + * @var ChatBroadcastSubscriber[][] + * @phpstan-var array> */ private array $broadcastSubscribers = []; @@ -1232,14 +1232,14 @@ private function startupPrepareNetworkInterfaces() : bool{ * Subscribes to a particular message broadcast channel. * The channel ID can be any arbitrary string. */ - public function subscribeToBroadcastChannel(string $channelId, BroadcastSubscriber $subscriber) : void{ + public function subscribeToBroadcastChannel(string $channelId, ChatBroadcastSubscriber $subscriber) : void{ $this->broadcastSubscribers[$channelId][spl_object_id($subscriber)] = $subscriber; } /** * Unsubscribes from a particular message broadcast channel. */ - public function unsubscribeFromBroadcastChannel(string $channelId, BroadcastSubscriber $subscriber) : void{ + public function unsubscribeFromBroadcastChannel(string $channelId, ChatBroadcastSubscriber $subscriber) : void{ if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){ unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]); if(count($this->broadcastSubscribers[$channelId]) === 0){ @@ -1251,7 +1251,7 @@ public function unsubscribeFromBroadcastChannel(string $channelId, BroadcastSubs /** * Unsubscribes from all broadcast channels. */ - public function unsubscribeFromAllBroadcastChannels(BroadcastSubscriber $subscriber) : void{ + public function unsubscribeFromAllBroadcastChannels(ChatBroadcastSubscriber $subscriber) : void{ foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){ $this->unsubscribeFromBroadcastChannel($channelId, $subscriber); } @@ -1260,15 +1260,15 @@ public function unsubscribeFromAllBroadcastChannels(BroadcastSubscriber $subscri /** * Returns a list of all the broadcast subscribers subscribed to the given broadcast channel. * - * @return BroadcastSubscriber[] - * @phpstan-return array + * @return ChatBroadcastSubscriber[] + * @phpstan-return array */ public function getBroadcastChannelSubscribers(string $channelId) : array{ return $this->broadcastSubscribers[$channelId] ?? []; } /** - * @param BroadcastSubscriber[]|null $recipients + * @param ChatBroadcastSubscriber[]|null $recipients */ public function broadcastMessage(Translatable|string $message, ?array $recipients = null) : int{ $recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS); diff --git a/src/event/player/PlayerChatEvent.php b/src/event/player/PlayerChatEvent.php index e958ecbb082..82f4b546bd6 100644 --- a/src/event/player/PlayerChatEvent.php +++ b/src/event/player/PlayerChatEvent.php @@ -23,7 +23,7 @@ namespace pocketmine\event\player; -use pocketmine\BroadcastSubscriber; +use pocketmine\ChatBroadcastSubscriber; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\player\Player; @@ -36,7 +36,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; /** - * @param BroadcastSubscriber[] $recipients + * @param ChatBroadcastSubscriber[] $recipients */ public function __construct( Player $player, @@ -71,17 +71,17 @@ public function setFormat(string $format) : void{ } /** - * @return BroadcastSubscriber[] + * @return ChatBroadcastSubscriber[] */ public function getRecipients() : array{ return $this->recipients; } /** - * @param BroadcastSubscriber[] $recipients + * @param ChatBroadcastSubscriber[] $recipients */ public function setRecipients(array $recipients) : void{ - Utils::validateArrayValueType($recipients, function(BroadcastSubscriber $_) : void{}); + Utils::validateArrayValueType($recipients, function(ChatBroadcastSubscriber $_) : void{}); $this->recipients = $recipients; } } diff --git a/src/player/Player.php b/src/player/Player.php index dc03566b967..0f0b2e1efff 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -27,7 +27,7 @@ use pocketmine\block\BlockTypeIds; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; -use pocketmine\BroadcastSubscriber; +use pocketmine\ChatBroadcastSubscriber; use pocketmine\command\CommandSender; use pocketmine\crafting\CraftingGrid; use pocketmine\data\java\GameModeIdMap; @@ -160,7 +160,7 @@ /** * Main class that handles networking, recovery, and packet sending to the server part */ -class Player extends Human implements BroadcastSubscriber, CommandSender, ChunkListener, IPlayer{ +class Player extends Human implements ChatBroadcastSubscriber, CommandSender, ChunkListener, IPlayer{ use PermissibleDelegateTrait; private const MOVES_PER_TICK = 2; diff --git a/src/utils/BroadcastLoggerForwarder.php b/src/utils/BroadcastLoggerForwarder.php index e0357cd2b5d..b8d47a6041d 100644 --- a/src/utils/BroadcastLoggerForwarder.php +++ b/src/utils/BroadcastLoggerForwarder.php @@ -23,7 +23,7 @@ namespace pocketmine\utils; -use pocketmine\BroadcastSubscriber; +use pocketmine\ChatBroadcastSubscriber; use pocketmine\lang\Language; use pocketmine\lang\Translatable; @@ -31,7 +31,7 @@ * Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and * command audit log messages to the server log file. */ -final class BroadcastLoggerForwarder implements BroadcastSubscriber{ +final class BroadcastLoggerForwarder implements ChatBroadcastSubscriber{ public function __construct( private \Logger $logger, From d772b190d714c4112e3bcd164adf82439401df52 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Nov 2024 16:42:44 +0000 Subject: [PATCH 3/6] Remove assumptions that MessageForwarder is an instance of Player, amongst other things this ended up turning into a rewrite of the broadcast system. It now also supports specialized broadcast handlers capable of handling tips, popups and titles. --- src/MessageChannel.php | 145 ++++++++++++++++++ ...riber.php => MessageChannelSubscriber.php} | 12 +- src/MinecraftMessageChannelSubscriber.php | 36 +++++ src/Server.php | 117 +++++++------- src/command/Command.php | 5 +- src/command/defaults/MeCommand.php | 7 +- src/command/defaults/SayCommand.php | 13 +- src/event/player/PlayerChatEvent.php | 10 +- src/player/Player.php | 68 ++++---- ...rwarder.php => MessageLoggerForwarder.php} | 12 +- 10 files changed, 320 insertions(+), 105 deletions(-) create mode 100644 src/MessageChannel.php rename src/{ChatBroadcastSubscriber.php => MessageChannelSubscriber.php} (68%) create mode 100644 src/MinecraftMessageChannelSubscriber.php rename src/utils/{BroadcastLoggerForwarder.php => MessageLoggerForwarder.php} (73%) diff --git a/src/MessageChannel.php b/src/MessageChannel.php new file mode 100644 index 00000000000..a8149f280eb --- /dev/null +++ b/src/MessageChannel.php @@ -0,0 +1,145 @@ + + */ + private ObjectSet $nonPermittedSubscribers; + + /** + * Only subscribers whose permissions have been checked. This is invalidated whenever a subscriber's permissions are + * updated. + * + * @var MessageChannelSubscriber[]|ObjectSet + * @phpstan-var ObjectSet + */ + private ObjectSet $permittedSubscribers; + + /** + * @var \Closure[] + * @phpstan-var array + */ + private array $permissionCallbacks = []; + + public function __construct( + private ?string $permission + ){ + $this->nonPermittedSubscribers = new ObjectSet(); + $this->permittedSubscribers = new ObjectSet(); + } + + public function getPermission() : ?string{ return $this->permission; } + + public function setPermission(?string $permission) : void{ + $this->permission = $permission; + foreach($this->permittedSubscribers as $subscriber){ + $this->updateSubscriberAccess($subscriber); + } + foreach($this->nonPermittedSubscribers as $subscriber){ + $this->updateSubscriberAccess($subscriber); + } + } + + private function updateSubscriberAccess(MessageChannelSubscriber $subscriber) : void{ + $permissible = $subscriber->getPermissible(); + + //If either the subscriber doesn't support permission restricting, or no permission is set, we send all messages + //to it. Mainly useful for programmatic message collectors like in plugins. + $permitted = $permissible === null || $this->permission === null || $permissible->hasPermission($this->permission); + + [$add, $remove] = $permitted ? + [$this->permittedSubscribers, $this->nonPermittedSubscribers] : + [$this->nonPermittedSubscribers, $this->permittedSubscribers]; + $add->add($subscriber); + $remove->remove($subscriber); + } + + /** + * Subscribes to a particular message broadcast channel. + * The channel ID can be any arbitrary string. + */ + public function subscribe(MessageChannelSubscriber $subscriber) : void{ + $this->updateSubscriberAccess($subscriber); + + $permCallback = $this->permissionCallbacks[spl_object_id($subscriber)] = fn() => $this->updateSubscriberAccess($subscriber); + $subscriber->getPermissible()?->getPermissionRecalculationCallbacks()->add($permCallback); + } + + /** + * Unsubscribes from a particular message broadcast channel. + */ + public function unsubscribe(MessageChannelSubscriber $subscriber) : void{ + $this->nonPermittedSubscribers->remove($subscriber); + $this->permittedSubscribers->remove($subscriber); + + $splObjectId = spl_object_id($subscriber); + $permCallback = $this->permissionCallbacks[$splObjectId] ?? null; + if($permCallback !== null){ + $subscriber->getPermissible()?->getPermissionRecalculationCallbacks()->remove($permCallback); + unset($this->permissionCallbacks[$splObjectId]); + } + } + + /** + * Returns a list of all subscribers with permission to receive messages from this channel. + * + * @return MessageChannelSubscriber[] + * @phpstan-return array + */ + public function getPermittedSubscribers() : array{ + //convert to an array so callers can't mess up the internal state of the channel + return $this->permittedSubscribers->toArray(); + } + + /** + * Returns a list of subscribers who don't have permission to receive messages from this channel. + * If their permissions are updated, they'll automatically start receiving new messages. + * + * @return MessageChannelSubscriber[] + * @phpstan-return array + */ + public function getNonPermittedSubscribers() : array{ + return $this->nonPermittedSubscribers->toArray(); + } + + /** + * Returns a list of all broadcast subscribers with permission to receive messages from this channel, and who can + * receive non-standard message types such as tips, popups and titles. + * + * @return MinecraftMessageChannelSubscriber[] + * @phpstan-return array + */ + public function getMinecraftPermittedSubscribers() : array{ + return array_filter($this->permittedSubscribers->toArray(), fn(MessageChannelSubscriber $s) => $s instanceof MinecraftMessageChannelSubscriber); + } +} diff --git a/src/ChatBroadcastSubscriber.php b/src/MessageChannelSubscriber.php similarity index 68% rename from src/ChatBroadcastSubscriber.php rename to src/MessageChannelSubscriber.php index 0a51f0279e9..a321d1393d2 100644 --- a/src/ChatBroadcastSubscriber.php +++ b/src/MessageChannelSubscriber.php @@ -23,17 +23,25 @@ namespace pocketmine; +use pocketmine\command\CommandSender; use pocketmine\lang\Translatable; +use pocketmine\permission\Permissible; /** * This interface can be implemented in order to receive messages from the server's global broadcast channels. */ -interface ChatBroadcastSubscriber{ +interface MessageChannelSubscriber{ /** * Called when a message is broadcasted on any channel that this receiver is subscribed to. * * @see Server::subscribeToBroadcastChannel() */ - public function onBroadcast(string $channelId, Translatable|string $message) : void; + public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void; + + /** + * Used to check if the subscriber is allowed to receive messages from channels with permission restrictions. + * If this function returns null, the subscriber will receive all messages regardless of permissions. + */ + public function getPermissible() : ?Permissible; } diff --git a/src/MinecraftMessageChannelSubscriber.php b/src/MinecraftMessageChannelSubscriber.php new file mode 100644 index 00000000000..f7f2f642c8f --- /dev/null +++ b/src/MinecraftMessageChannelSubscriber.php @@ -0,0 +1,36 @@ +> + * @var MessageChannel[] + * @phpstan-var array */ - private array $broadcastSubscribers = []; + private array $broadcastChannels = []; public function getName() : string{ return VersionInfo::NAME; @@ -977,6 +978,12 @@ public function __construct( $this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger); + $this->broadcastChannels = [ + self::BROADCAST_CHANNEL_ADMINISTRATIVE => new MessageChannel(DefaultPermissionNames::BROADCAST_ADMIN), + self::BROADCAST_CHANNEL_CHAT => new MessageChannel(DefaultPermissionNames::BROADCAST_USER), + self::BROADCAST_CHANNEL_GAME_EVENTS => new MessageChannel(DefaultPermissionNames::BROADCAST_USER), + ]; + $pluginGraylist = null; $graylistFile = Path::join($this->dataPath, "plugin_list.yml"); if(!file_exists($graylistFile)){ @@ -1056,9 +1063,10 @@ public function __construct( $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET))); $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3))))); - $forwarder = new BroadcastLoggerForwarder($this->logger, $this->language); + $forwarder = new MessageLoggerForwarder($this->logger, $this->language); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_CHAT, $forwarder); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_GAME_EVENTS, $forwarder); //TODO: move console parts to a separate component if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){ @@ -1247,110 +1255,103 @@ private function startupPrepareNetworkInterfaces() : bool{ return true; } + private function getBroadcastChannel(string $channelId) : ?MessageChannel{ + return $this->broadcastChannels[$channelId] ?? null; + } + + public function createBroadcastChannel(string $channelId, ?string $permission) : void{ + if(isset($this->broadcastChannels[$channelId])){ + throw new \InvalidArgumentException("Channel \"$channelId\" already exists"); + } + $this->broadcastChannels[$channelId] = new MessageChannel($permission); + } + /** * Subscribes to a particular message broadcast channel. * The channel ID can be any arbitrary string. */ - public function subscribeToBroadcastChannel(string $channelId, ChatBroadcastSubscriber $subscriber) : void{ - $this->broadcastSubscribers[$channelId][spl_object_id($subscriber)] = $subscriber; + public function subscribeToBroadcastChannel(string $channelId, MessageChannelSubscriber $subscriber) : void{ + $channel = $this->getBroadcastChannel($channelId) ?? throw new \InvalidArgumentException("Channel \"$channelId\" does not exist"); + $channel->subscribe($subscriber); } /** * Unsubscribes from a particular message broadcast channel. */ - public function unsubscribeFromBroadcastChannel(string $channelId, ChatBroadcastSubscriber $subscriber) : void{ - if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){ - if(count($this->broadcastSubscribers[$channelId]) === 1){ - unset($this->broadcastSubscribers[$channelId]); - }else{ - unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]); - } - } + public function unsubscribeFromBroadcastChannel(string $channelId, MessageChannelSubscriber $subscriber) : void{ + $this->getBroadcastChannel($channelId)?->unsubscribe($subscriber); } /** * Unsubscribes from all broadcast channels. */ - public function unsubscribeFromAllBroadcastChannels(ChatBroadcastSubscriber $subscriber) : void{ - foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){ - $this->unsubscribeFromBroadcastChannel($channelId, $subscriber); + public function unsubscribeFromAllBroadcastChannels(MessageChannelSubscriber $subscriber) : void{ + foreach(Utils::stringifyKeys($this->broadcastChannels) as $channelId => $channel){ + $channel->unsubscribe($subscriber); } } /** - * Returns a list of all the broadcast subscribers subscribed to the given broadcast channel. + * Returns a list of all the broadcast subscribers subscribed to the given broadcast channel with permission to + * receive messages from it. * - * @return ChatBroadcastSubscriber[] - * @phpstan-return array + * @return MessageChannelSubscriber[] + * @phpstan-return array */ public function getBroadcastChannelSubscribers(string $channelId) : array{ - return $this->broadcastSubscribers[$channelId] ?? []; + return $this->getBroadcastChannel($channelId)?->getPermittedSubscribers() ?? []; } /** - * @param ChatBroadcastSubscriber[]|null $recipients + * @param MessageChannelSubscriber[]|null $recipients */ - public function broadcastMessage(Translatable|string $message, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS); + public function broadcastMessage(string $channelId, Translatable|string $message, CommandSender $cause, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getPermittedSubscribers() ?? []; foreach($recipients as $recipient){ - $recipient->onBroadcast(self::BROADCAST_CHANNEL_USERS, $message); + $recipient->onMessage($channelId, $cause, $message); } return count($recipients); } /** - * @return Player[] - */ - private function getPlayerBroadcastSubscribers(string $channelId) : array{ - /** @var Player[] $players */ - $players = []; - foreach($this->broadcastSubscribers[$channelId] as $subscriber){ - if($subscriber instanceof Player){ - $players[spl_object_id($subscriber)] = $subscriber; - } - } - return $players; - } - - /** - * @param Player[]|null $recipients + * @param MinecraftMessageChannelSubscriber[]|null $recipients */ - public function broadcastTip(string $tip, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS); + public function broadcastTip(string $channelId, string $tip, CommandSender $source, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; foreach($recipients as $recipient){ - $recipient->sendTip($tip); + $recipient->onTip($channelId, $source, $tip); } return count($recipients); } /** - * @param Player[]|null $recipients + * @param MinecraftMessageChannelSubscriber[]|null $recipients */ - public function broadcastPopup(string $popup, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS); + public function broadcastPopup(string $channelId, string $popup, CommandSender $source, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; foreach($recipients as $recipient){ - $recipient->sendPopup($popup); + $recipient->onPopup($channelId, $source, $popup); } return count($recipients); } /** - * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. - * @param int $stay Duration in ticks to stay on screen for - * @param int $fadeOut Duration in ticks for fade-out. - * @param Player[]|null $recipients + * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. + * @param int $stay Duration in ticks to stay on screen for + * @param int $fadeOut Duration in ticks for fade-out. + * @param MinecraftMessageChannelSubscriber[]|null $recipients */ - public function broadcastTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS); + public function broadcastTitle(string $channelId, string $title, CommandSender $source, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; foreach($recipients as $recipient){ - $recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); + $recipient->onTitle($channelId, $source, $title, $subtitle, $fadeIn, $stay, $fadeOut); } return count($recipients); diff --git a/src/command/Command.php b/src/command/Command.php index 4560c0b9cc7..4c17c1107a9 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -230,10 +230,7 @@ public static function broadcastCommandMessage(CommandSender $source, Translatab } foreach($subscribers as $user){ - //TODO: this check is flaky, since the sender might have broadcast subscribers that we don't know about - if($user !== $source){ - $user->onBroadcast(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $broadcast); - } + $user->onMessage(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $source, $broadcast); } } diff --git a/src/command/defaults/MeCommand.php b/src/command/defaults/MeCommand.php index a6708840c3d..1bffefe2bcb 100644 --- a/src/command/defaults/MeCommand.php +++ b/src/command/defaults/MeCommand.php @@ -28,6 +28,7 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; +use pocketmine\Server; use pocketmine\utils\TextFormat; use function count; use function implode; @@ -48,7 +49,11 @@ public function execute(CommandSender $sender, string $commandLabel, array $args throw new InvalidCommandSyntaxException(); } - $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args))); + $sender->getServer()->broadcastMessage( + Server::BROADCAST_CHANNEL_CHAT, + KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args)), + $sender + ); return true; } diff --git a/src/command/defaults/SayCommand.php b/src/command/defaults/SayCommand.php index 5c3203b5f07..9ffee62ebe4 100644 --- a/src/command/defaults/SayCommand.php +++ b/src/command/defaults/SayCommand.php @@ -29,6 +29,7 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; +use pocketmine\Server; use pocketmine\utils\TextFormat; use function count; use function implode; @@ -49,10 +50,14 @@ public function execute(CommandSender $sender, string $commandLabel, array $args throw new InvalidCommandSyntaxException(); } - $sender->getServer()->broadcastMessage(KnownTranslationFactory::chat_type_announcement( - $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), - implode(" ", $args) - )->prefix(TextFormat::LIGHT_PURPLE)); + $sender->getServer()->broadcastMessage( + Server::BROADCAST_CHANNEL_CHAT, + KnownTranslationFactory::chat_type_announcement( + $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), + implode(" ", $args) + )->prefix(TextFormat::LIGHT_PURPLE), + $sender + ); return true; } } diff --git a/src/event/player/PlayerChatEvent.php b/src/event/player/PlayerChatEvent.php index 80737fe2f4c..2999149ee65 100644 --- a/src/event/player/PlayerChatEvent.php +++ b/src/event/player/PlayerChatEvent.php @@ -23,9 +23,9 @@ namespace pocketmine\event\player; -use pocketmine\ChatBroadcastSubscriber; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\MessageChannelSubscriber; use pocketmine\player\chat\ChatFormatter; use pocketmine\player\Player; use pocketmine\utils\Utils; @@ -37,7 +37,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; /** - * @param ChatBroadcastSubscriber[] $recipients + * @param MessageChannelSubscriber[] $recipients */ public function __construct( Player $player, @@ -72,17 +72,17 @@ public function setFormatter(ChatFormatter $formatter) : void{ } /** - * @return ChatBroadcastSubscriber[] + * @return MessageChannelSubscriber[] */ public function getRecipients() : array{ return $this->recipients; } /** - * @param ChatBroadcastSubscriber[] $recipients + * @param MessageChannelSubscriber[] $recipients */ public function setRecipients(array $recipients) : void{ - Utils::validateArrayValueType($recipients, function(ChatBroadcastSubscriber $_) : void{}); + Utils::validateArrayValueType($recipients, function(MessageChannelSubscriber $_) : void{}); $this->recipients = $recipients; } } diff --git a/src/player/Player.php b/src/player/Player.php index 1c6522e2174..305ba50cca0 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -29,7 +29,6 @@ use pocketmine\block\BlockTypeTags; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; -use pocketmine\ChatBroadcastSubscriber; use pocketmine\command\CommandSender; use pocketmine\crafting\CraftingGrid; use pocketmine\data\java\GameModeIdMap; @@ -105,6 +104,7 @@ use pocketmine\lang\Language; use pocketmine\lang\Translatable; use pocketmine\math\Vector3; +use pocketmine\MinecraftMessageChannelSubscriber; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\network\mcpe\NetworkSession; @@ -116,8 +116,8 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags; -use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissions; +use pocketmine\permission\Permissible; use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\player\chat\StandardChatFormatter; @@ -168,7 +168,7 @@ /** * Main class that handles networking, recovery, and packet sending to the server part */ -class Player extends Human implements ChatBroadcastSubscriber, CommandSender, ChunkListener, IPlayer{ +class Player extends Human implements MinecraftMessageChannelSubscriber, CommandSender, ChunkListener, IPlayer{ use PermissibleDelegateTrait; private const MOVES_PER_TICK = 2; @@ -872,19 +872,6 @@ static function() : void{ Timings::$playerChunkSend->stopTiming(); } - private function recheckBroadcastPermissions() : void{ - foreach([ - DefaultPermissionNames::BROADCAST_ADMIN => Server::BROADCAST_CHANNEL_ADMINISTRATIVE, - DefaultPermissionNames::BROADCAST_USER => Server::BROADCAST_CHANNEL_USERS - ] as $permission => $channel){ - if($this->hasPermission($permission)){ - $this->server->subscribeToBroadcastChannel($channel, $this); - }else{ - $this->server->unsubscribeFromBroadcastChannel($channel, $this); - } - } - } - /** * Called by the network system when the pre-spawn sequence is completed (e.g. after sending spawn chunks). * This fires join events and broadcasts join messages to other online players. @@ -894,19 +881,18 @@ public function doFirstSpawn() : void{ return; } $this->spawned = true; - $this->recheckBroadcastPermissions(); - $this->getPermissionRecalculationCallbacks()->add(function(array $changedPermissionsOldValues) : void{ - if(isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_ADMINISTRATIVE]) || isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_USERS])){ - $this->recheckBroadcastPermissions(); - } - }); + + //subscribing will only send us messages if we have the correct permissions + $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); + $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_CHAT, $this); + $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_GAME_EVENTS, $this); $ev = new PlayerJoinEvent($this, KnownTranslationFactory::multiplayer_player_joined($this->getDisplayName())->prefix(TextFormat::YELLOW) ); $ev->call(); if($ev->getJoinMessage() !== ""){ - $this->server->broadcastMessage($ev->getJoinMessage()); + $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $ev->getJoinMessage(), $this); } $this->noDamageTicks = 60; @@ -1528,10 +1514,15 @@ public function chat(string $message) : bool{ $this->server->dispatchCommand($this, substr($messagePart, 1)); Timings::$playerCommand->stopTiming(); }else{ - $ev = new PlayerChatEvent($this, $messagePart, $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_USERS), new StandardChatFormatter()); + $ev = new PlayerChatEvent($this, $messagePart, $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_CHAT), new StandardChatFormatter()); $ev->call(); if(!$ev->isCancelled()){ - $this->server->broadcastMessage($ev->getFormatter()->format($ev->getPlayer()->getDisplayName(), $ev->getMessage()), $ev->getRecipients()); + $this->server->broadcastMessage( + Server::BROADCAST_CHANNEL_CHAT, + $ev->getFormatter()->format($ev->getPlayer()->getDisplayName(), $ev->getMessage()), + $ev->getPlayer(), + $ev->getRecipients() + ); } } } @@ -2131,8 +2122,11 @@ public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut) : void{ } } - public function onBroadcast(string $channelId, Translatable|string $message) : void{ + public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void{ if($channelId === Server::BROADCAST_CHANNEL_ADMINISTRATIVE){ + if($source === $this){ + return; + } if($message instanceof Translatable){ $message = $message->prefix(TextFormat::GRAY . TextFormat::ITALIC); }else{ @@ -2142,6 +2136,24 @@ public function onBroadcast(string $channelId, Translatable|string $message) : v $this->sendMessage($message); } + public function onTip(string $channelId, CommandSender $source, Translatable|string $message) : void{ + //TODO: support translations in the proper plugin methods + $this->sendTip($message instanceof Translatable ? $this->getLanguage()->translate($message) : $message); + } + + public function onPopup(string $channelId, CommandSender $source, Translatable|string $message) : void{ + //TODO: support translations in the proper plugin methods + $this->sendPopup($message instanceof Translatable ? $this->getLanguage()->translate($message) : $message); + } + + public function onTitle(string $channelId, CommandSender $source, string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void{ + $this->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); + } + + public function getPermissible() : ?Permissible{ + return $this; + } + /** * Sends a direct chat message to a player */ @@ -2299,7 +2311,7 @@ public function onPostDisconnect(Translatable|string $reason, Translatable|strin $ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason); $ev->call(); if(($quitMessage = $ev->getQuitMessage()) != ""){ - $this->server->broadcastMessage($quitMessage); + $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $quitMessage, $this); } $this->save(); @@ -2430,7 +2442,7 @@ protected function onDeath() : void{ } if($ev->getDeathMessage() != ""){ - $this->server->broadcastMessage($ev->getDeathMessage()); + $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $ev->getDeathMessage(), $this); } $this->startDeathAnimation(); diff --git a/src/utils/BroadcastLoggerForwarder.php b/src/utils/MessageLoggerForwarder.php similarity index 73% rename from src/utils/BroadcastLoggerForwarder.php rename to src/utils/MessageLoggerForwarder.php index b8d47a6041d..21fea631f88 100644 --- a/src/utils/BroadcastLoggerForwarder.php +++ b/src/utils/MessageLoggerForwarder.php @@ -23,26 +23,32 @@ namespace pocketmine\utils; -use pocketmine\ChatBroadcastSubscriber; +use pocketmine\command\CommandSender; use pocketmine\lang\Language; use pocketmine\lang\Translatable; +use pocketmine\MessageChannelSubscriber; +use pocketmine\permission\Permissible; /** * Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and * command audit log messages to the server log file. */ -final class BroadcastLoggerForwarder implements ChatBroadcastSubscriber{ +final class MessageLoggerForwarder implements MessageChannelSubscriber{ public function __construct( private \Logger $logger, private Language $language ){} - public function onBroadcast(string $channelId, Translatable|string $message) : void{ + public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void{ if($message instanceof Translatable){ $this->logger->info($this->language->translate($message)); }else{ $this->logger->info($message); } } + + public function getPermissible() : ?Permissible{ + return null; //we don't care about channel permissions - we just want to receive everything + } } From e994606d50f2ecaf0f7c0701a1d3a1bdeb0e0176 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Nov 2024 22:35:33 +0000 Subject: [PATCH 4/6] tidy & remove non-essential changes --- ...ber.php => MessageBroadcastSubscriber.php} | 11 +- src/MessageChannel.php | 145 ------------------ ...> MinecraftMessageBroadcastSubscriber.php} | 8 +- src/Server.php | 111 ++++++++------ src/command/Command.php | 5 +- src/command/defaults/MeCommand.php | 6 +- src/command/defaults/SayCommand.php | 6 +- src/event/player/PlayerChatEvent.php | 10 +- src/player/Player.php | 53 ++++--- ...arder.php => BroadcastLoggerForwarder.php} | 11 +- 10 files changed, 117 insertions(+), 249 deletions(-) rename src/{MessageChannelSubscriber.php => MessageBroadcastSubscriber.php} (70%) delete mode 100644 src/MessageChannel.php rename src/{MinecraftMessageChannelSubscriber.php => MinecraftMessageBroadcastSubscriber.php} (64%) rename src/utils/{MessageLoggerForwarder.php => BroadcastLoggerForwarder.php} (76%) diff --git a/src/MessageChannelSubscriber.php b/src/MessageBroadcastSubscriber.php similarity index 70% rename from src/MessageChannelSubscriber.php rename to src/MessageBroadcastSubscriber.php index a321d1393d2..11f9f38a4da 100644 --- a/src/MessageChannelSubscriber.php +++ b/src/MessageBroadcastSubscriber.php @@ -25,23 +25,16 @@ use pocketmine\command\CommandSender; use pocketmine\lang\Translatable; -use pocketmine\permission\Permissible; /** * This interface can be implemented in order to receive messages from the server's global broadcast channels. */ -interface MessageChannelSubscriber{ +interface MessageBroadcastSubscriber{ /** * Called when a message is broadcasted on any channel that this receiver is subscribed to. * * @see Server::subscribeToBroadcastChannel() */ - public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void; - - /** - * Used to check if the subscriber is allowed to receive messages from channels with permission restrictions. - * If this function returns null, the subscriber will receive all messages regardless of permissions. - */ - public function getPermissible() : ?Permissible; + public function onMessage(CommandSender $source, Translatable|string $message, string $channelId) : void; } diff --git a/src/MessageChannel.php b/src/MessageChannel.php deleted file mode 100644 index a8149f280eb..00000000000 --- a/src/MessageChannel.php +++ /dev/null @@ -1,145 +0,0 @@ - - */ - private ObjectSet $nonPermittedSubscribers; - - /** - * Only subscribers whose permissions have been checked. This is invalidated whenever a subscriber's permissions are - * updated. - * - * @var MessageChannelSubscriber[]|ObjectSet - * @phpstan-var ObjectSet - */ - private ObjectSet $permittedSubscribers; - - /** - * @var \Closure[] - * @phpstan-var array - */ - private array $permissionCallbacks = []; - - public function __construct( - private ?string $permission - ){ - $this->nonPermittedSubscribers = new ObjectSet(); - $this->permittedSubscribers = new ObjectSet(); - } - - public function getPermission() : ?string{ return $this->permission; } - - public function setPermission(?string $permission) : void{ - $this->permission = $permission; - foreach($this->permittedSubscribers as $subscriber){ - $this->updateSubscriberAccess($subscriber); - } - foreach($this->nonPermittedSubscribers as $subscriber){ - $this->updateSubscriberAccess($subscriber); - } - } - - private function updateSubscriberAccess(MessageChannelSubscriber $subscriber) : void{ - $permissible = $subscriber->getPermissible(); - - //If either the subscriber doesn't support permission restricting, or no permission is set, we send all messages - //to it. Mainly useful for programmatic message collectors like in plugins. - $permitted = $permissible === null || $this->permission === null || $permissible->hasPermission($this->permission); - - [$add, $remove] = $permitted ? - [$this->permittedSubscribers, $this->nonPermittedSubscribers] : - [$this->nonPermittedSubscribers, $this->permittedSubscribers]; - $add->add($subscriber); - $remove->remove($subscriber); - } - - /** - * Subscribes to a particular message broadcast channel. - * The channel ID can be any arbitrary string. - */ - public function subscribe(MessageChannelSubscriber $subscriber) : void{ - $this->updateSubscriberAccess($subscriber); - - $permCallback = $this->permissionCallbacks[spl_object_id($subscriber)] = fn() => $this->updateSubscriberAccess($subscriber); - $subscriber->getPermissible()?->getPermissionRecalculationCallbacks()->add($permCallback); - } - - /** - * Unsubscribes from a particular message broadcast channel. - */ - public function unsubscribe(MessageChannelSubscriber $subscriber) : void{ - $this->nonPermittedSubscribers->remove($subscriber); - $this->permittedSubscribers->remove($subscriber); - - $splObjectId = spl_object_id($subscriber); - $permCallback = $this->permissionCallbacks[$splObjectId] ?? null; - if($permCallback !== null){ - $subscriber->getPermissible()?->getPermissionRecalculationCallbacks()->remove($permCallback); - unset($this->permissionCallbacks[$splObjectId]); - } - } - - /** - * Returns a list of all subscribers with permission to receive messages from this channel. - * - * @return MessageChannelSubscriber[] - * @phpstan-return array - */ - public function getPermittedSubscribers() : array{ - //convert to an array so callers can't mess up the internal state of the channel - return $this->permittedSubscribers->toArray(); - } - - /** - * Returns a list of subscribers who don't have permission to receive messages from this channel. - * If their permissions are updated, they'll automatically start receiving new messages. - * - * @return MessageChannelSubscriber[] - * @phpstan-return array - */ - public function getNonPermittedSubscribers() : array{ - return $this->nonPermittedSubscribers->toArray(); - } - - /** - * Returns a list of all broadcast subscribers with permission to receive messages from this channel, and who can - * receive non-standard message types such as tips, popups and titles. - * - * @return MinecraftMessageChannelSubscriber[] - * @phpstan-return array - */ - public function getMinecraftPermittedSubscribers() : array{ - return array_filter($this->permittedSubscribers->toArray(), fn(MessageChannelSubscriber $s) => $s instanceof MinecraftMessageChannelSubscriber); - } -} diff --git a/src/MinecraftMessageChannelSubscriber.php b/src/MinecraftMessageBroadcastSubscriber.php similarity index 64% rename from src/MinecraftMessageChannelSubscriber.php rename to src/MinecraftMessageBroadcastSubscriber.php index f7f2f642c8f..925ba8eca60 100644 --- a/src/MinecraftMessageChannelSubscriber.php +++ b/src/MinecraftMessageBroadcastSubscriber.php @@ -26,11 +26,11 @@ use pocketmine\command\CommandSender; use pocketmine\lang\Translatable; -interface MinecraftMessageChannelSubscriber extends MessageChannelSubscriber{ +interface MinecraftMessageBroadcastSubscriber extends MessageBroadcastSubscriber{ - public function onTip(string $channelId, CommandSender $source, Translatable|string $message) : void; + public function onTip(CommandSender $source, Translatable|string $message, string $channelId) : void; - public function onPopup(string $channelId, CommandSender $source, Translatable|string $message) : void; + public function onPopup(CommandSender $source, Translatable|string $message, string $channelId) : void; - public function onTitle(string $channelId, CommandSender $source, string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void; + public function onTitle(CommandSender $source, string $title, string $subtitle, int $fadeIn, int $stay, int $fadeOut, string $channelId) : void; } diff --git a/src/Server.php b/src/Server.php index 547738990e4..3621b0574b1 100644 --- a/src/Server.php +++ b/src/Server.php @@ -70,7 +70,6 @@ use pocketmine\network\query\QueryInfo; use pocketmine\network\upnp\UPnPNetworkInterface; use pocketmine\permission\BanList; -use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissions; use pocketmine\player\DatFilePlayerDataProvider; use pocketmine\player\GameMode; @@ -104,7 +103,7 @@ use pocketmine\utils\Filesystem; use pocketmine\utils\Internet; use pocketmine\utils\MainLogger; -use pocketmine\utils\MessageLoggerForwarder; +use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\NotCloneable; use pocketmine\utils\NotSerializable; use pocketmine\utils\Process; @@ -157,6 +156,7 @@ use function rename; use function round; use function sleep; +use function spl_object_id; use function sprintf; use function str_repeat; use function str_replace; @@ -181,8 +181,7 @@ class Server{ use NotSerializable; public const BROADCAST_CHANNEL_ADMINISTRATIVE = "pocketmine.broadcast.admin"; - public const BROADCAST_CHANNEL_CHAT = "pocketmine.broadcast.chat"; - public const BROADCAST_CHANNEL_GAME_EVENTS = "pocketmine.broadcast.gameEvents"; + public const BROADCAST_CHANNEL_USERS = "pocketmine.broadcast.user"; public const DEFAULT_SERVER_NAME = VersionInfo::NAME . " Server"; public const DEFAULT_MAX_PLAYERS = 20; @@ -299,10 +298,10 @@ class Server{ private SignalHandler $signalHandler; /** - * @var MessageChannel[] - * @phpstan-var array + * @var MessageBroadcastSubscriber[][] + * @phpstan-var array> */ - private array $broadcastChannels = []; + private array $broadcastSubscribers = []; public function getName() : string{ return VersionInfo::NAME; @@ -978,12 +977,6 @@ public function __construct( $this->resourceManager = new ResourcePackManager(Path::join($this->dataPath, "resource_packs"), $this->logger); - $this->broadcastChannels = [ - self::BROADCAST_CHANNEL_ADMINISTRATIVE => new MessageChannel(DefaultPermissionNames::BROADCAST_ADMIN), - self::BROADCAST_CHANNEL_CHAT => new MessageChannel(DefaultPermissionNames::BROADCAST_USER), - self::BROADCAST_CHANNEL_GAME_EVENTS => new MessageChannel(DefaultPermissionNames::BROADCAST_USER), - ]; - $pluginGraylist = null; $graylistFile = Path::join($this->dataPath, "plugin_list.yml"); if(!file_exists($graylistFile)){ @@ -1063,10 +1056,10 @@ public function __construct( $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_donate(TextFormat::AQUA . "https://patreon.com/pocketminemp" . TextFormat::RESET))); $this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_startFinished(strval(round(microtime(true) - $this->startTime, 3))))); - $forwarder = new MessageLoggerForwarder($this->logger, $this->language); + $forwarder = new BroadcastLoggerForwarder($this->logger, $this->language); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_CHAT, $forwarder); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_GAME_EVENTS, $forwarder); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); + $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); //TODO: move console parts to a separate component if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){ @@ -1270,24 +1263,29 @@ public function createBroadcastChannel(string $channelId, ?string $permission) : * Subscribes to a particular message broadcast channel. * The channel ID can be any arbitrary string. */ - public function subscribeToBroadcastChannel(string $channelId, MessageChannelSubscriber $subscriber) : void{ - $channel = $this->getBroadcastChannel($channelId) ?? throw new \InvalidArgumentException("Channel \"$channelId\" does not exist"); - $channel->subscribe($subscriber); + public function subscribeToBroadcastChannel(string $channelId, MessageBroadcastSubscriber $subscriber) : void{ + $this->broadcastSubscribers[$channelId][spl_object_id($subscriber)] = $subscriber; } /** * Unsubscribes from a particular message broadcast channel. */ - public function unsubscribeFromBroadcastChannel(string $channelId, MessageChannelSubscriber $subscriber) : void{ - $this->getBroadcastChannel($channelId)?->unsubscribe($subscriber); + public function unsubscribeFromBroadcastChannel(string $channelId, MessageBroadcastSubscriber $subscriber) : void{ + if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){ + if(count($this->broadcastSubscribers[$channelId]) === 1){ + unset($this->broadcastSubscribers[$channelId]); + }else{ + unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]); + } + } } /** * Unsubscribes from all broadcast channels. */ - public function unsubscribeFromAllBroadcastChannels(MessageChannelSubscriber $subscriber) : void{ - foreach(Utils::stringifyKeys($this->broadcastChannels) as $channelId => $channel){ - $channel->unsubscribe($subscriber); + public function unsubscribeFromAllBroadcastChannels(MessageBroadcastSubscriber $subscriber) : void{ + foreach(Utils::stringifyKeys($this->broadcastSubscribers) as $channelId => $recipients){ + $this->unsubscribeFromBroadcastChannel($channelId, $subscriber); } } @@ -1295,63 +1293,86 @@ public function unsubscribeFromAllBroadcastChannels(MessageChannelSubscriber $su * Returns a list of all the broadcast subscribers subscribed to the given broadcast channel with permission to * receive messages from it. * - * @return MessageChannelSubscriber[] - * @phpstan-return array + * @return MessageBroadcastSubscriber[] + * @phpstan-return array */ public function getBroadcastChannelSubscribers(string $channelId) : array{ - return $this->getBroadcastChannel($channelId)?->getPermittedSubscribers() ?? []; + return $this->broadcastSubscribers[$channelId] ?? []; } /** - * @param MessageChannelSubscriber[]|null $recipients + * @param MessageBroadcastSubscriber[]|null $recipients */ - public function broadcastMessage(string $channelId, Translatable|string $message, CommandSender $cause, ?array $recipients = null) : int{ + public function broadcastMessage(CommandSender $source, Translatable|string $message, string $channelId = self::BROADCAST_CHANNEL_USERS, ?array $recipients = null) : int{ $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getPermittedSubscribers() ?? []; foreach($recipients as $recipient){ - $recipient->onMessage($channelId, $cause, $message); + $recipient->onMessage($source, $message, $channelId); } return count($recipients); } /** - * @param MinecraftMessageChannelSubscriber[]|null $recipients + * @return MinecraftMessageBroadcastSubscriber[] + */ + private function getMinecraftBroadcastSubscribers(string $channelId) : array{ + /** @var Player[] $players */ + $players = []; + foreach($this->broadcastSubscribers[$channelId] ?? [] as $subscriber){ + if($subscriber instanceof MinecraftMessageBroadcastSubscriber){ + $players[spl_object_id($subscriber)] = $subscriber; + } + } + return $players; + } + + /** + * @param MinecraftMessageBroadcastSubscriber[]|null $recipients */ - public function broadcastTip(string $channelId, string $tip, CommandSender $source, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; + public function broadcastTip(CommandSender $source, string $tip, string $channelId = self::BROADCAST_CHANNEL_USERS, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getMinecraftBroadcastSubscribers($channelId); foreach($recipients as $recipient){ - $recipient->onTip($channelId, $source, $tip); + $recipient->onTip($source, $tip, $channelId); } return count($recipients); } /** - * @param MinecraftMessageChannelSubscriber[]|null $recipients + * @param MinecraftMessageBroadcastSubscriber[]|null $recipients */ - public function broadcastPopup(string $channelId, string $popup, CommandSender $source, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; + public function broadcastPopup(CommandSender $source, string $popup, string $channelId = self::BROADCAST_CHANNEL_USERS, ?array $recipients = null) : int{ + $recipients = $recipients ?? $this->getMinecraftBroadcastSubscribers($channelId); foreach($recipients as $recipient){ - $recipient->onPopup($channelId, $source, $popup); + $recipient->onPopup($source, $popup, $channelId); } return count($recipients); } /** - * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. - * @param int $stay Duration in ticks to stay on screen for - * @param int $fadeOut Duration in ticks for fade-out. - * @param MinecraftMessageChannelSubscriber[]|null $recipients + * @param int $fadeIn Duration in ticks for fade-in. If -1 is given, client-sided defaults will be used. + * @param int $stay Duration in ticks to stay on screen for + * @param int $fadeOut Duration in ticks for fade-out. + * @param MinecraftMessageBroadcastSubscriber[]|null $recipients */ - public function broadcastTitle(string $channelId, string $title, CommandSender $source, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getMinecraftPermittedSubscribers() ?? []; + public function broadcastTitle( + CommandSender $source, + string $title, + string $subtitle = "", + int $fadeIn = -1, + int $stay = -1, + int $fadeOut = -1, + string $channelId = self::BROADCAST_CHANNEL_USERS, + ?array $recipients = null + ) : int{ + $recipients = $recipients ?? $this->getMinecraftBroadcastSubscribers($channelId); foreach($recipients as $recipient){ - $recipient->onTitle($channelId, $source, $title, $subtitle, $fadeIn, $stay, $fadeOut); + $recipient->onTitle($source, $title, $subtitle, $fadeIn, $stay, $fadeOut, $channelId); } return count($recipients); diff --git a/src/command/Command.php b/src/command/Command.php index 4c17c1107a9..7f13c986ee4 100644 --- a/src/command/Command.php +++ b/src/command/Command.php @@ -222,16 +222,13 @@ public function setUsage(Translatable|string $usage) : void{ } public static function broadcastCommandMessage(CommandSender $source, Translatable|string $message, bool $sendToSource = true) : void{ - $subscribers = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); $broadcast = KnownTranslationFactory::chat_type_admin($source->getName(), $message); if($sendToSource){ $source->sendMessage($message); } - foreach($subscribers as $user){ - $user->onMessage(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $source, $broadcast); - } + $source->getServer()->broadcastMessage($source, $broadcast, Server::BROADCAST_CHANNEL_ADMINISTRATIVE); } public function __toString() : string{ diff --git a/src/command/defaults/MeCommand.php b/src/command/defaults/MeCommand.php index 1bffefe2bcb..321f5bc90dc 100644 --- a/src/command/defaults/MeCommand.php +++ b/src/command/defaults/MeCommand.php @@ -28,7 +28,6 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; -use pocketmine\Server; use pocketmine\utils\TextFormat; use function count; use function implode; @@ -50,9 +49,8 @@ public function execute(CommandSender $sender, string $commandLabel, array $args } $sender->getServer()->broadcastMessage( - Server::BROADCAST_CHANNEL_CHAT, - KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args)), - $sender + $sender, + KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args)) ); return true; diff --git a/src/command/defaults/SayCommand.php b/src/command/defaults/SayCommand.php index 9ffee62ebe4..379d98dab07 100644 --- a/src/command/defaults/SayCommand.php +++ b/src/command/defaults/SayCommand.php @@ -29,7 +29,6 @@ use pocketmine\lang\KnownTranslationFactory; use pocketmine\permission\DefaultPermissionNames; use pocketmine\player\Player; -use pocketmine\Server; use pocketmine\utils\TextFormat; use function count; use function implode; @@ -51,12 +50,11 @@ public function execute(CommandSender $sender, string $commandLabel, array $args } $sender->getServer()->broadcastMessage( - Server::BROADCAST_CHANNEL_CHAT, + $sender, KnownTranslationFactory::chat_type_announcement( $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), implode(" ", $args) - )->prefix(TextFormat::LIGHT_PURPLE), - $sender + )->prefix(TextFormat::LIGHT_PURPLE) ); return true; } diff --git a/src/event/player/PlayerChatEvent.php b/src/event/player/PlayerChatEvent.php index 2999149ee65..6658dd25908 100644 --- a/src/event/player/PlayerChatEvent.php +++ b/src/event/player/PlayerChatEvent.php @@ -25,7 +25,7 @@ use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; -use pocketmine\MessageChannelSubscriber; +use pocketmine\MessageBroadcastSubscriber; use pocketmine\player\chat\ChatFormatter; use pocketmine\player\Player; use pocketmine\utils\Utils; @@ -37,7 +37,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; /** - * @param MessageChannelSubscriber[] $recipients + * @param MessageBroadcastSubscriber[] $recipients */ public function __construct( Player $player, @@ -72,17 +72,17 @@ public function setFormatter(ChatFormatter $formatter) : void{ } /** - * @return MessageChannelSubscriber[] + * @return MessageBroadcastSubscriber[] */ public function getRecipients() : array{ return $this->recipients; } /** - * @param MessageChannelSubscriber[] $recipients + * @param MessageBroadcastSubscriber[] $recipients */ public function setRecipients(array $recipients) : void{ - Utils::validateArrayValueType($recipients, function(MessageChannelSubscriber $_) : void{}); + Utils::validateArrayValueType($recipients, function(MessageBroadcastSubscriber $_) : void{}); $this->recipients = $recipients; } } diff --git a/src/player/Player.php b/src/player/Player.php index 305ba50cca0..597e465786a 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -104,7 +104,7 @@ use pocketmine\lang\Language; use pocketmine\lang\Translatable; use pocketmine\math\Vector3; -use pocketmine\MinecraftMessageChannelSubscriber; +use pocketmine\MinecraftMessageBroadcastSubscriber; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\network\mcpe\NetworkSession; @@ -116,6 +116,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags; +use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissions; use pocketmine\permission\Permissible; use pocketmine\permission\PermissibleBase; @@ -168,7 +169,7 @@ /** * Main class that handles networking, recovery, and packet sending to the server part */ -class Player extends Human implements MinecraftMessageChannelSubscriber, CommandSender, ChunkListener, IPlayer{ +class Player extends Human implements MinecraftMessageBroadcastSubscriber, CommandSender, ChunkListener, IPlayer{ use PermissibleDelegateTrait; private const MOVES_PER_TICK = 2; @@ -872,6 +873,19 @@ static function() : void{ Timings::$playerChunkSend->stopTiming(); } + private function recheckBroadcastPermissions() : void{ + foreach([ + DefaultPermissionNames::BROADCAST_ADMIN => Server::BROADCAST_CHANNEL_ADMINISTRATIVE, + DefaultPermissionNames::BROADCAST_USER => Server::BROADCAST_CHANNEL_USERS + ] as $permission => $channel){ + if($this->hasPermission($permission)){ + $this->server->subscribeToBroadcastChannel($channel, $this); + }else{ + $this->server->unsubscribeFromBroadcastChannel($channel, $this); + } + } + } + /** * Called by the network system when the pre-spawn sequence is completed (e.g. after sending spawn chunks). * This fires join events and broadcasts join messages to other online players. @@ -882,17 +896,19 @@ public function doFirstSpawn() : void{ } $this->spawned = true; - //subscribing will only send us messages if we have the correct permissions - $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); - $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_CHAT, $this); - $this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_GAME_EVENTS, $this); + $this->recheckBroadcastPermissions(); + $this->getPermissionRecalculationCallbacks()->add(function(array $changedPermissionsOldValues) : void{ + if(isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_ADMINISTRATIVE]) || isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_USERS])){ + $this->recheckBroadcastPermissions(); + } + }); $ev = new PlayerJoinEvent($this, KnownTranslationFactory::multiplayer_player_joined($this->getDisplayName())->prefix(TextFormat::YELLOW) ); $ev->call(); if($ev->getJoinMessage() !== ""){ - $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $ev->getJoinMessage(), $this); + $this->server->broadcastMessage($this, $ev->getJoinMessage()); } $this->noDamageTicks = 60; @@ -1514,14 +1530,13 @@ public function chat(string $message) : bool{ $this->server->dispatchCommand($this, substr($messagePart, 1)); Timings::$playerCommand->stopTiming(); }else{ - $ev = new PlayerChatEvent($this, $messagePart, $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_CHAT), new StandardChatFormatter()); + $ev = new PlayerChatEvent($this, $messagePart, $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_USERS), new StandardChatFormatter()); $ev->call(); if(!$ev->isCancelled()){ $this->server->broadcastMessage( - Server::BROADCAST_CHANNEL_CHAT, - $ev->getFormatter()->format($ev->getPlayer()->getDisplayName(), $ev->getMessage()), $ev->getPlayer(), - $ev->getRecipients() + $ev->getFormatter()->format($ev->getPlayer()->getDisplayName(), $ev->getMessage()), + recipients: $ev->getRecipients() ); } } @@ -2122,7 +2137,7 @@ public function setTitleDuration(int $fadeIn, int $stay, int $fadeOut) : void{ } } - public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void{ + public function onMessage(CommandSender $source, Translatable|string $message, string $channelId) : void{ if($channelId === Server::BROADCAST_CHANNEL_ADMINISTRATIVE){ if($source === $this){ return; @@ -2136,24 +2151,20 @@ public function onMessage(string $channelId, CommandSender $source, Translatable $this->sendMessage($message); } - public function onTip(string $channelId, CommandSender $source, Translatable|string $message) : void{ + public function onTip(CommandSender $source, Translatable|string $message, string $channelId) : void{ //TODO: support translations in the proper plugin methods $this->sendTip($message instanceof Translatable ? $this->getLanguage()->translate($message) : $message); } - public function onPopup(string $channelId, CommandSender $source, Translatable|string $message) : void{ + public function onPopup(CommandSender $source, Translatable|string $message, string $channelId) : void{ //TODO: support translations in the proper plugin methods $this->sendPopup($message instanceof Translatable ? $this->getLanguage()->translate($message) : $message); } - public function onTitle(string $channelId, CommandSender $source, string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1) : void{ + public function onTitle(CommandSender $source, string $title, string $subtitle, int $fadeIn, int $stay, int $fadeOut, string $channelId) : void{ $this->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut); } - public function getPermissible() : ?Permissible{ - return $this; - } - /** * Sends a direct chat message to a player */ @@ -2311,7 +2322,7 @@ public function onPostDisconnect(Translatable|string $reason, Translatable|strin $ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason); $ev->call(); if(($quitMessage = $ev->getQuitMessage()) != ""){ - $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $quitMessage, $this); + $this->server->broadcastMessage($this, $quitMessage); } $this->save(); @@ -2442,7 +2453,7 @@ protected function onDeath() : void{ } if($ev->getDeathMessage() != ""){ - $this->server->broadcastMessage(Server::BROADCAST_CHANNEL_GAME_EVENTS, $ev->getDeathMessage(), $this); + $this->server->broadcastMessage($this, $ev->getDeathMessage()); } $this->startDeathAnimation(); diff --git a/src/utils/MessageLoggerForwarder.php b/src/utils/BroadcastLoggerForwarder.php similarity index 76% rename from src/utils/MessageLoggerForwarder.php rename to src/utils/BroadcastLoggerForwarder.php index 21fea631f88..dfc4de696e7 100644 --- a/src/utils/MessageLoggerForwarder.php +++ b/src/utils/BroadcastLoggerForwarder.php @@ -26,29 +26,24 @@ use pocketmine\command\CommandSender; use pocketmine\lang\Language; use pocketmine\lang\Translatable; -use pocketmine\MessageChannelSubscriber; -use pocketmine\permission\Permissible; +use pocketmine\MessageBroadcastSubscriber; /** * Forwards any messages it receives via sendMessage() to the given logger. Used for forwarding chat messages and * command audit log messages to the server log file. */ -final class MessageLoggerForwarder implements MessageChannelSubscriber{ +final class BroadcastLoggerForwarder implements MessageBroadcastSubscriber{ public function __construct( private \Logger $logger, private Language $language ){} - public function onMessage(string $channelId, CommandSender $source, Translatable|string $message) : void{ + public function onMessage(CommandSender $source, Translatable|string $message, string $channelId) : void{ if($message instanceof Translatable){ $this->logger->info($this->language->translate($message)); }else{ $this->logger->info($message); } } - - public function getPermissible() : ?Permissible{ - return null; //we don't care about channel permissions - we just want to receive everything - } } From f0d6d16406907425270c162277bcc968b50f7998 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Nov 2024 22:39:00 +0000 Subject: [PATCH 5/6] tidy --- src/Server.php | 14 +------------- src/player/Player.php | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Server.php b/src/Server.php index 3621b0574b1..47b6548c50f 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1059,7 +1059,6 @@ public function __construct( $forwarder = new BroadcastLoggerForwarder($this->logger, $this->language); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $forwarder); $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); - $this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $forwarder); //TODO: move console parts to a separate component if($this->configGroup->getPropertyBool(Yml::CONSOLE_ENABLE_INPUT, true)){ @@ -1248,17 +1247,6 @@ private function startupPrepareNetworkInterfaces() : bool{ return true; } - private function getBroadcastChannel(string $channelId) : ?MessageChannel{ - return $this->broadcastChannels[$channelId] ?? null; - } - - public function createBroadcastChannel(string $channelId, ?string $permission) : void{ - if(isset($this->broadcastChannels[$channelId])){ - throw new \InvalidArgumentException("Channel \"$channelId\" already exists"); - } - $this->broadcastChannels[$channelId] = new MessageChannel($permission); - } - /** * Subscribes to a particular message broadcast channel. * The channel ID can be any arbitrary string. @@ -1304,7 +1292,7 @@ public function getBroadcastChannelSubscribers(string $channelId) : array{ * @param MessageBroadcastSubscriber[]|null $recipients */ public function broadcastMessage(CommandSender $source, Translatable|string $message, string $channelId = self::BROADCAST_CHANNEL_USERS, ?array $recipients = null) : int{ - $recipients = $recipients ?? $this->getBroadcastChannel($channelId)?->getPermittedSubscribers() ?? []; + $recipients = $recipients ?? $this->getBroadcastChannelSubscribers($channelId); foreach($recipients as $recipient){ $recipient->onMessage($source, $message, $channelId); diff --git a/src/player/Player.php b/src/player/Player.php index 597e465786a..c420f8f44f3 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -895,7 +895,6 @@ public function doFirstSpawn() : void{ return; } $this->spawned = true; - $this->recheckBroadcastPermissions(); $this->getPermissionRecalculationCallbacks()->add(function(array $changedPermissionsOldValues) : void{ if(isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_ADMINISTRATIVE]) || isset($changedPermissionsOldValues[Server::BROADCAST_CHANNEL_USERS])){ From d50044805cf9f9c36cfba3c8e4733550ecd46eb2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Nov 2024 22:40:35 +0000 Subject: [PATCH 6/6] more tidy --- src/Server.php | 2 +- src/command/defaults/MeCommand.php | 5 +---- src/command/defaults/SayCommand.php | 11 ++++------- src/player/Player.php | 1 - 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Server.php b/src/Server.php index 47b6548c50f..123cacebd49 100644 --- a/src/Server.php +++ b/src/Server.php @@ -99,11 +99,11 @@ use pocketmine\timings\TimingsHandler; use pocketmine\updater\UpdateChecker; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\Config; use pocketmine\utils\Filesystem; use pocketmine\utils\Internet; use pocketmine\utils\MainLogger; -use pocketmine\utils\BroadcastLoggerForwarder; use pocketmine\utils\NotCloneable; use pocketmine\utils\NotSerializable; use pocketmine\utils\Process; diff --git a/src/command/defaults/MeCommand.php b/src/command/defaults/MeCommand.php index 321f5bc90dc..de043d109da 100644 --- a/src/command/defaults/MeCommand.php +++ b/src/command/defaults/MeCommand.php @@ -48,10 +48,7 @@ public function execute(CommandSender $sender, string $commandLabel, array $args throw new InvalidCommandSyntaxException(); } - $sender->getServer()->broadcastMessage( - $sender, - KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args)) - ); + $sender->getServer()->broadcastMessage($sender, KnownTranslationFactory::chat_type_emote($sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::RESET . implode(" ", $args))); return true; } diff --git a/src/command/defaults/SayCommand.php b/src/command/defaults/SayCommand.php index 379d98dab07..d7b5ba1f330 100644 --- a/src/command/defaults/SayCommand.php +++ b/src/command/defaults/SayCommand.php @@ -49,13 +49,10 @@ public function execute(CommandSender $sender, string $commandLabel, array $args throw new InvalidCommandSyntaxException(); } - $sender->getServer()->broadcastMessage( - $sender, - KnownTranslationFactory::chat_type_announcement( - $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), - implode(" ", $args) - )->prefix(TextFormat::LIGHT_PURPLE) - ); + $sender->getServer()->broadcastMessage($sender, KnownTranslationFactory::chat_type_announcement( + $sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), + implode(" ", $args) + )->prefix(TextFormat::LIGHT_PURPLE)); return true; } } diff --git a/src/player/Player.php b/src/player/Player.php index c420f8f44f3..8c3e5263402 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -118,7 +118,6 @@ use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags; use pocketmine\permission\DefaultPermissionNames; use pocketmine\permission\DefaultPermissions; -use pocketmine\permission\Permissible; use pocketmine\permission\PermissibleBase; use pocketmine\permission\PermissibleDelegateTrait; use pocketmine\player\chat\StandardChatFormatter;