A bunch of mostly inseparable changes to PlayerPreLoginEvent

including support for separated disconnect reasons and disconnect screen messages (closes #4512)

While the refactoring of kick reason -> kick flag wasn't exactly in my agenda, I realized that these changes would become pretty confusing and inconsistent with other events if they weren't refactored.
Hopefully I don't have to break this API again for a while...
This commit is contained in:
Dylan K. Taylor 2023-02-13 11:32:32 +00:00
parent 5d0388e747
commit 2d56aa50b9
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 70 additions and 38 deletions

View File

@ -40,20 +40,22 @@ use function count;
* could be a hacker posing as another player.
*/
class PlayerPreLoginEvent extends Event{
public const KICK_REASON_PLUGIN = 0;
public const KICK_REASON_SERVER_FULL = 1;
public const KICK_REASON_SERVER_WHITELISTED = 2;
public const KICK_REASON_BANNED = 3;
public const KICK_FLAG_PLUGIN = 0;
public const KICK_FLAG_SERVER_FULL = 1;
public const KICK_FLAG_SERVER_WHITELISTED = 2;
public const KICK_FLAG_BANNED = 3;
public const KICK_REASON_PRIORITY = [
self::KICK_REASON_PLUGIN, //Plugin reason should always take priority over anything else
self::KICK_REASON_SERVER_FULL,
self::KICK_REASON_SERVER_WHITELISTED,
self::KICK_REASON_BANNED
public const KICK_FLAG_PRIORITY = [
self::KICK_FLAG_PLUGIN, //Plugin reason should always take priority over anything else
self::KICK_FLAG_SERVER_FULL,
self::KICK_FLAG_SERVER_WHITELISTED,
self::KICK_FLAG_BANNED
];
/** @var Translatable[]|string[] reason const => associated message */
protected array $kickReasons = [];
protected array $disconnectReasons = [];
/** @var Translatable[]|string[] */
protected array $disconnectScreenMessages = [];
public function __construct(
private PlayerInfo $playerInfo,
@ -88,27 +90,31 @@ class PlayerPreLoginEvent extends Event{
}
/**
* Returns an array of kick reasons currently assigned.
* Returns an array of kick flags currently assigned.
*
* @return int[]
*/
public function getKickReasons() : array{
return array_keys($this->kickReasons);
public function getKickFlags() : array{
return array_keys($this->disconnectReasons);
}
/**
* Returns whether the given kick reason is set for this event.
* Returns whether the given kick flag is set for this event.
*/
public function isKickReasonSet(int $flag) : bool{
return isset($this->kickReasons[$flag]);
public function isKickFlagSet(int $flag) : bool{
return isset($this->disconnectReasons[$flag]);
}
/**
* Sets a reason to disallow the player to continue authenticating, with a message.
* This can also be used to change kick messages for already-set flags.
*
* @param Translatable|string $disconnectReason Shown in the server log - this should be a short one-line message
* @param Translatable|string|null $disconnectScreenMessage Shown on the player's disconnection screen (null will use the reason)
*/
public function setKickReason(int $flag, Translatable|string $message) : void{
$this->kickReasons[$flag] = $message;
public function setKickFlag(int $flag, Translatable|string $disconnectReason, Translatable|string|null $disconnectScreenMessage = null) : void{
$this->disconnectReasons[$flag] = $disconnectReason;
$this->disconnectScreenMessages[$flag] = $disconnectScreenMessage ?? $disconnectReason;
}
/**
@ -117,43 +123,69 @@ class PlayerPreLoginEvent extends Event{
*
* @param int $flag Specific flag to clear.
*/
public function clearKickReason(int $flag) : void{
unset($this->kickReasons[$flag]);
public function clearKickFlag(int $flag) : void{
unset($this->disconnectReasons[$flag], $this->disconnectScreenMessages[$flag]);
}
/**
* Clears all pre-assigned kick reasons, allowing the player to continue logging in.
*/
public function clearAllKickReasons() : void{
$this->kickReasons = [];
public function clearAllKickFlags() : void{
$this->disconnectReasons = [];
$this->disconnectScreenMessages = [];
}
/**
* Returns whether the player is allowed to continue logging in.
*/
public function isAllowed() : bool{
return count($this->kickReasons) === 0;
return count($this->disconnectReasons) === 0;
}
/**
* Returns the kick message provided for the given kick flag, or null if not set.
* Returns the disconnect reason provided for the given kick flag, or null if not set.
* This is the message which will be shown in the server log and on the console.
*/
public function getKickMessage(int $flag) : Translatable|string|null{
return $this->kickReasons[$flag] ?? null;
public function getDisconnectReason(int $flag) : Translatable|string|null{
return $this->disconnectReasons[$flag] ?? null;
}
/**
* Returns the final kick message which will be shown on the disconnect screen.
*
* Note: Only one message (the highest priority one) will be shown. See priority order to decide how to set your
* Returns the disconnect screen message provided for the given kick flag, or null if not set.
* This is the message shown to the player on the disconnect screen.
*/
public function getDisconnectScreenMessage(int $flag) : Translatable|string|null{
return $this->disconnectScreenMessages[$flag] ?? null;
}
/**
* Resolves the message that will be shown in the server log if the player is kicked.
* Only one message (the highest priority one) will be shown. See priority order to decide how to set your
* messages.
*
* @see PlayerPreLoginEvent::KICK_REASON_PRIORITY
* @see PlayerPreLoginEvent::KICK_FLAG_PRIORITY
*/
public function getFinalKickMessage() : Translatable|string{
foreach(self::KICK_REASON_PRIORITY as $p){
if(isset($this->kickReasons[$p])){
return $this->kickReasons[$p];
public function getFinalDisconnectReason() : Translatable|string{
foreach(self::KICK_FLAG_PRIORITY as $p){
if(isset($this->disconnectReasons[$p])){
return $this->disconnectReasons[$p];
}
}
return "";
}
/**
* Resolves the message that will be shown on the player's disconnect screen if they are kicked.
* Only one message (the highest priority one) will be shown. See priority order to decide how to set your
* messages.
*
* @see PlayerPreLoginEvent::KICK_FLAG_PRIORITY
*/
public function getFinalDisconnectScreenMessage() : Translatable|string{
foreach(self::KICK_FLAG_PRIORITY as $p){
if(isset($this->disconnectScreenMessages[$p])){
return $this->disconnectScreenMessages[$p];
}
}

View File

@ -117,10 +117,10 @@ class LoginPacketHandler extends PacketHandler{
$this->server->requiresAuthentication()
);
if($this->server->getNetwork()->getValidConnectionCount() > $this->server->getMaxPlayers()){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, KnownTranslationFactory::disconnectionScreen_serverFull());
$ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_FULL, KnownTranslationFactory::disconnectionScreen_serverFull());
}
if(!$this->server->isWhitelisted($playerInfo->getUsername())){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_WHITELISTED, KnownTranslationFactory::pocketmine_disconnect_whitelisted());
$ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_WHITELISTED, KnownTranslationFactory::pocketmine_disconnect_whitelisted());
}
$banMessage = null;
@ -132,12 +132,12 @@ class LoginPacketHandler extends PacketHandler{
$banMessage = KnownTranslationFactory::pocketmine_disconnect_ban($banReason !== "" ? $banReason : KnownTranslationFactory::pocketmine_disconnect_ban_ip());
}
if($banMessage !== null){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_BANNED, $banMessage);
$ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_BANNED, $banMessage);
}
$ev->call();
if(!$ev->isAllowed()){
$this->session->disconnect($ev->getFinalKickMessage());
$this->session->disconnect($ev->getFinalDisconnectReason(), $ev->getFinalDisconnectScreenMessage());
return true;
}