Merge commit '82d9e481d2a0a389fbbc6dfd3672fc366127febc'

This commit is contained in:
Dylan K. Taylor 2019-12-11 23:06:35 +00:00
commit c85c1c3c3f
58 changed files with 2030 additions and 296 deletions

20
changelogs/3.10.md Normal file
View File

@ -0,0 +1,20 @@
**For Minecraft: Bedrock Edition 1.13.0**
### Note about API versions
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
Plugin developers should **only** update their required API to this version if you need the changes in this build.
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
# 3.10.0
- Added support for Minecraft: Bedrock Edition 1.13.0
- Removed compatibility with 1.12.0
## Note about skins
PocketMine-MP **does not support skins made in the Charactor Creator** (known as Persona skins), due to technical changes which would require premature backwards compatibility breaks. The dev team has decided not to support Persona yet.
These skins will be **replaced with a random solid-colour skin. This is not a bug.**
Skins chosen from the Classic tab (classic skins) will continue to work as normal.
# 3.10.1
- Fixed custom plugin-created skins being invisible when no geometry name was specified.
- Updated RakLib to 0.12.6 to fix security bugs.

@ -1 +1 @@
Subproject commit b5a8c68c4262e5d9d7f8280c1d07c252a5e8dbf8
Subproject commit 0df4e593af7eca16c9e9dd4c6cf2f206be267fd3

View File

@ -48,6 +48,7 @@ use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\player\Player;
use pocketmine\utils\Limits;
use pocketmine\utils\UUID;
@ -153,7 +154,7 @@ class Human extends Living 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->broadcastPackets($targets ?? $this->hasSpawned, [$pk]);
}
@ -407,7 +408,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
protected function sendSpawnPacket(Player $player) : void{
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)]));
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]));
}
$pk = new AddPlayerPacket();

View File

@ -61,7 +61,7 @@ class InventoryManager{
$this->windowMap[ContainerIds::INVENTORY] = $this->player->getInventory();
$this->windowMap[ContainerIds::ARMOR] = $this->player->getArmorInventory();
$this->windowMap[ContainerIds::CURSOR] = $this->player->getCursorInventory();
$this->windowMap[ContainerIds::UI] = $this->player->getCursorInventory();
}
public function getWindowId(Inventory $inventory) : ?int{

View File

@ -73,6 +73,7 @@ use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\NetworkSessionManager;
use pocketmine\player\GameMode;
@ -726,7 +727,7 @@ class NetworkSession{
0,
$aliasObj,
[
[CommandParameter::standard("args", AvailableCommandsPacket::ARG_TYPE_RAWTEXT, true)]
[CommandParameter::standard("args", AvailableCommandsPacket::ARG_TYPE_RAWTEXT, 0, true)]
]
);
@ -830,12 +831,12 @@ class NetworkSession{
public function syncPlayerList() : void{
$this->sendDataPacket(PlayerListPacket::add(array_map(function(Player $player){
return PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
return PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid());
}, $this->server->getOnlinePlayers())));
}
public function onPlayerAdded(Player $p) : void{
$this->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($p->getUniqueId(), $p->getId(), $p->getDisplayName(), $p->getSkin(), $p->getXuid())]));
$this->sendDataPacket(PlayerListPacket::add([PlayerListEntry::createAdditionEntry($p->getUniqueId(), $p->getId(), $p->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($p->getSkin()), $p->getXuid())]));
}
public function onPlayerRemoved(Player $p) : void{

View File

@ -41,7 +41,11 @@ class DeathPacketHandler extends PacketHandler{
}
public function setUp() : void{
$this->session->sendDataPacket(RespawnPacket::create($this->player->getOffsetPosition($this->player->getSpawn())));
$this->session->sendDataPacket(RespawnPacket::create(
$this->player->getOffsetPosition($this->player->getSpawn()),
RespawnPacket::SEARCHING_FOR_SPAWN,
$this->player->getId()
));
}
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
@ -56,4 +60,16 @@ class DeathPacketHandler extends PacketHandler{
return false;
}
public function handleRespawn(RespawnPacket $packet) : bool{
if($packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){
$this->session->sendDataPacket(RespawnPacket::create(
$this->player->getOffsetPosition($this->player->getSpawn()),
RespawnPacket::READY_TO_SPAWN,
$this->player->getId()
));
return true;
}
return false;
}
}

View File

