Merge branch 'stable'

This commit is contained in:
Dylan K. Taylor
2021-03-07 20:15:11 +00:00
11 changed files with 113 additions and 117 deletions

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\network;
use pocketmine\event\player\PlayerDuplicateLoginEvent;
use pocketmine\network\mcpe\NetworkSession;
use function count;
use function spl_object_id;
@ -59,33 +58,6 @@ class NetworkSessionManager{
$this->updateSessions[spl_object_id($session)] = $session;
}
/**
* Checks whether this network session is a duplicate of an already-connected session (same player connecting from
* 2 locations).
*
* @return bool if the network session is still connected.
*/
public function kickDuplicates(NetworkSession $connectingSession) : bool{
foreach($this->sessions as $existingSession){
if($existingSession === $connectingSession){
continue;
}
$info = $existingSession->getPlayerInfo();
if($info !== null and ($info->getUsername() === $connectingSession->getPlayerInfo()->getUsername() or $info->getUuid()->equals($connectingSession->getPlayerInfo()->getUuid()))){
$ev = new PlayerDuplicateLoginEvent($connectingSession, $existingSession);
$ev->call();
if($ev->isCancelled()){
$connectingSession->disconnect($ev->getDisconnectMessage());
return false;
}
$existingSession->disconnect($ev->getDisconnectMessage());
}
}
return true;
}
/**
* Returns the number of known connected sessions.
*/
@ -93,6 +65,9 @@ class NetworkSessionManager{
return count($this->sessions);
}
/** @return NetworkSession[] */
public function getSessions() : array{ return $this->sessions; }
/**
* Updates all sessions which need it.
*/

View File

@ -30,6 +30,7 @@ use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Living;
use pocketmine\event\player\PlayerDuplicateLoginEvent;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\event\server\DataPacketSendEvent;
use pocketmine\form\Form;
@ -607,43 +608,68 @@ class NetworkSession{
}
$this->logger->debug("Xbox Live authenticated: " . ($this->authenticated ? "YES" : "NO"));
//TODO: make player data loading async
//TODO: we shouldn't be loading player data here at all, but right now we don't have any choice :(
$this->cachedOfflinePlayerData = $this->server->getOfflinePlayerData($this->info->getUsername());
if((bool) $this->server->getConfigGroup()->getProperty("player.verify-xuid", true)){
$recordedXUID = $this->cachedOfflinePlayerData !== null ? $this->cachedOfflinePlayerData->getTag("LastKnownXUID") : null;
if(!($recordedXUID instanceof StringTag)){
$this->logger->debug("No previous XUID recorded, no choice but to trust this player");
}elseif(($this->info instanceof XboxLivePlayerInfo ? $this->info->getXuid() : "") !== $recordedXUID->getValue()){
$checkXUID = (bool) $this->server->getConfigGroup()->getProperty("player.verify-xuid", true);
$myXUID = $this->info instanceof XboxLivePlayerInfo ? $this->info->getXuid() : "";
$kickForXUIDMismatch = function(string $xuid) use ($checkXUID, $myXUID) : bool{
if($checkXUID && $myXUID !== $xuid){
//TODO: Longer term, we should be identifying playerdata using something more reliable, like XUID or UUID.
//However, that would be a very disruptive change, so this will serve as a stopgap for now.
//Side note: this will also prevent offline players hijacking XBL playerdata on online servers, since their
//XUID will always be empty.
$this->disconnect("XUID does not match (possible impersonation attempt)");
return;
}else{
return true;
}
return false;
};
foreach($this->manager->getSessions() as $existingSession){
if($existingSession === $this){
continue;
}
$info = $existingSession->getPlayerInfo();
if($info !== null and ($info->getUsername() === $this->info->getUsername() or $info->getUuid()->equals($this->info->getUuid()))){
if($kickForXUIDMismatch($info instanceof XboxLivePlayerInfo ? $info->getXuid() : "")){
return;
}
$ev = new PlayerDuplicateLoginEvent($this, $existingSession);
$ev->call();
if($ev->isCancelled()){
$this->disconnect($ev->getDisconnectMessage());
return;
}
$existingSession->disconnect($ev->getDisconnectMessage());
}
}
//TODO: make player data loading async
//TODO: we shouldn't be loading player data here at all, but right now we don't have any choice :(
$this->cachedOfflinePlayerData = $this->server->getOfflinePlayerData($this->info->getUsername());
if($checkXUID){
$recordedXUID = $this->cachedOfflinePlayerData !== null ? $this->cachedOfflinePlayerData->getTag("LastKnownXUID") : null;
if(!($recordedXUID instanceof StringTag)){
$this->logger->debug("No previous XUID recorded, no choice but to trust this player");
}elseif(!$kickForXUIDMismatch($recordedXUID->getValue())){
$this->logger->debug("XUID match");
}
}
if($this->manager->kickDuplicates($this)){
if(EncryptionContext::$ENABLED){
$this->server->getAsyncPool()->submitTask(new PrepareEncryptionTask($clientPubKey, function(string $encryptionKey, string $handshakeJwt) : void{
if(!$this->connected){
return;
}
$this->sendDataPacket(ServerToClientHandshakePacket::create($handshakeJwt), true); //make sure this gets sent before encryption is enabled
if(EncryptionContext::$ENABLED){
$this->server->getAsyncPool()->submitTask(new PrepareEncryptionTask($clientPubKey, function(string $encryptionKey, string $handshakeJwt) : void{
if(!$this->connected){
return;
}
$this->sendDataPacket(ServerToClientHandshakePacket::create($handshakeJwt), true); //make sure this gets sent before encryption is enabled
$this->cipher = new EncryptionContext($encryptionKey);
$this->cipher = new EncryptionContext($encryptionKey);
$this->setHandler(new HandshakePacketHandler(function() : void{
$this->onServerLoginSuccess();
}));
$this->logger->debug("Enabled encryption");
$this->setHandler(new HandshakePacketHandler(function() : void{
$this->onServerLoginSuccess();
}));
}else{
$this->onServerLoginSuccess();
}
$this->logger->debug("Enabled encryption");
}));
}else{
$this->onServerLoginSuccess();
}
}

View File

@ -44,6 +44,7 @@ class ResourcePackClientResponsePacket extends DataPacket implements Serverbound
protected function decodePayload(PacketSerializer $in) : void{
$this->status = $in->getByte();
$entryCount = $in->getLShort();
$this->packIds = [];
while($entryCount-- > 0){
$this->packIds[] = $in->getString();
}