mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-07 18:41:47 +00:00
Rewrite message broadcasting system to not depend on PermissionManager subscriptions
relying on permission subscriptions for this was unreliable (a permissible is not always subscribed to a permission even when it does have it), and also difficult to control (for example there have been various bugs in the past where a Player ended up subscribed to broadcast permissions when it didn't expect to be, thanks to permission recalculation happening too early). In addition, we might in the future want to have broadcast receivers which are not permissibles (i.e. a more general interface than CommandSender (why does a broadcast receiver need to also be a command sender, anyway?)), which the permission system wouldn't be suitable for.
This commit is contained in:
parent
ee7fad2271
commit
dd200ca8cd
@ -62,7 +62,6 @@ use pocketmine\network\query\QueryInfo;
|
|||||||
use pocketmine\network\upnp\UPnP;
|
use pocketmine\network\upnp\UPnP;
|
||||||
use pocketmine\permission\BanList;
|
use pocketmine\permission\BanList;
|
||||||
use pocketmine\permission\DefaultPermissions;
|
use pocketmine\permission\DefaultPermissions;
|
||||||
use pocketmine\permission\PermissionManager;
|
|
||||||
use pocketmine\player\GameMode;
|
use pocketmine\player\GameMode;
|
||||||
use pocketmine\player\OfflinePlayer;
|
use pocketmine\player\OfflinePlayer;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
@ -277,6 +276,12 @@ class Server{
|
|||||||
/** @var Player[] */
|
/** @var Player[] */
|
||||||
private $playerList = [];
|
private $playerList = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CommandSender[][]
|
||||||
|
* @phpstan-var array<string, array<int, CommandSender>>
|
||||||
|
*/
|
||||||
|
private $broadcastSubscribers = [];
|
||||||
|
|
||||||
public function getName() : string{
|
public function getName() : string{
|
||||||
return VersionInfo::NAME;
|
return VersionInfo::NAME;
|
||||||
}
|
}
|
||||||
@ -1063,8 +1068,8 @@ class Server{
|
|||||||
|
|
||||||
//TODO: move console parts to a separate component
|
//TODO: move console parts to a separate component
|
||||||
$consoleSender = new ConsoleCommandSender($this, $this->language);
|
$consoleSender = new ConsoleCommandSender($this, $this->language);
|
||||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
||||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $consoleSender);
|
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender);
|
||||||
|
|
||||||
$consoleNotifier = new SleeperNotifier();
|
$consoleNotifier = new SleeperNotifier();
|
||||||
$this->console = new CommandReader($consoleNotifier);
|
$this->console = new CommandReader($consoleNotifier);
|
||||||
@ -1084,19 +1089,51 @@ class Server{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to a particular message broadcast channel.
|
||||||
|
* The channel ID can be any arbitrary string.
|
||||||
|
*/
|
||||||
|
public function subscribeToBroadcastChannel(string $channelId, CommandSender $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{
|
||||||
|
if(isset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)])){
|
||||||
|
unset($this->broadcastSubscribers[$channelId][spl_object_id($subscriber)]);
|
||||||
|
if(count($this->broadcastSubscribers[$channelId]) === 0){
|
||||||
|
unset($this->broadcastSubscribers[$channelId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribes from all broadcast channels.
|
||||||
|
*/
|
||||||
|
public function unsubscribeFromAllBroadcastChannels(CommandSender $subscriber) : void{
|
||||||
|
foreach($this->broadcastSubscribers as $channelId => $recipients){
|
||||||
|
$this->unsubscribeFromBroadcastChannel($channelId, $subscriber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all the CommandSenders subscribed to the given broadcast channel.
|
||||||
|
*
|
||||||
|
* @return CommandSender[]
|
||||||
|
* @phpstan-return array<int, CommandSender>
|
||||||
|
*/
|
||||||
|
public function getBroadcastChannelSubscribers(string $channelId) : array{
|
||||||
|
return $this->broadcastSubscribers[$channelId] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TranslationContainer|string $message
|
* @param TranslationContainer|string $message
|
||||||
* @param CommandSender[]|null $recipients
|
* @param CommandSender[]|null $recipients
|
||||||
*/
|
*/
|
||||||
public function broadcastMessage($message, ?array $recipients = null) : int{
|
public function broadcastMessage($message, ?array $recipients = null) : int{
|
||||||
if(!is_array($recipients)){
|
$recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||||
$recipients = [];
|
|
||||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){
|
|
||||||
if($permissible instanceof CommandSender and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){
|
|
||||||
$recipients[spl_object_id($permissible)] = $permissible; // do not send messages directly, or some might be repeated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($recipients as $recipient){
|
foreach($recipients as $recipient){
|
||||||
$recipient->sendMessage($message);
|
$recipient->sendMessage($message);
|
||||||
@ -1108,12 +1145,12 @@ class Server{
|
|||||||
/**
|
/**
|
||||||
* @return Player[]
|
* @return Player[]
|
||||||
*/
|
*/
|
||||||
private function selectPermittedPlayers(string $permission) : array{
|
private function getPlayerBroadcastSubscribers(string $channelId) : array{
|
||||||
/** @var Player[] $players */
|
/** @var Player[] $players */
|
||||||
$players = [];
|
$players = [];
|
||||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions($permission) as $permissible){
|
foreach($this->broadcastSubscribers[$channelId] as $subscriber){
|
||||||
if($permissible instanceof Player and $permissible->hasPermission($permission)){
|
if($subscriber instanceof Player){
|
||||||
$players[spl_object_id($permissible)] = $permissible; //prevent duplication
|
$players[spl_object_id($subscriber)] = $subscriber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $players;
|
return $players;
|
||||||
@ -1123,7 +1160,7 @@ class Server{
|
|||||||
* @param Player[]|null $recipients
|
* @param Player[]|null $recipients
|
||||||
*/
|
*/
|
||||||
public function broadcastTip(string $tip, ?array $recipients = null) : int{
|
public function broadcastTip(string $tip, ?array $recipients = null) : int{
|
||||||
$recipients = $recipients ?? $this->selectPermittedPlayers(self::BROADCAST_CHANNEL_USERS);
|
$recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||||
|
|
||||||
foreach($recipients as $recipient){
|
foreach($recipients as $recipient){
|
||||||
$recipient->sendTip($tip);
|
$recipient->sendTip($tip);
|
||||||
@ -1136,7 +1173,7 @@ class Server{
|
|||||||
* @param Player[]|null $recipients
|
* @param Player[]|null $recipients
|
||||||
*/
|
*/
|
||||||
public function broadcastPopup(string $popup, ?array $recipients = null) : int{
|
public function broadcastPopup(string $popup, ?array $recipients = null) : int{
|
||||||
$recipients = $recipients ?? $this->selectPermittedPlayers(self::BROADCAST_CHANNEL_USERS);
|
$recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||||
|
|
||||||
foreach($recipients as $recipient){
|
foreach($recipients as $recipient){
|
||||||
$recipient->sendPopup($popup);
|
$recipient->sendPopup($popup);
|
||||||
@ -1152,7 +1189,7 @@ class Server{
|
|||||||
* @param Player[]|null $recipients
|
* @param Player[]|null $recipients
|
||||||
*/
|
*/
|
||||||
public function broadcastTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{
|
public function broadcastTitle(string $title, string $subtitle = "", int $fadeIn = -1, int $stay = -1, int $fadeOut = -1, ?array $recipients = null) : int{
|
||||||
$recipients = $recipients ?? $this->selectPermittedPlayers(self::BROADCAST_CHANNEL_USERS);
|
$recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||||
|
|
||||||
foreach($recipients as $recipient){
|
foreach($recipients as $recipient){
|
||||||
$recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
$recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
||||||
|
@ -234,7 +234,7 @@ abstract class Command{
|
|||||||
* @param TranslationContainer|string $message
|
* @param TranslationContainer|string $message
|
||||||
*/
|
*/
|
||||||
public static function broadcastCommandMessage(CommandSender $source, $message, bool $sendToSource = true) : void{
|
public static function broadcastCommandMessage(CommandSender $source, $message, bool $sendToSource = true) : void{
|
||||||
$users = PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
$users = $source->getServer()->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_ADMINISTRATIVE);
|
||||||
if($message instanceof TranslationContainer){
|
if($message instanceof TranslationContainer){
|
||||||
$formatted = "[" . $source->getName() . ": " . ($source->getLanguage()->get($message->getText()) !== $message->getText() ? "%" : "") . $message->getText() . "]";
|
$formatted = "[" . $source->getName() . ": " . ($source->getLanguage()->get($message->getText()) !== $message->getText() ? "%" : "") . $message->getText() . "]";
|
||||||
|
|
||||||
@ -250,7 +250,6 @@ abstract class Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach($users as $user){
|
foreach($users as $user){
|
||||||
if($user instanceof CommandSender){
|
|
||||||
if($user instanceof ConsoleCommandSender){
|
if($user instanceof ConsoleCommandSender){
|
||||||
$user->sendMessage($result);
|
$user->sendMessage($result);
|
||||||
}elseif($user !== $source){
|
}elseif($user !== $source){
|
||||||
@ -258,7 +257,6 @@ abstract class Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string{
|
public function __toString() : string{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
|
@ -26,11 +26,8 @@ namespace pocketmine\event\player;
|
|||||||
use pocketmine\command\CommandSender;
|
use pocketmine\command\CommandSender;
|
||||||
use pocketmine\event\Cancellable;
|
use pocketmine\event\Cancellable;
|
||||||
use pocketmine\event\CancellableTrait;
|
use pocketmine\event\CancellableTrait;
|
||||||
use pocketmine\permission\PermissionManager;
|
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\Server;
|
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
use function spl_object_id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a player chats something
|
* Called when a player chats something
|
||||||
@ -50,22 +47,14 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
|||||||
/**
|
/**
|
||||||
* @param CommandSender[] $recipients
|
* @param CommandSender[] $recipients
|
||||||
*/
|
*/
|
||||||
public function __construct(Player $player, string $message, string $format = "chat.type.text", ?array $recipients = null){
|
public function __construct(Player $player, string $message, array $recipients, string $format = "chat.type.text"){
|
||||||
$this->player = $player;
|
$this->player = $player;
|
||||||
$this->message = $message;
|
$this->message = $message;
|
||||||
|
|
||||||
$this->format = $format;
|
$this->format = $format;
|
||||||
|
|
||||||
if($recipients === null){
|
|
||||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS) as $permissible){
|
|
||||||
if($permissible instanceof CommandSender){
|
|
||||||
$this->recipients[spl_object_id($permissible)] = $permissible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
$this->recipients = $recipients;
|
$this->recipients = $recipients;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage() : string{
|
public function getMessage() : string{
|
||||||
return $this->message;
|
return $this->message;
|
||||||
|
@ -97,7 +97,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
|||||||
use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags;
|
use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags;
|
||||||
use pocketmine\permission\PermissibleBase;
|
use pocketmine\permission\PermissibleBase;
|
||||||
use pocketmine\permission\PermissibleDelegateTrait;
|
use pocketmine\permission\PermissibleDelegateTrait;
|
||||||
use pocketmine\permission\PermissionManager;
|
|
||||||
use pocketmine\Server;
|
use pocketmine\Server;
|
||||||
use pocketmine\timings\Timings;
|
use pocketmine\timings\Timings;
|
||||||
use pocketmine\utils\TextFormat;
|
use pocketmine\utils\TextFormat;
|
||||||
@ -524,9 +523,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function recalculatePermissions() : void{
|
public function recalculatePermissions() : void{
|
||||||
$permManager = PermissionManager::getInstance();
|
$this->server->unsubscribeFromBroadcastChannel(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||||
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
$this->server->unsubscribeFromBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||||
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
|
||||||
|
|
||||||
if($this->perm === null){
|
if($this->perm === null){
|
||||||
return;
|
return;
|
||||||
@ -538,10 +536,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
|
|
||||||
if($this->spawned){
|
if($this->spawned){
|
||||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||||
}
|
}
|
||||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
||||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->networkSession->syncAvailableCommands();
|
$this->networkSession->syncAvailableCommands();
|
||||||
@ -781,10 +779,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
}
|
}
|
||||||
$this->spawned = true;
|
$this->spawned = true;
|
||||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||||
}
|
}
|
||||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
||||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ev = new PlayerJoinEvent($this,
|
$ev = new PlayerJoinEvent($this,
|
||||||
@ -1363,7 +1361,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
$this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1));
|
$this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1));
|
||||||
Timings::$playerCommandTimer->stopTiming();
|
Timings::$playerCommandTimer->stopTiming();
|
||||||
}else{
|
}else{
|
||||||
$ev = new PlayerChatEvent($this, $ev->getMessage());
|
$ev = new PlayerChatEvent($this, $ev->getMessage(), $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_USERS));
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if(!$ev->isCancelled()){
|
if(!$ev->isCancelled()){
|
||||||
$this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients());
|
$this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients());
|
||||||
@ -1972,8 +1970,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//prevent the player receiving their own disconnect message
|
//prevent the player receiving their own disconnect message
|
||||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
$this->server->unsubscribeFromAllBroadcastChannels($this);
|
||||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
|
||||||
|
|
||||||
$ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason);
|
$ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason);
|
||||||
$ev->call();
|
$ev->call();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user