Merge branch 'drew-1.13' into stable

This commit is contained in:
Dylan K. Taylor 2019-12-06 20:38:49 +00:00
commit 394c999710
53 changed files with 1976 additions and 294 deletions

View File

@ -146,6 +146,10 @@ use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\network\mcpe\protocol\types\SkinImage;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
use pocketmine\network\mcpe\VerifyLoginTask;
@ -1112,9 +1116,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
}
protected function sendRespawnPacket(Vector3 $pos){
protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){
$pk = new RespawnPacket();
$pk->position = $pos->add(0, $this->baseOffset, 0);
$pk->respawnState = $respawnState;
$pk->entityRuntimeId = $this->getId();
$this->dataPacket($pk);
}
@ -1913,14 +1919,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->uuid = UUID::fromString($packet->clientUUID);
$this->rawUUID = $this->uuid->toBinary();
$skin = new Skin(
$animations = [];
foreach($packet->clientData["AnimatedImageData"] as $animation){
$animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], $animation["Image"]), $animation["Type"], $animation["Frames"]);
}
$skinData = new SkinData(
$packet->clientData["SkinId"],
base64_decode($packet->clientData["SkinData"] ?? ""),
base64_decode($packet->clientData["CapeData"] ?? ""),
$packet->clientData["SkinGeometryName"] ?? "",
base64_decode($packet->clientData["SkinGeometry"] ?? "")
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])),
$animations,
new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")),
base64_decode($packet->clientData["SkinGeometryData"] ?? ""),
base64_decode($packet->clientData["AnimationData"] ?? ""),
$packet->clientData["PremiumSkin"] ?? false,
$packet->clientData["PersonaSkin"] ?? false,
$packet->clientData["CapeOnClassicSkin"] ?? false,
$packet->clientData["CapeId"] ?? ""
);
$skin = SkinAdapterSingleton::get()->fromSkinData($skinData);
if(!$skin->isValid()){
$this->close("", "disconnectionScreen.invalidSkin");
@ -2156,7 +2175,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->spawnZ = $spawnPosition->getFloorZ();
$pk->hasAchievementsDisabled = true;
$pk->time = $this->level->getTime();
$pk->eduMode = false;
$pk->eduEditionOffer = 0;
$pk->rainLevel = 0; //TODO: implement these properly
$pk->lightningLevel = 0;
$pk->commandsEnabled = true;
@ -2491,6 +2510,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
if($this->isUsingItem()){
$slot = $this->inventory->getItemInHand();
if($slot instanceof Consumable){
$ev = new PlayerItemConsumeEvent($this, $slot);
if($this->hasItemCooldown($slot)){
$ev->setCancelled();
}
$ev->call();
if($ev->isCancelled() or !$this->consumeObject($slot)){
$this->inventory->sendContents($this);
return true;
}
$this->resetItemCooldown($slot);
if($this->isSurvival()){
$slot->pop();
$this->inventory->setItemInHand($slot);
$this->inventory->addItem($slot->getResidue());
}
$this->setUsingItem(false);
}
}
$directionVector = $this->getDirectionVector();
if($this->isCreative()){
@ -2644,33 +2684,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
return true;
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME:
$slot = $this->inventory->getItemInHand();
if($slot instanceof Consumable){
$ev = new PlayerItemConsumeEvent($this, $slot);
if($this->hasItemCooldown($slot)){
$ev->setCancelled();
}
$ev->call();
if($ev->isCancelled() or !$this->consumeObject($slot)){
$this->inventory->sendContents($this);
return true;
}
$this->resetItemCooldown($slot);
if($this->isSurvival()){
$slot->pop();
$this->inventory->setItemInHand($slot);
$this->inventory->addItem($slot->getResidue());
}
return true;
}
break;
default:
break;
}
@ -2913,6 +2926,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
public function handleRespawn(RespawnPacket $packet) : bool{
if(!$this->isAlive() && $packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){
$this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN);
return true;
}
return false;
}
/**
* Drops an item on the ground in front of the player. Returns if the item drop was successful.
*
@ -3805,7 +3827,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true);
$this->cursorInventory = new PlayerCursorInventory($this);
$this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true);
$this->addWindow($this->cursorInventory, ContainerIds::UI, true);
$this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL);

View File

@ -76,6 +76,7 @@ use pocketmine\network\mcpe\protocol\DataPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\RakLibInterface;
use pocketmine\network\Network;
use pocketmine\network\query\QueryHandler;
@ -2395,7 +2396,7 @@ class Server{
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId);
$pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, SkinAdapterSingleton::get()->toSkinData($skin), $xboxUserId);
$this->broadcastPacket($players ?? $this->playerList, $pk);
}
@ -2418,7 +2419,7 @@ class Server{
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
foreach($this->playerList as $player){
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid());
$pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid());
}
$p->dataPacket($pk);

View File

@ -53,6 +53,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\Player;
use pocketmine\utils\UUID;
use function array_filter;
@ -182,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public function sendSkin(?array $targets = null) : void{
$pk = new PlayerSkinPacket();
$pk->uuid = $this->getUniqueId();
$pk->skin = $this->skin;
$pk->skin = SkinAdapterSingleton::get()->toSkinData($this->skin);
$this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk);
}
@ -850,7 +851,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
$pk = new PlayerListPacket();
$pk->type = PlayerListPacket::TYPE_ADD;
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)];
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))];
$player->dataPacket($pk);
}

View File

@ -38,7 +38,6 @@ use pocketmine\level\particle\HugeExplodeSeedParticle;
use pocketmine\level\utils\SubChunkIteratorManager;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\ExplodePacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\tile\Chest;
use pocketmine\tile\Container;
@ -260,12 +259,6 @@ class Explosion{
$send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
}
$pk = new ExplodePacket();
$pk->position = $this->source->asVector3();
$pk->radius = $this->size;
$pk->records = $send;
$this->level->broadcastPacketToViewers($source, $pk);
$this->level->addParticle(new HugeExplodeSeedParticle($source));
$this->level->broadcastLevelSoundEvent($source, LevelSoundEventPacket::SOUND_EXPLODE);

View File

@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\utils\UUID;
use function str_repeat;
@ -96,7 +97,9 @@ class FloatingTextParticle extends Particle{
$add = new PlayerListPacket();
$add->type = PlayerListPacket::TYPE_ADD;
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))];
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin(
"Standard_Custom", str_repeat("\x00", 8192), "", "geometry.humanoid.custom"
)))];
$p[] = $add;
$pk = new AddPlayerPacket();

View File

@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe;
use pocketmine\entity\Attribute;
use pocketmine\entity\Entity;
use pocketmine\entity\Skin;
use pocketmine\item\Durable;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
@ -37,6 +38,9 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
use pocketmine\network\mcpe\protocol\types\EntityLink;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\network\mcpe\protocol\types\SkinImage;
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
use pocketmine\network\mcpe\protocol\types\StructureSettings;
use pocketmine\utils\BinaryStream;
use pocketmine\utils\UUID;
@ -74,6 +78,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());
}
public function getSlot() : Item{
$id = $this->getVarInt();
if($id === 0){

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,6 +55,7 @@ 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;
@ -61,8 +63,9 @@ use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
use pocketmine\network\mcpe\protocol\DataPacket;
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;
@ -90,12 +93,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;
@ -134,6 +140,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;
@ -144,11 +151,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;
@ -247,7 +255,7 @@ abstract class NetworkSession{
return false;
}
public function handleExplode(ExplodePacket $packet) : bool{
public function handleTickSync(TickSyncPacket $packet) : bool{
return false;
}
@ -679,11 +687,11 @@ abstract class NetworkSession{
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;
}
@ -698,4 +706,36 @@ abstract class NetworkSession{
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

@ -59,12 +59,15 @@ use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
use pocketmine\network\mcpe\protocol\RespawnPacket;
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
@ -181,6 +184,10 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
return $this->player->handleAnimate($packet);
}
public function handleRespawn(RespawnPacket $packet) : bool{
return $this->player->handleRespawn($packet);
}
public function handleContainerClose(ContainerClosePacket $packet) : bool{
return $this->player->handleContainerClose($packet);
}
@ -248,7 +255,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
}
public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{
return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName);
return $this->player->changeSkin(SkinAdapterSingleton::get()->fromSkinData($packet->skin), $packet->newSkinName, $packet->oldSkinName);
}
public function handleBookEdit(BookEditPacket $packet) : bool{

View File

@ -74,6 +74,8 @@ class AddPlayerPacket extends DataPacket{
/** @var string */
public $deviceId = ""; //TODO: fill player's device ID (???)
/** @var int */
public $buildPlatform = -1;
protected function decodePayload(){
$this->uuid = $this->getUUID();
@ -103,6 +105,7 @@ class AddPlayerPacket extends DataPacket{
}
$this->deviceId = $this->getString();
$this->buildPlatform = $this->getLInt();
}
protected function encodePayload(){
@ -133,6 +136,7 @@ class AddPlayerPacket extends DataPacket{
}
$this->putString($this->deviceId);
$this->putLInt($this->buildPlatform);
}
public function handle(NetworkSession $session) : 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\NetworkSession;
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(NetworkSession $handler) : bool{
return $handler->handleAnvilDamage($this);
}
}

