mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-11 08:19:45 +00:00
Added network components for skins, to collect instead of throw out data
This commit is contained in:
parent
7f3460190b
commit
be9c413a9e
@ -146,6 +146,9 @@ 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\types\SkinAnimation;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinImage;
|
||||
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\mcpe\VerifyLoginTask;
|
||||
@ -1915,9 +1918,32 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->uuid = UUID::fromString($packet->clientUUID);
|
||||
$this->rawUUID = $this->uuid->toBinary();
|
||||
|
||||
$animations = [];
|
||||
foreach($packet->clientData["AnimatedImageData"] as $animation){
|
||||
$animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], $animation["Image"]), $animation["Type"], $animation["Frames"]);
|
||||
}
|
||||
new SkinData(
|
||||
$packet->clientData["SkinId"],
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
|
||||
new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])),
|
||||
$animations,
|
||||
new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")),
|
||||
base64_decode($packet->clientData["SkinGeometryData"] ?? ""),
|
||||
base64_decode($packet->clientData["AnimationData"] ?? ""),
|
||||
$packet->clientData["PremiumSkin"] ?? false,
|
||||
$packet->clientData["PersonaSkin"] ?? false,
|
||||
$packet->clientData["CapeOnClassicSkin"] ?? false,
|
||||
$packet->clientData["CapeId"] ?? ""
|
||||
);
|
||||
|
||||
$skinData = base64_decode($packet->clientData["SkinData"]);
|
||||
if((bool) $packet->clientData["PersonaSkin"]){
|
||||
$skinData = str_repeat(random_bytes(3) . "\xff", 2048);
|
||||
}
|
||||
|
||||
$skin = new Skin(
|
||||
$packet->clientData["SkinId"],
|
||||
base64_decode($packet->clientData["SkinData"] ?? ""),
|
||||
$skinData,
|
||||
base64_decode($packet->clientData["CapeData"] ?? ""),
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
|
||||
base64_decode($packet->clientData["SkinGeometryData"] ?? "")
|
||||
|
@ -76,6 +76,7 @@ use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\RakLibInterface;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\query\QueryHandler;
|
||||
@ -2399,7 +2400,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId);
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, SkinAdapterSingleton::get()->toSkinData($skin), $xboxUserId);
|
||||
|
||||
$this->broadcastPacket($players ?? $this->playerList, $pk);
|
||||
}
|
||||
@ -2422,7 +2423,7 @@ class Server{
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
foreach($this->playerList as $player){
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
|
||||
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid());
|
||||
}
|
||||
|
||||
$p->dataPacket($pk);
|
||||
|
@ -53,6 +53,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\UUID;
|
||||
use function array_filter;
|
||||
@ -182,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
public function sendSkin(?array $targets = null) : void{
|
||||
$pk = new PlayerSkinPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->skin = $this->skin;
|
||||
$pk->skin = SkinAdapterSingleton::get()->toSkinData($this->skin);
|
||||
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
|
||||
}
|
||||
|
||||
@ -850,7 +851,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\utils\UUID;
|
||||
use function str_repeat;
|
||||
|
||||
@ -96,7 +97,9 @@ class FloatingTextParticle extends Particle{
|
||||
|
||||
$add = new PlayerListPacket();
|
||||
$add->type = PlayerListPacket::TYPE_ADD;
|
||||
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))];
|
||||
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin(
|
||||
"Standard_Custom", str_repeat("\x00", 8192), "", '{"geometry" : {"default" : "geometry.humanoid.custom"}}' //TODO: Remove, hack to fix ftp
|
||||
)))];
|
||||
$p[] = $add;
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
|
@ -38,6 +38,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinImage;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
use pocketmine\network\mcpe\protocol\types\StructureSettings;
|
||||
@ -77,7 +78,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putLInt($uuid->getPart(2));
|
||||
}
|
||||
|
||||
public function getSkin() : Skin{
|
||||
public function getSkin() : SkinData{
|
||||
$skinId = $this->getString();
|
||||
$skinResourcePatch = $this->getString();
|
||||
$skinData = $this->getSkinImage();
|
||||
@ -99,32 +100,26 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$capeId = $this->getString();
|
||||
$fullSkinId = $this->getString();
|
||||
|
||||
return new Skin($skinId, $skinData->getData(), $capeData->getData(), $skinResourcePatch, $geometryData);
|
||||
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId);
|
||||
}
|
||||
|
||||
public function putSkin(Skin $skin){
|
||||
$this->putString($skin->getSkinId());
|
||||
$this->putString($skin->getGeometryName()); //resource patch
|
||||
$this->putSkinImage(SkinImage::fromLegacy($skin->getSkinData()));
|
||||
/** @var SkinAnimation[] $animations */
|
||||
$animations = [];
|
||||
$this->putLInt(count($animations));
|
||||
foreach($animations as $animation){
|
||||
public function putSkin(SkinData $skin){
|
||||
$this->putString($skin->skinId);
|
||||
$this->putString($skin->resourcePatch); //resource patch
|
||||
$this->putSkinImage($skin->skinImage);
|
||||
$this->putLInt(count($skin->animations));
|
||||
foreach($skin->animations as $animation){
|
||||
$this->putSkinImage($animation->getImage());
|
||||
$this->putLInt($animation->getType());
|
||||
$this->putLFloat($animation->getFrames());
|
||||
}
|
||||
if($skin->getCapeData() !== ""){
|
||||
$this->putSkinImage(new SkinImage(32, 64, $skin->getCapeData()));
|
||||
}else{
|
||||
$this->putSkinImage(new SkinImage(0, 0, ""));
|
||||
}
|
||||
$this->putString($skin->getGeometryData());
|
||||
$this->putString(""); //animation data
|
||||
$this->putBool(false); //isPremium
|
||||
$this->putBool(false); //isPersona
|
||||
$this->putBool(false); //isCapeOnClassic
|
||||
$this->putString(""); //capeId
|
||||
$this->putSkinImage($skin->capeImage);
|
||||
$this->putString($skin->geometryData);
|
||||
$this->putString($skin->animationData);
|
||||
$this->putBool($skin->premium);
|
||||
$this->putBool($skin->persona);
|
||||
$this->putBool($skin->capeOnClassic);
|
||||
$this->putString($skin->capeId);
|
||||
|
||||
//this has to be unique or the client will do stupid things
|
||||
$this->putString(UUID::fromRandom()->toString()); //full skin ID
|
||||
|
@ -66,6 +66,8 @@ use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\TextPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
@ -253,7 +255,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
}
|
||||
|
||||
public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{
|
||||
return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName);
|
||||
return $this->player->changeSkin(SkinAdapterSingleton::get()->fromSkinData($packet->skin), $packet->newSkinName, $packet->oldSkinName);
|
||||
}
|
||||
|
||||
public function handleBookEdit(BookEditPacket $packet) : bool{
|
||||
|
@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use function count;
|
||||
|
||||
@ -81,7 +82,7 @@ class PlayerListPacket extends DataPacket{
|
||||
$this->putString($entry->xboxUserId);
|
||||
$this->putString($entry->platformChatId);
|
||||
$this->putLInt($entry->buildPlatform);
|
||||
$this->putSkin($entry->skin);
|
||||
$this->putSkin($entry->skinData);
|
||||
$this->putBool($entry->isTeacher);
|
||||
$this->putBool($entry->isHost);
|
||||
}else{
|
||||
|
@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinData;
|
||||
use pocketmine\utils\UUID;
|
||||
|
||||
class PlayerSkinPacket extends DataPacket{
|
||||
@ -38,7 +38,7 @@ class PlayerSkinPacket extends DataPacket{
|
||||
public $oldSkinName = "";
|
||||
/** @var string */
|
||||
public $newSkinName = "";
|
||||
/** @var Skin */
|
||||
/** @var SkinData */
|
||||
public $skin;
|
||||
|
||||
protected function decodePayload(){
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
|
||||
class LegacySkinAdapter implements SkinAdapter{
|
||||
|
||||
public function toSkinData(Skin $skin) : SkinData{
|
||||
return new SkinData($skin->getSkinId(), $skin->getGeometryName(), SkinImage::fromLegacy($skin->getSkinData()), [], new SkinImage(32, 64, $skin->getCapeData()), $skin->getGeometryData());
|
||||
}
|
||||
|
||||
public function fromSkinData(SkinData $data) : Skin{
|
||||
return new Skin($data->skinId, $data->skinImage->getData(), $data->capeImage->getData(), $data->resourcePatch, $data->geometryData);
|
||||
}
|
||||
}
|
@ -34,8 +34,8 @@ class PlayerListEntry{
|
||||
public $entityUniqueId;
|
||||
/** @var string */
|
||||
public $username;
|
||||
/** @var Skin */
|
||||
public $skin;
|
||||
/** @var SkinData */
|
||||
public $skinData;
|
||||
/** @var string */
|
||||
public $xboxUserId;
|
||||
/** @var string */
|
||||
@ -54,12 +54,12 @@ class PlayerListEntry{
|
||||
return $entry;
|
||||
}
|
||||
|
||||
public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{
|
||||
public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, SkinData $skinData, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{
|
||||
$entry = new PlayerListEntry();
|
||||
$entry->uuid = $uuid;
|
||||
$entry->entityUniqueId = $entityUniqueId;
|
||||
$entry->username = $username;
|
||||
$entry->skin = $skin;
|
||||
$entry->skinData = $skinData;
|
||||
$entry->xboxUserId = $xboxUserId;
|
||||
$entry->platformChatId = $platformChatId;
|
||||
$entry->buildPlatform = $buildPlatform;
|
||||
|
33
src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php
Normal file
33
src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
use pocketmine\entity\Skin;
|
||||
|
||||
interface SkinAdapter{
|
||||
|
||||
public function toSkinData(Skin $skin) : SkinData;
|
||||
|
||||
public function fromSkinData(SkinData $data) : Skin;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
class SkinAdapterSingleton{
|
||||
|
||||
private static $skinAdapter = null;
|
||||
|
||||
public static function get() : SkinAdapter{
|
||||
if(self::$skinAdapter === null){
|
||||
self::$skinAdapter = new LegacySkinAdapter();
|
||||
}
|
||||
return self::$skinAdapter;
|
||||
}
|
||||
|
||||
public static function set(SkinAdapter $adapter){
|
||||
self::$skinAdapter = $adapter;
|
||||
}
|
||||
}
|
64
src/pocketmine/network/mcpe/protocol/types/SkinData.php
Normal file
64
src/pocketmine/network/mcpe/protocol/types/SkinData.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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\protocol\types;
|
||||
|
||||
class SkinData{
|
||||
|
||||
/** @var string */
|
||||
public $skinId;
|
||||
/** @var string */
|
||||
public $resourcePatch;
|
||||
/** @var SkinImage */
|
||||
public $skinImage;
|
||||
/** @var SkinAnimation[] */
|
||||
public $animations;
|
||||
/** @var SkinImage */
|
||||
public $capeImage;
|
||||
/** @var string */
|
||||
public $geometryData;
|
||||
/** @var string */
|
||||
public $animationData;
|
||||
/** @var bool */
|
||||
public $persona;
|
||||
/** @var bool */
|
||||
public $premium;
|
||||
/** @var bool */
|
||||
public $capeOnClassic;
|
||||
/** @var string */
|
||||
public $capeId;
|
||||
|
||||
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $capeOnClassic = false, string $capeId = ""){
|
||||
$this->skinId = $skinId;
|
||||
$this->resourcePatch = $resourcePatch;
|
||||
$this->skinImage = $skinImage;
|
||||
$this->animations = $animations;
|
||||
$this->capeImage = $capeImage;
|
||||
$this->geometryData = $geometryData;
|
||||
$this->animationData = $animationData;
|
||||
$this->premium = $premium;
|
||||
$this->persona = $persona;
|
||||
$this->capeOnClassic = $capeOnClassic;
|
||||
$this->capeId = $capeId;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user