mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-14 17:59:41 +00:00
Initial mass migration to session handlers
This introduces several new session handlers, splitting up session handling into several new states: - Login: Only allows handling the LoginPacket. This is the only time LoginPacket can be sent, and it'll be discarded when sent at any other time. - Resource packs: Handles only the resource packs sequence (downloading packs and such). This is the only time ResourcePackClientResponse and ResourcePackChunkRequest will be handled. - Pre-spawn: Only chunk radius requests are accepted during this state. SimpleNetworkHandler handles all the "rest" of the logic that hasn't yet been separated out into their own dedicated handlers. There's also a NullNetworkHandler which discards all packets while it's active. This solves a large number of issues with the security of the login sequence. It solves a range of possible DoS attacks and crashes, while also allowing great code simplification and cleanup.
This commit is contained in:
parent
97a1483f75
commit
f626b9e8a0
@ -114,26 +114,16 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
|
||||
use pocketmine\network\mcpe\protocol\RespawnPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTitlePacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\TransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandData;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandEnum;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandParameter;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
@ -143,7 +133,6 @@ use pocketmine\permission\PermissibleBase;
|
||||
use pocketmine\permission\PermissionAttachment;
|
||||
use pocketmine\permission\PermissionAttachmentInfo;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use pocketmine\tile\ItemFrame;
|
||||
use pocketmine\tile\Spawnable;
|
||||
use pocketmine\tile\Tile;
|
||||
@ -182,9 +171,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var NetworkSession */
|
||||
protected $networkSession;
|
||||
|
||||
/** @var int */
|
||||
protected $protocol = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* Last measurement of player's latency in milliseconds.
|
||||
@ -993,7 +979,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
protected function doFirstSpawn(){
|
||||
$this->spawned = true;
|
||||
|
||||
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
|
||||
$this->networkSession->onSpawn();
|
||||
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
@ -1799,59 +1785,17 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleLogin(LoginPacket $packet) : bool{
|
||||
if($this->loggedIn){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->protocol = $packet->protocol;
|
||||
|
||||
if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){
|
||||
if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){
|
||||
$this->sendPlayStatus(PlayStatusPacket::LOGIN_FAILED_CLIENT, true);
|
||||
}else{
|
||||
$this->sendPlayStatus(PlayStatusPacket::LOGIN_FAILED_SERVER, true);
|
||||
}
|
||||
|
||||
//This pocketmine disconnect message will only be seen by the console (PlayStatusPacket causes the messages to be shown for the client)
|
||||
$this->close("", $this->server->getLanguage()->translateString("pocketmine.disconnect.incompatibleProtocol", [$packet->protocol ?? "unknown"]), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!self::isValidUserName($packet->username)){
|
||||
$this->close("", "disconnectionScreen.invalidName");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->username = TextFormat::clean($packet->username);
|
||||
$this->displayName = $this->username;
|
||||
$this->iusername = strtolower($this->username);
|
||||
|
||||
if($packet->locale !== null){
|
||||
$this->locale = $packet->locale;
|
||||
}
|
||||
|
||||
$this->locale = $packet->locale;
|
||||
$this->randomClientId = $packet->clientId;
|
||||
|
||||
$this->uuid = UUID::fromString($packet->clientUUID);
|
||||
$this->rawUUID = $this->uuid->toBinary();
|
||||
|
||||
$skin = new Skin(
|
||||
$packet->clientData["SkinId"],
|
||||
base64_decode($packet->clientData["SkinData"] ?? ""),
|
||||
base64_decode($packet->clientData["CapeData"] ?? ""),
|
||||
$packet->clientData["SkinGeometryName"] ?? "",
|
||||
base64_decode($packet->clientData["SkinGeometry"] ?? "")
|
||||
);
|
||||
$this->setSkin($packet->skin);
|
||||
|
||||
if(!$skin->isValid()){
|
||||
$this->close("", "disconnectionScreen.invalidSkin");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->setSkin($skin);
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason"));
|
||||
if($ev->isCancelled()){
|
||||
@ -1884,13 +1828,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendPlayStatus(int $status, bool $immediate = false){
|
||||
$pk = new PlayStatusPacket();
|
||||
$pk->status = $status;
|
||||
$pk->protocol = $this->protocol;
|
||||
$this->sendDataPacket($pk, $immediate);
|
||||
}
|
||||
|
||||
public function onVerifyCompleted(LoginPacket $packet, ?string $error, bool $signedByMojang) : void{
|
||||
if($this->closed){
|
||||
return;
|
||||
@ -1935,64 +1872,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
$this->sendPlayStatus(PlayStatusPacket::LOGIN_SUCCESS);
|
||||
|
||||
$this->loggedIn = true;
|
||||
$this->server->onPlayerLogin($this);
|
||||
|
||||
$pk = new ResourcePacksInfoPacket();
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pk->resourcePackEntries = $manager->getResourceStack();
|
||||
$pk->mustAccept = $manager->resourcePacksRequired();
|
||||
$this->dataPacket($pk);
|
||||
$this->networkSession->onLoginSuccess();
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
switch($packet->status){
|
||||
case ResourcePackClientResponsePacket::STATUS_REFUSED:
|
||||
//TODO: add lang strings for this
|
||||
$this->close("", "You must accept resource packs to join this server.", true);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
foreach($packet->packIds as $uuid){
|
||||
$pack = $manager->getPackById($uuid);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
//Client requested a resource pack but we don't have it available on the server
|
||||
$this->close("", "disconnectionScreen.resourcePack", true);
|
||||
$this->server->getLogger()->debug("Got a resource pack request for unknown pack with UUID " . $uuid . ", available packs: " . implode(", ", $manager->getPackIdList()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$pk = new ResourcePackDataInfoPacket();
|
||||
$pk->packId = $pack->getPackId();
|
||||
$pk->maxChunkSize = 1048576; //1MB
|
||||
$pk->chunkCount = (int) ceil($pack->getPackSize() / $pk->maxChunkSize);
|
||||
$pk->compressedPackSize = $pack->getPackSize();
|
||||
$pk->sha256 = $pack->getSha256();
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
|
||||
$pk = new ResourcePackStackPacket();
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pk->resourcePackStack = $manager->getResourceStack();
|
||||
$pk->mustAccept = $manager->resourcePacksRequired();
|
||||
$this->dataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
$this->_actuallyConstruct();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _actuallyConstruct(){
|
||||
public function _actuallyConstruct(){
|
||||
$namedtag = $this->server->getOfflinePlayerData($this->username); //TODO: make this async
|
||||
|
||||
if(($level = $this->server->getLevelByName($namedtag->getString("Level", "", true))) === null){
|
||||
@ -2021,50 +1906,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$spawnPosition = $this->getSpawn();
|
||||
|
||||
$pk = new StartGamePacket();
|
||||
$pk->entityUniqueId = $this->id;
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->playerGamemode = Player::getClientFriendlyGamemode($this->gamemode);
|
||||
|
||||
$pk->playerPosition = $this->getOffsetPosition($this);
|
||||
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->seed = -1;
|
||||
$pk->dimension = DimensionIds::OVERWORLD; //TODO: implement this properly
|
||||
$pk->worldGamemode = Player::getClientFriendlyGamemode($this->server->getGamemode());
|
||||
$pk->difficulty = $this->level->getDifficulty();
|
||||
$pk->spawnX = $spawnPosition->getFloorX();
|
||||
$pk->spawnY = $spawnPosition->getFloorY();
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
$pk->hasAchievementsDisabled = true;
|
||||
$pk->time = $this->level->getTime();
|
||||
$pk->eduMode = false;
|
||||
$pk->rainLevel = 0; //TODO: implement these properly
|
||||
$pk->lightningLevel = 0;
|
||||
$pk->commandsEnabled = true;
|
||||
$pk->levelId = "";
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->level->sendTime($this);
|
||||
|
||||
$this->sendAttributes(true);
|
||||
$this->sendCommandData();
|
||||
$this->sendSettings();
|
||||
$this->sendPotionEffects($this);
|
||||
$this->sendData($this);
|
||||
|
||||
$this->sendAllInventories();
|
||||
$this->inventory->sendCreativeContents();
|
||||
$this->inventory->sendHeldItem($this);
|
||||
$this->networkSession->getInterface()->putPacket($this, $this->server->getCraftingManager()->getCraftingDataPacket());
|
||||
|
||||
$this->server->addOnlinePlayer($this);
|
||||
$this->server->sendFullPlayerListData($this);
|
||||
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logIn", [
|
||||
TextFormat::AQUA . $this->username . TextFormat::WHITE,
|
||||
$this->networkSession->getIp(),
|
||||
@ -2075,6 +1916,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
round($this->y, 4),
|
||||
round($this->z, 4)
|
||||
]));
|
||||
|
||||
$this->server->addOnlinePlayer($this);
|
||||
}
|
||||
|
||||
protected function initHumanData() : void{
|
||||
@ -2128,7 +1971,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return bool
|
||||
*/
|
||||
public function chat(string $message) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2172,7 +2015,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET);
|
||||
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3());
|
||||
//Still getting movements from before teleport, ignore them
|
||||
}elseif((!$this->isAlive() or !$this->spawned) and $newPos->distanceSquared($this) > 0.01){
|
||||
}elseif((!$this->isAlive()) and $newPos->distanceSquared($this) > 0.01){
|
||||
$this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET);
|
||||
$this->server->getLogger()->debug("Reverted movement of " . $this->getName() . " due to not alive or not spawned, received " . $newPos . ", locked at " . $this->asVector3());
|
||||
}else{
|
||||
@ -2204,7 +2047,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleEntityEvent(EntityEventPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->doCloseInventory();
|
||||
@ -2232,7 +2075,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return bool
|
||||
*/
|
||||
public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2594,7 +2437,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2644,7 +2487,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
|
||||
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST)){
|
||||
if(!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2668,7 +2511,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->stopSleep();
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_RESPAWN:
|
||||
if(!$this->spawned or $this->isAlive() or !$this->isOnline()){
|
||||
if($this->isAlive() or !$this->isOnline()){
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2781,7 +2624,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleAnimate(AnimatePacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2805,7 +2648,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return bool if the item was dropped or if the item was null
|
||||
*/
|
||||
public function dropItem(Item $item) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2854,7 +2697,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleBlockEntityData(BlockEntityDataPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->doCloseInventory();
|
||||
@ -2890,7 +2733,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
if(!$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2917,25 +2760,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pack = $manager->getPackById($packet->packId);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
$this->close("", "disconnectionScreen.resourcePack", true);
|
||||
$this->server->getLogger()->debug("Got a resource pack chunk request for unknown pack with UUID " . $packet->packId . ", available packs: " . implode(", ", $manager->getPackIdList()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$pk = new ResourcePackChunkDataPacket();
|
||||
$pk->packId = $pack->getPackId();
|
||||
$pk->chunkIndex = $packet->chunkIndex;
|
||||
$pk->data = $pack->getPackChunk(1048576 * $packet->chunkIndex, 1048576);
|
||||
$pk->progress = (1048576 * $packet->chunkIndex);
|
||||
$this->dataPacket($pk);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleBookEdit(BookEditPacket $packet) : bool{
|
||||
/** @var WritableBook $oldBook */
|
||||
$oldBook = $this->inventory->getItem($packet->inventorySlot);
|
||||
@ -3732,7 +3556,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return bool
|
||||
*/
|
||||
public function doCloseWindow(int $windowId) : bool{
|
||||
if(!$this->spawned or $windowId === 0){
|
||||
if($windowId === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3844,7 +3668,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendAllInventories(){
|
||||
public function sendAllInventories(){
|
||||
foreach($this->windowIndex as $id => $inventory){
|
||||
$inventory->sendContents($this);
|
||||
}
|
||||
|
@ -25,11 +25,15 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||
use pocketmine\event\server\DataPacketSendEvent;
|
||||
use pocketmine\network\mcpe\handler\LoginSessionHandler;
|
||||
use pocketmine\network\mcpe\handler\PreSpawnSessionHandler;
|
||||
use pocketmine\network\mcpe\handler\ResourcePacksSessionHandler;
|
||||
use pocketmine\network\mcpe\handler\SessionHandler;
|
||||
use pocketmine\network\mcpe\handler\SimpleSessionHandler;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\DisconnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\NetworkInterface;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
@ -60,7 +64,7 @@ class NetworkSession{
|
||||
$this->ip = $ip;
|
||||
$this->port = $port;
|
||||
|
||||
$this->setHandler(new SimpleSessionHandler($player));
|
||||
$this->setHandler(new LoginSessionHandler($player, $this));
|
||||
}
|
||||
|
||||
public function getInterface() : NetworkInterface{
|
||||
@ -144,4 +148,29 @@ class NetworkSession{
|
||||
}
|
||||
$this->interface->close($this->player, $notify ? $reason : "");
|
||||
}
|
||||
|
||||
//TODO: onEnableEncryption() step
|
||||
|
||||
public function onLoginSuccess() : void{
|
||||
$pk = new PlayStatusPacket();
|
||||
$pk->status = PlayStatusPacket::LOGIN_SUCCESS;
|
||||
$this->sendDataPacket($pk);
|
||||
|
||||
$this->setHandler(new ResourcePacksSessionHandler($this->server, $this->player, $this));
|
||||
}
|
||||
|
||||
public function onResourcePacksDone() : void{
|
||||
$this->player->_actuallyConstruct();
|
||||
|
||||
$this->setHandler(new PreSpawnSessionHandler($this->server, $this->player, $this));
|
||||
}
|
||||
|
||||
public function onSpawn() : void{
|
||||
$pk = new PlayStatusPacket();
|
||||
$pk->status = PlayStatusPacket::PLAYER_SPAWN;
|
||||
$this->sendDataPacket($pk);
|
||||
|
||||
//TODO: split this up even further
|
||||
$this->setHandler(new SimpleSessionHandler($this->player));
|
||||
}
|
||||
}
|
||||
|
89
src/pocketmine/network/mcpe/handler/LoginSessionHandler.php
Normal file
89
src/pocketmine/network/mcpe/handler/LoginSessionHandler.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?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\network\mcpe\handler;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\LoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Handles the initial login phase of the session. This handler is used as the initial state.
|
||||
*/
|
||||
class LoginSessionHandler extends SessionHandler{
|
||||
|
||||
/** @var Player */
|
||||
private $player;
|
||||
/** @var NetworkSession */
|
||||
private $session;
|
||||
|
||||
public function __construct(Player $player, NetworkSession $session){
|
||||
$this->player = $player;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function handleLogin(LoginPacket $packet) : bool{
|
||||
if(!$this->isCompatibleProtocol($packet->protocol)){
|
||||
$pk = new PlayStatusPacket();
|
||||
$pk->status = $packet->protocol < ProtocolInfo::CURRENT_PROTOCOL ?
|
||||
PlayStatusPacket::LOGIN_FAILED_CLIENT : PlayStatusPacket::LOGIN_FAILED_SERVER;
|
||||
$pk->protocol = $packet->protocol;
|
||||
$this->session->sendDataPacket($pk, true);
|
||||
|
||||
//This pocketmine disconnect message will only be seen by the console (PlayStatusPacket causes the messages to be shown for the client)
|
||||
$this->player->close(
|
||||
"",
|
||||
$this->player->getServer()->getLanguage()->translateString("pocketmine.disconnect.incompatibleProtocol", [$packet->protocol]),
|
||||
false
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!Player::isValidUserName($packet->username)){
|
||||
$this->player->close("", "disconnectionScreen.invalidName");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($packet->skin === null or !$packet->skin->isValid()){
|
||||
$this->player->close("", "disconnectionScreen.invalidSkin");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->player->handleLogin($packet)){
|
||||
if($this->session->getHandler() === $this){ //when login verification is disabled, the handler will already have been replaced
|
||||
$this->session->setHandler(new NullSessionHandler()); //drop packets received during login verification
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isCompatibleProtocol(int $protocolVersion) : bool{
|
||||
return $protocolVersion === ProtocolInfo::CURRENT_PROTOCOL;
|
||||
}
|
||||
}
|
31
src/pocketmine/network/mcpe/handler/NullSessionHandler.php
Normal file
31
src/pocketmine/network/mcpe/handler/NullSessionHandler.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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\network\mcpe\handler;
|
||||
|
||||
/**
|
||||
* Handler which simply ignores all packets received.
|
||||
*/
|
||||
class NullSessionHandler extends SessionHandler{
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?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\network\mcpe\handler;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
/**
|
||||
* Handler used for the pre-spawn phase of the session.
|
||||
*/
|
||||
class PreSpawnSessionHandler extends SessionHandler{
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var Player */
|
||||
private $player;
|
||||
/** @var NetworkSession */
|
||||
private $session;
|
||||
|
||||
public function __construct(Server $server, Player $player, NetworkSession $session){
|
||||
$this->player = $player;
|
||||
$this->server = $server;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function setUp() : void{
|
||||
$spawnPosition = $this->player->getSpawn();
|
||||
|
||||
$pk = new StartGamePacket();
|
||||
$pk->entityUniqueId = $this->player->getId();
|
||||
$pk->entityRuntimeId = $this->player->getId();
|
||||
$pk->playerGamemode = Player::getClientFriendlyGamemode($this->player->getGamemode());
|
||||
$pk->playerPosition = $this->player->getOffsetPosition($this->player);
|
||||
$pk->pitch = $this->player->pitch;
|
||||
$pk->yaw = $this->player->yaw;
|
||||
$pk->seed = -1;
|
||||
$pk->dimension = DimensionIds::OVERWORLD; //TODO: implement this properly
|
||||
$pk->worldGamemode = Player::getClientFriendlyGamemode($this->server->getGamemode());
|
||||
$pk->difficulty = $this->player->getLevel()->getDifficulty();
|
||||
$pk->spawnX = $spawnPosition->getFloorX();
|
||||
$pk->spawnY = $spawnPosition->getFloorY();
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
$pk->hasAchievementsDisabled = true;
|
||||
$pk->time = $this->player->getLevel()->getTime();
|
||||
$pk->eduMode = false;
|
||||
$pk->rainLevel = 0; //TODO: implement these properly
|
||||
$pk->lightningLevel = 0;
|
||||
$pk->commandsEnabled = true;
|
||||
$pk->levelId = "";
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$this->session->sendDataPacket($pk);
|
||||
|
||||
$this->player->getLevel()->sendTime($this->player);
|
||||
|
||||
$this->player->sendAttributes(true);
|
||||
$this->player->sendCommandData();
|
||||
$this->player->sendSettings();
|
||||
$this->player->sendPotionEffects($this->player);
|
||||
$this->player->sendData($this->player);
|
||||
|
||||
$this->player->sendAllInventories();
|
||||
$this->player->getInventory()->sendCreativeContents();
|
||||
$this->player->getInventory()->sendHeldItem($this->player);
|
||||
$this->session->getInterface()->putPacket($this->player, $this->server->getCraftingManager()->getCraftingDataPacket());
|
||||
|
||||
$this->server->sendFullPlayerListData($this->player);
|
||||
}
|
||||
|
||||
public function handleRequestChunkRadius(RequestChunkRadiusPacket $packet) : bool{
|
||||
$this->player->setViewDistance($packet->radius);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
<?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\network\mcpe\handler;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\resourcepacks\ResourcePack;
|
||||
use pocketmine\Server;
|
||||
|
||||
/**
|
||||
* Handler used for the resource packs sequence phase of the session. This handler takes care of downloading resource
|
||||
* packs to the client.
|
||||
*/
|
||||
class ResourcePacksSessionHandler extends SessionHandler{
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var Player */
|
||||
private $player;
|
||||
/** @var NetworkSession */
|
||||
private $session;
|
||||
|
||||
public function __construct(Server $server, Player $player, NetworkSession $session){
|
||||
$this->server = $server;
|
||||
$this->player = $player;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function setUp() : void{
|
||||
$pk = new ResourcePacksInfoPacket();
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pk->resourcePackEntries = $manager->getResourceStack();
|
||||
$pk->mustAccept = $manager->resourcePacksRequired();
|
||||
$this->session->sendDataPacket($pk);
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
switch($packet->status){
|
||||
case ResourcePackClientResponsePacket::STATUS_REFUSED:
|
||||
//TODO: add lang strings for this
|
||||
$this->player->close("", "You must accept resource packs to join this server.", true);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
foreach($packet->packIds as $uuid){
|
||||
$pack = $manager->getPackById($uuid);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
//Client requested a resource pack but we don't have it available on the server
|
||||
$this->player->close("", "disconnectionScreen.resourcePack", true);
|
||||
$this->server->getLogger()->debug("Got a resource pack request for unknown pack with UUID " . $uuid . ", available packs: " . implode(", ", $manager->getPackIdList()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$pk = new ResourcePackDataInfoPacket();
|
||||
$pk->packId = $pack->getPackId();
|
||||
$pk->maxChunkSize = 1048576; //1MB
|
||||
$pk->chunkCount = (int) ceil($pack->getPackSize() / $pk->maxChunkSize);
|
||||
$pk->compressedPackSize = $pack->getPackSize();
|
||||
$pk->sha256 = $pack->getSha256();
|
||||
$this->session->sendDataPacket($pk);
|
||||
}
|
||||
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
|
||||
$pk = new ResourcePackStackPacket();
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pk->resourcePackStack = $manager->getResourceStack();
|
||||
$pk->mustAccept = $manager->resourcePacksRequired();
|
||||
$this->session->sendDataPacket($pk);
|
||||
break;
|
||||
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
|
||||
$this->session->onResourcePacksDone();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
$manager = $this->server->getResourcePackManager();
|
||||
$pack = $manager->getPackById($packet->packId);
|
||||
if(!($pack instanceof ResourcePack)){
|
||||
$this->player->close("", "disconnectionScreen.resourcePack", true);
|
||||
$this->server->getLogger()->debug("Got a resource pack chunk request for unknown pack with UUID " . $packet->packId . ", available packs: " . implode(", ", $manager->getPackIdList()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$pk = new ResourcePackChunkDataPacket();
|
||||
$pk->packId = $pack->getPackId();
|
||||
$pk->chunkIndex = $packet->chunkIndex;
|
||||
$pk->data = $pack->getPackChunk(1048576 * $packet->chunkIndex, 1048576);
|
||||
$pk->progress = (1048576 * $packet->chunkIndex);
|
||||
$this->session->sendDataPacket($pk);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -42,7 +42,6 @@ use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\LabTablePacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\MapInfoRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
@ -53,8 +52,6 @@ use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
@ -77,18 +74,10 @@ class SimpleSessionHandler extends SessionHandler{
|
||||
$this->player = $player;
|
||||
}
|
||||
|
||||
public function handleLogin(LoginPacket $packet) : bool{
|
||||
return $this->player->handleLogin($packet);
|
||||
}
|
||||
|
||||
public function handleClientToServerHandshake(ClientToServerHandshakePacket $packet) : bool{
|
||||
return false; //TODO
|
||||
}
|
||||
|
||||
public function handleResourcePackClientResponse(ResourcePackClientResponsePacket $packet) : bool{
|
||||
return $this->player->handleResourcePackClientResponse($packet);
|
||||
}
|
||||
|
||||
public function handleText(TextPacket $packet) : bool{
|
||||
if($packet->type === TextPacket::TYPE_CHAT){
|
||||
return $this->player->chat($packet->message);
|
||||
@ -207,10 +196,6 @@ class SimpleSessionHandler extends SessionHandler{
|
||||
return false; //TODO
|
||||
}
|
||||
|
||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||
return $this->player->handleResourcePackChunkRequest($packet);
|
||||
}
|
||||
|
||||
public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{
|
||||
return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\network\mcpe\handler\SessionHandler;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\MainLogger;
|
||||
@ -52,6 +53,8 @@ class LoginPacket extends DataPacket{
|
||||
public $serverAddress;
|
||||
/** @var string */
|
||||
public $locale;
|
||||
/** @var Skin|null */
|
||||
public $skin;
|
||||
|
||||
/** @var array (the "chain" index contains one or more JWTs) */
|
||||
public $chainData = [];
|
||||
@ -136,7 +139,15 @@ class LoginPacket extends DataPacket{
|
||||
$this->clientId = $this->clientData["ClientRandomId"] ?? null;
|
||||
$this->serverAddress = $this->clientData["ServerAddress"] ?? null;
|
||||
|
||||
$this->locale = $this->clientData["LanguageCode"] ?? null;
|
||||
$this->locale = $this->clientData["LanguageCode"] ?? "en_US";
|
||||
|
||||
$this->skin = new Skin(
|
||||
$this->clientData["SkinId"] ?? "",
|
||||
base64_decode($this->clientData["SkinData"] ?? ""),
|
||||
base64_decode($this->clientData["CapeData"] ?? ""),
|
||||
$this->clientData["SkinGeometryName"] ?? "",
|
||||
base64_decode($this->clientData["SkinGeometry"] ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
|
Loading…
x
Reference in New Issue
Block a user