File diff suppressed because one or more lines are too long

View File

@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\CommandData;
use pocketmine\network\mcpe\protocol\types\CommandEnum;
use pocketmine\network\mcpe\protocol\types\CommandEnumConstraint;
use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\utils\BinaryDataException;
use function count;
@ -56,17 +57,17 @@ class AvailableCommandsPacket extends DataPacket{
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:
@ -79,12 +80,23 @@ class AvailableCommandsPacket extends DataPacket{
*/
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
@ -92,6 +104,12 @@ class AvailableCommandsPacket extends DataPacket{
*/
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(){
/** @var string[] $enumValues */
$enumValues = [];
@ -108,7 +126,10 @@ class AvailableCommandsPacket extends DataPacket{
/** @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->enumName])){
$this->hardcodedEnums[] = $enum;
}
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
@ -118,6 +139,10 @@ class AvailableCommandsPacket extends DataPacket{
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);
}
}
/**
@ -210,6 +235,50 @@ class AvailableCommandsPacket extends DataPacket{
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $enumValues
*
* @return CommandEnumConstraint
*/
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 \UnexpectedValueException("Enum constraint refers to unknown enum value index $valueIndex");
}
$enumIndex = $this->getLInt();
if(!isset($enums[$enumIndex])){
throw new \UnexpectedValueException("Enum constraint refers to unknown enum index $enumIndex");
}
$enum = $enums[$enumIndex];
$valueOffset = array_search($enumValues[$valueIndex], $enum->enumValues, true);
if($valueOffset === false){
throw new \UnexpectedValueException("Value \"" . $enumValues[$valueIndex] . "\" does not belong to enum \"$enum->enumName\"");
}
$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()->enumName]);
$this->putUnsignedVarInt(count($constraint->getConstraints()));
foreach($constraint->getConstraints() as $v){
$this->putByte($v);
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $postfixes
@ -349,27 +418,28 @@ class AvailableCommandsPacket extends DataPacket{
$enumIndexes = [];
/** @var CommandEnum[] $enums */
$enums = [];
$addEnumFn = static function(CommandEnum $enum) use (&$enums, &$enumIndexes, &$enumValueIndexes){
if(!isset($enumIndexes[$enum->enumName])){
$enums[$enumIndexes[$enum->enumName] = count($enumIndexes)] = $enum;
}
foreach($enum->enumValues as $str){
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes);
}
};
foreach($this->hardcodedEnums as $enum){
$addEnumFn($enum);
}
foreach($this->commandData as $commandData){
if($commandData->aliases !== null){
if(!isset($enumIndexes[$commandData->aliases->enumName])){
$enums[$enumIndexes[$commandData->aliases->enumName] = count($enumIndexes)] = $commandData->aliases;
}
foreach($commandData->aliases->enumValues as $str){
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); //latest index
}
$addEnumFn($commandData->aliases);
}
/** @var CommandParameter[] $overload */
foreach($commandData->overloads as $overload){
/** @var CommandParameter $parameter */
foreach($overload as $parameter){
if($parameter->enum !== null){
if(!isset($enumIndexes[$parameter->enum->enumName])){
$enums[$enumIndexes[$parameter->enum->enumName] = count($enumIndexes)] = $parameter->enum;
}
foreach($parameter->enum->enumValues as $str){
$enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes);
}
$addEnumFn($parameter->enum);
}
if($parameter->postfix !== null){
@ -403,6 +473,11 @@ class AvailableCommandsPacket extends DataPacket{
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(NetworkSession $session) : bool{

View File

@ -26,11 +26,14 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use function file_get_contents;
class BiomeDefinitionListPacket extends DataPacket{
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{
}
protected function encodePayload(){
$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(NetworkSession $session) : 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\NetworkSession;
class CompletedUsingItemPacket extends DataPacket{
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(NetworkSession $session) : bool{
return $session->handleCompletedUsingItem($this);
}
}

View File

@ -33,6 +33,8 @@ use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
use pocketmine\network\mcpe\protocol\types\PotionTypeRecipe;
#ifndef COMPILE
use pocketmine\utils\Binary;
#endif
@ -53,6 +55,10 @@ class CraftingDataPacket extends DataPacket{
/** @var object[] */
public $entries = [];
/** @var PotionTypeRecipe[] */
public $potionTypeRecipes = [];
/** @var PotionContainerChangeRecipe[] */
public $potionContainerRecipes = [];
/** @var bool */
public $cleanRecipes = false;
@ -140,6 +146,18 @@ class CraftingDataPacket extends DataPacket{
}
$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();
}
@ -240,6 +258,18 @@ class CraftingDataPacket extends DataPacket{
$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\NetworkSession;
class EducationSettingsPacket extends DataPacket{
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(NetworkSession $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\NetworkSession;
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(NetworkSession $handler) : bool{
return $handler->handleEmote($this);
}
}

View File

@ -1,73 +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\NetworkSession;
use function count;
class ExplodePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::EXPLODE_PACKET;
/** @var Vector3 */
public $position;
/** @var float */
public $radius;
/** @var Vector3[] */
public $records = [];
public function clean(){
$this->records = [];
return parent::clean();
}
protected function decodePayload(){
$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(){
$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(NetworkSession $session) : bool{
return $session->handleExplode($this);
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
use function count;
@ -74,7 +75,26 @@ class InventoryTransactionPacket extends DataPacket{
$this->transactionType = $this->getUnsignedVarInt();
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->actions[] = (new NetworkInventoryAction())->read($this);
$this->actions[] = $action = (new NetworkInventoryAction())->read($this);
if(
$action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and
$action->windowId === ContainerIds::UI and
$action->inventorySlot === 50 and
!$action->oldItem->equalsExact($action->newItem)
){
$this->isCraftingPart = true;
if(!$action->oldItem->isNull() and $action->newItem->isNull()){
$this->isFinalCraftingPart = true;
}
}elseif(
$action->sourceType === NetworkInventoryAction::SOURCE_TODO and (
$action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
$action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
)
){
$this->isCraftingPart = true;
}
}
$this->trData = new \stdClass();

View File

@ -70,7 +70,7 @@ class MoveActorDeltaPacket extends DataPacket{
protected function decodePayload(){
$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);
@ -93,7 +93,7 @@ class MoveActorDeltaPacket extends DataPacket{
protected function encodePayload(){
$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\NetworkSession;
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(NetworkSession $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\NetworkSession;
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(NetworkSession $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\NetworkSession;
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(NetworkSession $handler) : bool{
return $handler->handlePlayerAuthInput($this);
}
}

View File

@ -26,8 +26,8 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\entity\Skin;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use function count;
@ -57,22 +57,12 @@ class PlayerListPacket extends DataPacket{
$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();
$entry->skin = new Skin(
$skinId,
$skinData,
$capeData,
$geometryName,
$geometryData
);
$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();
}
@ -89,13 +79,12 @@ class PlayerListPacket extends DataPacket{
$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,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\entity\Skin;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\SkinData;
use pocketmine\utils\UUID;
class PlayerSkinPacket extends DataPacket{
@ -38,39 +38,21 @@ class PlayerSkinPacket extends DataPacket{
public $oldSkinName = "";
/** @var string */
public $newSkinName = "";
/** @var Skin */
/** @var SkinData */
public $skin;
/** @var bool */
public $premiumSkin = false;
protected function decodePayload(){
$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();
$this->skin = new Skin($skinId, $skinData, $capeData, $geometryModel, $geometryData);
$this->premiumSkin = $this->getBool();
}
protected function encodePayload(){
$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(NetworkSession $session) : bool{

View File

@ -39,15 +39,15 @@ interface 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;
@ -71,7 +71,7 @@ interface 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;
@ -180,10 +180,18 @@ interface 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

@ -46,15 +46,14 @@ class ResourcePackChunkDataPacket extends DataPacket{
$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(){
$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(NetworkSession $session) : bool{

View File

@ -44,6 +44,8 @@ class ResourcePackStackPacket extends DataPacket{
/** @var bool */
public $isExperimental = false;
/** @var string */
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
protected function decodePayload(){
$this->mustAccept = $this->getBool();
@ -62,6 +64,7 @@ class ResourcePackStackPacket extends DataPacket{
}
$this->isExperimental = $this->getBool();
$this->baseGameVersion = $this->getString();
}
protected function encodePayload(){
@ -82,6 +85,7 @@ class ResourcePackStackPacket extends DataPacket{
}
$this->putBool($this->isExperimental);
$this->putString($this->baseGameVersion);
}
public function handle(NetworkSession $session) : bool{

View File

@ -32,15 +32,27 @@ use pocketmine\network\mcpe\NetworkSession;
class RespawnPacket extends DataPacket{
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;
protected function decodePayload(){
$this->position = $this->getVector3();
$this->respawnState = $this->getByte();
$this->entityRuntimeId = $this->getEntityRuntimeId();
}
protected function encodePayload(){
$this->putVector3($this->position);
$this->putByte($this->respawnState);
$this->putEntityRuntimeId($this->entityRuntimeId);
}
public function handle(NetworkSession $session) : 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;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
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(NetworkSession $handler) : bool{
return $handler->handleSettingsCommand($this);
}
}

View File

@ -27,6 +27,10 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\math\Vector3;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
@ -79,8 +83,8 @@ class StartGamePacket extends DataPacket{
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 +134,8 @@ class StartGamePacket extends DataPacket{
/** @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 +144,8 @@ class StartGamePacket extends DataPacket{
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 +153,7 @@ class StartGamePacket extends DataPacket{
/** @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 +177,7 @@ class StartGamePacket extends DataPacket{
$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 +201,22 @@ class StartGamePacket extends DataPacket{
$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 NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
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 +247,7 @@ class StartGamePacket extends DataPacket{
$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 +271,12 @@ class StartGamePacket extends DataPacket{
$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 +284,11 @@ class StartGamePacket extends DataPacket{
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 NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates()));
}
$this->put(self::$blockTableCache);
}else{
$this->put(self::serializeBlockTable($this->blockTable));
$this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable));
}
if($this->itemTable === null){
if(self::$itemTableCache === null){
@ -292,17 +302,6 @@ class StartGamePacket extends DataPacket{
$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\NetworkSession;
use pocketmine\network\mcpe\protocol\types\StructureSettings;
class StructureTemplateDataExportRequestPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET;
class StructureTemplateDataRequestPacket extends DataPacket{
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{
}
public function handle(NetworkSession $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\NetworkSession;
class StructureTemplateDataExportResponsePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET;
class StructureTemplateDataResponsePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET;
/** @var string */
public $structureTemplateName;
@ -51,6 +51,6 @@ class StructureTemplateDataExportResponsePacket extends DataPacket{
}
public function handle(NetworkSession $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\NetworkSession;
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(NetworkSession $handler) : bool{
return $handler->handleTickSync($this);
}
}

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 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->enumValues[$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->enumValues[$this->valueOffset];
}
/**
* @return int[]
*/
public function getConstraints() : array{
return $this->constraints;
}
}

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
class CommandParameter{
public const FLAG_FORCE_COLLAPSE_ENUM = 0x1;
public const FLAG_HAS_ENUM_CONSTRAINT = 0x2;
/** @var string */
public $paramName;
/** @var int */

View File

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

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,63 @@
<?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());
}
return new SkinData(
$skin->getSkinId(),
json_encode(["geometry" => ["default" => $skin->getGeometryName()]]),
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), "", "geometry.humanoid.custom");
}elseif($data->isPersonaCapeOnClassic()){
$capeData = "";
}
return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, $geometryName, $data->getGeometryData());
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\inventory\CraftingGrid;
use pocketmine\inventory\transaction\action\CreativeInventoryAction;
use pocketmine\inventory\transaction\action\DropItemAction;
use pocketmine\inventory\transaction\action\InventoryAction;
@ -36,7 +37,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;
/**
@ -108,17 +108,8 @@ class NetworkInventoryAction{
break;
case self::SOURCE_CREATIVE:
break;
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
$this->windowId = $packet->getVarInt();
switch($this->windowId){
/** @noinspection PhpMissingBreakStatementInspection */
case self::SOURCE_TYPE_CRAFTING_RESULT:
$packet->isFinalCraftingPart = true;
case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT:
$packet->isCraftingPart = true;
break;
}
break;
default:
throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType");
@ -146,7 +137,6 @@ class NetworkInventoryAction{
break;
case self::SOURCE_CREATIVE:
break;
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
$packet->putVarInt($this->windowId);
break;
@ -167,11 +157,37 @@ class NetworkInventoryAction{
* @throws \UnexpectedValueException
*/
public function createInventoryAction(Player $player){
if($this->oldItem->equalsExact($this->newItem)){
//filter out useless noise in 1.13
return null;
}
switch($this->sourceType){
case self::SOURCE_CONTAINER:
$window = $player->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->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("Player " . $player->getName() . " has no open container with window ID $this->windowId");
@ -195,7 +211,6 @@ class NetworkInventoryAction{
}
return new CreativeInventoryAction($this->oldItem, $this->newItem, $type);
case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO:
//These types need special handling.
switch($this->windowId){

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

@ -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

@ -24,6 +24,12 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\block\BlockIds;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
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,57 @@ 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 NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"));
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 NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"));
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");
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
$mappedState->setName("block");
$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 +119,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 +164,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;
}
}

@ -1 +1 @@
Subproject commit b5a8c68c4262e5d9d7f8280c1d07c252a5e8dbf8
Subproject commit a38b42788883fa2094f67874f15594044be1ac4d