mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-03 08:39:53 +00:00
Allow offering different resource packs to different players (#6249)
closes #6248
This commit is contained in:
parent
98042f844f
commit
90409b50d1
106
src/event/player/PlayerResourcePackOfferEvent.php
Normal file
106
src/event/player/PlayerResourcePackOfferEvent.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?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\event\player;
|
||||||
|
|
||||||
|
use pocketmine\event\Event;
|
||||||
|
use pocketmine\player\PlayerInfo;
|
||||||
|
use pocketmine\resourcepacks\ResourcePack;
|
||||||
|
use function array_unshift;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after a player authenticates and is being offered resource packs to download.
|
||||||
|
*
|
||||||
|
* This event should be used to decide which resource packs to offer the player and whether to require the player to
|
||||||
|
* download the packs before they can join the server.
|
||||||
|
*/
|
||||||
|
class PlayerResourcePackOfferEvent extends Event{
|
||||||
|
/**
|
||||||
|
* @param ResourcePack[] $resourcePacks
|
||||||
|
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||||
|
*
|
||||||
|
* @phpstan-param list<ResourcePack> $resourcePacks
|
||||||
|
* @phpstan-param array<string, string> $encryptionKeys
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly PlayerInfo $playerInfo,
|
||||||
|
private array $resourcePacks,
|
||||||
|
private array $encryptionKeys,
|
||||||
|
private bool $mustAccept
|
||||||
|
){}
|
||||||
|
|
||||||
|
public function getPlayerInfo() : PlayerInfo{
|
||||||
|
return $this->playerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a resource pack to the top of the stack.
|
||||||
|
* The resources in this pack will be applied over the top of any existing packs.
|
||||||
|
*/
|
||||||
|
public function addResourcePack(ResourcePack $entry, ?string $encryptionKey = null) : void{
|
||||||
|
array_unshift($this->resourcePacks, $entry);
|
||||||
|
if($encryptionKey !== null){
|
||||||
|
$this->encryptionKeys[$entry->getPackId()] = $encryptionKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resource packs to offer. Packs are applied from the highest key to the lowest, with each pack
|
||||||
|
* overwriting any resources from the previous pack. This means that the pack at index 0 gets the final say on which
|
||||||
|
* resources are used.
|
||||||
|
*
|
||||||
|
* @param ResourcePack[] $resourcePacks
|
||||||
|
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||||
|
*
|
||||||
|
* @phpstan-param list<ResourcePack> $resourcePacks
|
||||||
|
* @phpstan-param array<string, string> $encryptionKeys
|
||||||
|
*/
|
||||||
|
public function setResourcePacks(array $resourcePacks, array $encryptionKeys) : void{
|
||||||
|
$this->resourcePacks = $resourcePacks;
|
||||||
|
$this->encryptionKeys = $encryptionKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ResourcePack[]
|
||||||
|
* @phpstan-return list<ResourcePack>
|
||||||
|
*/
|
||||||
|
public function getResourcePacks() : array{
|
||||||
|
return $this->resourcePacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
* @phpstan-return array<string, string>
|
||||||
|
*/
|
||||||
|
public function getEncryptionKeys() : array{
|
||||||
|
return $this->encryptionKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMustAccept(bool $mustAccept) : void{
|
||||||
|
$this->mustAccept = $mustAccept;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mustAccept() : bool{
|
||||||
|
return $this->mustAccept;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
|
|||||||
|
|
||||||
use pocketmine\entity\effect\EffectInstance;
|
use pocketmine\entity\effect\EffectInstance;
|
||||||
use pocketmine\event\player\PlayerDuplicateLoginEvent;
|
use pocketmine\event\player\PlayerDuplicateLoginEvent;
|
||||||
|
use pocketmine\event\player\PlayerResourcePackOfferEvent;
|
||||||
use pocketmine\event\server\DataPacketDecodeEvent;
|
use pocketmine\event\server\DataPacketDecodeEvent;
|
||||||
use pocketmine\event\server\DataPacketReceiveEvent;
|
use pocketmine\event\server\DataPacketReceiveEvent;
|
||||||
use pocketmine\event\server\DataPacketSendEvent;
|
use pocketmine\event\server\DataPacketSendEvent;
|
||||||
@ -844,7 +845,19 @@ class NetworkSession{
|
|||||||
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS));
|
$this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS));
|
||||||
|
|
||||||
$this->logger->debug("Initiating resource packs phase");
|
$this->logger->debug("Initiating resource packs phase");
|
||||||
$this->setHandler(new ResourcePacksPacketHandler($this, $this->server->getResourcePackManager(), function() : void{
|
|
||||||
|
$packManager = $this->server->getResourcePackManager();
|
||||||
|
$resourcePacks = $packManager->getResourceStack();
|
||||||
|
$keys = [];
|
||||||
|
foreach($resourcePacks as $resourcePack){
|
||||||
|
$key = $packManager->getPackEncryptionKey($resourcePack->getPackId());
|
||||||
|
if($key !== null){
|
||||||
|
$keys[$resourcePack->getPackId()] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$event = new PlayerResourcePackOfferEvent($this->info, $resourcePacks, $keys, $packManager->resourcePacksRequired());
|
||||||
|
$event->call();
|
||||||
|
$this->setHandler(new ResourcePacksPacketHandler($this, $event->getResourcePacks(), $event->getEncryptionKeys(), $event->mustAccept(), function() : void{
|
||||||
$this->createPlayer();
|
$this->createPlayer();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,13 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
|
|||||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
|
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
|
||||||
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
|
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
|
||||||
use pocketmine\resourcepacks\ResourcePack;
|
use pocketmine\resourcepacks\ResourcePack;
|
||||||
use pocketmine\resourcepacks\ResourcePackManager;
|
use function array_keys;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
use function ceil;
|
use function ceil;
|
||||||
use function count;
|
use function count;
|
||||||
use function implode;
|
use function implode;
|
||||||
use function strpos;
|
use function strpos;
|
||||||
|
use function strtolower;
|
||||||
use function substr;
|
use function substr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,35 +53,55 @@ use function substr;
|
|||||||
class ResourcePacksPacketHandler extends PacketHandler{
|
class ResourcePacksPacketHandler extends PacketHandler{
|
||||||
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB
|
private const PACK_CHUNK_SIZE = 128 * 1024; //128KB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ResourcePack[]
|
||||||
|
* @phpstan-var array<string, ResourcePack>
|
||||||
|
*/
|
||||||
|
private array $resourcePacksById = [];
|
||||||
|
|
||||||
/** @var bool[][] uuid => [chunk index => hasSent] */
|
/** @var bool[][] uuid => [chunk index => hasSent] */
|
||||||
private array $downloadedChunks = [];
|
private array $downloadedChunks = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param ResourcePack[] $resourcePackStack
|
||||||
|
* @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted
|
||||||
|
*
|
||||||
|
* @phpstan-param list<ResourcePack> $resourcePackStack
|
||||||
|
* @phpstan-param array<string, string> $encryptionKeys
|
||||||
* @phpstan-param \Closure() : void $completionCallback
|
* @phpstan-param \Closure() : void $completionCallback
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private NetworkSession $session,
|
private NetworkSession $session,
|
||||||
private ResourcePackManager $resourcePackManager,
|
private array $resourcePackStack,
|
||||||
|
private array $encryptionKeys,
|
||||||
|
private bool $mustAccept,
|
||||||
private \Closure $completionCallback
|
private \Closure $completionCallback
|
||||||
){}
|
){
|
||||||
|
foreach($resourcePackStack as $pack){
|
||||||
|
$this->resourcePacksById[$pack->getPackId()] = $pack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPackById(string $id) : ?ResourcePack{
|
||||||
|
return $this->resourcePacksById[strtolower($id)] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
public function setUp() : void{
|
public function setUp() : void{
|
||||||
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
|
$resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{
|
||||||
//TODO: more stuff
|
//TODO: more stuff
|
||||||
$encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId());
|
|
||||||
|
|
||||||
return new ResourcePackInfoEntry(
|
return new ResourcePackInfoEntry(
|
||||||
$pack->getPackId(),
|
$pack->getPackId(),
|
||||||
$pack->getPackVersion(),
|
$pack->getPackVersion(),
|
||||||
$pack->getPackSize(),
|
$pack->getPackSize(),
|
||||||
$encryptionKey ?? "",
|
$this->encryptionKeys[$pack->getPackId()] ?? "",
|
||||||
"",
|
"",
|
||||||
$pack->getPackId(),
|
$pack->getPackId(),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}, $this->resourcePackManager->getResourceStack());
|
}, $this->resourcePackStack);
|
||||||
// TODO: support forcing server packs
|
// TODO: support forcing server packs
|
||||||
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, []));
|
$this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->mustAccept, false, false, []));
|
||||||
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
|
$this->session->getLogger()->debug("Waiting for client to accept resource packs");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +125,11 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
|||||||
if($splitPos !== false){
|
if($splitPos !== false){
|
||||||
$uuid = substr($uuid, 0, $splitPos);
|
$uuid = substr($uuid, 0, $splitPos);
|
||||||
}
|
}
|
||||||
$pack = $this->resourcePackManager->getPackById($uuid);
|
$pack = $this->getPackById($uuid);
|
||||||
|
|
||||||
if(!($pack instanceof ResourcePack)){
|
if(!($pack instanceof ResourcePack)){
|
||||||
//Client requested a resource pack but we don't have it available on the server
|
//Client requested a resource pack but we don't have it available on the server
|
||||||
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
|
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +149,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
|||||||
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
|
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
|
||||||
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
|
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
|
||||||
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
|
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
|
||||||
}, $this->resourcePackManager->getResourceStack());
|
}, $this->resourcePackStack);
|
||||||
|
|
||||||
//we support chemistry blocks by default, the client should already have this installed
|
//we support chemistry blocks by default, the client should already have this installed
|
||||||
$stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", "");
|
$stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", "");
|
||||||
@ -151,9 +172,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{
|
||||||
$pack = $this->resourcePackManager->getPackById($packet->packId);
|
$pack = $this->getPackById($packet->packId);
|
||||||
if(!($pack instanceof ResourcePack)){
|
if(!($pack instanceof ResourcePack)){
|
||||||
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList()));
|
$this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,6 +650,11 @@ parameters:
|
|||||||
count: 2
|
count: 2
|
||||||
path: ../../../src/network/mcpe/NetworkSession.php
|
path: ../../../src/network/mcpe/NetworkSession.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$playerInfo of class pocketmine\\\\event\\\\player\\\\PlayerResourcePackOfferEvent constructor expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: ../../../src/network/mcpe/NetworkSession.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user