@ -75,7 +75,10 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\NetworkInventoryAction;
use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData;
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
@ -197,8 +200,24 @@ class InGamePacketHandler extends PacketHandler{
$isCrafting = false;
$isFinalCraftingPart = false;
foreach($data->getActions() as $networkInventoryAction){
$isCrafting = $isCrafting || $networkInventoryAction->isCraftingPart();
$isFinalCraftingPart = $isFinalCraftingPart || $networkInventoryAction->isFinalCraftingPart();
if(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and
$networkInventoryAction->windowId === ContainerIds::UI and
$networkInventoryAction->inventorySlot === 50 and
!$networkInventoryAction->oldItem->equalsExact($networkInventoryAction->newItem)
){
$isCrafting = true;
if(!$networkInventoryAction->oldItem->isNull() and $networkInventoryAction->newItem->isNull()){
$isFinalCraftingPart = true;
}
}elseif(
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
)
){
$isCrafting = true;
}
try{
$action = $networkInventoryAction->createInventoryAction($this->player);
@ -285,6 +304,10 @@ class InGamePacketHandler extends PacketHandler{
}
return true;
case UseItemTransactionData::ACTION_CLICK_AIR:
if($this->player->isUsingItem() and !$this->player->consumeHeldItem()){
$this->session->getInvManager()->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
return true;
}
if(!$this->player->useHeldItem()){
$this->session->getInvManager()->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
}
@ -347,11 +370,6 @@ class InGamePacketHandler extends PacketHandler{
$this->session->getInvManager()->syncContents($this->player->getInventory());
}
return true;
case ReleaseItemTransactionData::ACTION_CONSUME:
if(!$this->player->consumeHeldItem()){
$this->session->getInvManager()->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
}
return true;
}
return false;
@ -603,7 +621,7 @@ class InGamePacketHandler extends PacketHandler{
}
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 handleSubClientLogin(SubClientLoginPacket $packet) : bool{

View File

@ -30,6 +30,10 @@ 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\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\network\mcpe\protocol\types\SkinImage;
use pocketmine\player\Player;
use pocketmine\player\PlayerInfo;
use pocketmine\Server;
@ -72,13 +76,34 @@ class LoginPacketHandler extends PacketHandler{
}
try{
$skin = new Skin(
/** @var SkinAnimation[] $animations */
$animations = [];
foreach($packet->clientData[LoginPacket::I_ANIMATION_IMAGES] as $animation){
$animations[] = new SkinAnimation(
new SkinImage(
$animation[LoginPacket::I_ANIMATION_IMAGE_HEIGHT],
$animation[LoginPacket::I_ANIMATION_IMAGE_WIDTH],
base64_decode($animation[LoginPacket::I_ANIMATION_IMAGE_DATA])
),
$animation[LoginPacket::I_ANIMATION_IMAGE_TYPE],
$animation[LoginPacket::I_ANIMATION_IMAGE_FRAMES]
);
}
$skinData = new SkinData(
$packet->clientData[LoginPacket::I_SKIN_ID],
base64_decode($packet->clientData[LoginPacket::I_SKIN_DATA]),
base64_decode($packet->clientData[LoginPacket::I_CAPE_DATA]),
$packet->clientData[LoginPacket::I_GEOMETRY_NAME],
base64_decode($packet->clientData[LoginPacket::I_GEOMETRY_DATA])
base64_decode($packet->clientData[LoginPacket::I_SKIN_RESOURCE_PATCH]),
new SkinImage($packet->clientData[LoginPacket::I_SKIN_HEIGHT], $packet->clientData[LoginPacket::I_SKIN_WIDTH], base64_decode($packet->clientData[LoginPacket::I_SKIN_DATA])),
$animations,
new SkinImage($packet->clientData[LoginPacket::I_CAPE_HEIGHT], $packet->clientData[LoginPacket::I_CAPE_WIDTH], base64_decode($packet->clientData[LoginPacket::I_CAPE_DATA])),
base64_decode($packet->clientData[LoginPacket::I_GEOMETRY_DATA]),
base64_decode($packet->clientData[LoginPacket::I_ANIMATION_DATA]),
$packet->clientData[LoginPacket::I_PREMIUM_SKIN],
$packet->clientData[LoginPacket::I_PERSONA_SKIN],
$packet->clientData[LoginPacket::I_PERSONA_CAPE_ON_CLASSIC_SKIN],
$packet->clientData[LoginPacket::I_CAPE_ID]
);
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
}catch(\InvalidArgumentException $e){
$this->session->getLogger()->debug("Invalid skin: " . $e->getMessage());
$this->session->disconnect("disconnectionScreen.invalidSkin");

View File

@ -34,6 +34,7 @@ use pocketmine\network\mcpe\protocol\AddPaintingPacket;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
@ -54,14 +55,16 @@ use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket;
use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket;
use pocketmine\network\mcpe\protocol\CommandOutputPacket;
use pocketmine\network\mcpe\protocol\CommandRequestPacket;
use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
use pocketmine\network\mcpe\protocol\DisconnectPacket;
use pocketmine\network\mcpe\protocol\EducationSettingsPacket;
use pocketmine\network\mcpe\protocol\EmotePacket;
use pocketmine\network\mcpe\protocol\EventPacket;
use pocketmine\network\mcpe\protocol\ExplodePacket;
use pocketmine\network\mcpe\protocol\GameRulesChangedPacket;
use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket;
use pocketmine\network\mcpe\protocol\HurtArmorPacket;
@ -89,12 +92,15 @@ use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\MultiplayerSettingsPacket;
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
use pocketmine\network\mcpe\protocol\NetworkSettingsPacket;
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
use pocketmine\network\mcpe\protocol\OnScreenTextureAnimationPacket;
use pocketmine\network\mcpe\protocol\PhotoTransferPacket;
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
@ -133,6 +139,7 @@ use pocketmine\network\mcpe\protocol\SetScoreboardIdentityPacket;
use pocketmine\network\mcpe\protocol\SetScorePacket;
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
use pocketmine\network\mcpe\protocol\SetTimePacket;
use pocketmine\network\mcpe\protocol\SettingsCommandPacket;
use pocketmine\network\mcpe\protocol\SetTitlePacket;
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\ShowProfilePacket;
@ -143,11 +150,12 @@ use pocketmine\network\mcpe\protocol\SpawnParticleEffectPacket;
use pocketmine\network\mcpe\protocol\StartGamePacket;
use pocketmine\network\mcpe\protocol\StopSoundPacket;
use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
use pocketmine\network\mcpe\protocol\StructureTemplateDataExportRequestPacket;
use pocketmine\network\mcpe\protocol\StructureTemplateDataExportResponsePacket;
use pocketmine\network\mcpe\protocol\StructureTemplateDataRequestPacket;
use pocketmine\network\mcpe\protocol\StructureTemplateDataResponsePacket;
use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\TickSyncPacket;
use pocketmine\network\mcpe\protocol\TransferPacket;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
@ -254,7 +262,7 @@ abstract class PacketHandler{
return false;
}
public function handleExplode(ExplodePacket $packet) : bool{
public function handleTickSync(TickSyncPacket $packet) : bool{
return false;
}
@ -686,11 +694,11 @@ abstract class PacketHandler{
return false;
}
public function handleStructureTemplateDataExportRequest(StructureTemplateDataExportRequestPacket $packet) : bool{
public function handleStructureTemplateDataRequest(StructureTemplateDataRequestPacket $packet) : bool{
return false;
}
public function handleStructureTemplateDataExportResponse(StructureTemplateDataExportResponsePacket $packet) : bool{
public function handleStructureTemplateDataResponse(StructureTemplateDataResponsePacket $packet) : bool{
return false;
}
@ -705,4 +713,36 @@ abstract class PacketHandler{
public function handleClientCacheMissResponse(ClientCacheMissResponsePacket $packet) : bool{
return false;
}
public function handleEducationSettings(EducationSettingsPacket $packet) : bool{
return false;
}
public function handleEmote(EmotePacket $packet) : bool{
return false;
}
public function handleMultiplayerSettings(MultiplayerSettingsPacket $packet) : bool{
return false;
}
public function handleSettingsCommand(SettingsCommandPacket $packet) : bool{
return false;
}
public function handleAnvilDamage(AnvilDamagePacket $packet) : bool{
return false;
}
public function handleCompletedUsingItem(CompletedUsingItemPacket $packet) : bool{
return false;
}
public function handleNetworkSettings(NetworkSettingsPacket $packet) : bool{
return false;
}
public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{
return false;
}
}

View File

@ -71,7 +71,7 @@ class PreSpawnPacketHandler extends PacketHandler{
$pk->spawnZ = $spawnPosition->getFloorZ();
$pk->hasAchievementsDisabled = true;
$pk->time = $location->getWorld()->getTime();
$pk->eduMode = false;
$pk->eduEditionOffer = 0;
$pk->rainLevel = 0; //TODO: implement these properly
$pk->lightningLevel = 0;
$pk->commandsEnabled = true;

View File

@ -75,6 +75,8 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{
/** @var string */
public $deviceId = ""; //TODO: fill player's device ID (???)
/** @var int */
public $buildPlatform = -1;
protected function decodePayload() : void{
$this->uuid = $this->getUUID();
@ -104,6 +106,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{
}
$this->deviceId = $this->getString();
$this->buildPlatform = $this->getLInt();
}
protected function encodePayload() : void{
@ -134,6 +137,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{
}
$this->putString($this->deviceId);
$this->putLInt($this->buildPlatform);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -0,0 +1,78 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class AnvilDamagePacket extends DataPacket implements ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::ANVIL_DAMAGE_PACKET;
/** @var int */
private $x;
/** @var int */
private $y;
/** @var int */
private $z;
/** @var int */
private $damageAmount;
public static function create(int $x, int $y, int $z, int $damageAmount) : self{
$result = new self;
[$result->x, $result->y, $result->z] = [$x, $y, $z];
$result->damageAmount = $damageAmount;
return $result;
}
public function getDamageAmount() : int{
return $this->damageAmount;
}
public function getX() : int{
return $this->x;
}
public function getY() : int{
return $this->y;
}
public function getZ() : int{
return $this->z;
}
protected function decodePayload() : void{
$this->damageAmount = $this->getByte();
$this->getBlockPosition($this->x, $this->y, $this->z);
}
protected function encodePayload() : void{
$this->putByte($this->damageAmount);
$this->putBlockPosition($this->x, $this->y, $this->z);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleAnvilDamage($this);
}
}

File diff suppressed because one or more lines are too long

View File

@ -29,6 +29,7 @@ use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\command\CommandData;
use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
use pocketmine\network\mcpe\protocol\types\command\CommandEnumConstraint;
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
use pocketmine\utils\BinaryDataException;
use function count;
@ -57,17 +58,17 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
public const ARG_TYPE_FILEPATH = 0x0e;
public const ARG_TYPE_STRING = 0x1b;
public const ARG_TYPE_STRING = 0x1d;
public const ARG_TYPE_POSITION = 0x1d;
public const ARG_TYPE_POSITION = 0x25;
public const ARG_TYPE_MESSAGE = 0x20;
public const ARG_TYPE_MESSAGE = 0x29;
public const ARG_TYPE_RAWTEXT = 0x22;
public const ARG_TYPE_RAWTEXT = 0x2b;
public const ARG_TYPE_JSON = 0x25;
public const ARG_TYPE_JSON = 0x2f;
public const ARG_TYPE_COMMAND = 0x2c;
public const ARG_TYPE_COMMAND = 0x36;
/**
* Enums are a little different: they are composed as follows:
@ -80,12 +81,23 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
*/
public const ARG_FLAG_POSTFIX = 0x1000000;
public const HARDCODED_ENUM_NAMES = [
"CommandName" => true
];
/**
* @var CommandData[]
* List of command data, including name, description, alias indexes and parameters.
*/
public $commandData = [];
/**
* @var CommandEnum[]
* List of enums which aren't directly referenced by any vanilla command.
* This is used for the `CommandName` enum, which is a magic enum used by the `command` argument type.
*/
public $hardcodedEnums = [];
/**
* @var CommandEnum[]
* List of dynamic command enums, also referred to as "soft" enums. These can by dynamically updated mid-game
@ -93,6 +105,12 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
*/
public $softEnums = [];
/**
* @var CommandEnumConstraint[]
* List of constraints for enum members. Used to constrain gamerules that can bechanged in nocheats mode and more.
*/
public $enumConstraints = [];
protected function decodePayload() : void{
/** @var string[] $enumValues */
$enumValues = [];
@ -109,7 +127,10 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
/** @var CommandEnum[] $enums */
$enums = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$enums[] = $this->getEnum($enumValues);
$enums[] = $enum = $this->getEnum($enumValues);
if(isset(self::HARDCODED_ENUM_NAMES[$enum->getName()])){
$this->hardcodedEnums[] = $enum;
}
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
@ -119,6 +140,10 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->softEnums[] = $this->getSoftEnum();
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->enumConstraints[] = $this->getEnumConstraint($enums, $enumValues);
}
}
/**
@ -217,6 +242,52 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $enumValues
*
* @return CommandEnumConstraint
* @throws BadPacketException
* @throws BinaryDataException
*/
protected function getEnumConstraint(array $enums, array $enumValues) : CommandEnumConstraint{
//wtf, what was wrong with an offset inside the enum? :(
$valueIndex = $this->getLInt();
if(!isset($enumValues[$valueIndex])){
throw new BadPacketException("Enum constraint refers to unknown enum value index $valueIndex");
}
$enumIndex = $this->getLInt();
if(!isset($enums[$enumIndex])){
throw new BadPacketException("Enum constraint refers to unknown enum index $enumIndex");
}
$enum = $enums[$enumIndex];
$valueOffset = array_search($enumValues[$valueIndex], $enum->getValues(), true);
if($valueOffset === false){
throw new BadPacketException("Value \"" . $enumValues[$valueIndex] . "\" does not belong to enum \"" . $enum->getName() . "\"");
}
$constraintIds = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$constraintIds[] = $this->getByte();
}
return new CommandEnumConstraint($enum, $valueOffset, $constraintIds);
}
/**
* @param CommandEnumConstraint $constraint
* @param int[] $enumIndexes string enum name -> int index
* @param int[] $enumValueIndexes string value -> int index
*/
protected function putEnumConstraint(CommandEnumConstraint $constraint, array $enumIndexes, array $enumValueIndexes) : void{
$this->putLInt($enumValueIndexes[$constraint->getAffectedValue()]);
$this->putLInt($enumIndexes[$constraint->getEnum()->getName()]);
$this->putUnsignedVarInt(count($constraint->getConstraints()));
foreach($constraint->getConstraints() as $v){
$this->putByte($v);
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $postfixes
@ -365,6 +436,9 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); //latest index
}
};
foreach($this->hardcodedEnums as $enum){
$addEnumFn($enum);
}
foreach($this->commandData as $commandData){
if($commandData->aliases !== null){
$addEnumFn($commandData->aliases);
@ -408,6 +482,11 @@ class AvailableCommandsPacket extends DataPacket implements ClientboundPacket{
foreach($this->softEnums as $enum){
$this->putSoftEnum($enum);
}
$this->putUnsignedVarInt(count($this->enumConstraints));
foreach($this->enumConstraints as $constraint){
$this->putEnumConstraint($constraint, $enumIndexes, $enumValueIndexes);
}
}
public function handle(PacketHandler $handler) : bool{

View File

@ -26,11 +26,14 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
use function file_get_contents;
class BiomeDefinitionListPacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::BIOME_DEFINITION_LIST_PACKET;
public const HARDCODED_NBT_BLOB = "CgAKDWJhbWJvb19qdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTYmFtYm9vX2p1bmdsZV9oaWxscwUIZG93bmZhbGxmZmY/BQt0ZW1wZXJhdHVyZTMzcz8ACgViZWFjaAUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACgxiaXJjaF9mb3Jlc3QFCGRvd25mYWxsmpkZPwULdGVtcGVyYXR1cmWamRk/AAoSYmlyY2hfZm9yZXN0X2hpbGxzBQhkb3duZmFsbJqZGT8FC3RlbXBlcmF0dXJlmpkZPwAKGmJpcmNoX2ZvcmVzdF9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFGJpcmNoX2ZvcmVzdF9tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKCmNvbGRfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw9AAoKY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgpjb2xkX3RhaWdhBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKEGNvbGRfdGFpZ2FfaGlsbHMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmUAAAC/AAoSY29sZF90YWlnYV9tdXRhdGVkBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKD2RlZXBfY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8AChFkZWVwX2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChNkZWVwX2x1a2V3YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCmRlZXBfb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAoPZGVlcF93YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKBmRlc2VydAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgxkZXNlcnRfaGlsbHMFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoOZGVzZXJ0X211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoNZXh0cmVtZV9oaWxscwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4AChJleHRyZW1lX2hpbGxzX2VkZ2UFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoVZXh0cmVtZV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbJqZmT4FC3RlbXBlcmF0dXJlzcxMPgAKGGV4dHJlbWVfaGlsbHNfcGx1c190cmVlcwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACiBleHRyZW1lX2hpbGxzX3BsdXNfdHJlZXNfbXV0YXRlZAUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACg1mbG93ZXJfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKBmZvcmVzdAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgxmb3Jlc3RfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzMzM/AAoMZnJvemVuX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAAAAKDGZyb3plbl9yaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACgRoZWxsBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDWljZV9tb3VudGFpbnMFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAAAAAoKaWNlX3BsYWlucwUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChFpY2VfcGxhaW5zX3NwaWtlcwUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZQAAAAAACgZqdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoLanVuZ2xlX2VkZ2UFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzM3M/AAoTanVuZ2xlX2VkZ2VfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzcz8ACgxqdW5nbGVfaGlsbHMFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoOanVuZ2xlX211dGF0ZWQFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTbGVnYWN5X2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACg5sdWtld2FybV9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgptZWdhX3RhaWdhBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKEG1lZ2FfdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmWamZk+AAoEbWVzYQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgptZXNhX2JyeWNlBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDG1lc2FfcGxhdGVhdQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChRtZXNhX3BsYXRlYXVfbXV0YXRlZAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChJtZXNhX3BsYXRlYXVfc3RvbmUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoabWVzYV9wbGF0ZWF1X3N0b25lX211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoPbXVzaHJvb21faXNsYW5kBQhkb3duZmFsbAAAgD8FC3RlbXBlcmF0dXJlZmZmPwAKFW11c2hyb29tX2lzbGFuZF9zaG9yZQUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZWZmZj8ACgVvY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgZwbGFpbnMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmXNzEw/AAobcmVkd29vZF90YWlnYV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKFXJlZHdvb2RfdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgVyaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACg1yb29mZWRfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFXJvb2ZlZF9mb3Jlc3RfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgdzYXZhbm5hBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlmpmZPwAKD3NhdmFubmFfbXV0YXRlZAUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZc3MjD8ACg9zYXZhbm5hX3BsYXRlYXUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAIA/AAoXc2F2YW5uYV9wbGF0ZWF1X211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAIA/AAoLc3RvbmVfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoQc3VuZmxvd2VyX3BsYWlucwUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACglzd2FtcGxhbmQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoRc3dhbXBsYW5kX211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoFdGFpZ2EFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoLdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoNdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgd0aGVfZW5kBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCndhcm1fb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAA=";
/** @var string|null */
private static $DEFAULT_NBT_CACHE = null;
/** @var string */
public $namedtag;
@ -39,7 +42,11 @@ class BiomeDefinitionListPacket extends DataPacket implements ClientboundPacket{
}
protected function encodePayload() : void{
$this->put($this->namedtag ?? self::HARDCODED_NBT_BLOB);
$this->put(
$this->namedtag ??
self::$DEFAULT_NBT_CACHE ??
(self::$DEFAULT_NBT_CACHE = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/biome_definitions.nbt'))
);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -0,0 +1,66 @@
<?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;
use pocketmine\network\mcpe\handler\PacketHandler;
class CompletedUsingItemPacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::COMPLETED_USING_ITEM_PACKET;
public const ACTION_UNKNOWN = -1;
public const ACTION_EQUIP_ARMOR = 0;
public const ACTION_EAT = 1;
public const ACTION_ATTACK = 2;
public const ACTION_CONSUME = 3;
public const ACTION_THROW = 4;
public const ACTION_SHOOT = 5;
public const ACTION_PLACE = 6;
public const ACTION_FILL_BOTTLE = 7;
public const ACTION_FILL_BUCKET = 8;
public const ACTION_POUR_BUCKET = 9;
public const ACTION_USE_TOOL = 10;
public const ACTION_INTERACT = 11;
public const ACTION_RETRIEVED = 12;
public const ACTION_DYED = 13;
public const ACTION_TRADED = 14;
/** @var int */
public $itemId;
/** @var int */
public $action;
public function decodePayload() : void{
$this->itemId = $this->getShort();
$this->action = $this->getLInt();
}
public function encodePayload() : void{
$this->putShort($this->itemId);
$this->putLInt($this->action);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleCompletedUsingItem($this);
}
}

View File

@ -33,6 +33,8 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
use pocketmine\network\mcpe\protocol\types\PotionTypeRecipe;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
#ifndef COMPILE
use pocketmine\utils\Binary;
@ -54,6 +56,10 @@ class CraftingDataPacket extends DataPacket implements ClientboundPacket{
/** @var object[] */
public $entries = [];
/** @var PotionTypeRecipe[] */
public $potionTypeRecipes = [];
/** @var PotionContainerChangeRecipe[] */
public $potionContainerRecipes = [];
/** @var bool */
public $cleanRecipes = false;
@ -139,6 +145,18 @@ class CraftingDataPacket extends DataPacket implements ClientboundPacket{
}
$this->decodedEntries[] = $entry;
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$ingredient = $this->getVarInt();
$output = $this->getVarInt();
$this->potionTypeRecipes[] = new PotionTypeRecipe($input, $ingredient, $output);
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$ingredient = $this->getVarInt();
$output = $this->getVarInt();
$this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output);
}
$this->cleanRecipes = $this->getBool();
}
@ -239,6 +257,18 @@ class CraftingDataPacket extends DataPacket implements ClientboundPacket{
$writer->reset();
}
$this->putUnsignedVarInt(count($this->potionTypeRecipes));
foreach($this->potionTypeRecipes as $recipe){
$this->putVarInt($recipe->getInputPotionType());
$this->putVarInt($recipe->getIngredientItemId());
$this->putVarInt($recipe->getOutputPotionType());
}
$this->putUnsignedVarInt(count($this->potionContainerRecipes));
foreach($this->potionContainerRecipes as $recipe){
$this->putVarInt($recipe->getInputItemId());
$this->putVarInt($recipe->getIngredientItemId());
$this->putVarInt($recipe->getOutputItemId());
}
$this->putBool($this->cleanRecipes);
}

View File

@ -0,0 +1,66 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class EducationSettingsPacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::EDUCATION_SETTINGS_PACKET;
/** @var string */
private $codeBuilderDefaultUri;
/** @var bool */
private $hasQuiz;
public static function create(string $codeBuilderDefaultUri, bool $hasQuiz) : self{
$result = new self;
$result->codeBuilderDefaultUri = $codeBuilderDefaultUri;
$result->hasQuiz = $hasQuiz;
return $result;
}
public function getCodeBuilderDefaultUri() : string{
return $this->codeBuilderDefaultUri;
}
public function getHasQuiz() : bool{
return $this->hasQuiz;
}
protected function decodePayload() : void{
$this->codeBuilderDefaultUri = $this->getString();
$this->hasQuiz = $this->getBool();
}
protected function encodePayload() : void{
$this->putString($this->codeBuilderDefaultUri);
$this->putBool($this->hasQuiz);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleEducationSettings($this);
}
}

View File

@ -0,0 +1,81 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class EmotePacket extends DataPacket implements ClientboundPacket, ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::EMOTE_PACKET;
private const FLAG_SERVER = 1 << 0;
/** @var int */
private $entityRuntimeId;
/** @var string */
private $emoteId;
/** @var int */
private $flags;
public static function create(int $entityRuntimeId, string $emoteId, int $flags) : self{
$result = new self;
$result->entityRuntimeId = $entityRuntimeId;
$result->emoteId = $emoteId;
$result->flags = $flags;
return $result;
}
/**
* TODO: we can't call this getEntityRuntimeId() because of base class collision (crap architecture, thanks Shoghi)
* @return int
*/
public function getEntityRuntimeIdField() : int{
return $this->entityRuntimeId;
}
public function getEmoteId() : string{
return $this->emoteId;
}
public function getFlags() : int{
return $this->flags;
}
protected function decodePayload() : void{
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->emoteId = $this->getString();
$this->flags = $this->getByte();
}
protected function encodePayload() : void{
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putString($this->emoteId);
$this->putByte($this->flags);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleEmote($this);
}
}

View File

@ -1,83 +0,0 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\handler\PacketHandler;
use function count;
class ExplodePacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::EXPLODE_PACKET;
/** @var Vector3 */
public $position;
/** @var float */
public $radius;
/** @var Vector3[] */
public $records = [];
/**
* @param Vector3 $center
* @param float $radius
* @param Vector3[] $records
*
* @return ExplodePacket
*/
public static function create(Vector3 $center, float $radius, array $records) : self{
$result = new self;
$result->position = $center;
$result->radius = $radius;
$result->records = $records;
return $result;
}
protected function decodePayload() : void{
$this->position = $this->getVector3();
$this->radius = (float) ($this->getVarInt() / 32);
$count = $this->getUnsignedVarInt();
for($i = 0; $i < $count; ++$i){
$x = $y = $z = null;
$this->getSignedBlockPosition($x, $y, $z);
$this->records[$i] = new Vector3($x, $y, $z);
}
}
protected function encodePayload() : void{
$this->putVector3($this->position);
$this->putVarInt((int) ($this->radius * 32));
$this->putUnsignedVarInt(count($this->records));
if(count($this->records) > 0){
foreach($this->records as $record){
$this->putSignedBlockPosition((int) $record->x, (int) $record->y, (int) $record->z);
}
}
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleExplode($this);
}
}

View File

@ -52,11 +52,32 @@ class LoginPacket extends DataPacket implements ServerboundPacket{
public const I_SERVER_ADDRESS = 'ServerAddress';
public const I_LANGUAGE_CODE = 'LanguageCode';
public const I_SKIN_RESOURCE_PATCH = 'SkinResourcePatch';
public const I_SKIN_ID = 'SkinId';
public const I_SKIN_HEIGHT = 'SkinImageHeight';
public const I_SKIN_WIDTH = 'SkinImageWidth';
public const I_SKIN_DATA = 'SkinData';
public const I_CAPE_ID = 'CapeId';
public const I_CAPE_HEIGHT = 'CapeImageHeight';
public const I_CAPE_WIDTH = 'CapeImageWidth';
public const I_CAPE_DATA = 'CapeData';
public const I_GEOMETRY_NAME = 'SkinGeometryName';
public const I_GEOMETRY_DATA = 'SkinGeometry';
public const I_GEOMETRY_DATA = 'SkinGeometryData';
public const I_ANIMATION_DATA = 'SkinAnimationData';
public const I_ANIMATION_IMAGES = 'AnimatedImageData';
public const I_ANIMATION_IMAGE_HEIGHT = 'ImageHeight';
public const I_ANIMATION_IMAGE_WIDTH = 'ImageWidth';
public const I_ANIMATION_IMAGE_FRAMES = 'Frames';
public const I_ANIMATION_IMAGE_TYPE = 'Type';
public const I_ANIMATION_IMAGE_DATA = 'Image';
public const I_PREMIUM_SKIN = 'PremiumSkin';
public const I_PERSONA_SKIN = 'PersonaSkin';
public const I_PERSONA_CAPE_ON_CLASSIC_SKIN = 'CapeOnClassicSkin';
/** @var int */
public $protocol;
@ -164,11 +185,32 @@ class LoginPacket extends DataPacket implements ServerboundPacket{
$v->required(self::I_SERVER_ADDRESS)->string();
$v->required(self::I_LANGUAGE_CODE)->string();
$v->required(self::I_SKIN_RESOURCE_PATCH)->string();
$v->required(self::I_SKIN_ID)->string();
$v->required(self::I_SKIN_DATA)->string();
$v->required(self::I_SKIN_HEIGHT)->integer(true);
$v->required(self::I_SKIN_WIDTH)->integer(true);
$v->required(self::I_CAPE_ID, null, true)->string();
$v->required(self::I_CAPE_DATA, null, true)->string();
$v->required(self::I_GEOMETRY_NAME)->string();
$v->required(self::I_CAPE_HEIGHT)->integer(true);
$v->required(self::I_CAPE_WIDTH)->integer(true);
$v->required(self::I_GEOMETRY_DATA, null, true)->string();
$v->required(self::I_ANIMATION_DATA, null, true)->string();
$v->required(self::I_ANIMATION_IMAGES, null, true)->isArray()->each(function(Validator $vSub) : void{
$vSub->required(self::I_ANIMATION_IMAGE_HEIGHT)->integer(true);
$vSub->required(self::I_ANIMATION_IMAGE_WIDTH)->integer(true);
$vSub->required(self::I_ANIMATION_IMAGE_FRAMES)->numeric(); //float() doesn't accept ints ???
$vSub->required(self::I_ANIMATION_IMAGE_TYPE)->integer(true);
$vSub->required(self::I_ANIMATION_IMAGE_DATA)->string();
});
$v->required(self::I_PREMIUM_SKIN)->bool();
$v->required(self::I_PERSONA_SKIN)->bool();
$v->required(self::I_PERSONA_CAPE_ON_CLASSIC_SKIN)->bool();
self::validate($v, 'clientData', $clientData);
$this->clientData = $clientData;

View File

@ -83,7 +83,7 @@ class MoveActorDeltaPacket extends DataPacket implements ClientboundPacket{
protected function decodePayload() : void{
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->flags = $this->getByte();
$this->flags = $this->getLShort();
$this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X);
$this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y);
$this->zDiff = $this->maybeReadCoord(self::FLAG_HAS_Z);
@ -106,7 +106,7 @@ class MoveActorDeltaPacket extends DataPacket implements ClientboundPacket{
protected function encodePayload() : void{
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putByte($this->flags);
$this->putLShort($this->flags);
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff);
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff);
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zDiff);

View File

@ -0,0 +1,61 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class MultiplayerSettingsPacket extends DataPacket implements ServerboundPacket{ //TODO: this might be clientbound too, but unsure
public const NETWORK_ID = ProtocolInfo::MULTIPLAYER_SETTINGS_PACKET;
public const ACTION_ENABLE_MULTIPLAYER = 0;
public const ACTION_DISABLE_MULTIPLAYER = 1;
public const ACTION_REFRESH_JOIN_CODE = 2;
/** @var int */
private $action;
public static function create(int $action) : self{
$result = new self;
$result->action = $action;
return $result;
}
public function getAction() : int{
return $this->action;
}
protected function decodePayload() : void{
$this->action = $this->getVarInt();
}
protected function encodePayload() : void{
$this->putVarInt($this->action);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleMultiplayerSettings($this);
}
}

View File

@ -0,0 +1,60 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class NetworkSettingsPacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::NETWORK_SETTINGS_PACKET;
public const COMPRESS_NOTHING = 0;
public const COMPRESS_EVERYTHING = 1;
/** @var int */
private $compressionThreshold;
public static function create(int $compressionThreshold) : self{
$result = new self;
$result->compressionThreshold = $compressionThreshold;
return $result;
}
public function getCompressionThreshold() : int{
return $this->compressionThreshold;
}
protected function decodePayload() : void{
$this->compressionThreshold = $this->getLShort();
}
protected function encodePayload() : void{
$this->putLShort($this->compressionThreshold);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleNetworkSettings($this);
}
}

View File

@ -54,7 +54,7 @@ class PacketPool{
static::registerPacket(new RiderJumpPacket());
static::registerPacket(new UpdateBlockPacket());
static::registerPacket(new AddPaintingPacket());
static::registerPacket(new ExplodePacket());
static::registerPacket(new TickSyncPacket());
static::registerPacket(new LevelSoundEventPacketV1());
static::registerPacket(new LevelEventPacket());
static::registerPacket(new BlockEventPacket());
@ -162,11 +162,19 @@ class PacketPool{
static::registerPacket(new ClientCacheStatusPacket());
static::registerPacket(new OnScreenTextureAnimationPacket());
static::registerPacket(new MapCreateLockedCopyPacket());
static::registerPacket(new StructureTemplateDataExportRequestPacket());
static::registerPacket(new StructureTemplateDataExportResponsePacket());
static::registerPacket(new StructureTemplateDataRequestPacket());
static::registerPacket(new StructureTemplateDataResponsePacket());
static::registerPacket(new UpdateBlockPropertiesPacket());
static::registerPacket(new ClientCacheBlobStatusPacket());
static::registerPacket(new ClientCacheMissResponsePacket());
static::registerPacket(new EducationSettingsPacket());
static::registerPacket(new EmotePacket());
static::registerPacket(new MultiplayerSettingsPacket());
static::registerPacket(new SettingsCommandPacket());
static::registerPacket(new AnvilDamagePacket());
static::registerPacket(new CompletedUsingItemPacket());
static::registerPacket(new NetworkSettingsPacket());
static::registerPacket(new PlayerAuthInputPacket());
}
/**

View File

@ -0,0 +1,175 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\InputMode;
use pocketmine\network\mcpe\protocol\types\PlayMode;
use function assert;
class PlayerAuthInputPacket extends DataPacket implements ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::PLAYER_AUTH_INPUT_PACKET;
/** @var Vector3 */
private $position;
/** @var float */
private $pitch;
/** @var float */
private $yaw;
/** @var float */
private $headYaw;
/** @var float */
private $moveVecX;
/** @var float */
private $moveVecZ;
/** @var int */
private $inputFlags;
/** @var int */
private $inputMode;
/** @var int */
private $playMode;
/** @var Vector3|null */
private $vrGazeDirection = null;
/**
* @param Vector3 $position
* @param float $pitch
* @param float $yaw
* @param float $headYaw
* @param float $moveVecX
* @param float $moveVecZ
* @param int $inputFlags
* @param int $inputMode @see InputMode
* @param int $playMode @see PlayMode
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
*
* @return self
*/
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection = null) : self{
if($playMode === PlayMode::VR and $vrGazeDirection === null){
//yuck, can we get a properly written packet just once? ...
throw new \InvalidArgumentException("Gaze direction must be provided for VR play mode");
}
$result = new self;
$result->position = $position->asVector3();
$result->pitch = $pitch;
$result->yaw = $yaw;
$result->headYaw = $headYaw;
$result->moveVecX = $moveVecX;
$result->moveVecZ = $moveVecZ;
$result->inputFlags = $inputFlags;
$result->inputMode = $inputMode;
$result->playMode = $playMode;
if($vrGazeDirection !== null){
$this->vrGazeDirection = $vrGazeDirection->asVector3();
}
return $result;
}
public function getPosition() : Vector3{
return $this->position;
}
public function getPitch() : float{
return $this->pitch;
}
public function getYaw() : float{
return $this->yaw;
}
public function getHeadYaw() : float{
return $this->headYaw;
}
public function getMoveVecX() : float{
return $this->moveVecX;
}
public function getMoveVecZ() : float{
return $this->moveVecZ;
}
public function getInputFlags() : int{
return $this->inputFlags;
}
/**
* @see InputMode
* @return int
*/
public function getInputMode() : int{
return $this->inputMode;
}
/**
* @see PlayMode
* @return int
*/
public function getPlayMode() : int{
return $this->playMode;
}
public function getVrGazeDirection() : ?Vector3{
return $this->vrGazeDirection;
}
protected function decodePayload() : void{
$this->yaw = $this->getLFloat();
$this->pitch = $this->getLFloat();
$this->position = $this->getVector3();
$this->moveVecX = $this->getLFloat();
$this->moveVecZ = $this->getLFloat();
$this->headYaw = $this->getLFloat();
$this->inputFlags = $this->getUnsignedVarLong();
$this->inputMode = $this->getUnsignedVarInt();
$this->playMode = $this->getUnsignedVarInt();
if($this->playMode === PlayMode::VR){
$this->vrGazeDirection = $this->getVector3();
}
}
protected function encodePayload() : void{
$this->putLFloat($this->yaw);
$this->putLFloat($this->pitch);
$this->putVector3($this->position);
$this->putLFloat($this->moveVecX);
$this->putLFloat($this->moveVecZ);
$this->putLFloat($this->headYaw);
$this->putUnsignedVarLong($this->inputFlags);
$this->putUnsignedVarInt($this->inputMode);
$this->putUnsignedVarInt($this->playMode);
if($this->playMode === PlayMode::VR){
assert($this->vrGazeDirection !== null);
$this->putVector3($this->vrGazeDirection);
}
}
public function handle(PacketHandler $handler) : bool{
return $handler->handlePlayerAuthInput($this);
}
}

View File

@ -26,8 +26,6 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\entity\Skin;
use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use function count;
@ -67,26 +65,12 @@ class PlayerListPacket extends DataPacket implements ClientboundPacket{
$entry->uuid = $this->getUUID();
$entry->entityUniqueId = $this->getEntityUniqueId();
$entry->username = $this->getString();
$skinId = $this->getString();
$skinData = $this->getString();
$capeData = $this->getString();
$geometryName = $this->getString();
$geometryData = $this->getString();
try{
$entry->skin = new Skin(
$skinId,
$skinData,
$capeData,
$geometryName,
$geometryData
);
}catch(\InvalidArgumentException $e){
throw new BadPacketException($e->getMessage(), 0, $e);
}
$entry->xboxUserId = $this->getString();
$entry->platformChatId = $this->getString();
$entry->buildPlatform = $this->getLInt();
$entry->skinData = $this->getSkin();
$entry->isTeacher = $this->getBool();
$entry->isHost = $this->getBool();
}else{
$entry->uuid = $this->getUUID();
}
@ -103,13 +87,12 @@ class PlayerListPacket extends DataPacket implements ClientboundPacket{
$this->putUUID($entry->uuid);
$this->putEntityUniqueId($entry->entityUniqueId);
$this->putString($entry->username);
$this->putString($entry->skin->getSkinId());
$this->putString($entry->skin->getSkinData());
$this->putString($entry->skin->getCapeData());
$this->putString($entry->skin->getGeometryName());
$this->putString($entry->skin->getGeometryData());
$this->putString($entry->xboxUserId);
$this->putString($entry->platformChatId);
$this->putLInt($entry->buildPlatform);
$this->putSkin($entry->skinData);
$this->putBool($entry->isTeacher);
$this->putBool($entry->isHost);
}else{
$this->putUUID($entry->uuid);
}

View File

@ -25,9 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\entity\Skin;
use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\utils\UUID;
class PlayerSkinPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{
@ -39,43 +38,21 @@ class PlayerSkinPacket extends DataPacket implements ClientboundPacket, Serverbo
public $oldSkinName = "";
/** @var string */
public $newSkinName = "";
/** @var Skin */
/** @var SkinData */
public $skin;
/** @var bool */
public $premiumSkin = false;
protected function decodePayload() : void{
$this->uuid = $this->getUUID();
$skinId = $this->getString();
$this->skin = $this->getSkin();
$this->newSkinName = $this->getString();
$this->oldSkinName = $this->getString();
$skinData = $this->getString();
$capeData = $this->getString();
$geometryModel = $this->getString();
$geometryData = $this->getString();
try{
$this->skin = new Skin($skinId, $skinData, $capeData, $geometryModel, $geometryData);
}catch(\InvalidArgumentException $e){
throw new BadPacketException($e->getMessage(), 0, $e);
}
$this->premiumSkin = $this->getBool();
}
protected function encodePayload() : void{
$this->putUUID($this->uuid);
$this->putString($this->skin->getSkinId());
$this->putSkin($this->skin);
$this->putString($this->newSkinName);
$this->putString($this->oldSkinName);
$this->putString($this->skin->getSkinData());
$this->putString($this->skin->getCapeData());
$this->putString($this->skin->getGeometryName());
$this->putString($this->skin->getGeometryData());
$this->putBool($this->premiumSkin);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -43,15 +43,15 @@ final class ProtocolInfo{
/**
* Actual Minecraft: PE protocol version
*/
public const CURRENT_PROTOCOL = 361;
public const CURRENT_PROTOCOL = 388;
/**
* Current Minecraft PE version reported by the server. This is usually the earliest currently supported version.
*/
public const MINECRAFT_VERSION = 'v1.12.0';
public const MINECRAFT_VERSION = 'v1.13.0';
/**
* Version number sent to clients in ping responses.
*/
public const MINECRAFT_VERSION_NETWORK = '1.12.0';
public const MINECRAFT_VERSION_NETWORK = '1.13.0';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;
@ -75,7 +75,7 @@ final class ProtocolInfo{
public const RIDER_JUMP_PACKET = 0x14;
public const UPDATE_BLOCK_PACKET = 0x15;
public const ADD_PAINTING_PACKET = 0x16;
public const EXPLODE_PACKET = 0x17;
public const TICK_SYNC_PACKET = 0x17;
public const LEVEL_SOUND_EVENT_PACKET_V1 = 0x18;
public const LEVEL_EVENT_PACKET = 0x19;
public const BLOCK_EVENT_PACKET = 0x1a;
@ -184,10 +184,18 @@ final class ProtocolInfo{
public const CLIENT_CACHE_STATUS_PACKET = 0x81;
public const ON_SCREEN_TEXTURE_ANIMATION_PACKET = 0x82;
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83;
public const STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET = 0x84;
public const STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET = 0x85;
public const STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET = 0x84;
public const STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET = 0x85;
public const UPDATE_BLOCK_PROPERTIES_PACKET = 0x86;
public const CLIENT_CACHE_BLOB_STATUS_PACKET = 0x87;
public const CLIENT_CACHE_MISS_RESPONSE_PACKET = 0x88;
public const EDUCATION_SETTINGS_PACKET = 0x89;
public const EMOTE_PACKET = 0x8a;
public const MULTIPLAYER_SETTINGS_PACKET = 0x8b;
public const SETTINGS_COMMAND_PACKET = 0x8c;
public const ANVIL_DAMAGE_PACKET = 0x8d;
public const COMPLETED_USING_ITEM_PACKET = 0x8e;
public const NETWORK_SETTINGS_PACKET = 0x8f;
public const PLAYER_AUTH_INPUT_PACKET = 0x90;
}

View File

@ -55,15 +55,14 @@ class ResourcePackChunkDataPacket extends DataPacket implements ClientboundPacke
$this->packId = $this->getString();
$this->chunkIndex = $this->getLInt();
$this->progress = $this->getLLong();
$this->data = $this->get($this->getLInt());
$this->data = $this->getString();
}
protected function encodePayload() : void{
$this->putString($this->packId);
$this->putLInt($this->chunkIndex);
$this->putLLong($this->progress);
$this->putLInt(strlen($this->data));
$this->put($this->data);
$this->putString($this->data);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -44,6 +44,8 @@ class ResourcePackStackPacket extends DataPacket implements ClientboundPacket{
/** @var bool */
public $isExperimental = false;
/** @var string */
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
/**
* @param ResourcePackStackEntry[] $resourcePacks
@ -75,6 +77,7 @@ class ResourcePackStackPacket extends DataPacket implements ClientboundPacket{
}
$this->isExperimental = $this->getBool();
$this->baseGameVersion = $this->getString();
}
protected function encodePayload() : void{
@ -91,6 +94,7 @@ class ResourcePackStackPacket extends DataPacket implements ClientboundPacket{
}
$this->putBool($this->isExperimental);
$this->putString($this->baseGameVersion);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -29,24 +29,38 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\handler\PacketHandler;
class RespawnPacket extends DataPacket implements ClientboundPacket{
class RespawnPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::RESPAWN_PACKET;
public const SEARCHING_FOR_SPAWN = 0;
public const READY_TO_SPAWN = 1;
public const CLIENT_READY_TO_SPAWN = 2;
/** @var Vector3 */
public $position;
/** @var int */
public $respawnState = self::SEARCHING_FOR_SPAWN;
/** @var int */
public $entityRuntimeId;
public static function create(Vector3 $position) : self{
public static function create(Vector3 $position, int $respawnStatus, int $entityRuntimeId) : self{
$result = new self;
$result->position = $position->asVector3();
$result->respawnState = $respawnStatus;
$result->entityRuntimeId = $entityRuntimeId;
return $result;
}
protected function decodePayload() : void{
$this->position = $this->getVector3();
$this->respawnState = $this->getByte();
$this->entityRuntimeId = $this->getEntityRuntimeId();
}
protected function encodePayload() : void{
$this->putVector3($this->position);
$this->putByte($this->respawnState);
$this->putEntityRuntimeId($this->entityRuntimeId);
}
public function handle(PacketHandler $handler) : bool{

View File

@ -27,7 +27,7 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\handler\PacketHandler;
class ScriptCustomEventPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{
class ScriptCustomEventPacket extends DataPacket{ //TODO: this doesn't have handlers in either client or server in the game as of 1.8
public const NETWORK_ID = ProtocolInfo::SCRIPT_CUSTOM_EVENT_PACKET;
/** @var string */

View File

@ -0,0 +1,66 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class SettingsCommandPacket extends DataPacket implements ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::SETTINGS_COMMAND_PACKET;
/** @var string */
private $command;
/** @var bool */
private $suppressOutput;
public static function create(string $command, bool $suppressOutput) : self{
$result = new self;
$result->command = $command;
$result->suppressOutput = $suppressOutput;
return $result;
}
public function getCommand() : string{
return $this->command;
}
public function getSuppressOutput() : bool{
return $this->suppressOutput;
}
protected function decodePayload() : void{
$this->command = $this->getString();
$this->suppressOutput = $this->getBool();
}
protected function encodePayload() : void{
$this->putString($this->command);
$this->putBool($this->suppressOutput);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleSettingsCommand($this);
}
}

View File

@ -27,10 +27,13 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\math\Vector3;
use pocketmine\nbt\TreeRoot;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
use pocketmine\network\mcpe\serializer\NetworkNbtSerializer;
use function count;
use function file_get_contents;
use function json_decode;
@ -79,8 +82,8 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
public $hasAchievementsDisabled = true;
/** @var int */
public $time = -1;
/** @var bool */
public $eduMode = false;
/** @var int */
public $eduEditionOffer = 0;
/** @var bool */
public $hasEduFeaturesEnabled = false;
/** @var float */
@ -130,6 +133,8 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
/** @var bool */
public $onlySpawnV1Villagers = false;
/** @var string */
public $vanillaVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
/** @var string */
public $levelId = ""; //base64 string, usually the same as world folder name in vanilla
/** @var string */
@ -138,6 +143,8 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
public $premiumWorldTemplateId = "";
/** @var bool */
public $isTrial = false;
/** @var bool */
public $isMovementServerAuthoritative = false;
/** @var int */
public $currentTick = 0; //only used if isTrial is true
/** @var int */
@ -145,7 +152,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
/** @var string */
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
/** @var array|null ["name" (string), "data" (int16), "legacy_id" (int16)] */
/** @var ListTag|null */
public $blockTable = null;
/** @var array|null string (name) => int16 (legacyID) */
public $itemTable = null;
@ -169,7 +176,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
$this->getBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
$this->hasAchievementsDisabled = $this->getBool();
$this->time = $this->getVarInt();
$this->eduMode = $this->getBool();
$this->eduEditionOffer = $this->getVarInt();
$this->hasEduFeaturesEnabled = $this->getBool();
$this->rainLevel = $this->getLFloat();
$this->lightningLevel = $this->getLFloat();
@ -193,22 +200,22 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
$this->isWorldTemplateOptionLocked = $this->getBool();
$this->onlySpawnV1Villagers = $this->getBool();
$this->vanillaVersion = $this->getString();
$this->levelId = $this->getString();
$this->worldName = $this->getString();
$this->premiumWorldTemplateId = $this->getString();
$this->isTrial = $this->getBool();
$this->isMovementServerAuthoritative = $this->getBool();
$this->currentTick = $this->getLLong();
$this->enchantmentSeed = $this->getVarInt();
$this->blockTable = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$id = $this->getString();
$data = $this->getSignedLShort();
$unknown = $this->getSignedLShort();
$this->blockTable[$i] = ["name" => $id, "data" => $data, "legacy_id" => $unknown];
$blockTable = (new NetworkNbtSerializer())->read($this->buffer, $this->offset, 512)->getTag();
if(!($blockTable instanceof ListTag)){
throw new \UnexpectedValueException("Wrong block table root NBT tag type");
}
$this->blockTable = $blockTable;
$this->itemTable = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$id = $this->getString();
@ -239,7 +246,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
$this->putBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
$this->putBool($this->hasAchievementsDisabled);
$this->putVarInt($this->time);
$this->putBool($this->eduMode);
$this->putVarInt($this->eduEditionOffer);
$this->putBool($this->hasEduFeaturesEnabled);
$this->putLFloat($this->rainLevel);
$this->putLFloat($this->lightningLevel);
@ -263,10 +270,12 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
$this->putBool($this->isWorldTemplateOptionLocked);
$this->putBool($this->onlySpawnV1Villagers);
$this->putString($this->vanillaVersion);
$this->putString($this->levelId);
$this->putString($this->worldName);
$this->putString($this->premiumWorldTemplateId);
$this->putBool($this->isTrial);
$this->putBool($this->isMovementServerAuthoritative);
$this->putLLong($this->currentTick);
$this->putVarInt($this->enchantmentSeed);
@ -274,11 +283,11 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
if($this->blockTable === null){
if(self::$blockTableCache === null){
//this is a really nasty hack, but it'll do for now
self::$blockTableCache = self::serializeBlockTable(RuntimeBlockMapping::getBedrockKnownStates());
self::$blockTableCache = (new NetworkNbtSerializer())->write(new TreeRoot(new ListTag(RuntimeBlockMapping::getBedrockKnownStates())));
}
$this->put(self::$blockTableCache);
}else{
$this->put(self::serializeBlockTable($this->blockTable));
$this->put((new NetworkNbtSerializer())->write(new TreeRoot($this->blockTable)));
}
if($this->itemTable === null){
if(self::$itemTableCache === null){
@ -292,17 +301,6 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{
$this->putString($this->multiplayerCorrelationId);
}
private static function serializeBlockTable(array $table) : string{
$stream = new NetworkBinaryStream();
$stream->putUnsignedVarInt(count($table));
foreach($table as $v){
$stream->putString($v["name"]);
$stream->putLShort($v["data"]);
$stream->putLShort($v["legacy_id"]);
}
return $stream->getBuffer();
}
private static function serializeItemTable(array $table) : string{
$stream = new NetworkBinaryStream();
$stream->putUnsignedVarInt(count($table));

View File

@ -28,8 +28,8 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\StructureSettings;
class StructureTemplateDataExportRequestPacket extends DataPacket implements ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET;
class StructureTemplateDataRequestPacket extends DataPacket implements ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET;
public const TYPE_ALWAYS_LOAD = 1;
public const TYPE_CREATE_AND_LOAD = 2;
@ -62,6 +62,6 @@ class StructureTemplateDataExportRequestPacket extends DataPacket implements Ser
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleStructureTemplateDataExportRequest($this);
return $handler->handleStructureTemplateDataRequest($this);
}
}

View File

@ -27,8 +27,8 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\handler\PacketHandler;
class StructureTemplateDataExportResponsePacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET;
class StructureTemplateDataResponsePacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET;
/** @var string */
public $structureTemplateName;
@ -51,6 +51,6 @@ class StructureTemplateDataExportResponsePacket extends DataPacket implements Cl
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleStructureTemplateDataExportResponse($this);
return $handler->handleStructureTemplateDataResponse($this);
}
}

View File

@ -0,0 +1,73 @@
<?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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\PacketHandler;
class TickSyncPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{
public const NETWORK_ID = ProtocolInfo::TICK_SYNC_PACKET;
/** @var int */
private $clientSendTime;
/** @var int */
private $serverReceiveTime;
public static function request(int $clientTime) : self{
$result = new self;
$result->clientSendTime = $clientTime;
$result->serverReceiveTime = 0; //useless
return $result;
}
public static function response(int $clientSendTime, int $serverReceiveTime) : self{
$result = new self;
$result->clientSendTime = $clientSendTime;
$result->serverReceiveTime = $serverReceiveTime;
return $result;
}
public function getClientSendTime() : int{
return $this->clientSendTime;
}
public function getServerReceiveTime() : int{
return $this->serverReceiveTime;
}
protected function decodePayload() : void{
$this->clientSendTime = $this->getLLong();
$this->serverReceiveTime = $this->getLLong();
}
protected function encodePayload() : void{
$this->putLLong($this->clientSendTime);
$this->putLLong($this->serverReceiveTime);
}
public function handle(PacketHandler $handler) : bool{
return $handler->handleTickSync($this);
}
}

View File

@ -0,0 +1,36 @@
<?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;
final class InputMode{
private function __construct(){
//NOOP
}
public const MOUSE_KEYBOARD = 1;
public const TOUCHSCREEN = 2;
public const GAME_PAD = 3;
public const MOTION_CONTROLLER = 4;
}

View File

@ -0,0 +1,67 @@
<?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;
use function is_array;
use function is_string;
class LegacySkinAdapter implements SkinAdapter{
public function toSkinData(Skin $skin) : SkinData{
$capeData = new SkinImage(32, 64, $skin->getCapeData());
if($skin->getCapeData() === ""){
$capeData = new SkinImage(0, 0, $skin->getCapeData());
}
$geometryName = $skin->getGeometryName();
if($geometryName === ""){
$geometryName = "geometry.humanoid.custom";
}
return new SkinData(
$skin->getSkinId(),
json_encode(["geometry" => ["default" => $geometryName]]),
SkinImage::fromLegacy($skin->getSkinData()), [],
$capeData,
$skin->getGeometryData()
);
}
public function fromSkinData(SkinData $data) : Skin{
$capeData = $data->getCapeImage()->getData();
$geometryName = "";
$resourcePatch = json_decode($data->getResourcePatch(), true);
if(is_array($resourcePatch["geometry"]) && is_string($resourcePatch["geometry"]["default"])){
$geometryName = $resourcePatch["geometry"]["default"];
}else{
//TODO: Kick for invalid skin
}
if($data->isPersona()){
return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048));
}elseif($data->isPersonaCapeOnClassic()){
$capeData = "";
}
return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, $geometryName, $data->getGeometryData());
}
}

View File

@ -0,0 +1,45 @@
<?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;
/**
* Enum used by PlayerAuthInputPacket. Most of these names don't make any sense, but that isn't surprising.
*/
final class PlayMode{
private function __construct(){
//NOOP
}
public const NORMAL = 0;
public const TEASER = 1;
public const SCREEN = 2;
public const VIEWER = 3;
public const VR = 4;
public const PLACEMENT = 5;
public const LIVING_ROOM = 6;
public const EXIT_LEVEL = 7;
public const EXIT_LEVEL_LIVING_ROOM = 8;
}

View File

@ -34,12 +34,18 @@ class PlayerListEntry{
public $entityUniqueId;
/** @var string */
public $username;
/** @var Skin */
public $skin;
/** @var SkinData */
public $skinData;
/** @var string */
public $xboxUserId;
/** @var string */
public $platformChatId = "";
/** @var int */
public $buildPlatform = -1;
/** @var bool */
public $isTeacher = false;
/** @var bool */
public $isHost = false;
public static function createRemovalEntry(UUID $uuid) : PlayerListEntry{
$entry = new PlayerListEntry();
@ -48,14 +54,17 @@ class PlayerListEntry{
return $entry;
}
public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "") : 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;
$entry->isTeacher = $isTeacher;
$entry->isHost = $isHost;
return $entry;
}

View File

@ -0,0 +1,51 @@
<?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 PotionContainerChangeRecipe{
/** @var int */
private $inputItemId;
/** @var int */
private $ingredientItemId;
/** @var int */
private $outputItemId;
public function __construct(int $inputItemId, int $ingredientItemId, int $outputItemId){
$this->inputItemId = $inputItemId;
$this->ingredientItemId = $ingredientItemId;
$this->outputItemId = $outputItemId;
}
public function getInputItemId() : int{
return $this->inputItemId;
}
public function getIngredientItemId() : int{
return $this->ingredientItemId;
}
public function getOutputItemId() : int{
return $this->outputItemId;
}
}

View File

@ -0,0 +1,51 @@
<?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 PotionTypeRecipe{
/** @var int */
private $inputPotionType;
/** @var int */
private $ingredientItemId;
/** @var int */
private $outputPotionType;
public function __construct(int $inputPotionType, int $ingredientItemId, int $outputPotionType){
$this->inputPotionType = $inputPotionType;
$this->ingredientItemId = $ingredientItemId;
$this->outputPotionType = $outputPotionType;
}
public function getInputPotionType() : int{
return $this->inputPotionType;
}
public function getIngredientItemId() : int{
return $this->ingredientItemId;
}
public function getOutputPotionType() : int{
return $this->outputPotionType;
}
}

View File

@ -24,6 +24,12 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\block\BlockLegacyIds;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\serializer\NetworkNbtSerializer;
use pocketmine\utils\BinaryDataException;
use function file_get_contents;
use function getmypid;
use function json_decode;
@ -40,7 +46,7 @@ final class RuntimeBlockMapping{
private static $legacyToRuntimeMap = [];
/** @var int[] */
private static $runtimeToLegacyMap = [];
/** @var mixed[]|null */
/** @var CompoundTag[]|null */
private static $bedrockKnownStates = null;
private function __construct(){
@ -48,32 +54,54 @@ final class RuntimeBlockMapping{
}
public static function init() : void{
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
$compressedTable = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.json"), true);
$decompressed = [];
foreach($compressedTable as $prefix => $entries){
foreach($entries as $shortStringId => $states){
foreach($states as $state){
$name = "$prefix:$shortStringId";
$decompressed[] = [
"name" => $name,
"data" => $state,
"legacy_id" => $legacyIdMap[$name]
];
}
}
$tag = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"))->getTag();
if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy
throw new \RuntimeException("Invalid blockstates table, expected TAG_List<TAG_Compound> root");
}
self::$bedrockKnownStates = self::randomizeTable($decompressed);
foreach(self::$bedrockKnownStates as $k => $obj){
if($obj["data"] > 15){
//TODO: in 1.12 they started using data values bigger than 4 bits which we can't handle right now
/** @var CompoundTag[] $list */
$list = $tag->getValue();
self::$bedrockKnownStates = self::randomizeTable($list);
self::setupLegacyMappings();
}
private static function setupLegacyMappings() : void{
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
$legacyStateMap = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"))->getTag();
if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){
throw new \RuntimeException("Invalid legacy states mapping table, expected TAG_List<TAG_Compound> root");
}
/**
* @var int[][] $idToStatesMap string id -> int[] list of candidate state indices
*/
$idToStatesMap = [];
foreach(self::$bedrockKnownStates as $k => $state){
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
}
/** @var CompoundTag $pair */
foreach($legacyStateMap as $pair){
$oldState = $pair->getCompoundTag("old");
$id = $legacyIdMap[$oldState->getString("name")];
$data = $oldState->getShort("val");
if($data > 15){
//we can't handle metadata with more than 4 bits
continue;
}
//this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries
self::registerMapping($k, $obj["legacy_id"], $obj["data"]);
$mappedState = $pair->getCompoundTag("new");
$mappedName = $mappedState->getString("name");
if(!isset($idToStatesMap[$mappedName])){
throw new \RuntimeException("Mapped new state does not appear in network table");
}
foreach($idToStatesMap[$mappedName] as $k){
$networkState = self::$bedrockKnownStates[$k];
if($mappedState->equals($networkState->getCompoundTag("block"))){
self::registerMapping($k, $id, $data);
continue 2;
}
}
throw new \RuntimeException("Mapped new state does not appear in network table");
}
}
@ -88,9 +116,9 @@ final class RuntimeBlockMapping{
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
* aren't supposed to do, so we have to deliberately break it to make them stop.
*
* @param array $table
* @param CompoundTag[] $table
*
* @return array
* @return CompoundTag[]
*/
private static function randomizeTable(array $table) : array{
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
@ -133,7 +161,7 @@ final class RuntimeBlockMapping{
}
/**
* @return array
* @return CompoundTag[]
*/
public static function getBedrockKnownStates() : array{
self::lazyInit();

View File

@ -0,0 +1,48 @@
<?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;
/**
* Used to convert new skin data to the skin entity or old skin entity to skin data.
*/
interface SkinAdapter{
/**
* Allows you to convert a skin entity to skin data.
*
* @param Skin $skin
* @return SkinData
*/
public function toSkinData(Skin $skin) : SkinData;
/**
* Allows you to convert skin data to a skin entity.
*
* @param SkinData $data
* @return Skin
*/
public function fromSkinData(SkinData $data) : Skin;
}

View File

@ -0,0 +1,43 @@
<?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;
/**
* Accessor for SkinAdapter
*/
class SkinAdapterSingleton{
/** @var SkinAdapter|null */
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) : void{
self::$skinAdapter = $adapter;
}
}

View File

@ -0,0 +1,71 @@
<?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 SkinAnimation{
public const TYPE_HEAD = 1;
public const TYPE_BODY_32 = 2;
public const TYPE_BODY_64 = 3;
/** @var SkinImage */
private $image;
/** @var int */
private $type;
/** @var float */
private $frames;
public function __construct(SkinImage $image, int $type, float $frames){
$this->image = $image;
$this->type = $type;
$this->frames = $frames;
}
/**
* Image of the animation.
*
* @return SkinImage
*/
public function getImage() : SkinImage{
return $this->image;
}
/**
* The type of animation you are applying.
*
* @return int
*/
public function getType() : int{
return $this->type;
}
/**
* The total amount of frames in an animation.
*
* @return float
*/
public function getFrames() : float{
return $this->frames;
}
}

View File

@ -0,0 +1,155 @@
<?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 */
private $skinId;
/** @var string */
private $resourcePatch;
/** @var SkinImage */
private $skinImage;
/** @var SkinAnimation[] */
private $animations;
/** @var SkinImage */
private $capeImage;
/** @var string */
private $geometryData;
/** @var string */
private $animationData;
/** @var bool */
private $persona;
/** @var bool */
private $premium;
/** @var bool */
private $personaCapeOnClassic;
/** @var string */
private $capeId;
/**
* @param string $skinId
* @param string $resourcePatch
* @param SkinImage $skinImage
* @param SkinAnimation[] $animations
* @param SkinImage|null $capeImage
* @param string $geometryData
* @param string $animationData
* @param bool $premium
* @param bool $persona
* @param bool $personaCapeOnClassic
* @param string $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 $personaCapeOnClassic = 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->personaCapeOnClassic = $personaCapeOnClassic;
$this->capeId = $capeId;
}
/**
* @return string
*/
public function getSkinId() : string{
return $this->skinId;
}
/**
* @return string
*/
public function getResourcePatch() : string{
return $this->resourcePatch;
}
/**
* @return SkinImage
*/
public function getSkinImage() : SkinImage{
return $this->skinImage;
}
/**
* @return SkinAnimation[]
*/
public function getAnimations() : array{
return $this->animations;
}
/**
* @return SkinImage
*/
public function getCapeImage() : SkinImage{
return $this->capeImage;
}
/**
* @return string
*/
public function getGeometryData() : string{
return $this->geometryData;
}
/**
* @return string
*/
public function getAnimationData() : string{
return $this->animationData;
}
/**
* @return bool
*/
public function isPersona() : bool{
return $this->persona;
}
/**
* @return bool
*/
public function isPremium() : bool{
return $this->premium;
}
/**
* @return bool
*/
public function isPersonaCapeOnClassic() : bool{
return $this->personaCapeOnClassic;
}
/**
* @return string
*/
public function getCapeId() : string{
return $this->capeId;
}
}

View File

@ -0,0 +1,67 @@
<?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 SkinImage{
/** @var int */
private $height;
/** @var int */
private $width;
/** @var string */
private $data;
public function __construct(int $height, int $width, string $data){
$this->height = $height;
$this->width = $width;
$this->data = $data;
}
public static function fromLegacy(string $data) : SkinImage{
switch(strlen($data)){
case 64 * 32 * 4:
return new self(64, 32, $data);
case 64 * 64 * 4:
return new self(64, 64, $data);
case 128 * 64 * 4:
return new self(128, 64, $data);
case 128 * 128 * 4:
return new self(128, 128, $data);
}
throw new \InvalidArgumentException("Unknown size");
}
public function getHeight() : int{
return $this->height;
}
public function getWidth() : int{
return $this->width;
}
public function getData() : string{
return $this->data;
}
}

View File

@ -0,0 +1,67 @@
<?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\command;
class CommandEnumConstraint{
/** @var CommandEnum */
private $enum;
/** @var int */
private $valueOffset;
/** @var int[] */
private $constraints; //TODO: find constants
/**
* @param CommandEnum $enum
* @param int $valueOffset
* @param int[] $constraints
*/
public function __construct(CommandEnum $enum, int $valueOffset, array $constraints){
(static function(int ...$_){})(...$constraints);
if(!isset($enum->getValues()[$valueOffset])){
throw new \InvalidArgumentException("Invalid enum value offset $valueOffset");
}
$this->enum = $enum;
$this->valueOffset = $valueOffset;
$this->constraints = $constraints;
}
public function getEnum() : CommandEnum{
return $this->enum;
}
public function getValueOffset() : int{
return $this->valueOffset;
}
public function getAffectedValue() : string{
return $this->enum->getValues()[$this->valueOffset];
}
/**
* @return int[]
*/
public function getConstraints() : array{
return $this->constraints;
}
}

View File

@ -26,6 +26,9 @@ namespace pocketmine\network\mcpe\protocol\types\command;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
class CommandParameter{
public const FLAG_FORCE_COLLAPSE_ENUM = 0x1;
public const FLAG_HAS_ENUM_CONSTRAINT = 0x2;
/** @var string */
public $paramName;
/** @var int */
@ -39,28 +42,28 @@ class CommandParameter{
/** @var string|null */
public $postfix;
private static function baseline(string $name, int $type, bool $optional) : self{
private static function baseline(string $name, int $type, int $flags, bool $optional) : self{
$result = new self;
$result->paramName = $name;
$result->paramType = $type;
$result->flags = $flags;
$result->isOptional = $optional;
return $result;
}
public static function standard(string $name, int $type, bool $optional = false) : self{
return self::baseline($name, AvailableCommandsPacket::ARG_FLAG_VALID | $type, $optional);
public static function standard(string $name, int $type, int $flags = 0, bool $optional = false) : self{
return self::baseline($name, AvailableCommandsPacket::ARG_FLAG_VALID | $type, $flags, $optional);
}
public static function postfixed(string $name, string $postfix, bool $optional = false) : self{
$result = self::baseline($name, AvailableCommandsPacket::ARG_FLAG_POSTFIX, $optional);
public static function postfixed(string $name, string $postfix, int $flags = 0, bool $optional = false) : self{
$result = self::baseline($name, AvailableCommandsPacket::ARG_FLAG_POSTFIX, $flags, $optional);
$result->postfix = $postfix;
return $result;
}
public static function enum(string $name, CommandEnum $enum, int $flags, bool $optional = false) : self{
$result = self::baseline($name, AvailableCommandsPacket::ARG_FLAG_ENUM | AvailableCommandsPacket::ARG_FLAG_VALID, $optional);
$result = self::baseline($name, AvailableCommandsPacket::ARG_FLAG_ENUM | AvailableCommandsPacket::ARG_FLAG_VALID, $flags, $optional);
$result->enum = $enum;
$result->flags = $flags;
return $result;
}
}

View File

@ -38,6 +38,6 @@ final class ContainerIds{
public const CREATIVE = 121;
public const HOTBAR = 122;
public const FIXED_INVENTORY = 123;
public const CURSOR = 124;
public const UI = 124;
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory;
use pocketmine\crafting\CraftingGrid;
use pocketmine\inventory\AnvilInventory;
use pocketmine\inventory\EnchantInventory;
use pocketmine\inventory\transaction\action\CreateItemAction;
@ -41,7 +42,6 @@ class NetworkInventoryAction{
public const SOURCE_WORLD = 2; //drop/pickup item entity
public const SOURCE_CREATIVE = 3;
public const SOURCE_CRAFTING_GRID = 100;
public const SOURCE_TODO = 99999;
/**
@ -116,7 +116,6 @@ class NetworkInventoryAction{
break;
case self::SOURCE_CREATIVE:
break;
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
$this->windowId = $packet->getVarInt();
break;
@ -148,7 +147,6 @@ class NetworkInventoryAction{
break;
case self::SOURCE_CREATIVE:
break;
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
$packet->putVarInt($this->windowId);
break;
@ -169,11 +167,37 @@ class NetworkInventoryAction{
* @throws \UnexpectedValueException
*/
public function createInventoryAction(Player $player) : ?InventoryAction{
if($this->oldItem->equalsExact($this->newItem)){
//filter out useless noise in 1.13
return null;
}
switch($this->sourceType){
case self::SOURCE_CONTAINER:
$window = $player->getNetworkSession()->getInvManager()->getWindow($this->windowId);
if($this->windowId === ContainerIds::UI and $this->inventorySlot > 0){
if($this->inventorySlot === 50){
return null; //useless noise
}
if($this->inventorySlot >= 28 and $this->inventorySlot <= 31){
$window = $player->getCraftingGrid();
if($window->getGridWidth() !== CraftingGrid::SIZE_SMALL){
throw new \UnexpectedValueException("Expected small crafting grid");
}
$slot = $this->inventorySlot - 28;
}elseif($this->inventorySlot >= 32 and $this->inventorySlot <= 40){
$window = $player->getCraftingGrid();
if($window->getGridWidth() !== CraftingGrid::SIZE_BIG){
throw new \UnexpectedValueException("Expected big crafting grid");
}
$slot = $this->inventorySlot - 32;
}else{
throw new \UnexpectedValueException("Unhandled magic UI slot offset $this->inventorySlot");
}
}else{
$window = $player->getNetworkSession()->getInvManager()->getWindow($this->windowId);
$slot = $this->inventorySlot;
}
if($window !== null){
return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem);
return new SlotChangeAction($window, $slot, $this->oldItem, $this->newItem);
}
throw new \UnexpectedValueException("No open container with window ID $this->windowId");
@ -193,7 +217,6 @@ class NetworkInventoryAction{
throw new \UnexpectedValueException("Unexpected creative action type $this->inventorySlot");
}
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
//These types need special handling.
switch($this->windowId){
@ -226,18 +249,4 @@ class NetworkInventoryAction{
throw new \UnexpectedValueException("Unknown inventory source type $this->sourceType");
}
}
/**
* Hack to allow identifying crafting transaction parts.
*
* @return bool
*/
public function isCraftingPart() : bool{
return ($this->sourceType === self::SOURCE_TODO or $this->sourceType === self::SOURCE_CRAFTING_GRID) and
($this->windowId === self::SOURCE_TYPE_CRAFTING_RESULT or $this->windowId === self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT);
}
public function isFinalCraftingPart() : bool{
return $this->isCraftingPart() and $this->windowId === self::SOURCE_TYPE_CRAFTING_RESULT;
}
}

View File

@ -30,9 +30,12 @@ final class ResourcePackType{
}
public const INVALID = 0;
public const RESOURCES = 1;
public const BEHAVIORS = 2;
public const WORLD_TEMPLATE = 3;
public const ADDON = 4; //scripts?
public const SKINS = 5;
public const ADDON = 1;
public const CACHED = 2;
public const COPY_PROTECTED = 3;
public const BEHAVIORS = 4;
public const PERSONA_PIECE = 5;
public const RESOURCES = 6;
public const SKINS = 7;
public const WORLD_TEMPLATE = 8;
}

View File

@ -48,6 +48,9 @@ use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\ShortMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\Vec3MetadataProperty;
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;
use pocketmine\utils\BinaryDataException;
use pocketmine\utils\BinaryStream;
@ -94,6 +97,66 @@ class NetworkBinaryStream extends BinaryStream{
$this->putLInt($uuid->getPart(2));
}
public function getSkin() : SkinData{
$skinId = $this->getString();
$skinResourcePatch = $this->getString();
$skinData = $this->getSkinImage();
$animationCount = $this->getLInt();
$animations = [];
for($i = 0; $i < $animationCount; ++$i){
$animations[] = new SkinAnimation(
$skinImage = $this->getSkinImage(),
$animationType = $this->getLInt(),
$animationFrames = $this->getLFloat()
);
}
$capeData = $this->getSkinImage();
$geometryData = $this->getString();
$animationData = $this->getString();
$premium = $this->getBool();
$persona = $this->getBool();
$capeOnClassic = $this->getBool();
$capeId = $this->getString();
$fullSkinId = $this->getString();
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId);
}
public function putSkin(SkinData $skin){
$this->putString($skin->getSkinId());
$this->putString($skin->getResourcePatch());
$this->putSkinImage($skin->getSkinImage());
$this->putLInt(count($skin->getAnimations()));
foreach($skin->getAnimations() as $animation){
$this->putSkinImage($animation->getImage());
$this->putLInt($animation->getType());
$this->putLFloat($animation->getFrames());
}
$this->putSkinImage($skin->getCapeImage());
$this->putString($skin->getGeometryData());
$this->putString($skin->getAnimationData());
$this->putBool($skin->isPremium());
$this->putBool($skin->isPersona());
$this->putBool($skin->isPersonaCapeOnClassic());
$this->putString($skin->getCapeId());
//this has to be unique or the client will do stupid things
$this->putString(UUID::fromRandom()->toString()); //full skin ID
}
private function getSkinImage() : SkinImage{
$width = $this->getLInt();
$height = $this->getLInt();
$data = $this->getString();
return new SkinImage($height, $width, $data);
}
private function putSkinImage(SkinImage $image) : void{
$this->putLInt($image->getWidth());
$this->putLInt($image->getHeight());
$this->putString($image->getData());
}
/**
* @return Item
*

View File

@ -37,7 +37,6 @@ use pocketmine\item\ItemFactory;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\ExplodePacket;
use pocketmine\world\particle\HugeExplodeSeedParticle;
use pocketmine\world\sound\ExplodeSound;
use pocketmine\world\utils\SubChunkIteratorManager;
@ -251,8 +250,6 @@ class Explosion{
$send[] = $pos->subtract($source);
}
$this->world->broadcastPacketToViewers($source, ExplodePacket::create($this->source->asVector3(), $this->size, $send));
$this->world->addParticle($source, new HugeExplodeSeedParticle());
$this->world->addSound($source, new ExplodeSound());

View File

@ -35,6 +35,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\utils\UUID;
use function str_repeat;
@ -92,7 +93,7 @@ class FloatingTextParticle implements Particle{
$uuid = UUID::fromRandom();
$name = $this->title . ($this->text !== "" ? "\n" . $this->text : "");
$p[] = PlayerListPacket::add([PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))]);
$p[] = PlayerListPacket::add([PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin("Standard_Custom", str_repeat("\x00", 8192))))]);
$pk = new AddPlayerPacket();
$pk->uuid = $uuid;