public function saveMacro($name, Macro $macro) { $tag = new tag\Compound(); $tag["author"] = new tag\String("author", $macro->getAuthor()); $tag["description"] = new tag\String("description", $macro->getDescription()); $tag["ops"] = new tag\Enum("ops"); foreach ($macro->getOperations() as $i => $log) { $tag["ops"][$i] = $log->toTag(); } $nbt = new NBT(); $nbt->setData($tag); $file = $this->getFile($name); $stream = fopen($file, "wb"); if (!is_resource($stream)) { throw new \RuntimeException("Unable to open stream. Maybe the macro name is not a valid filename?"); } $compression = $this->getMain()->getConfig()->getAll()["data providers"]["macro"]["mcr"]["compression"]; if ($compression === 0) { $data = $nbt->write(); } else { $data = $nbt->writeCompressed($compression); } fwrite($stream, chr($compression) . $data); fclose($stream); }
public function generateChunk($x, $z) { $nbt = new Compound("Level", []); $nbt->xPos = new Int("xPos", $this->getX() * 32 + $x); $nbt->zPos = new Int("zPos", $this->getZ() * 32 + $z); $nbt->LastUpdate = new Long("LastUpdate", 0); $nbt->LightPopulated = new Byte("LightPopulated", 0); $nbt->TerrainPopulated = new Byte("TerrainPopulated", 0); $nbt->V = new Byte("V", self::VERSION); $nbt->InhabitedTime = new Long("InhabitedTime", 0); $biomes = str_repeat(Binary::writeByte(-1), 256); $nbt->Biomes = new ByteArray("Biomes", $biomes); $nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("…²J"))); $nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); $nbt->Sections = new Enum("Sections", []); $nbt->Sections->setTagType(NBT::TAG_Compound); $nbt->Entities = new Enum("Entities", []); $nbt->Entities->setTagType(NBT::TAG_Compound); $nbt->TileEntities = new Enum("TileEntities", []); $nbt->TileEntities->setTagType(NBT::TAG_Compound); $nbt->TileTicks = new Enum("TileTicks", []); $nbt->TileTicks->setTagType(NBT::TAG_Compound); $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", ["Level" => $nbt])); $chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); $this->saveChunk($x, $z, $chunkData); }
public function requestChunkTask($x, $z) { $chunk = $this->getChunk($x, $z, false); if (!$chunk instanceof Chunk) { throw new ChunkException("Invalid Chunk sent"); } $tiles = ""; if (count($chunk->getTiles()) > 0) { $nbt = new NBT(NBT::LITTLE_ENDIAN); $list = []; foreach ($chunk->getTiles() as $tile) { if ($tile instanceof Spawnable) { $list[] = $tile->getSpawnCompound(); } } $nbt->setData($list); $tiles = $nbt->write(true); } $extraData = new BinaryStream(); $extraData->putLInt(count($chunk->getBlockExtraDataArray())); foreach ($chunk->getBlockExtraDataArray() as $key => $value) { $extraData->putLInt($key); $extraData->putLShort($value); } $ordered = $chunk->getBlockIdArray() . $chunk->getBlockDataArray() . $chunk->getBlockSkyLightArray() . $chunk->getBlockLightArray() . pack("C*", ...$chunk->getHeightMapArray()) . pack("N*", ...$chunk->getBiomeColorArray()) . $extraData->getBuffer() . $tiles; $this->getLevel()->chunkRequestCallback($x, $z, $ordered, FullChunkDataPacket::ORDER_LAYERED); return null; }
public function write(NBT $nbt, bool $network = false) { foreach ($this as $tag) { if ($tag instanceof Tag and !$tag instanceof EndTag) { $nbt->writeTag($tag, $network); } } $nbt->writeTag(new EndTag(), $network); }
public function write(NBT $nbt) { foreach ($this as $tag) { if ($tag instanceof Tag and !$tag instanceof End) { $nbt->writeTag($tag); } } $nbt->writeTag(new End()); }
/** * * @param string $name * * @return CompoundTag */ public function getOfflinePlayerData($name) { $name = strtolower($name); $path = $this->datapath . "players/"; if (file_exists($path . "{$name}.dat")) { try { $nbt = new NBT(NBT::BIG_ENDIAN); $nbt->readCompressed(file_get_contents($path . "{$name}.dat")); return $nbt->getData(); } catch (\Throwable $e) { // zlib decode error / corrupt data rename($path . "{$name}.dat", $path . "{$name}.dat.bak"); } } $spawn = explode(':', $this->spawn); $nbt = new CompoundTag("", [new LongTag("firstPlayed", floor(microtime(true) * 1000)), new LongTag("lastPlayed", floor(microtime(true) * 1000)), new ListTag("Pos", [new DoubleTag(0, $spawn[0]), new DoubleTag(1, $spawn[1]), new DoubleTag(2, $spawn[2])]), new StringTag("Level", $spawn[3]), new ListTag("Inventory", []), new CompoundTag("Achievements", []), new IntTag("playerGameType", $this->gamemode), new ListTag("Motion", [new DoubleTag(0, 0.0), new DoubleTag(1, 0.0), new DoubleTag(2, 0.0)]), new ListTag("Rotation", [new FloatTag(0, 0.0), new FloatTag(1, 0.0)]), new FloatTag("FallDistance", 0.0), new ShortTag("Fire", 0), new ShortTag("Air", 300), new ByteTag("OnGround", 1), new ByteTag("Invulnerable", 0), new StringTag("NameTag", $name)]); $nbt->Pos->setTagType(NBT::TAG_Double); $nbt->Inventory->setTagType(NBT::TAG_Compound); $nbt->Motion->setTagType(NBT::TAG_Double); $nbt->Rotation->setTagType(NBT::TAG_Float); if (file_exists($path . "{$name}.yml")) { // Importing old PocketMine-MP files $data = new Config($path . "{$name}.yml", Config::YAML, []); $nbt["playerGameType"] = (int) $data->get("gamemode"); $nbt["Level"] = $data->get("position")["level"]; $nbt["Pos"][0] = $data->get("position")["x"]; $nbt["Pos"][1] = $data->get("position")["y"]; $nbt["Pos"][2] = $data->get("position")["z"]; $nbt["SpawnLevel"] = $data->get("spawn")["level"]; $nbt["SpawnX"] = (int) $data->get("spawn")["x"]; $nbt["SpawnY"] = (int) $data->get("spawn")["y"]; $nbt["SpawnZ"] = (int) $data->get("spawn")["z"]; foreach ($data->get("inventory") as $slot => $item) { if (count($item) === 3) { $nbt->Inventory[$slot + 9] = new CompoundTag("", [new ShortTag("id", $item[0]), new ShortTag("Damage", $item[1]), new ByteTag("Count", $item[2]), new ByteTag("Slot", $slot + 9), new ByteTag("TrueSlot", $slot + 9)]); } } foreach ($data->get("hotbar") as $slot => $itemSlot) { if (isset($nbt->Inventory[$itemSlot + 9])) { $item = $nbt->Inventory[$itemSlot + 9]; $nbt->Inventory[$slot] = new CompoundTag("", [new ShortTag("id", $item["id"]), new ShortTag("Damage", $item["Damage"]), new ByteTag("Count", $item["Count"]), new ByteTag("Slot", $slot), new ByteTag("TrueSlot", $item["TrueSlot"])]); } } foreach ($data->get("armor") as $slot => $item) { if (count($item) === 2) { $nbt->Inventory[$slot + 100] = new CompoundTag("", [new ShortTag("id", $item[0]), new ShortTag("Damage", $item[1]), new ByteTag("Count", 1), new ByteTag("Slot", $slot + 100)]); } } foreach ($data->get("achievements") as $achievement => $status) { $nbt->Achievements[$achievement] = new ByteTag($achievement, $status == true ? 1 : 0); } unlink($path . "{$name}.yml"); } return $nbt; }
public function spawnTo(Player $player) { if ($this->closed) { return \false; } $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->setData($this->getSpawnCompound()); $pk = new TileEntityDataPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->namedtag = $nbt->write(); $player->dataPacket($pk->setChannel(Network::CHANNEL_WORLD_EVENTS)); return \true; }
public function spawnTo(Player $player) { if ($this->closed) { return false; } $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->setData($this->getSpawnCompound()); $pk = new TileEntityDataPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->namedtag = $nbt->write(); $player->dataPacket($pk); return true; }
public function __construct(Human $player, $contents = null) { $this->hotbar = range(0, $this->getHotbarSize() - 1, 1); parent::__construct($player, InventoryType::get(InventoryType::PLAYER)); if ($contents !== null) { if ($contents instanceof ListTag) { //Saved data to be loaded into the inventory foreach ($contents as $item) { if ($item["Slot"] >= 0 and $item["Slot"] < $this->getHotbarSize()) { //Hotbar if (isset($item["TrueSlot"])) { //Valid slot was found, change the linkage to this slot if (0 <= $item["TrueSlot"] and $item["TrueSlot"] < $this->getSize()) { $this->hotbar[$item["Slot"]] = $item["TrueSlot"]; } elseif ($item["TrueSlot"] < 0) { //Link to an empty slot (empty hand) $this->hotbar[$item["Slot"]] = -1; } } /* If TrueSlot is not set, leave the slot index as its default which was filled in above * This only overwrites slot indexes for valid links */ } elseif ($item["Slot"] >= 100 and $item["Slot"] < 104) { //Armor $this->setItem($this->getSize() + $item["Slot"] - 100, NBT::getItemHelper($item), false); } else { $this->setItem($item["Slot"] - $this->getHotbarSize(), NBT::getItemHelper($item), false); } } } else { throw new \InvalidArgumentException("Expecting ListTag, received " . gettype($contents)); } } }
public function __construct(Level $level, Chunk $chunk) { $this->levelId = $level->getId(); $this->chunk = $chunk->toFastBinary(); $this->chunkX = $chunk->getX(); $this->chunkZ = $chunk->getZ(); $tiles = ""; $nbt = new NBT(NBT::LITTLE_ENDIAN); foreach ($chunk->getTiles() as $tile) { if ($tile instanceof Spawnable) { $nbt->setData($tile->getSpawnCompound()); $tiles .= $nbt->write(); } } $this->tiles = $tiles; }
public function setItem(Item $item, bool $setChanged = true) { $nbtItem = NBT::putItemHelper($item); $nbtItem->setName("Item"); $this->namedtag->Item = $nbtItem; if ($setChanged) { $this->setChanged(); } }
/** * Return the defined game rules as NBT. */ public function writeToNBT() { $compoundarray = []; foreach ($this->theGameRules as $key => $value) { $compoundarray[] = NBT::fromArrayGuesser($key, $value); } $nbttagcompound = new CompoundTag("GameRules", $compoundarray); return $nbttagcompound; }
public function setItem(Item $item) { $tag = NBT::putItemHelper($item); $tag->setName("Item"); $this->namedtag->Item = $tag; $this->spawnToAll(); if ($this->chunk) { $this->chunk->setChanged(); $this->level->clearChunkCache($this->chunk->getX(), $this->chunk->getZ()); } }
public function __construct(Anvil $level, $levelId, $chunkX, $chunkZ) { $this->levelId = $levelId; $this->chunkX = $chunkX; $this->chunkZ = $chunkZ; $chunk = $level->getChunk($chunkX, $chunkZ, false); if (!$chunk instanceof Chunk) { throw new ChunkException("Invalid Chunk sent"); } $this->biomeIds = $chunk->getBiomeIdArray(); $this->biomeColors = $chunk->getBiomeColorArray(); $this->sections = $chunk->getSections(); $tiles = ""; $nbt = new NBT(NBT::LITTLE_ENDIAN); foreach ($chunk->getTiles() as $tile) { if ($tile instanceof Spawnable) { $nbt->setData($tile->getSpawnCompound()); $tiles .= $nbt->write(); } } $this->tiles = $tiles; $this->compressionLevel = Level::$COMPRESSION_LEVEL; }
public function onRun($currentTicks) { $this->getOwner()->updateVars(); foreach ($this->getOwner()->getServer()->getLevels() as $lv) { if (count($lv->getPlayers()) == 0) { continue; } foreach ($lv->getTiles() as $tile) { if (!$tile instanceof Sign) { continue; } $sign = $tile->getText(); $text = $this->getOwner()->getLiveSign($sign); if ($text == null) { continue; } $pk = new TileEntityDataPacket(); $data = $tile->getSpawnCompound(); $data->Text1 = new String("Text1", $text[0]); $data->Text2 = new String("Text2", $text[1]); $data->Text3 = new String("Text3", $text[2]); $data->Text4 = new String("Text4", $text[3]); $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->setData($data); $pk->x = $tile->getX(); $pk->y = $tile->getY(); $pk->z = $tile->getZ(); $pk->namedtag = $nbt->write(); foreach ($lv->getPlayers() as $pl) { $pl->dataPacket($pk); } //foreach Players } //foreach Tiles } // foreach Levels }
public function read(NBT $nbt) { $this->value = []; $size = $nbt->endianness === 1 ? \PHP_INT_SIZE === 8 ? \unpack("N", $nbt->get(4))[1] << 32 >> 32 : \unpack("N", $nbt->get(4))[1] : (\PHP_INT_SIZE === 8 ? \unpack("V", $nbt->get(4))[1] << 32 >> 32 : \unpack("V", $nbt->get(4))[1]); $value = \unpack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", $nbt->get($size * 4)); foreach ($value as $i => $v) { $this->value[$i - 1] = $v; } }
public function saveArea(Area $area) { $nbt = new NBT(NBT::BIG_ENDIAN); $data = new Compound(); $data->CaseName = new String("CaseName", $area->getName()); $data->SerializedShape = new String("SerializedShape", serialize($area->getShape())); $data->UserFlags = new Int("UserFlags", $area->getUserFlags()); $data->NonUserFlags = new Int("NonUserFlags", $area->getNonUserFlags()); $data->Owner = new String("Owner", $area->getOwner()); $data->Users = new Enum("Users", array_map(function ($user) { return new String("", $user); }, $area->getUsers())); $file = str_replace('$${areaname}', strtolower($area->getName()), $this->areaFile); file_put_contents($file, $nbt->writeCompressed()); }
public function execute(CommandSender $sender, $currentAlias, array $args) { if (!$this->testPermission($sender)) { return true; } if (count($args) < 2) { $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $player = $sender->getServer()->getPlayer($args[0]); $item = Item::fromString($args[1]); if (!isset($args[2])) { $item->setCount($item->getMaxStackSize()); } else { $item->setCount((int) $args[2]); } if (isset($args[3])) { $tags = $exception = null; $data = implode(" ", array_slice($args, 3)); try { $tags = NBT::parseJSON($data); } catch (\Throwable $ex) { $exception = $ex; } if (!$tags instanceof CompoundTag or $exception !== null) { $sender->sendMessage(new TranslationContainer("commands.give.tagError", [$exception !== null ? $exception->getMessage() : "Invalid tag conversion"])); return true; } $item->setNamedTag($tags); } if ($player instanceof Player) { if ($item->getId() === 0) { $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]])); return true; } //TODO: overflow $player->getInventory()->addItem(clone $item); } else { $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.give.success", [$item->getName() . " (" . $item->getId() . ":" . $item->getDamage() . ")", (string) $item->getCount(), $player->getName()])); return true; }
/** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item) { $i = $this->getSlotIndex($index); $d = NBT::putItemHelper($item, $index); if ($item->getId() === Item::AIR or $item->getCount() <= 0) { if ($i >= 0) { unset($this->namedtag->Items[$i]); } } elseif ($i < 0) { for ($i = 0; $i <= $this->getSize(); ++$i) { if (!isset($this->namedtag->Items[$i])) { break; } } $this->namedtag->Items[$i] = $d; } else { $this->namedtag->Items[$i] = $d; } return true; }
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null) { if ($face > 1) { $faces = [2 => 3, 3 => 2, 4 => 1, 5 => 4]; $itemTag = NBT::putItemHelper(Item::get(Item::AIR)); $itemTag->setName("Item"); $nbt = new CompoundTag("", [new StringTag("id", MainClass::TILE_ITEM_FRAME), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new CompoundTag("Item", $itemTag->getValue()), new FloatTag("ItemDropChance", 1.0), new ByteTag("ItemRotation", 0)]); if ($item->hasCustomBlockData()) { foreach ($item->getCustomBlockData() as $key => $value) { //Reformat: support "Item" tag by give command if ($key === "Item" and $value instanceof CompoundTag) { $value = NBT::putItemHelper(NBT::getItemHelper($value)); $value->setName("Item"); } $nbt->{$key} = $value; } } //var_dump($nbt);//debug Tile::createTile("ItemFrame", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); $this->getLevel()->setBlock($block, Block::get(MainClass::BLOCK_ITEM_FRAME, $faces[$face]), true, true); return true; } return false; }
/** * @param Vector3 $source * @param Item $item * @param Vector3 $motion * @param int $delay */ public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, $delay = 10) { $motion = $motion === null ? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion; $itemTag = NBT::putItemHelper($item); $itemTag->setName("Item"); if ($item->getId() > 0 and $item->getCount() > 0) { $itemEntity = Entity::createEntity("Item", $this->getChunk($source->getX() >> 4, $source->getZ() >> 4, true), new Compound("", ["Pos" => new Enum("Pos", [new Double("", $source->getX()), new Double("", $source->getY()), new Double("", $source->getZ())]), "Motion" => new Enum("Motion", [new Double("", $motion->x), new Double("", $motion->y), new Double("", $motion->z)]), "Rotation" => new Enum("Rotation", [new Float("", lcg_value() * 360), new Float("", 0)]), "Health" => new Short("Health", 5), "Item" => $itemTag, "PickupDelay" => new Short("PickupDelay", $delay)])); $itemEntity->spawnToAll(); } }
/** * @param string $name * @param Compound $nbtTag * @param bool $async */ public function saveOfflinePlayerData($name, Compound $nbtTag, $async = false) { $nbt = new NBT(NBT::BIG_ENDIAN); try { $nbt->setData($nbtTag); if ($async) { $this->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed())); } else { file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed()); } } catch (\Exception $e) { $this->logger->critical($this->getLanguage()->translateString("pocketmine.data.saveError", [$name, $e->getMessage()])); if (\pocketmine\DEBUG > 1 and $this->logger instanceof MainLogger) { $this->logger->logException($e); } } }
public function write(NBT $nbt, bool $network = false) { $nbt->putByte($this->value); }
public function write(NBT $nbt) { $nbt->putInt(strlen($this->value)); $nbt->put($this->value); }
public function toBinary() { $nbt = clone $this->getNBT(); $nbt->xPos = new Int("xPos", $this->x); $nbt->zPos = new Int("zPos", $this->z); if ($this->isGenerated()) { $nbt->Blocks = new ByteArray("Blocks", $this->getBlockIdArray()); $nbt->Data = new ByteArray("Data", $this->getBlockDataArray()); $nbt->SkyLight = new ByteArray("SkyLight", $this->getBlockSkyLightArray()); $nbt->BlockLight = new ByteArray("BlockLight", $this->getBlockLightArray()); $nbt->Biomes = new ByteArray("Biomes", $this->getBiomeIdArray()); $nbt->BiomeColors = new IntArray("BiomeColors", $this->getBiomeColorArray()); $nbt->HeightMap = new IntArray("HeightMap", $this->getHeightMapArray()); } $entities = []; foreach ($this->getEntities() as $entity) { if (!$entity instanceof Player and !$entity->closed) { $entity->saveNBT(); $entities[] = $entity->namedtag; } } $nbt->Entities = new Enum("Entities", $entities); $nbt->Entities->setTagType(NBT::TAG_Compound); $tiles = []; foreach ($this->getTiles() as $tile) { $tile->saveNBT(); $tiles[] = $tile->namedtag; } $nbt->TileEntities = new Enum("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", ["Level" => $nbt])); return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); }
public final function deepEquals(Item $item, $checkDamage = true, $checkCompound = true) { if ($this->equals($item, $checkDamage, $checkCompound)) { return true; } elseif ($item->hasCompoundTag() and $this->hasCompoundTag()) { return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); } return false; }
public function write(NBT $nbt) { $nbt->putLong($this->value); }
public function saveNBT() { parent::saveNBT(); $this->namedtag->Item = NBT::putItemHelper($this->item); $this->namedtag->Health = new Short("Health", $this->getHealth()); $this->namedtag->Age = new Short("Age", $this->age); $this->namedtag->PickupDelay = new Short("PickupDelay", $this->pickupDelay); if ($this->owner !== null) { $this->namedtag->Owner = new String("Owner", $this->owner); } if ($this->thrower !== null) { $this->namedtag->Thrower = new String("Thrower", $this->thrower); } }
public function saveNBT() { parent::saveNBT(); $this->namedtag->Inventory = new Enum("Inventory", []); $this->namedtag->Inventory->setTagType(NBT::TAG_Compound); if ($this->inventory !== null) { for ($slot = 0; $slot < 9; ++$slot) { $hotbarSlot = $this->inventory->getHotbarSlotIndex($slot); if ($hotbarSlot !== -1) { $item = $this->inventory->getItem($hotbarSlot); if ($item->getId() !== 0 and $item->getCount() > 0) { $tag = NBT::putItemHelper($item, $slot); $tag->TrueSlot = new Byte("TrueSlot", $hotbarSlot); $this->namedtag->Inventory[$slot] = $tag; continue; } } $this->namedtag->Inventory[$slot] = new Compound("", [new Byte("Count", 0), new Short("Damage", 0), new Byte("Slot", $slot), new Byte("TrueSlot", -1), new Short("id", 0)]); } //Normal inventory $slotCount = Player::SURVIVAL_SLOTS + 9; //$slotCount = (($this instanceof Player and ($this->gamemode & 0x01) === 1) ? Player::CREATIVE_SLOTS : Player::SURVIVAL_SLOTS) + 9; for ($slot = 9; $slot < $slotCount; ++$slot) { $item = $this->inventory->getItem($slot - 9); $this->namedtag->Inventory[$slot] = NBT::putItemHelper($item, $slot); } //Armor for ($slot = 100; $slot < 104; ++$slot) { $item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100); if ($item instanceof ItemItem and $item->getId() !== ItemItem::AIR) { $this->namedtag->Inventory[$slot] = NBT::putItemHelper($item, $slot); } } } if (strlen($this->getSkinData()) > 0) { $this->namedtag->Skin = new Compound("Skin", ["Data" => new String("Data", $this->getSkinData()), "Slim" => new Byte("Slim", $this->isSkinSlim() ? 1 : 0), "Transparent" => new Byte("Transparent", $this->isSkinTransparent() ? 1 : 0)]); } }
/** * Handles a Minecraft packet * TODO: Separate all of this in handlers * * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @param DataPacket $packet */ public function handleDataPacket(DataPacket $packet) { if ($this->connected === false) { return; } if ($packet::NETWORK_ID === ProtocolInfo::BATCH_PACKET) { /** @var BatchPacket $packet */ $this->server->getNetwork()->processBatch($packet, $this); return; } $timings = Timings::getReceiveDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet)); if ($ev->isCancelled()) { $timings->stopTiming(); return; } switch ($packet::NETWORK_ID) { case ProtocolInfo::LOGIN_PACKET: if ($this->loggedIn) { break; } $this->username = TextFormat::clean($packet->username); $this->displayName = $this->username; $this->setNameTag($this->username); $this->iusername = strtolower($this->username); if (count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)) { break; } if ($packet->protocol1 !== ProtocolInfo::CURRENT_PROTOCOL) { if ($packet->protocol1 < ProtocolInfo::CURRENT_PROTOCOL) { $message = "disconnectionScreen.outdatedClient"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_CLIENT; $this->directDataPacket($pk); } else { $message = "disconnectionScreen.outdatedServer"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_SERVER; $this->directDataPacket($pk); } $this->close("", $message, false); break; } $this->randomClientId = $packet->clientId; $this->loginData = ["clientId" => $packet->clientId, "loginData" => null]; $this->uuid = $packet->clientUUID; $this->rawUUID = $this->uuid->toBinary(); $this->clientSecret = $packet->clientSecret; $valid = true; $len = strlen($packet->username); if ($len > 16 or $len < 3) { $valid = false; } for ($i = 0; $i < $len and $valid; ++$i) { $c = ord($packet->username[$i]); if ($c >= ord("a") and $c <= ord("z") or $c >= ord("A") and $c <= ord("Z") or $c >= ord("0") and $c <= ord("9") or $c === ord("_")) { continue; } $valid = false; break; } if (!$valid or $this->iusername === "rcon" or $this->iusername === "console") { $this->close("", "disconnectionScreen.invalidName"); break; } if (strlen($packet->skin) !== 64 * 32 * 4 and strlen($packet->skin) !== 64 * 64 * 4) { $this->close("", "disconnectionScreen.invalidSkin"); break; } $this->setSkin($packet->skin, $packet->skinname, $packet->oldclient, $packet->slim, $packet->transparent); $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); if ($ev->isCancelled()) { $this->close("", $ev->getKickMessage()); break; } $this->onPlayerPreLogin(); break; case ProtocolInfo::MOVE_PLAYER_PACKET: $newPos = new Vector3($packet->x, $packet->y - $this->getEyeHeight(), $packet->z); $revert = false; if (!$this->isAlive() or $this->spawned !== true) { $revert = true; $this->forceMovement = new Vector3($this->x, $this->y, $this->z); } if ($this->teleportPosition !== null or $this->forceMovement instanceof Vector3 and (($dist = $newPos->distanceSquared($this->forceMovement)) > 0.1 or $revert)) { $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch); } else { $packet->yaw %= 360; $packet->pitch %= 360; if ($packet->yaw < 0) { $packet->yaw += 360; } $this->setRotation($packet->yaw, $packet->pitch); $this->newPosition = $newPos; $this->forceMovement = null; } break; case ProtocolInfo::MOB_EQUIPMENT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } if ($packet->slot === 0x28 or $packet->slot === 0 or $packet->slot === 255) { //0 for 0.8.0 compatibility $packet->slot = -1; //Air } else { $packet->slot -= 9; //Get real block slot } /** @var Item $item */ $item = null; if ($this->isCreative()) { //Creative mode match $item = $packet->item; $slot = Item::getCreativeItemIndex($item); } else { $item = $this->inventory->getItem($packet->slot); $slot = $packet->slot; } if ($packet->slot === -1) { //Air if ($this->isCreative()) { $found = false; for ($i = 0; $i < $this->inventory->getHotbarSize(); ++$i) { if ($this->inventory->getHotbarSlotIndex($i) === -1) { $this->inventory->setHeldItemIndex($i); $found = true; break; } } if (!$found) { //couldn't find a empty slot (error) $this->inventory->sendContents($this); break; } } else { if ($packet->selectedSlot >= 0 and $packet->selectedSlot < 9) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setHeldItemSlot($packet->slot); } else { $this->inventory->sendContents($this); break; } } } elseif ($item === null or $slot === -1 or !$item->deepEquals($packet->item)) { // packet error or not implemented $this->inventory->sendContents($this); break; } elseif ($this->isCreative()) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setItem($packet->selectedSlot, $item); $this->inventory->setHeldItemSlot($packet->selectedSlot); } else { if ($packet->selectedSlot >= 0 and $packet->selectedSlot < $this->inventory->getHotbarSize()) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setHeldItemSlot($slot); } else { $this->inventory->sendContents($this); break; } } $this->inventory->sendHeldItem($this->hasSpawned); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::USE_ITEM_PACKET: if ($this->spawned === false or !$this->isAlive() or $this->blocked) { break; } $blockVector = new Vector3($packet->x, $packet->y, $packet->z); $this->craftingType = 0; if ($packet->face >= 0 and $packet->face <= 5) { //Use Block, place $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); if (!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()) { } elseif ($this->isCreative()) { $item = $this->inventory->getItemInHand(); if ($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true) { break; } } elseif (!$this->inventory->getItemInHand()->deepEquals($packet->item)) { $this->inventory->sendHeldItem($this); } else { $item = $this->inventory->getItemInHand(); $oldItem = clone $item; //TODO: Implement adventure mode checks if ($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this)) { if (!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()) { $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } break; } } $this->inventory->sendHeldItem($this); if ($blockVector->distanceSquared($this) > 10000) { break; } $target = $this->level->getBlock($blockVector); $block = $target->getSide($packet->face); $this->level->sendBlocks([$this], [$target, $block], UpdateBlockPacket::FLAG_ALL_PRIORITY); break; } elseif ($packet->face === 0xff) { $aimPos = (new Vector3($packet->x / 32768, $packet->y / 32768, $packet->z / 32768))->normalize(); if ($this->isCreative()) { $item = $this->inventory->getItemInHand(); } elseif (!$this->inventory->getItemInHand()->deepEquals($packet->item)) { $this->inventory->sendHeldItem($this); break; } else { $item = $this->inventory->getItemInHand(); } $ev = new PlayerInteractEvent($this, $item, $aimPos, $packet->face, PlayerInteractEvent::RIGHT_CLICK_AIR); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendHeldItem($this); break; } if ($item->getId() === Item::SNOWBALL) { $nbt = new Compound("", ["Pos" => new Enum("Pos", [new Double("", $this->x), new Double("", $this->y + $this->getEyeHeight()), new Double("", $this->z)]), "Motion" => new Enum("Motion", [new Double("", $aimPos->x), new Double("", $aimPos->y), new Double("", $aimPos->z)]), "Rotation" => new Enum("Rotation", [new Float("", $this->yaw), new Float("", $this->pitch)])]); $f = 1.5; $snowball = Entity::createEntity("Snowball", $this->chunk, $nbt, $this); $snowball->setMotion($snowball->getMotion()->multiply($f)); if ($this->isSurvival()) { $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if ($snowball instanceof Projectile) { $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball)); if ($projectileEv->isCancelled()) { $snowball->kill(); } else { $snowball->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } } else { $snowball->spawnToAll(); } } $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, true); $this->startAction = $this->server->getTick(); } break; case ProtocolInfo::PLAYER_ACTION_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE) { break; } $packet->eid = $this->id; $pos = new Vector3($packet->x, $packet->y, $packet->z); switch ($packet->action) { case PlayerActionPacket::ACTION_START_BREAK: if ($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000) { break; } $target = $this->level->getBlock($pos); $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK); $this->getServer()->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendHeldItem($this); break; } $this->lastBreak = microtime(true); break; case PlayerActionPacket::ACTION_ABORT_BREAK: $this->lastBreak = PHP_INT_MAX; break; case PlayerActionPacket::ACTION_RELEASE_ITEM: if ($this->startAction > -1 and $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION)) { if ($this->inventory->getItemInHand()->getId() === Item::BOW) { $bow = $this->inventory->getItemInHand(); if ($this->isSurvival() and !$this->inventory->contains(Item::get(Item::ARROW, 0, 1))) { $this->inventory->sendContents($this); break; } $nbt = new Compound("", ["Pos" => new Enum("Pos", [new Double("", $this->x), new Double("", $this->y + $this->getEyeHeight()), new Double("", $this->z)]), "Motion" => new Enum("Motion", [new Double("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new Double("", -sin($this->pitch / 180 * M_PI)), new Double("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI))]), "Rotation" => new Enum("Rotation", [new Float("", $this->yaw), new Float("", $this->pitch)]), "Fire" => new Short("Fire", $this->isOnFire() ? 45 * 60 : 0)]); $diff = $this->server->getTick() - $this->startAction; $p = $diff / 20; $f = min(($p ** 2 + $p * 2) / 3, 1) * 2; $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->chunk, $nbt, $this, $f == 2 ? true : false), $f); if ($f < 0.1 or $diff < 5) { $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $ev->getProjectile()->kill(); $this->inventory->sendContents($this); } else { $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); if ($this->isSurvival()) { $this->inventory->removeItem(Item::get(Item::ARROW, 0, 1)); $bow->setDamage($bow->getDamage() + 1); if ($bow->getDamage() >= 385) { $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0)); } else { $this->inventory->setItemInHand($bow); } } if ($ev->getProjectile() instanceof Projectile) { $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile())); if ($projectileEv->isCancelled()) { $ev->getProjectile()->kill(); } else { $ev->getProjectile()->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } } else { $ev->getProjectile()->spawnToAll(); } } } } elseif ($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1) { //Milk! $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); if ($this->isSurvival()) { $slot = $this->inventory->getItemInHand(); --$slot->count; $this->inventory->setItemInHand($slot); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 1)); } $this->removeAllEffects(); } else { $this->inventory->sendContents($this); } break; case PlayerActionPacket::ACTION_STOP_SLEEPING: $this->stopSleep(); break; case PlayerActionPacket::ACTION_RESPAWN: if ($this->spawned === false or $this->isAlive() or !$this->isOnline()) { break; } if ($this->server->isHardcore()) { $this->setBanned(true); break; } $this->craftingType = 0; $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); $this->teleport($ev->getRespawnPosition()); $this->setSprinting(false); $this->setSneaking(false); $this->extinguish(); $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); $this->deadTicks = 0; $this->noDamageTicks = 60; $this->setHealth($this->getMaxHealth()); $this->removeAllEffects(); $this->sendData($this); $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); $this->blocked = false; $this->spawnToAll(); $this->scheduleUpdate(); break; case PlayerActionPacket::ACTION_START_SPRINT: $ev = new PlayerToggleSprintEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSprinting(true); } break; case PlayerActionPacket::ACTION_STOP_SPRINT: $ev = new PlayerToggleSprintEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSprinting(false); } break; case PlayerActionPacket::ACTION_START_SNEAK: $ev = new PlayerToggleSneakEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSneaking(true); } break; case PlayerActionPacket::ACTION_STOP_SNEAK: $ev = new PlayerToggleSneakEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSneaking(false); } break; } $this->startAction = -1; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::REMOVE_BLOCK_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $vector = new Vector3($packet->x, $packet->y, $packet->z); if ($this->isCreative()) { $item = $this->inventory->getItemInHand(); } else { $item = $this->inventory->getItemInHand(); } $oldItem = clone $item; if ($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this)) { if ($this->isSurvival()) { if (!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()) { $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } } break; } $this->inventory->sendContents($this); $target = $this->level->getBlock($vector); $tile = $this->level->getTile($vector); $this->level->sendBlocks([$this], [$target], UpdateBlockPacket::FLAG_ALL_PRIORITY); $this->inventory->sendHeldItem($this); if ($tile instanceof Spawnable) { $tile->spawnTo($this); } break; case ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET: break; case ProtocolInfo::INTERACT_PACKET: if ($this->spawned === false or !$this->isAlive() or $this->blocked) { break; } $this->craftingType = 0; $target = $this->level->getEntity($packet->target); $cancelled = false; if ($target instanceof Player and $this->server->getConfigBoolean("pvp", true) === false) { $cancelled = true; } if ($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->isAlive() and $target->isAlive()) { if ($target instanceof DroppedItem or $target instanceof Arrow) { $this->kick("Attempting to attack an invalid entity"); $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); break; } $item = $this->inventory->getItemInHand(); $damageTable = [Item::WOODEN_SWORD => 4, Item::GOLD_SWORD => 4, Item::STONE_SWORD => 5, Item::IRON_SWORD => 6, Item::DIAMOND_SWORD => 7, Item::WOODEN_AXE => 3, Item::GOLD_AXE => 3, Item::STONE_AXE => 3, Item::IRON_AXE => 5, Item::DIAMOND_AXE => 6, Item::WOODEN_PICKAXE => 2, Item::GOLD_PICKAXE => 2, Item::STONE_PICKAXE => 3, Item::IRON_PICKAXE => 4, Item::DIAMOND_PICKAXE => 5, Item::WOODEN_SHOVEL => 1, Item::GOLD_SHOVEL => 1, Item::STONE_SHOVEL => 2, Item::IRON_SHOVEL => 3, Item::DIAMOND_SHOVEL => 4]; $damage = [EntityDamageEvent::MODIFIER_BASE => isset($damageTable[$item->getId()]) ? $damageTable[$item->getId()] : 1]; if (!$this->canInteract($target, 8)) { $cancelled = true; } elseif ($target instanceof Player) { if (($target->getGamemode() & 0x1) > 0) { break; } elseif ($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0) { $cancelled = true; } $armorValues = [Item::LEATHER_CAP => 1, Item::LEATHER_TUNIC => 3, Item::LEATHER_PANTS => 2, Item::LEATHER_BOOTS => 1, Item::CHAIN_HELMET => 1, Item::CHAIN_CHESTPLATE => 5, Item::CHAIN_LEGGINGS => 4, Item::CHAIN_BOOTS => 1, Item::GOLD_HELMET => 1, Item::GOLD_CHESTPLATE => 5, Item::GOLD_LEGGINGS => 3, Item::GOLD_BOOTS => 1, Item::IRON_HELMET => 2, Item::IRON_CHESTPLATE => 6, Item::IRON_LEGGINGS => 5, Item::IRON_BOOTS => 2, Item::DIAMOND_HELMET => 3, Item::DIAMOND_CHESTPLATE => 8, Item::DIAMOND_LEGGINGS => 6, Item::DIAMOND_BOOTS => 3]; $points = 0; foreach ($target->getInventory()->getArmorContents() as $index => $i) { if (isset($armorValues[$i->getId()])) { $points += $armorValues[$i->getId()]; } } $damage[EntityDamageEvent::MODIFIER_ARMOR] = -floor($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04); } $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage); if ($cancelled) { $ev->setCancelled(); } $target->attack($ev->getFinalDamage(), $ev); if ($ev->isCancelled()) { if ($item->isTool() and $this->isSurvival()) { $this->inventory->sendContents($this); } break; } if ($item->isTool() and $this->isSurvival()) { if ($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()) { $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); } else { $this->inventory->setItemInHand($item); } } } break; case ProtocolInfo::ANIMATE_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } $this->server->getPluginManager()->callEvent($ev = new PlayerAnimationEvent($this, $packet->action)); if ($ev->isCancelled()) { break; } $pk = new AnimatePacket(); $pk->eid = $this->getId(); $pk->action = $ev->getAnimationType(); Server::broadcastPacket($this->getViewers(), $pk); break; case ProtocolInfo::SET_HEALTH_PACKET: //Not used break; case ProtocolInfo::ENTITY_EVENT_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); //TODO: check if this should be true switch ($packet->event) { case 9: //Eating $items = [Item::APPLE => 4, Item::MUSHROOM_STEW => 10, Item::BEETROOT_SOUP => 10, Item::BREAD => 5, Item::RAW_PORKCHOP => 3, Item::COOKED_PORKCHOP => 8, Item::RAW_BEEF => 3, Item::STEAK => 8, Item::COOKED_CHICKEN => 6, Item::RAW_CHICKEN => 2, Item::MELON_SLICE => 2, Item::GOLDEN_APPLE => 10, Item::PUMPKIN_PIE => 8, Item::CARROT => 4, Item::POTATO => 1, Item::BAKED_POTATO => 6, Item::COOKIE => 2, Item::COOKED_FISH => 5, Item::COOKED_SALMON => 6, Item::RAW_FISH => 2, Item::RAW_SALMON => 2, Item::CLOWN_FISH => 1, Item::PUFFER_FISH => 1]; $slot = $this->inventory->getItemInHand(); if ($this->getHealth() < $this->getMaxHealth() and isset($items[$slot->getId()])) { $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $slot)); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); $amount = $items[$slot->getId()]; $ev = new EntityRegainHealthEvent($this, $amount, EntityRegainHealthEvent::CAUSE_EATING); $this->heal($ev->getAmount(), $ev); --$slot->count; $this->inventory->setItemInHand($slot); if ($slot->getId() === Item::MUSHROOM_STEW or $slot->getId() === Item::BEETROOT_SOUP) { $this->inventory->addItem(Item::get(Item::BOWL, 0, 1)); } elseif ($slot->getId() === Item::PUFFER_FISH) { //Pufferfish //$this->addEffect(Effect::getEffect(Effect::HUNGER)->setAmplifier(2)->setDuration(15 * 20)); $this->addEffect(Effect::getEffect(Effect::NAUSEA)->setAmplifier(1)->setDuration(15 * 20)); $this->addEffect(Effect::getEffect(Effect::POISON)->setAmplifier(3)->setDuration(60 * 20)); } } break; } break; case ProtocolInfo::DROP_ITEM_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $item = $this->inventory->getItemInHand(); $ev = new PlayerDropItemEvent($this, $item); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); $motion = $this->getDirectionVector()->multiply(0.4); $this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::TEXT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } $this->craftingType = 0; if ($packet->type === TextPacket::TYPE_CHAT) { $packet->message = TextFormat::clean($packet->message, $this->removeFormat); foreach (explode("\n", $packet->message) as $message) { if (trim($message) != "" and strlen($message) <= 255 and $this->messageCounter-- > 0) { $ev = new PlayerCommandPreprocessEvent($this, $message); if (mb_strlen($ev->getMessage(), "UTF-8") > 320) { $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { break; } if (substr($ev->getMessage(), 0, 1) === "/") { //Command Timings::$playerCommandTimer->startTiming(); $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); Timings::$playerCommandTimer->stopTiming(); } else { $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); if (!$ev->isCancelled()) { $this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients()); } } } } } break; case ProtocolInfo::CONTAINER_CLOSE_PACKET: if ($this->spawned === false or $packet->windowid === 0) { break; } $this->craftingType = 0; $this->currentTransaction = null; if (isset($this->windowIndex[$packet->windowid])) { $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowid], $this)); $this->removeWindow($this->windowIndex[$packet->windowid]); } else { unset($this->windowIndex[$packet->windowid]); } break; case ProtocolInfo::CRAFTING_EVENT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } elseif (!isset($this->windowIndex[$packet->windowId])) { $this->inventory->sendContents($this); $pk = new ContainerClosePacket(); $pk->windowid = $packet->windowId; $this->dataPacket($pk); break; } $recipe = $this->server->getCraftingManager()->getRecipe($packet->id); if ($recipe === null or ($recipe instanceof BigShapelessRecipe or $recipe instanceof BigShapedRecipe) and $this->craftingType === 0) { $this->inventory->sendContents($this); break; } foreach ($packet->input as $i => $item) { if ($item->getDamage() === -1 or $item->getDamage() === 0xffff) { $item->setDamage(null); } if ($i < 9 and $item->getId() > 0) { $item->setCount(1); } } $canCraft = true; if ($recipe instanceof ShapedRecipe) { for ($x = 0; $x < 3 and $canCraft; ++$x) { for ($y = 0; $y < 3; ++$y) { $item = $packet->input[$y * 3 + $x]; $ingredient = $recipe->getIngredient($x, $y); if ($item->getCount() > 0) { if ($ingredient === null or !$ingredient->deepEquals($item, $ingredient->getDamage() !== null, $ingredient->getCompoundTag() !== null)) { $canCraft = false; break; } } } } } elseif ($recipe instanceof ShapelessRecipe) { $needed = $recipe->getIngredientList(); for ($x = 0; $x < 3 and $canCraft; ++$x) { for ($y = 0; $y < 3; ++$y) { $item = clone $packet->input[$y * 3 + $x]; foreach ($needed as $k => $n) { if ($n->deepEquals($item, $n->getDamage() !== null, $n->getCompoundTag() !== null)) { $remove = min($n->getCount(), $item->getCount()); $n->setCount($n->getCount() - $remove); $item->setCount($item->getCount() - $remove); if ($n->getCount() === 0) { unset($needed[$k]); } } } if ($item->getCount() > 0) { $canCraft = false; break; } } } if (count($needed) > 0) { $canCraft = false; } } else { $canCraft = false; } /** @var Item[] $ingredients */ $ingredients = $packet->input; $result = $packet->output[0]; if (!$canCraft or !$recipe->getResult()->deepEquals($result)) { $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": expected " . $recipe->getResult() . ", got " . $result . ", using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $used = array_fill(0, $this->inventory->getSize(), 0); foreach ($ingredients as $ingredient) { $slot = -1; foreach ($this->inventory->getContents() as $index => $i) { if ($ingredient->getId() !== 0 and $ingredient->deepEquals($i, $i->getDamage() !== null) and $i->getCount() - $used[$index] >= 1) { $slot = $index; $used[$index]++; break; } } if ($ingredient->getId() !== 0 and $slot === -1) { $canCraft = false; break; } } if (!$canCraft) { $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": client does not have enough items, using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } foreach ($used as $slot => $count) { if ($count === 0) { continue; } $item = $this->inventory->getItem($slot); if ($item->getCount() > $count) { $newItem = clone $item; $newItem->setCount($item->getCount() - $count); } else { $newItem = Item::get(Item::AIR, 0, 0); } $this->inventory->setItem($slot, $newItem); } $extraItem = $this->inventory->addItem($recipe->getResult()); if (count($extraItem) > 0) { foreach ($extraItem as $item) { $this->level->dropItem($this, $item); } } switch ($recipe->getResult()->getId()) { case Item::WORKBENCH: $this->awardAchievement("buildWorkBench"); break; case Item::WOODEN_PICKAXE: $this->awardAchievement("buildPickaxe"); break; case Item::FURNACE: $this->awardAchievement("buildFurnace"); break; case Item::WOODEN_HOE: $this->awardAchievement("buildHoe"); break; case Item::BREAD: $this->awardAchievement("makeBread"); break; case Item::CAKE: //TODO: detect complex recipes like cake that leave remains $this->awardAchievement("bakeCake"); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); break; case Item::STONE_PICKAXE: case Item::GOLD_PICKAXE: case Item::IRON_PICKAXE: case Item::DIAMOND_PICKAXE: $this->awardAchievement("buildBetterPickaxe"); break; case Item::WOODEN_SWORD: $this->awardAchievement("buildSword"); break; case Item::DIAMOND: $this->awardAchievement("diamond"); break; } break; case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } if ($packet->slot < 0) { break; } if ($packet->windowid === 0) { //Our inventory if ($packet->slot >= $this->inventory->getSize()) { break; } if ($this->isCreative()) { if (Item::getCreativeItemIndex($packet->item) !== -1) { $this->inventory->setItem($packet->slot, $packet->item); $this->inventory->setHotbarSlotIndex($packet->slot, $packet->slot); //links $hotbar[$packet->slot] to $slots[$packet->slot] } } $transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item); } elseif ($packet->windowid === ContainerSetContentPacket::SPECIAL_ARMOR) { //Our armor if ($packet->slot >= 4) { break; } $transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $this->inventory->getArmorItem($packet->slot), $packet->item); } elseif (isset($this->windowIndex[$packet->windowid])) { $this->craftingType = 0; $inv = $this->windowIndex[$packet->windowid]; $transaction = new BaseTransaction($inv, $packet->slot, $inv->getItem($packet->slot), $packet->item); } else { break; } if ($transaction->getSourceItem()->deepEquals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()) { //No changes! //No changes, just a local inventory update sent by the server break; } if ($this->currentTransaction === null or $this->currentTransaction->getCreationTime() < microtime(true) - 8) { if ($this->currentTransaction !== null) { foreach ($this->currentTransaction->getInventories() as $inventory) { if ($inventory instanceof PlayerInventory) { $inventory->sendArmorContents($this); } $inventory->sendContents($this); } } $this->currentTransaction = new SimpleTransactionGroup($this); } $this->currentTransaction->addTransaction($transaction); if ($this->currentTransaction->canExecute()) { $achievements = []; foreach ($this->currentTransaction->getTransactions() as $ts) { $inv = $ts->getInventory(); if ($inv instanceof FurnaceInventory) { if ($ts->getSlot() === 2) { switch ($inv->getResult()->getId()) { case Item::IRON_INGOT: $achievements[] = "acquireIron"; break; } } } } if ($this->currentTransaction->execute()) { foreach ($achievements as $a) { $this->awardAchievement($a); } } $this->currentTransaction = null; } break; case ProtocolInfo::BLOCK_ENTITY_DATA_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $pos = new Vector3($packet->x, $packet->y, $packet->z); if ($pos->distanceSquared($this) > 10000) { break; } $t = $this->level->getTile($pos); if ($t instanceof Sign) { $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->read($packet->namedtag); $nbt = $nbt->getData(); if ($nbt["id"] !== Tile::SIGN) { $t->spawnTo($this); } else { $ev = new SignChangeEvent($t->getBlock(), $this, [TextFormat::clean($nbt["Text1"], $this->removeFormat), TextFormat::clean($nbt["Text2"], $this->removeFormat), TextFormat::clean($nbt["Text3"], $this->removeFormat), TextFormat::clean($nbt["Text4"], $this->removeFormat)]); if (!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->getRawUniqueId()) { $ev->setCancelled(); } else { foreach ($ev->getLines() as $line) { if (mb_strlen($line, "UTF-8") > 16) { $ev->setCancelled(); } } } $this->server->getPluginManager()->callEvent($ev); if (!$ev->isCancelled()) { $t->setText($ev->getLine(0), $ev->getLine(1), $ev->getLine(2), $ev->getLine(3)); } else { $t->spawnTo($this); } } } break; default: break; } $timings->stopTiming(); }