Protocol changes for 1.16.100

This commit is contained in:
Dylan K. Taylor 2020-11-21 01:07:25 +00:00
parent 8273f789ee
commit 98cdc80d37
35 changed files with 1206 additions and 219 deletions

View File

@ -100,6 +100,7 @@ use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
@ -148,6 +149,7 @@ use pocketmine\network\mcpe\protocol\types\CommandEnum;
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\Experiments;
use pocketmine\network\mcpe\protocol\types\GameMode;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
@ -1939,7 +1941,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$animation["ImageWidth"],
base64_decode($animation["Image"], true)),
$animation["Type"],
$animation["Frames"]
$animation["Frames"],
$animation["AnimationExpression"]
);
}
@ -2179,6 +2182,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
//but it does have an annoying side-effect when true: it makes
//the client remove its own non-server-supplied resource packs.
$pk->mustAccept = false;
$pk->experiments = new Experiments([], false);
$this->dataPacket($pk);
break;
case ResourcePackClientResponsePacket::STATUS_COMPLETED:
@ -2244,6 +2248,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$pk->commandsEnabled = true;
$pk->levelId = "";
$pk->worldName = $this->server->getMotd();
$pk->experiments = new Experiments([], false);
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
$this->dataPacket($pk);
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
@ -2870,7 +2876,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
}
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE_REQUEST)){
if(!$this->spawned or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN)){
return true;
}
@ -2957,7 +2963,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case PlayerActionPacket::ACTION_STOP_SWIMMING:
//TODO: handle this when it doesn't spam every damn tick (yet another spam bug!!)
break;
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //ignored (for now)
case PlayerActionPacket::ACTION_INTERACT_BLOCK: //TODO: ignored (for now)
break;
case PlayerActionPacket::ACTION_CREATIVE_PLAYER_DESTROY_BLOCK:
//TODO: do we need to handle this?
break;
default:
$this->server->getLogger()->debug("Unhandled/unknown player action type " . $packet->action . " from " . $this->getName());
@ -3039,6 +3048,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true;
}
/** @var int|null */
private $closingWindowId = null;
/** @internal */
public function getClosingWindowId() : ?int{ return $this->closingWindowId; }
public function handleContainerClose(ContainerClosePacket $packet) : bool{
if(!$this->spawned){
return true;
@ -3050,12 +3065,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
unset($this->openHardcodedWindows[$packet->windowId]);
$pk = new ContainerClosePacket();
$pk->windowId = $packet->windowId;
$pk->server = false;
$this->sendDataPacket($pk);
return true;
}
if(isset($this->windowIndex[$packet->windowId])){
$this->closingWindowId = $packet->windowId;
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
$this->removeWindow($this->windowIndex[$packet->windowId]);
$this->closingWindowId = null;
//removeWindow handles sending the appropriate
return true;
}

View File

@ -64,6 +64,7 @@ abstract class ContainerInventory extends BaseInventory{
public function onClose(Player $who) : void{
$pk = new ContainerClosePacket();
$pk->windowId = $who->getWindowId($this);
$pk->server = $who->getClosingWindowId() !== $pk->windowId;
$who->dataPacket($pk);
parent::onClose($who);
}

View File

@ -163,6 +163,7 @@ class CraftingTransaction extends InventoryTransaction{
*/
$pk = new ContainerClosePacket();
$pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID;
$pk->server = true;
$this->source->dataPacket($pk);
}

View File

@ -35,6 +35,9 @@ use pocketmine\math\Vector3;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\NamedTag;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
use pocketmine\network\mcpe\protocol\types\EntityLink;
use pocketmine\network\mcpe\protocol\types\GameRuleType;
@ -91,7 +94,8 @@ class NetworkBinaryStream extends BinaryStream{
$skinImage = $this->getSkinImage();
$animationType = $this->getLInt();
$animationFrames = $this->getLFloat();
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames);
$expressionType = $this->getLInt();
$animations[] = new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
}
$capeData = $this->getSkinImage();
$geometryData = $this->getString();
@ -186,15 +190,17 @@ class NetworkBinaryStream extends BinaryStream{
}
public function getSlot() : Item{
$id = $this->getVarInt();
if($id === 0){
$netId = $this->getVarInt();
if($netId === 0){
return ItemFactory::get(0, 0, 0);
}
$auxValue = $this->getVarInt();
$data = $auxValue >> 8;
$netData = $auxValue >> 8;
$cnt = $auxValue & 0xff;
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($netId, $netData);
$nbtLen = $this->getLShort();
/** @var CompoundTag|null $nbt */
@ -223,12 +229,12 @@ class NetworkBinaryStream extends BinaryStream{
$this->getString();
}
if($id === ItemIds::SHIELD){
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
$this->getVarLong(); //"blocking tick" (ffs mojang)
}
if($nbt !== null){
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
$data = $nbt->getInt(self::DAMAGE_TAG);
$meta = $nbt->getInt(self::DAMAGE_TAG);
$nbt->removeTag(self::DAMAGE_TAG);
if($nbt->count() === 0){
$nbt = null;
@ -242,7 +248,7 @@ class NetworkBinaryStream extends BinaryStream{
}
}
end:
return ItemFactory::get($id, $data, $cnt, $nbt);
return ItemFactory::get($id, $meta, $cnt, $nbt);
}
public function putSlot(Item $item) : void{
@ -252,8 +258,10 @@ class NetworkBinaryStream extends BinaryStream{
return;
}
$this->putVarInt($item->getId());
$auxValue = (($item->getDamage() & 0x7fff) << 8) | $item->getCount();
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
$this->putVarInt($netId);
$auxValue = (($netData & 0x7fff) << 8) | $item->getCount();
$this->putVarInt($auxValue);
$nbt = null;
@ -284,20 +292,18 @@ class NetworkBinaryStream extends BinaryStream{
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
$this->putVarInt(0); //CanDestroy entry count (TODO)
if($item->getId() === ItemIds::SHIELD){
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
$this->putVarLong(0); //"blocking tick" (ffs mojang)
}
}
public function getRecipeIngredient() : Item{
$id = $this->getVarInt();
if($id === 0){
$netId = $this->getVarInt();
if($netId === 0){
return ItemFactory::get(ItemIds::AIR, 0, 0);
}
$meta = $this->getVarInt();
if($meta === 0x7fff){
$meta = -1;
}
$netData = $this->getVarInt();
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($netId, $netData);
$count = $this->getVarInt();
return ItemFactory::get($id, $meta, $count);
}
@ -306,8 +312,14 @@ class NetworkBinaryStream extends BinaryStream{
if($item->isNull()){
$this->putVarInt(0);
}else{
$this->putVarInt($item->getId());
$this->putVarInt($item->getDamage() & 0x7fff);
if($item->hasAnyDamageValue()){
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($item->getId(), 0);
$netData = 0x7fff;
}else{
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
}
$this->putVarInt($netId);
$this->putVarInt($netData);
$this->putVarInt($item->getCount());
}
}
@ -750,6 +762,23 @@ class NetworkBinaryStream extends BinaryStream{
$this->putVarInt($structureEditorData->structureRedstoneSaveMove);
}
public function getNbtRoot() : NamedTag{
$offset = $this->getOffset();
try{
return (new NetworkLittleEndianNBTStream())->read($this->getBuffer(), false, $offset, 512);
}finally{
$this->setOffset($offset);
}
}
public function getNbtCompoundRoot() : CompoundTag{
$root = $this->getNbtRoot();
if(!($root instanceof CompoundTag)){
throw new \UnexpectedValueException("Expected TAG_Compound root");
}
return $root;
}
public function readGenericTypeNetworkId() : int{
return $this->getVarInt();
}

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorFallPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AddActorPacket;
use pocketmine\network\mcpe\protocol\AddBehaviorTreePacket;
@ -33,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddItemActorPacket;
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimateEntityPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
use pocketmine\network\mcpe\protocol\AnvilDamagePacket;
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
@ -45,6 +45,7 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
use pocketmine\network\mcpe\protocol\BookEditPacket;
use pocketmine\network\mcpe\protocol\BossEventPacket;
use pocketmine\network\mcpe\protocol\CameraPacket;
use pocketmine\network\mcpe\protocol\CameraShakePacket;
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
@ -60,6 +61,7 @@ use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\network\mcpe\protocol\CorrectPlayerMovePredictionPacket;
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\CraftingEventPacket;
use pocketmine\network\mcpe\protocol\CreativeContentPacket;
@ -77,6 +79,7 @@ use pocketmine\network\mcpe\protocol\InteractPacket;
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
use pocketmine\network\mcpe\protocol\ItemComponentPacket;
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
use pocketmine\network\mcpe\protocol\ItemStackRequestPacket;
use pocketmine\network\mcpe\protocol\ItemStackResponsePacket;
@ -96,6 +99,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
use pocketmine\network\mcpe\protocol\MotionPredictionHintsPacket;
use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket;
use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
@ -111,6 +115,7 @@ use pocketmine\network\mcpe\protocol\PlayerActionPacket;
use pocketmine\network\mcpe\protocol\PlayerArmorDamagePacket;
use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket;
use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket;
use pocketmine\network\mcpe\protocol\PlayerFogPacket;
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket;
@ -171,7 +176,6 @@ use pocketmine\network\mcpe\protocol\TickSyncPacket;
use pocketmine\network\mcpe\protocol\TransferPacket;
use pocketmine\network\mcpe\protocol\UpdateAttributesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockPropertiesPacket;
use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket;
use pocketmine\network\mcpe\protocol\UpdateEquipPacket;
use pocketmine\network\mcpe\protocol\UpdatePlayerGameTypePacket;
@ -325,10 +329,6 @@ abstract class NetworkSession{
return false;
}
public function handleActorFall(ActorFallPacket $packet) : bool{
return false;
}
public function handleHurtArmor(HurtArmorPacket $packet) : bool{
return false;
}
@ -705,10 +705,6 @@ abstract class NetworkSession{
return false;
}
public function handleUpdateBlockProperties(UpdateBlockPropertiesPacket $packet) : bool{
return false;
}
public function handleClientCacheBlobStatus(ClientCacheBlobStatusPacket $packet) : bool{
return false;
}
@ -796,4 +792,28 @@ abstract class NetworkSession{
public function handlePacketViolationWarning(PacketViolationWarningPacket $packet) : bool{
return false;
}
public function handleMotionPredictionHints(MotionPredictionHintsPacket $packet) : bool{
return false;
}
public function handleAnimateEntity(AnimateEntityPacket $packet) : bool{
return false;
}
public function handleCameraShake(CameraShakePacket $packet) : bool{
return false;
}
public function handlePlayerFog(PlayerFogPacket $packet) : bool{
return false;
}
public function handleCorrectPlayerMovePrediction(CorrectPlayerMovePredictionPacket $packet) : bool{
return false;
}
public function handleItemComponent(ItemComponentPacket $packet) : bool{
return false;
}
}

View File

@ -25,7 +25,6 @@ namespace pocketmine\network\mcpe;
use pocketmine\event\server\DataPacketReceiveEvent;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\ActorFallPacket;
use pocketmine\network\mcpe\protocol\ActorPickRequestPacket;
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
use pocketmine\network\mcpe\protocol\AnimatePacket;
@ -174,10 +173,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
return $this->player->handlePlayerAction($packet);
}
public function handleActorFall(ActorFallPacket $packet) : bool{
return true; //Not used
}
public function handleAnimate(AnimatePacket $packet) : bool{
return $this->player->handleAnimate($packet);
}

View File

@ -0,0 +1,183 @@
<?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\convert;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use function array_key_exists;
use function file_get_contents;
use function is_array;
use function is_numeric;
use function is_string;
use function json_decode;
/**
* This class handles translation between network item ID+metadata to PocketMine-MP internal ID+metadata and vice versa.
*/
final class ItemTranslator{
use SingletonTrait;
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private $simpleCoreToNetMapping = [];
/**
* @var int[]
* @phpstan-var array<int, int>
*/
private $simpleNetToCoreMapping = [];
/**
* runtimeId = array[internalId][metadata]
* @var int[][]
* @phpstan-var array<int, array<int, int>>
*/
private $complexCoreToNetMapping = [];
/**
* [internalId, metadata] = array[runtimeId]
* @var int[][]
* @phpstan-var array<int, array{int, int}>
*/
private $complexNetToCoreMapping = [];
private static function make() : self{
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/r16_to_current_item_map.json');
if($data === false) throw new AssumptionFailedError("Missing required resource file");
$json = json_decode($data, true);
if(!is_array($json) or !isset($json["simple"], $json["complex"]) || !is_array($json["simple"]) || !is_array($json["complex"])){
throw new AssumptionFailedError("Invalid item table format");
}
$legacyStringToIntMapRaw = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/item_id_map.json');
if($legacyStringToIntMapRaw === false){
throw new AssumptionFailedError("Missing required resource file");
}
$legacyStringToIntMap = json_decode($legacyStringToIntMapRaw, true);
if(!is_array($legacyStringToIntMap)){
throw new AssumptionFailedError("Invalid mapping table format");
}
/** @phpstan-var array<string, int> $simpleMappings */
$simpleMappings = [];
foreach($json["simple"] as $oldId => $newId){
if(!is_string($oldId) || !is_string($newId)){
throw new AssumptionFailedError("Invalid item table format");
}
$simpleMappings[$newId] = $legacyStringToIntMap[$oldId];
}
foreach($legacyStringToIntMap as $stringId => $intId){
if(isset($simpleMappings[$stringId])){
throw new \UnexpectedValueException("Old ID $stringId collides with new ID");
}
$simpleMappings[$stringId] = $intId;
}
/** @phpstan-var array<string, array{int, int}> $complexMappings */
$complexMappings = [];
foreach($json["complex"] as $oldId => $map){
if(!is_string($oldId) || !is_array($map)){
throw new AssumptionFailedError("Invalid item table format");
}
foreach($map as $meta => $newId){
if(!is_numeric($meta) || !is_string($newId)){
throw new AssumptionFailedError("Invalid item table format");
}
$complexMappings[$newId] = [$legacyStringToIntMap[$oldId], (int) $meta];
}
}
return new self(ItemTypeDictionary::getInstance(), $simpleMappings, $complexMappings);
}
/**
* @param int[] $simpleMappings
* @param int[][] $complexMappings
* @phpstan-param array<string, int> $simpleMappings
* @phpstan-param array<string, array<int, int>> $complexMappings
*/
public function __construct(ItemTypeDictionary $dictionary, array $simpleMappings, array $complexMappings){
foreach($dictionary->getEntries() as $entry){
$stringId = $entry->getStringId();
$netId = $entry->getNumericId();
if(isset($complexMappings[$stringId])){
[$id, $meta] = $complexMappings[$stringId];
$this->complexCoreToNetMapping[$id][$meta] = $netId;
$this->complexNetToCoreMapping[$netId] = [$id, $meta];
}elseif(isset($simpleMappings[$stringId])){
$this->simpleCoreToNetMapping[$simpleMappings[$stringId]] = $netId;
$this->simpleNetToCoreMapping[$netId] = $simpleMappings[$stringId];
}elseif($stringId !== "minecraft:unknown"){
throw new \InvalidArgumentException("Unmapped entry " . $stringId);
}
}
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function toNetworkId(int $internalId, int $internalMeta) : array{
if(isset($this->complexCoreToNetMapping[$internalId][$internalMeta])){
return [$this->complexCoreToNetMapping[$internalId][$internalMeta], 0];
}
if(array_key_exists($internalId, $this->simpleCoreToNetMapping)){
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
}
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function fromNetworkId(int $networkId, int $networkMeta, ?bool &$isComplexMapping = null) : array{
if(isset($this->complexNetToCoreMapping[$networkId])){
if($networkMeta !== 0){
throw new \UnexpectedValueException("Unexpected non-zero network meta on complex item mapping");
}
$isComplexMapping = true;
return $this->complexNetToCoreMapping[$networkId];
}
$isComplexMapping = false;
if(isset($this->simpleNetToCoreMapping[$networkId])){
return [$this->simpleNetToCoreMapping[$networkId], $networkMeta];
}
throw new \UnexpectedValueException("Unmapped network ID/metadata combination $networkId:$networkMeta");
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function fromNetworkIdWithWildcardHandling(int $networkId, int $networkMeta) : array{
$isComplexMapping = false;
if($networkMeta !== 0x7fff){
return $this->fromNetworkId($networkId, $networkMeta);
}
[$id, $meta] = $this->fromNetworkId($networkId, 0, $isComplexMapping);
return [$id, $isComplexMapping ? $meta : -1];
}
}

View File

@ -0,0 +1,106 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\convert;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use function array_key_exists;
use function file_get_contents;
use function is_array;
use function is_bool;
use function is_int;
use function is_string;
use function json_decode;
final class ItemTypeDictionary{
use SingletonTrait;
/**
* @var ItemTypeEntry[]
* @phpstan-var list<ItemTypeEntry>
*/
private $itemTypes;
/**
* @var string[]
* @phpstan-var array<int, string>
*/
private $intToStringIdMap = [];
/**
* @var int[]
* @phpstan-var array<string, int>
*/
private $stringToIntMap = [];
private static function make() : self{
$data = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/required_item_list.json');
if($data === false) throw new AssumptionFailedError("Missing required resource file");
$table = json_decode($data, true);
if(!is_array($table)){
throw new AssumptionFailedError("Invalid item list format");
}
$params = [];
foreach($table as $name => $entry){
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){
throw new AssumptionFailedError("Invalid item list format");
}
$params[] = new ItemTypeEntry($name, $entry["runtime_id"], $entry["component_based"]);
}
return new self($params);
}
/**
* @param ItemTypeEntry[] $itemTypes
*/
public function __construct(array $itemTypes){
$this->itemTypes = $itemTypes;
foreach($this->itemTypes as $type){
$this->stringToIntMap[$type->getStringId()] = $type->getNumericId();
$this->intToStringIdMap[$type->getNumericId()] = $type->getStringId();
}
}
/**
* @return ItemTypeEntry[]
* @phpstan-return list<ItemTypeEntry>
*/
public function getEntries() : array{
return $this->itemTypes;
}
public function fromStringId(string $stringId) : int{
if(!array_key_exists($stringId, $this->stringToIntMap)){
throw new \InvalidArgumentException("Unmapped string ID \"$stringId\"");
}
return $this->stringToIntMap[$stringId];
}
public function fromIntId(int $intId) : string{
if(!array_key_exists($intId, $this->intToStringIdMap)){
throw new \InvalidArgumentException("Unmapped int ID $intId");
}
return $this->intToStringIdMap[$intId];
}
}

View File

@ -27,14 +27,10 @@ use pocketmine\block\BlockIds;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\utils\AssumptionFailedError;
use function file_get_contents;
use function getmypid;
use function json_decode;
use function mt_rand;
use function mt_srand;
use function shuffle;
/**
* @internal
@ -53,14 +49,16 @@ final class RuntimeBlockMapping{
}
public static function init() : void{
$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");
$canonicalBlockStatesFile = file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/canonical_block_states.nbt");
if($canonicalBlockStatesFile === false){
throw new AssumptionFailedError("Missing required resource file");
}
/** @var CompoundTag[] $list */
$list = $tag->getValue();
self::$bedrockKnownStates = self::randomizeTable($list);
$stream = new NetworkBinaryStream($canonicalBlockStatesFile);
$list = [];
while(!$stream->feof()){
$list[] = $stream->getNbtCompoundRoot();
}
self::$bedrockKnownStates = $list;
self::setupLegacyMappings();
}
@ -90,7 +88,7 @@ final class RuntimeBlockMapping{
*/
$idToStatesMap = [];
foreach(self::$bedrockKnownStates as $k => $state){
$idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k;
$idToStatesMap[$state->getString("name")][] = $k;
}
foreach($legacyStateMap as $pair){
$id = $legacyIdMap[$pair->getId()] ?? null;
@ -105,14 +103,14 @@ final class RuntimeBlockMapping{
$mappedState = $pair->getBlockState();
//TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values
$mappedState->setName("block");
$mappedState->setName("");
$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"))){
if($mappedState->equals($networkState)){
self::registerMapping($k, $id, $data);
continue 2;
}
@ -127,23 +125,6 @@ final class RuntimeBlockMapping{
}
}
/**
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
* 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 CompoundTag[] $table
*
* @return CompoundTag[]
*/
private static function randomizeTable(array $table) : array{
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
mt_srand(getmypid()); //Use a seed which is the same on all threads. This isn't a secure seed, but we don't care.
shuffle($table);
mt_srand($postSeed); //restore a good quality seed that isn't dependent on PID
return $table;
}
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
self::lazyInit();
/*

View File

@ -0,0 +1,108 @@
<?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;
use function count;
class AnimateEntityPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::ANIMATE_ENTITY_PACKET;
/** @var string */
private $animation;
/** @var string */
private $nextState;
/** @var string */
private $stopExpression;
/** @var string */
private $controller;
/** @var float */
private $blendOutTime;
/**
* @var int[]
* @phpstan-var list<int>
*/
private $actorRuntimeIds;
/**
* @param int[] $actorRuntimeIds
* @phpstan-param list<int> $actorRuntimeIds
*/
public static function create(string $animation, string $nextState, string $stopExpression, string $controller, float $blendOutTime, array $actorRuntimeIds) : self{
$result = new self;
$result->animation = $animation;
$result->nextState = $nextState;
$result->stopExpression = $stopExpression;
$result->controller = $controller;
$result->blendOutTime = $blendOutTime;
$result->actorRuntimeIds = $actorRuntimeIds;
return $result;
}
public function getAnimation() : string{ return $this->animation; }
public function getNextState() : string{ return $this->nextState; }
public function getStopExpression() : string{ return $this->stopExpression; }
public function getController() : string{ return $this->controller; }
public function getBlendOutTime() : float{ return $this->blendOutTime; }
/**
* @return int[]
* @phpstan-return list<int>
*/
public function getActorRuntimeIds() : array{ return $this->actorRuntimeIds; }
protected function decodePayload() : void{
$this->animation = $this->getString();
$this->nextState = $this->getString();
$this->stopExpression = $this->getString();
$this->controller = $this->getString();
$this->blendOutTime = $this->getLFloat();
$this->actorRuntimeIds = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$this->actorRuntimeIds[] = $this->getEntityRuntimeId();
}
}
protected function encodePayload() : void{
$this->putString($this->animation);
$this->putString($this->nextState);
$this->putString($this->stopExpression);
$this->putString($this->controller);
$this->putLFloat($this->blendOutTime);
$this->putUnsignedVarInt(count($this->actorRuntimeIds));
foreach($this->actorRuntimeIds as $id){
$this->putEntityRuntimeId($id);
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleAnimateEntity($this);
}
}

View File

@ -0,0 +1,72 @@
<?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 CameraShakePacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::CAMERA_SHAKE_PACKET;
public const TYPE_POSITIONAL = 0;
public const TYPE_ROTATIONAL = 1;
/** @var float */
private $intensity;
/** @var float */
private $duration;
/** @var int */
private $shakeType;
public static function create(float $intensity, float $duration, int $shakeType) : self{
$result = new self;
$result->intensity = $intensity;
$result->duration = $duration;
$result->shakeType = $shakeType;
return $result;
}
public function getIntensity() : float{ return $this->intensity; }
public function getDuration() : float{ return $this->duration; }
public function getShakeType() : int{ return $this->shakeType; }
protected function decodePayload() : void{
$this->intensity = $this->getLFloat();
$this->duration = $this->getLFloat();
$this->shakeType = $this->getByte();
}
protected function encodePayload() : void{
$this->putLFloat($this->intensity);
$this->putLFloat($this->duration);
$this->putByte($this->shakeType);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleCameraShake($this);
}
}

View File

@ -32,13 +32,17 @@ class ContainerClosePacket extends DataPacket{
/** @var int */
public $windowId;
/** @var bool */
public $server = false;
protected function decodePayload(){
$this->windowId = $this->getByte();
$this->server = $this->getBool();
}
protected function encodePayload(){
$this->putByte($this->windowId);
$this->putBool($this->server);
}
public function handle(NetworkSession $session) : bool{

View File

@ -0,0 +1,77 @@
<?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;
class CorrectPlayerMovePredictionPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::CORRECT_PLAYER_MOVE_PREDICTION_PACKET;
/** @var Vector3 */
private $position;
/** @var Vector3 */
private $delta;
/** @var bool */
private $onGround;
/** @var int */
private $tick;
public static function create(Vector3 $position, Vector3 $delta, bool $onGround, int $tick) : self{
$result = new self;
$result->position = $position;
$result->delta = $delta;
$result->onGround = $onGround;
$result->tick = $tick;
return $result;
}
public function getPosition() : Vector3{ return $this->position; }
public function getDelta() : Vector3{ return $this->delta; }
public function isOnGround() : bool{ return $this->onGround; }
public function getTick() : int{ return $this->tick; }
protected function decodePayload() : void{
$this->position = $this->getVector3();
$this->delta = $this->getVector3();
$this->onGround = $this->getBool();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload() : void{
$this->putVector3($this->position);
$this->putVector3($this->delta);
$this->putBool($this->onGround);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleCorrectPlayerMovePrediction($this);
}
}

View File

@ -30,6 +30,7 @@ use pocketmine\inventory\ShapedRecipe;
use pocketmine\inventory\ShapelessRecipe;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe;
@ -124,13 +125,12 @@ class CraftingDataPacket extends DataPacket{
break;
case self::ENTRY_FURNACE:
case self::ENTRY_FURNACE_DATA:
$inputId = $this->getVarInt();
$inputData = -1;
if($recipeType === self::ENTRY_FURNACE_DATA){
$inputData = $this->getVarInt();
if($inputData === 0x7fff){
$inputData = -1;
}
$inputIdNet = $this->getVarInt();
if($recipeType === self::ENTRY_FURNACE){
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, 0x7fff);
}else{
$inputMetaNet = $this->getVarInt();
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, $inputMetaNet);
}
$entry["input"] = ItemFactory::get($inputId, $inputData);
$entry["output"] = $out = $this->getSlot();
@ -150,18 +150,25 @@ class CraftingDataPacket extends DataPacket{
$this->decodedEntries[] = $entry;
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$inputMeta = $this->getVarInt();
$ingredient = $this->getVarInt();
$ingredientMeta = $this->getVarInt();
$output = $this->getVarInt();
$outputMeta = $this->getVarInt();
$inputIdNet = $this->getVarInt();
$inputMetaNet = $this->getVarInt();
[$input, $inputMeta] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, $inputMetaNet);
$ingredientIdNet = $this->getVarInt();
$ingredientMetaNet = $this->getVarInt();
[$ingredient, $ingredientMeta] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, $ingredientMetaNet);
$outputIdNet = $this->getVarInt();
$outputMetaNet = $this->getVarInt();
[$output, $outputMeta] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, $outputMetaNet);
$this->potionTypeRecipes[] = new PotionTypeRecipe($input, $inputMeta, $ingredient, $ingredientMeta, $output, $outputMeta);
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$input = $this->getVarInt();
$ingredient = $this->getVarInt();
$output = $this->getVarInt();
//TODO: we discard inbound ID here, not safe because netID on its own might map to internalID+internalMeta for us
$inputIdNet = $this->getVarInt();
[$input, ] = ItemTranslator::getInstance()->fromNetworkId($inputIdNet, 0);
$ingredientIdNet = $this->getVarInt();
[$ingredient, ] = ItemTranslator::getInstance()->fromNetworkId($ingredientIdNet, 0);
$outputIdNet = $this->getVarInt();
[$output, ] = ItemTranslator::getInstance()->fromNetworkId($outputIdNet, 0);
$this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output);
}
$this->cleanRecipes = $this->getBool();
@ -230,15 +237,18 @@ class CraftingDataPacket extends DataPacket{
}
private static function writeFurnaceRecipe(FurnaceRecipe $recipe, NetworkBinaryStream $stream) : int{
$stream->putVarInt($recipe->getInput()->getId());
$result = CraftingDataPacket::ENTRY_FURNACE;
if(!$recipe->getInput()->hasAnyDamageValue()){ //Data recipe
$stream->putVarInt($recipe->getInput()->getDamage());
$result = CraftingDataPacket::ENTRY_FURNACE_DATA;
$input = $recipe->getInput();
if($input->hasAnyDamageValue()){
[$netId, ] = ItemTranslator::getInstance()->toNetworkId($input->getId(), 0);
$netData = 0x7fff;
}else{
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($input->getId(), $input->getDamage());
}
$stream->putVarInt($netId);
$stream->putVarInt($netData);
$stream->putSlot($recipe->getResult());
$stream->putString("furnace"); //TODO: blocktype (no prefix) (this might require internal API breaks)
return $result;
return CraftingDataPacket::ENTRY_FURNACE_DATA;
}
/**

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\nbt\NetworkLittleEndianNBTStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\ItemComponentPacketEntry;
use function count;
class ItemComponentPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::ITEM_COMPONENT_PACKET;
/**
* @var ItemComponentPacketEntry[]
* @phpstan-var list<ItemComponentPacketEntry>
*/
private $entries;
/**
* @param ItemComponentPacketEntry[] $entries
* @phpstan-param list<ItemComponentPacketEntry> $entries
*/
public static function create(array $entries) : self{
$result = new self;
$result->entries = $entries;
return $result;
}
/**
* @return ItemComponentPacketEntry[]
* @phpstan-return list<ItemComponentPacketEntry>
*/
public function getEntries() : array{ return $this->entries; }
protected function decodePayload() : void{
$this->entries = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$name = $this->getString();
$nbt = $this->getNbtCompoundRoot();
$this->entries[] = new ItemComponentPacketEntry($name, $nbt);
}
}
protected function encodePayload() : void{
$this->putUnsignedVarInt(count($this->entries));
foreach($this->entries as $entry){
$this->putString($entry->getName());
$this->put((new NetworkLittleEndianNBTStream())->write($entry->getComponentNbt()));
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleItemComponent($this);
}
}

View File

@ -0,0 +1,70 @@
<?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;
class MotionPredictionHintsPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::MOTION_PREDICTION_HINTS_PACKET;
/** @var int */
private $entityRuntimeId;
/** @var Vector3 */
private $motion;
/** @var bool */
private $onGround;
public static function create(int $entityRuntimeId, Vector3 $motion, bool $onGround) : self{
$result = new self;
$result->entityRuntimeId = $entityRuntimeId;
$result->motion = $motion;
$result->onGround = $onGround;
return $result;
}
public function getEntityRuntimeIdField() : int{ return $this->entityRuntimeId; } //TODO: rename this on PM4 (crap architecture, thanks shoghi)
public function getMotion() : Vector3{ return $this->motion; }
public function isOnGround() : bool{ return $this->onGround; }
protected function decodePayload() : void{
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->motion = $this->getVector3();
$this->onGround = $this->getBool();
}
protected function encodePayload() : void{
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putVector3($this->motion);
$this->putBool($this->onGround);
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleMotionPredictionHints($this);
}
}

View File

@ -44,12 +44,12 @@ class MoveActorDeltaPacket extends DataPacket{
public $entityRuntimeId;
/** @var int */
public $flags;
/** @var int */
public $xDiff = 0;
/** @var int */
public $yDiff = 0;
/** @var int */
public $zDiff = 0;
/** @var float */
public $xPos = 0;
/** @var float */
public $yPos = 0;
/** @var float */
public $zPos = 0;
/** @var float */
public $xRot = 0.0;
/** @var float */
@ -57,9 +57,9 @@ class MoveActorDeltaPacket extends DataPacket{
/** @var float */
public $zRot = 0.0;
private function maybeReadCoord(int $flag) : int{
private function maybeReadCoord(int $flag) : float{
if(($this->flags & $flag) !== 0){
return $this->getVarInt();
return $this->getLFloat();
}
return 0;
}
@ -74,17 +74,17 @@ class MoveActorDeltaPacket extends DataPacket{
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$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);
$this->xPos = $this->maybeReadCoord(self::FLAG_HAS_X);
$this->yPos = $this->maybeReadCoord(self::FLAG_HAS_Y);
$this->zPos = $this->maybeReadCoord(self::FLAG_HAS_Z);
$this->xRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_X);
$this->yRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Y);
$this->zRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Z);
}
private function maybeWriteCoord(int $flag, int $val) : void{
private function maybeWriteCoord(int $flag, float $val) : void{
if(($this->flags & $flag) !== 0){
$this->putVarInt($val);
$this->putLFloat($val);
}
}
@ -97,9 +97,9 @@ class MoveActorDeltaPacket extends DataPacket{
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$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);
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xPos);
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yPos);
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zPos);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_X, $this->xRot);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Y, $this->yRot);
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Z, $this->zRot);

