mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +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\permission\BanList;
|
||||
use pocketmine\permission\DefaultPermissions;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\player\GameMode;
|
||||
use pocketmine\player\OfflinePlayer;
|
||||
use pocketmine\player\Player;
|
||||
@ -277,6 +276,12 @@ class Server{
|
||||
/** @var Player[] */
|
||||
private $playerList = [];
|
||||
|
||||
/**
|
||||
* @var CommandSender[][]
|
||||
* @phpstan-var array<string, array<int, CommandSender>>
|
||||
*/
|
||||
private $broadcastSubscribers = [];
|
||||
|
||||
public function getName() : string{
|
||||
return VersionInfo::NAME;
|
||||
}
|
||||
@ -1063,8 +1068,8 @@ class Server{
|
||||
|
||||
//TODO: move console parts to a separate component
|
||||
$consoleSender = new ConsoleCommandSender($this, $this->language);
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $consoleSender);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_ADMINISTRATIVE, $consoleSender);
|
||||
$this->subscribeToBroadcastChannel(self::BROADCAST_CHANNEL_USERS, $consoleSender);
|
||||
|
||||
$consoleNotifier = new SleeperNotifier();
|
||||
$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 CommandSender[]|null $recipients
|
||||
*/
|
||||
public function broadcastMessage($message, ?array $recipients = null) : int{
|
||||
if(!is_array($recipients)){
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
$recipients = $recipients ?? $this->getBroadcastChannelSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendMessage($message);
|
||||
@ -1108,12 +1145,12 @@ class Server{
|
||||
/**
|
||||
* @return Player[]
|
||||
*/
|
||||
private function selectPermittedPlayers(string $permission) : array{
|
||||
private function getPlayerBroadcastSubscribers(string $channelId) : array{
|
||||
/** @var Player[] $players */
|
||||
$players = [];
|
||||
foreach(PermissionManager::getInstance()->getPermissionSubscriptions($permission) as $permissible){
|
||||
if($permissible instanceof Player and $permissible->hasPermission($permission)){
|
||||
$players[spl_object_id($permissible)] = $permissible; //prevent duplication
|
||||
foreach($this->broadcastSubscribers[$channelId] as $subscriber){
|
||||
if($subscriber instanceof Player){
|
||||
$players[spl_object_id($subscriber)] = $subscriber;
|
||||
}
|
||||
}
|
||||
return $players;
|
||||
@ -1123,7 +1160,7 @@ class Server{
|
||||
* @param Player[]|null $recipients
|
||||
*/
|
||||
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){
|
||||
$recipient->sendTip($tip);
|
||||
@ -1136,7 +1173,7 @@ class Server{
|
||||
* @param Player[]|null $recipients
|
||||
*/
|
||||
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){
|
||||
$recipient->sendPopup($popup);
|
||||
@ -1152,7 +1189,7 @@ class Server{
|
||||
* @param Player[]|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->selectPermittedPlayers(self::BROADCAST_CHANNEL_USERS);
|
||||
$recipients = $recipients ?? $this->getPlayerBroadcastSubscribers(self::BROADCAST_CHANNEL_USERS);
|
||||
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendTitle($title, $subtitle, $fadeIn, $stay, $fadeOut);
|
||||
|
@ -234,7 +234,7 @@ abstract class Command{
|
||||
* @param TranslationContainer|string $message
|
||||
*/
|
||||
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){
|
||||
$formatted = "[" . $source->getName() . ": " . ($source->getLanguage()->get($message->getText()) !== $message->getText() ? "%" : "") . $message->getText() . "]";
|
||||
|
||||
@ -250,12 +250,10 @@ abstract class Command{
|
||||
}
|
||||
|
||||
foreach($users as $user){
|
||||
if($user instanceof CommandSender){
|
||||
if($user instanceof ConsoleCommandSender){
|
||||
$user->sendMessage($result);
|
||||
}elseif($user !== $source){
|
||||
$user->sendMessage($colored);
|
||||
}
|
||||
if($user instanceof ConsoleCommandSender){
|
||||
$user->sendMessage($result);
|
||||
}elseif($user !== $source){
|
||||
$user->sendMessage($colored);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +26,8 @@ namespace pocketmine\event\player;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Utils;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Called when a player chats something
|
||||
@ -50,21 +47,13 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
|
||||
/**
|
||||
* @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->message = $message;
|
||||
|
||||
$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{
|
||||
|
@ -97,7 +97,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags;
|
||||
use pocketmine\permission\PermissibleBase;
|
||||
use pocketmine\permission\PermissibleDelegateTrait;
|
||||
use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\TextFormat;
|
||||
@ -524,9 +523,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
public function recalculatePermissions() : void{
|
||||
$permManager = PermissionManager::getInstance();
|
||||
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
$permManager->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->server->unsubscribeFromBroadcastChannel(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
$this->server->unsubscribeFromBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
|
||||
if($this->perm === null){
|
||||
return;
|
||||
@ -538,10 +536,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
if($this->spawned){
|
||||
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)){
|
||||
$permManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
}
|
||||
|
||||
$this->networkSession->syncAvailableCommands();
|
||||
@ -781,10 +779,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
$this->spawned = true;
|
||||
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)){
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->server->subscribeToBroadcastChannel(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $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));
|
||||
Timings::$playerCommandTimer->stopTiming();
|
||||
}else{
|
||||
$ev = new PlayerChatEvent($this, $ev->getMessage());
|
||||
$ev = new PlayerChatEvent($this, $ev->getMessage(), $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_USERS));
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$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
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->server->unsubscribeFromAllBroadcastChannels($this);
|
||||
|
||||
$ev = new PlayerQuitEvent($this, $quitMessage ?? $this->getLeaveMessage(), $reason);
|
||||
$ev->call();
|
||||
|
Loading…
x
Reference in New Issue
Block a user