Added PlayerInfo, Player is no longer accessible during PlayerPreLoginEvent

This commit is contained in:
Dylan K. Taylor 2019-01-13 19:32:30 +00:00
parent d2082c0383
commit 9c53b41851
6 changed files with 191 additions and 66 deletions

View File

@ -191,11 +191,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var NetworkSession */
protected $networkSession;
/** @var float */
public $creationTime = 0;
/** @var bool */
public $loggedIn = false;
protected $loggedIn = false;
/** @var bool */
public $spawned = false;
@ -212,6 +209,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
protected $xuid = "";
/** @var bool */
protected $authenticated = false;
/** @var PlayerInfo|null */
protected $playerInfo = null;
protected $windowCnt = 2;
/** @var int[] */
@ -379,9 +378,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* as SimpleAuth for authentication. This is NOT SAFE anymore as this UUID is now what was given by the client, NOT
* a server-computed UUID.)
*
* @return UUID|null
* @return UUID
*/
public function getUniqueId() : ?UUID{
public function getUniqueId() : UUID{
return parent::getUniqueId();
}
@ -693,8 +692,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->chunksPerTick = (int) $this->server->getProperty("chunk-sending.per-tick", 4);
$this->spawnThreshold = (int) (($this->server->getProperty("chunk-sending.spawn-radius", 4) ** 2) * M_PI);
$this->creationTime = microtime(true);
$this->allowMovementCheats = (bool) $this->server->getProperty("player.anti-cheat.allow-movement-cheats", false);
}
@ -1741,20 +1738,25 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handleLogin(LoginPacket $packet) : bool{
$this->username = TextFormat::clean($packet->username);
$this->playerInfo = $packet->playerInfo;
$this->username = TextFormat::clean($this->playerInfo->getUsername());
$this->displayName = $this->username;
$this->iusername = strtolower($this->username);
$this->locale = $packet->locale;
$this->randomClientId = $packet->clientId;
$this->locale = $this->playerInfo->getLocale();
$this->randomClientId = $this->playerInfo->getClientId();
$this->uuid = UUID::fromString($packet->clientUUID);
$this->uuid = $this->playerInfo->getUuid();
$this->rawUUID = $this->uuid->toBinary();
$this->xuid = $packet->xuid;
$this->xuid = $this->playerInfo->getXuid();
$this->setSkin($packet->skin);
$this->setSkin($this->playerInfo->getSkin());
$ev = new PlayerPreLoginEvent($this, $this->server->requiresAuthentication());
$ev = new PlayerPreLoginEvent(
$this->playerInfo,
$this->networkSession->getIp(),
$this->networkSession->getPort(),
$this->server->requiresAuthentication()
);
if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers()){
$ev->setKickReason(PlayerPreLoginEvent::KICK_REASON_SERVER_FULL, "disconnectionScreen.serverFull");
}

View File

@ -0,0 +1,97 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine;
use pocketmine\entity\Skin;
use pocketmine\utils\UUID;
/**
* Encapsulates data needed to create a player.
*/
class PlayerInfo{
/** @var string */
private $username;
/** @var UUID */
private $uuid;
/** @var Skin */
private $skin;
/** @var string */
private $locale;
/** @var string */
private $xuid;
/** @var int */
private $clientId;
public function __construct(string $username, UUID $uuid, Skin $skin, string $locale, string $xuid, int $clientId){
$this->username = $username;
$this->uuid = $uuid;
$this->skin = $skin;
$this->locale = $locale;
$this->xuid = $xuid;
$this->clientId = $clientId;
}
/**
* @return string
*/
public function getUsername() : string{
return $this->username;
}
/**
* @return UUID
*/
public function getUuid() : UUID{
return $this->uuid;
}
/**
* @return Skin
*/
public function getSkin() : Skin{
return $this->skin;
}
/**
* @return string
*/
public function getLocale() : string{
return $this->locale;
}
/**
* @return string
*/
public function getXuid() : string{
return $this->xuid;
}
/**
* @return int
*/
public function getClientId() : int{
return $this->clientId;
}
}