View File

@ -56,6 +56,8 @@ class MovePlayerPacket extends DataPacket{
public $teleportCause = 0;
/** @var int */
public $teleportItem = 0;
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
@ -70,6 +72,7 @@ class MovePlayerPacket extends DataPacket{
$this->teleportCause = $this->getLInt();
$this->teleportItem = $this->getLInt();
}
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
@ -85,6 +88,7 @@ class MovePlayerPacket extends DataPacket{
$this->putLInt($this->teleportCause);
$this->putLInt($this->teleportItem);
}
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

View File

@ -71,7 +71,6 @@ class PacketPool{
static::registerPacket(new BlockPickRequestPacket());
static::registerPacket(new ActorPickRequestPacket());
static::registerPacket(new PlayerActionPacket());
static::registerPacket(new ActorFallPacket());
static::registerPacket(new HurtArmorPacket());
static::registerPacket(new SetActorDataPacket());
static::registerPacket(new SetActorMotionPacket());
@ -166,7 +165,6 @@ class PacketPool{
static::registerPacket(new MapCreateLockedCopyPacket());
static::registerPacket(new StructureTemplateDataRequestPacket());
static::registerPacket(new StructureTemplateDataResponsePacket());
static::registerPacket(new UpdateBlockPropertiesPacket());
static::registerPacket(new ClientCacheBlobStatusPacket());
static::registerPacket(new ClientCacheMissResponsePacket());
static::registerPacket(new EducationSettingsPacket());
@ -189,6 +187,12 @@ class PacketPool{
static::registerPacket(new PositionTrackingDBClientRequestPacket());
static::registerPacket(new DebugInfoPacket());
static::registerPacket(new PacketViolationWarningPacket());
static::registerPacket(new MotionPredictionHintsPacket());
static::registerPacket(new AnimateEntityPacket());
static::registerPacket(new CameraShakePacket());
static::registerPacket(new PlayerFogPacket());
static::registerPacket(new CorrectPlayerMovePredictionPacket());
static::registerPacket(new ItemComponentPacket());
}
/**

View File

@ -43,7 +43,7 @@ class PlayerActionPacket extends DataPacket{
public const ACTION_STOP_SPRINT = 10;
public const ACTION_START_SNEAK = 11;
public const ACTION_STOP_SNEAK = 12;
public const ACTION_DIMENSION_CHANGE_REQUEST = 13; //sent when dying in different dimension
public const ACTION_CREATIVE_PLAYER_DESTROY_BLOCK = 13;
public const ACTION_DIMENSION_CHANGE_ACK = 14; //sent when spawning in a different dimension to tell the server we spawned
public const ACTION_START_GLIDE = 15;
public const ACTION_STOP_GLIDE = 16;

View File

@ -54,13 +54,17 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
private $playMode;
/** @var Vector3|null */
private $vrGazeDirection = null;
/** @var int */
private $tick;
/** @var Vector3 */
private $delta;
/**
* @param int $inputMode @see InputMode
* @param int $playMode @see PlayMode
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
*/
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{
public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection, int $tick, Vector3 $delta) : 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");
@ -78,6 +82,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
if($vrGazeDirection !== null){
$result->vrGazeDirection = $vrGazeDirection->asVector3();
}
$result->tick = $tick;
$result->delta = $delta;
return $result;
}
@ -127,6 +133,10 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
return $this->vrGazeDirection;
}
public function getTick() : int{ return $this->tick; }
public function getDelta() : Vector3{ return $this->delta; }
protected function decodePayload() : void{
$this->pitch = $this->getLFloat();
$this->yaw = $this->getLFloat();
@ -140,6 +150,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
if($this->playMode === PlayMode::VR){
$this->vrGazeDirection = $this->getVector3();
}
$this->tick = $this->getUnsignedVarLong();
$this->delta = $this->getVector3();
}
protected function encodePayload() : void{
@ -156,6 +168,8 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
assert($this->vrGazeDirection !== null);
$this->putVector3($this->vrGazeDirection);
}
$this->putUnsignedVarLong($this->tick);
$this->putVector3($this->delta);
}
public function handle(NetworkSession $handler) : bool{

View File

@ -25,31 +25,49 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\NetworkSession;
use function count;
class UpdateBlockPropertiesPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::UPDATE_BLOCK_PROPERTIES_PACKET;
class PlayerFogPacket extends DataPacket/* implements ClientboundPacket*/{
public const NETWORK_ID = ProtocolInfo::PLAYER_FOG_PACKET;
/** @var string */
private $nbt;
/**
* @var string[]
* @phpstan-var list<string>
*/
private $fogLayers;
public static function create(CompoundTag $data) : self{
/**
* @param string[] $fogLayers
* @phpstan-param list<string> $fogLayers
*/
public static function create(array $fogLayers) : self{
$result = new self;
$result->nbt = (new NetworkLittleEndianNBTStream())->write($data);
$result->fogLayers = $fogLayers;
return $result;
}
/**
* @return string[]
* @phpstan-return list<string>
*/
public function getFogLayers() : array{ return $this->fogLayers; }
protected function decodePayload() : void{
$this->nbt = $this->getRemaining();
$this->fogLayers = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$this->fogLayers[] = $this->getString();
}
}
protected function encodePayload() : void{
$this->put($this->nbt);
$this->putUnsignedVarInt(count($this->fogLayers));
foreach($this->fogLayers as $fogLayer){
$this->putString($fogLayer);
}
}
public function handle(NetworkSession $handler) : bool{
return $handler->handleUpdateBlockProperties($this);
return $handler->handlePlayerFog($this);
}
}

View File

@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
public const CURRENT_PROTOCOL = 408;
public const CURRENT_PROTOCOL = 419;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v1.16.20';
public const MINECRAFT_VERSION = 'v1.16.100';
/** Version number sent to clients in ping responses. */
public const MINECRAFT_VERSION_NETWORK = '1.16.20';
public const MINECRAFT_VERSION_NETWORK = '1.16.100';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;
@ -79,7 +79,7 @@ interface ProtocolInfo{
public const BLOCK_PICK_REQUEST_PACKET = 0x22;
public const ACTOR_PICK_REQUEST_PACKET = 0x23;
public const PLAYER_ACTION_PACKET = 0x24;
public const ACTOR_FALL_PACKET = 0x25;
public const HURT_ARMOR_PACKET = 0x26;
public const SET_ACTOR_DATA_PACKET = 0x27;
public const SET_ACTOR_MOTION_PACKET = 0x28;
@ -176,7 +176,7 @@ interface ProtocolInfo{
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83;
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;
@ -199,5 +199,11 @@ interface ProtocolInfo{
public const POSITION_TRACKING_D_B_CLIENT_REQUEST_PACKET = 0x9a;
public const DEBUG_INFO_PACKET = 0x9b;
public const PACKET_VIOLATION_WARNING_PACKET = 0x9c;
public const MOTION_PREDICTION_HINTS_PACKET = 0x9d;
public const ANIMATE_ENTITY_PACKET = 0x9e;
public const CAMERA_SHAKE_PACKET = 0x9f;
public const PLAYER_FOG_PACKET = 0xa0;
public const CORRECT_PLAYER_MOVE_PREDICTION_PACKET = 0xa1;
public const ITEM_COMPONENT_PACKET = 0xa2;
}

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\Experiments;
use pocketmine\resourcepacks\ResourcePack;
use function count;
@ -40,11 +41,12 @@ class ResourcePackStackPacket extends DataPacket{
/** @var ResourcePack[] */
public $resourcePackStack = [];
/** @var bool */
public $isExperimental = false;
/** @var string */
public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
/** @var Experiments */
public $experiments;
protected function decodePayload(){
$this->mustAccept = $this->getBool();
$behaviorPackCount = $this->getUnsignedVarInt();
@ -61,8 +63,8 @@ class ResourcePackStackPacket extends DataPacket{
$this->getString();
}
$this->isExperimental = $this->getBool();
$this->baseGameVersion = $this->getString();
$this->experiments = Experiments::read($this);
}
protected function encodePayload(){
@ -82,8 +84,8 @@ class ResourcePackStackPacket extends DataPacket{
$this->putString(""); //TODO: subpack name
}
$this->putBool($this->isExperimental);
$this->putString($this->baseGameVersion);
$this->experiments->write($this);
}
public function handle(NetworkSession $session) : bool{

View File

@ -38,14 +38,19 @@ class SetActorDataPacket extends DataPacket{
*/
public $metadata;
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->metadata = $this->getEntityMetadata();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putEntityMetadata($this->metadata);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

View File

@ -27,29 +27,22 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\math\Vector3;
use pocketmine\nbt\NetworkLittleEndianNBTStream;
use pocketmine\nbt\tag\ListTag;
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry;
use pocketmine\network\mcpe\protocol\types\EducationEditionOffer;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\GameRuleType;
use pocketmine\network\mcpe\protocol\types\GeneratorType;
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
use pocketmine\network\mcpe\protocol\types\MultiplayerGameVisibility;
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
use function count;
use function file_get_contents;
use function json_decode;
use const pocketmine\RESOURCE_PATH;
class StartGamePacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::START_GAME_PACKET;
/** @var string|null */
private static $blockTableCache = null;
/** @var string|null */
private static $itemTableCache = null;
/** @var int */
public $entityUniqueId;
/** @var int */
@ -116,6 +109,8 @@ class StartGamePacket extends DataPacket{
public $gameRules = [ //TODO: implement this
"naturalregeneration" => [GameRuleType::BOOL, false] //Hack for client side regeneration
];
/** @var Experiments */
public $experiments;
/** @var bool */
public $hasBonusChestEnabled = false;
/** @var bool */
@ -159,8 +154,8 @@ class StartGamePacket extends DataPacket{
public $premiumWorldTemplateId = "";
/** @var bool */
public $isTrial = false;
/** @var bool */
public $isMovementServerAuthoritative = false;
/** @var int */
public $playerMovementType = PlayerMovementType::LEGACY;
/** @var int */
public $currentTick = 0; //only used if isTrial is true
/** @var int */
@ -168,13 +163,17 @@ class StartGamePacket extends DataPacket{
/** @var string */
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
/** @var ListTag|null */
public $blockTable = null;
/**
* @var int[]|null string (name) => int16 (legacyID)
* @phpstan-var array<string, int>|null
* @var BlockPaletteEntry[]
* @phpstan-var list<BlockPaletteEntry>
*/
public $itemTable = null;
public $blockPalette = [];
/**
* @var ItemTypeEntry[]
* @phpstan-var list<ItemTypeEntry>
*/
public $itemTable;
/** @var bool */
public $enableNewInventorySystem = false; //TODO
@ -210,6 +209,7 @@ class StartGamePacket extends DataPacket{
$this->commandsEnabled = $this->getBool();
$this->isTexturePacksRequired = $this->getBool();
$this->gameRules = $this->getGameRules();
$this->experiments = Experiments::read($this);
$this->hasBonusChestEnabled = $this->getBool();
$this->hasStartWithMapEnabled = $this->getBool();
$this->defaultPlayerPermission = $this->getVarInt();
@ -235,23 +235,25 @@ class StartGamePacket extends DataPacket{
$this->worldName = $this->getString();
$this->premiumWorldTemplateId = $this->getString();
$this->isTrial = $this->getBool();
$this->isMovementServerAuthoritative = $this->getBool();
$this->playerMovementType = $this->getVarInt();
$this->currentTick = $this->getLLong();
$this->enchantmentSeed = $this->getVarInt();
$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->blockPalette = [];
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
$blockName = $this->getString();
$state = $this->getNbtCompoundRoot();
$this->blockPalette[] = new BlockPaletteEntry($blockName, $state);
}
$this->blockTable = $blockTable;
$this->itemTable = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$id = $this->getString();
$legacyId = $this->getSignedLShort();
$stringId = $this->getString();
$numericId = $this->getSignedLShort();
$isComponentBased = $this->getBool();
$this->itemTable[$id] = $legacyId;
$this->itemTable[] = new ItemTypeEntry($stringId, $numericId, $isComponentBased);
}
$this->multiplayerCorrelationId = $this->getString();
@ -290,6 +292,7 @@ class StartGamePacket extends DataPacket{
$this->putBool($this->commandsEnabled);
$this->putBool($this->isTexturePacksRequired);
$this->putGameRules($this->gameRules);
$this->experiments->write($this);
$this->putBool($this->hasBonusChestEnabled);
$this->putBool($this->hasStartWithMapEnabled);
$this->putVarInt($this->defaultPlayerPermission);
@ -314,47 +317,28 @@ class StartGamePacket extends DataPacket{
$this->putString($this->worldName);
$this->putString($this->premiumWorldTemplateId);
$this->putBool($this->isTrial);
$this->putBool($this->isMovementServerAuthoritative);
$this->putVarInt($this->playerMovementType);
$this->putLLong($this->currentTick);
$this->putVarInt($this->enchantmentSeed);
if($this->blockTable === null){
if(self::$blockTableCache === null){
//this is a really nasty hack, but it'll do for now
self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates()));
}
$this->put(self::$blockTableCache);
}else{
$this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable));
$this->putUnsignedVarInt(count($this->blockPalette));
$nbtWriter = new NetworkLittleEndianNBTStream();
foreach($this->blockPalette as $entry){
$this->putString($entry->getName());
$this->put($nbtWriter->write($entry->getStates()));
}
if($this->itemTable === null){
if(self::$itemTableCache === null){
self::$itemTableCache = self::serializeItemTable(json_decode(file_get_contents(RESOURCE_PATH . '/vanilla/item_id_map.json'), true));
}
$this->put(self::$itemTableCache);
}else{
$this->put(self::serializeItemTable($this->itemTable));
$this->putUnsignedVarInt(count($this->itemTable));
foreach($this->itemTable as $entry){
$this->putString($entry->getStringId());
$this->putLShort($entry->getNumericId());
$this->putBool($entry->isComponentBased());
}
$this->putString($this->multiplayerCorrelationId);
$this->putBool($this->enableNewInventorySystem);
}
/**
* @param int[] $table
* @phpstan-param array<string, int> $table
*/
private static function serializeItemTable(array $table) : string{
$stream = new NetworkBinaryStream();
$stream->putUnsignedVarInt(count($table));
foreach($table as $name => $legacyId){
$stream->putString($name);
$stream->putLShort($legacyId);
}
return $stream->getBuffer();
}
public function handle(NetworkSession $session) : bool{
return $session->handleStartGame($this);
}

View File

@ -35,15 +35,19 @@ class UpdateAttributesPacket extends DataPacket{
public $entityRuntimeId;
/** @var Attribute[] */
public $entries = [];
/** @var int */
public $tick = 0;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->entries = $this->getAttributeList();
$this->tick = $this->getUnsignedVarLong();
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putAttributeList(...$this->entries);
$this->putUnsignedVarLong($this->tick);
}
public function handle(NetworkSession $session) : bool{

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;
use pocketmine\nbt\tag\CompoundTag;
final class BlockPaletteEntry{
/** @var string */
private $name;
/** @var CompoundTag */
private $states;
public function __construct(string $name, CompoundTag $states){
$this->name = $name;
$this->states = $states;
}
public function getName() : string{ return $this->name; }
public function getStates() : CompoundTag{ return $this->states; }
}

View File

@ -0,0 +1,72 @@
<?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\network\mcpe\NetworkBinaryStream;
use function count;
final class Experiments{
/**
* @var bool[]
* @phpstan-var array<string, bool>
*/
private $experiments;
/** @var bool */
private $hasPreviouslyUsedExperiments;
/**
* @param bool[] $experiments
* @phpstan-param array<string, bool> $experiments
*/
public function __construct(array $experiments, bool $hasPreviouslyUsedExperiments){
$this->experiments = $experiments;
$this->hasPreviouslyUsedExperiments = $hasPreviouslyUsedExperiments;
}
/** @return bool[] */
public function getExperiments() : array{ return $this->experiments; }
public function hasPreviouslyUsedExperiments() : bool{ return $this->hasPreviouslyUsedExperiments; }
public static function read(NetworkBinaryStream $in) : self{
$experiments = [];
for($i = 0, $len = $in->getLInt(); $i < $len; ++$i){
$experimentName = $in->getString();
$enabled = $in->getBool();
$experiments[$experimentName] = $enabled;
}
$hasPreviouslyUsedExperiments = $in->getBool();
return new self($experiments, $hasPreviouslyUsedExperiments);
}
public function write(NetworkBinaryStream $out) : void{
$out->putLInt(count($this->experiments));
foreach($this->experiments as $experimentName => $enabled){
$out->putString($experimentName);
$out->putBool($enabled);
}
$out->putBool($this->hasPreviouslyUsedExperiments);
}
}

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;
use pocketmine\nbt\tag\CompoundTag;
final class ItemComponentPacketEntry{
/** @var string */
private $name;
/** @var CompoundTag */
private $componentNbt;
public function __construct(string $name, CompoundTag $componentNbt){
$this->name = $name;
$this->componentNbt = $componentNbt;
}
public function getName() : string{ return $this->name; }
public function getComponentNbt() : CompoundTag{ return $this->componentNbt; }
}

View File

@ -21,35 +21,26 @@
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol;
namespace pocketmine\network\mcpe\protocol\types;
#include <rules/DataPacket.h>
use pocketmine\network\mcpe\NetworkSession;
class ActorFallPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::ACTOR_FALL_PACKET;
final class ItemTypeEntry{
/** @var string */
private $stringId;
/** @var int */
public $entityRuntimeId;
/** @var float */
public $fallDistance;
private $numericId;
/** @var bool */
public $isInVoid;
private $componentBased;
protected function decodePayload(){
$this->entityRuntimeId = $this->getEntityRuntimeId();
$this->fallDistance = $this->getLFloat();
$this->isInVoid = $this->getBool();
public function __construct(string $stringId, int $numericId, bool $componentBased){
$this->stringId = $stringId;
$this->numericId = $numericId;
$this->componentBased = $componentBased;
}
protected function encodePayload(){
$this->putEntityRuntimeId($this->entityRuntimeId);
$this->putLFloat($this->fallDistance);
$this->putBool($this->isInVoid);
}
public function getStringId() : string{ return $this->stringId; }
public function handle(NetworkSession $session) : bool{
return $session->handleActorFall($this);
}
public function getNumericId() : int{ return $this->numericId; }
public function isComponentBased() : bool{ return $this->componentBased; }
}

View File

@ -0,0 +1,31 @@
<?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 PlayerMovementType{
public const LEGACY = 0; //MovePlayerPacket
public const SERVER_AUTHORITATIVE_V1 = 1; //PlayerAuthInputPacket
public const SERVER_AUTHORITATIVE_V2_REWIND = 2; //PlayerAuthInputPacket + a bunch of junk that solves a nonexisting problem
}

View File

@ -29,17 +29,23 @@ class SkinAnimation{
public const TYPE_BODY_32 = 2;
public const TYPE_BODY_64 = 3;
public const EXPRESSION_LINEAR = 0; //???
public const EXPRESSION_BLINKING = 1;
/** @var SkinImage */
private $image;
/** @var int */
private $type;
/** @var float */
private $frames;
/** @var int */
private $expressionType;
public function __construct(SkinImage $image, int $type, float $frames){
public function __construct(SkinImage $image, int $type, float $frames, int $expressionType){
$this->image = $image;
$this->type = $type;
$this->frames = $frames;
$this->expressionType = $expressionType;
}
/**
@ -62,4 +68,6 @@ class SkinAnimation{
public function getFrames() : float{
return $this->frames;
}
public function getExpressionType() : int{ return $this->expressionType; }
}

View File

@ -28,8 +28,13 @@ use function count;
final class ItemStackResponse{
/** @var bool */
private $ok;
public const RESULT_OK = 0;
public const RESULT_ERROR = 1;
//TODO: there are a ton more possible result types but we don't need them yet and they are wayyyyyy too many for me
//to waste my time on right now...
/** @var int */
private $result;
/** @var int */
private $requestId;
/** @var ItemStackResponseContainerInfo[] */
@ -38,13 +43,13 @@ final class ItemStackResponse{
/**
* @param ItemStackResponseContainerInfo[] $containerInfos
*/
public function __construct(bool $ok, int $requestId, array $containerInfos){
$this->ok = $ok;
public function __construct(int $result, int $requestId, array $containerInfos){
$this->result = $result;
$this->requestId = $requestId;
$this->containerInfos = $containerInfos;
}
public function isOk() : bool{ return $this->ok; }
public function getResult() : int{ return $this->result; }
public function getRequestId() : int{ return $this->requestId; }
@ -52,17 +57,17 @@ final class ItemStackResponse{
public function getContainerInfos() : array{ return $this->containerInfos; }
public static function read(NetworkBinaryStream $in) : self{
$ok = $in->getBool();
$result = $in->getByte();
$requestId = $in->readGenericTypeNetworkId();
$containerInfos = [];
for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){
$containerInfos[] = ItemStackResponseContainerInfo::read($in);
}
return new self($ok, $requestId, $containerInfos);
return new self($result, $requestId, $containerInfos);
}
public function write(NetworkBinaryStream $out) : void{
$out->putBool($this->ok);
$out->putByte($this->result);
$out->writeGenericTypeNetworkId($this->requestId);
$out->putUnsignedVarInt(count($this->containerInfos));
foreach($this->containerInfos as $containerInfo){

@ -1 +1 @@
Subproject commit afc885cccae38048d309911f2c5ddcdcb6af8152
Subproject commit 14f4a765eba40b2c65c6dcd061cbfea6e1d3d4cc