View File

@ -129,9 +129,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
/**
* @return UUID|null
* @return UUID
*/
public function getUniqueId() : ?UUID{
public function getUniqueId() : UUID{
return $this->uuid;
}

View File

@ -23,7 +23,8 @@ declare(strict_types=1);
namespace pocketmine\event\player;
use pocketmine\Player;
use pocketmine\event\Event;
use pocketmine\PlayerInfo;
use function array_keys;
/**
@ -35,11 +36,8 @@ use function array_keys;
*
* WARNING: Any information about the player CANNOT be trusted at this stage, because they are not authenticated and
* could be a hacker posing as another player.
*
* WARNING: Due to internal bad architecture, the player is not fully constructed at this stage, and errors might occur
* when calling API methods on the player. Tread with caution.
*/
class PlayerPreLoginEvent extends PlayerEvent{
class PlayerPreLoginEvent extends Event{
public const KICK_REASON_PLUGIN = 0;
public const KICK_REASON_SERVER_FULL = 1;
public const KICK_REASON_SERVER_WHITELISTED = 2;
@ -52,6 +50,12 @@ class PlayerPreLoginEvent extends PlayerEvent{
self::KICK_REASON_BANNED
];
/** @var PlayerInfo */
private $playerInfo;
/** @var string */
private $ip;
/** @var int */
private $port;
/** @var bool */
protected $authRequired;
@ -59,14 +63,43 @@ class PlayerPreLoginEvent extends PlayerEvent{
protected $kickReasons = [];
/**
* @param Player $player
* @param bool $authRequired
* @param PlayerInfo $playerInfo
* @param string $ip
* @param int $port
* @param bool $authRequired
*/
public function __construct(Player $player, bool $authRequired){
$this->player = $player;
public function __construct(PlayerInfo $playerInfo, string $ip, int $port, bool $authRequired){
$this->playerInfo = $playerInfo;
$this->ip = $ip;
$this->port = $port;
$this->authRequired = $authRequired;
}
/**
* Returns an object containing self-proclaimed information about the connecting player.
* WARNING: THE PLAYER IS NOT VERIFIED DURING THIS EVENT. At this point, it's unknown if the player is real or a
* hacker.
*
* @return PlayerInfo
*/
public function getPlayerInfo() : PlayerInfo{
return $this->playerInfo;
}
/**
* @return string
*/
public function getIp() : string{
return $this->ip;
}
/**
* @return int
*/
public function getPort() : int{
return $this->port;
}
/**
* @return bool
*/

View File

@ -60,13 +60,13 @@ class LoginSessionHandler extends SessionHandler{
return true;
}
if(!Player::isValidUserName($packet->username)){
if(!Player::isValidUserName($packet->playerInfo->getUsername())){
$this->session->disconnect("disconnectionScreen.invalidName");
return true;
}
if($packet->skin === null or !$packet->skin->isValid()){
if(!$packet->playerInfo->getSkin()->isValid()){
$this->session->disconnect("disconnectionScreen.invalidSkin");
return true;

View File

@ -29,8 +29,10 @@ namespace pocketmine\network\mcpe\protocol;
use Particle\Validator\Validator;
use pocketmine\entity\Skin;
use pocketmine\network\mcpe\handler\SessionHandler;
use pocketmine\PlayerInfo;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\Utils;
use pocketmine\utils\UUID;
use function array_filter;
use function base64_decode;
use function count;
@ -44,6 +46,10 @@ class LoginPacket extends DataPacket{
public const EDITION_POCKET = 0;
public const I_USERNAME = 'displayName';
public const I_UUID = 'identity';
public const I_XUID = 'XUID';
public const I_CLIENT_RANDOM_ID = 'ClientRandomId';
public const I_SERVER_ADDRESS = 'ServerAddress';
public const I_LANGUAGE_CODE = 'LanguageCode';
@ -54,27 +60,16 @@ class LoginPacket extends DataPacket{
public const I_GEOMETRY_NAME = 'SkinGeometryName';
public const I_GEOMETRY_DATA = 'SkinGeometry';
/** @var string */
public $username;
/** @var int */
public $protocol;
/** @var string */
public $clientUUID;
/** @var int */
public $clientId;
/** @var string */
public $xuid;
/** @var string */
public $identityPublicKey;
/** @var string */
public $serverAddress;
/** @var string */
public $locale;
/** @var Skin|null */
public $skin;
/** @var PlayerInfo */
public $playerInfo;
/** @var string[] array of encoded JWT */
public $chainDataJwt = [];
/** @var array|null extraData index of whichever JWT has it */
public $extraData = null;
/** @var string */
public $clientDataJwt;
/** @var array decoded payload of the clientData JWT */
@ -138,8 +133,6 @@ class LoginPacket extends DataPacket{
self::validate($vd, "chainData", $chainData);
$this->chainDataJwt = $chainData['chain'];
$hasExtraData = false;
foreach($this->chainDataJwt as $k => $chain){
//validate every chain element
$claims = Utils::getJwtClaims($chain);
@ -147,23 +140,20 @@ class LoginPacket extends DataPacket{
if(!is_array($claims["extraData"])){
throw new \UnexpectedValueException("'extraData' key should be an array");
}
if($hasExtraData){
if($this->extraData !== null){
throw new \UnexpectedValueException("Found 'extraData' more than once in chainData");
}
$hasExtraData = true;
$extraV = new Validator();
$extraV->required('displayName')->string();
$extraV->required('identity')->uuid();
$extraV->required('XUID')->string()->digits()->allowEmpty(true);
$extraV->required(self::I_USERNAME)->string();
$extraV->required(self::I_UUID)->uuid();
$extraV->required(self::I_XUID)->string()->digits()->allowEmpty(true);
self::validate($extraV, "chain.$k.extraData", $claims['extraData']);
$this->username = $claims["extraData"]["displayName"];
$this->clientUUID = $claims["extraData"]["identity"];
$this->xuid = $claims["extraData"]["XUID"];
$this->extraData = $claims['extraData'];
}
}
if(!$hasExtraData){
if($this->extraData === null){
throw new \UnexpectedValueException("'extraData' not found in chain data");
}
@ -184,16 +174,19 @@ class LoginPacket extends DataPacket{
$this->clientData = $clientData;
$this->clientId = $this->clientData[self::I_CLIENT_RANDOM_ID];
$this->serverAddress = $this->clientData[self::I_SERVER_ADDRESS];
$this->locale = $this->clientData[self::I_LANGUAGE_CODE];
$this->skin = new Skin(
$this->clientData[self::I_SKIN_ID],
base64_decode($this->clientData[self::I_SKIN_DATA]),
base64_decode($this->clientData[self::I_CAPE_DATA]),
$this->clientData[self::I_GEOMETRY_NAME],
base64_decode($this->clientData[self::I_GEOMETRY_DATA])
$this->playerInfo = new PlayerInfo(
$this->extraData[self::I_USERNAME],
UUID::fromString($this->extraData[self::I_UUID]),
new Skin(
$this->clientData[self::I_SKIN_ID],
base64_decode($this->clientData[self::I_SKIN_DATA]),
base64_decode($this->clientData[self::I_CAPE_DATA]),
$this->clientData[self::I_GEOMETRY_NAME],
base64_decode($this->clientData[self::I_GEOMETRY_DATA])
),
$this->clientData[self::I_LANGUAGE_CODE],
$this->extraData[self::I_XUID],
$this->clientData[self::I_CLIENT_RANDOM_ID]
);
}