/**
* 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;
}
$this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet));
if ($ev->isCancelled()) {
return;
}
switch ($packet->pid()) {
case ProtocolInfo::LOGIN_PACKET:
if ($this->loggedIn === true) {
break;
}
$this->username = TextFormat::clean($packet->username);
$this->displayName = $this->username;
$this->nameTag = $this->username;
$this->iusername = strtolower($this->username);
$this->loginData = ["clientId" => $packet->clientId, "loginData" => $packet->loginData];
if (count($this->server->getOnlinePlayers()) > $this->server->getMaxPlayers()) {
if ($this->kick("server full") === true) {
return;
}
}
if ($packet->protocol1 !== ProtocolInfo::CURRENT_PROTOCOL) {
if ($packet->protocol1 < ProtocolInfo::CURRENT_PROTOCOL) {
$pk = new LoginStatusPacket();
$pk->status = 1;
$this->dataPacket($pk);
} else {
$pk = new LoginStatusPacket();
$pk->status = 2;
$this->dataPacket($pk);
}
$this->close("", "Incorrect protocol #" . $packet->protocol1, false);
return;
}
if (strpos($packet->username, "") !== false or preg_match('#^[a-zA-Z0-9_]{3,16}$#', $packet->username) == 0 or $this->username === "" or $this->iusername === "rcon" or $this->iusername === "console" or strlen($packet->username) > 16 or strlen($packet->username) < 3) {
$this->close("", "Bad username");
return;
}
$this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason"));
if ($ev->isCancelled()) {
$this->close("", $ev->getKickMessage());
return;
}
if (!$this->server->isWhitelisted(strtolower($this->getName()))) {
$this->close(TextFormat::YELLOW . $this->username . " has left the game", "Server is white-listed");
return;
} elseif ($this->server->getNameBans()->isBanned(strtolower($this->getName())) or $this->server->getIPBans()->isBanned($this->getAddress())) {
$this->close(TextFormat::YELLOW . $this->username . " has left the game", "You are banned");
return;
}
if ($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)) {
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
}
if ($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)) {
$this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
}
foreach ($this->server->getOnlinePlayers() as $p) {
if ($p !== $this and strtolower($p->getName()) === strtolower($this->getName())) {
if ($p->kick("logged in from another location") === false) {
$this->close(TextFormat::YELLOW . $this->getName() . " has left the game", "Logged in from another location");
return;
} else {
break;
}
}
}
$nbt = $this->server->getOfflinePlayerData($this->username);
if (!isset($nbt->NameTag)) {
$nbt->NameTag = new String("NameTag", $this->username);
} else {
$nbt["NameTag"] = $this->username;
}
$this->gamemode = $nbt["playerGameType"] & 0x3;
if ($this->server->getForceGamemode()) {
$this->gamemode = $this->server->getGamemode();
$nbt->playerGameType = new Int("playerGameType", $this->gamemode);
}
if (($level = $this->server->getLevelByName($nbt["Level"])) === null) {
$this->setLevel($this->server->getDefaultLevel(), true);
$nbt["Level"] = $this->level->getName();
$nbt["Pos"][0] = $this->level->getSpawnLocation()->x;
$nbt["Pos"][1] = $this->level->getSpawnLocation()->y;
$nbt["Pos"][2] = $this->level->getSpawnLocation()->z;
} else {
$this->setLevel($level, true);
}
if (!$nbt instanceof Compound) {
$this->close(TextFormat::YELLOW . $this->username . " has left the game", "Invalid data");
return;
}
$this->achievements = [];
/** @var Byte $achievement */
foreach ($nbt->Achievements as $achievement) {
$this->achievements[$achievement->getName()] = $achievement->getValue() > 0 ? true : false;
}
$nbt["lastPlayed"] = floor(microtime(true) * 1000);
$this->server->saveOfflinePlayerData($this->username, $nbt);
parent::__construct($this->level->getChunk($nbt["Pos"][0] >> 4, $nbt["Pos"][2] >> 4, true), $nbt);
$this->loggedIn = true;
$this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason"));
if ($ev->isCancelled()) {
$this->close(TextFormat::YELLOW . $this->username . " has left the game", $ev->getKickMessage());
return;
}
if ($this->isCreative()) {
$this->inventory->setHeldItemSlot(0);
} else {
$this->inventory->setHeldItemSlot(0);
}
$pk = new LoginStatusPacket();
$pk->status = 0;
$this->dataPacket($pk);
if ($this->spawnPosition === null and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level) {
$this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level);
}
$spawnPosition = $this->getSpawn();
$this->dead = false;
$pk = new StartGamePacket();
$pk->seed = $this->level->getSeed();
$pk->x = $this->x;
$pk->y = $this->y;
$pk->z = $this->z;
$pk->spawnX = (int) $spawnPosition->x;
$pk->spawnY = (int) $spawnPosition->y;
$pk->spawnZ = (int) $spawnPosition->z;
$pk->generator = 1;
//0 old, 1 infinite, 2 flat
$pk->gamemode = $this->gamemode & 0x1;
$pk->eid = 0;
//Always use EntityID as zero for the actual player
$this->dataPacket($pk);
$pk = new SetTimePacket();
$pk->time = $this->level->getTime();
$pk->started = $this->level->stopTime == false;
$this->dataPacket($pk);
$pk = new SetSpawnPositionPacket();
$pk->x = (int) $spawnPosition->x;
$pk->y = (int) $spawnPosition->y;
$pk->z = (int) $spawnPosition->z;
$this->dataPacket($pk);
$pk = new SetHealthPacket();
$pk->health = $this->getHealth();
$this->dataPacket($pk);
if ($this->getHealth() <= 0) {
$this->dead = true;
}
$pk = new SetDifficultyPacket();
$pk->difficulty = $this->server->getDifficulty();
$this->dataPacket($pk);
$this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged in with entity id " . $this->id . " at (" . $this->level->getName() . ", " . round($this->x, 4) . ", " . round($this->y, 4) . ", " . round($this->z, 4) . ")");
$this->orderChunks();
$this->sendNextChunk();
break;
case ProtocolInfo::ROTATE_HEAD_PACKET:
if ($this->spawned === false or $this->dead === true) {
break;
}
$this->setRotation($packet->yaw, $this->pitch);
break;
case ProtocolInfo::MOVE_PLAYER_PACKET:
$newPos = new Vector3($packet->x, $packet->y, $packet->z);
$revert = false;
if ($this->dead === true or $this->spawned !== true) {
$revert = true;
$this->forceMovement = new Vector3($this->x, $this->y, $this->z);
}
if ($this->forceMovement instanceof Vector3 and ($revert or $newPos->distanceSquared($this->forceMovement) > 0.04)) {
$pk = new MovePlayerPacket();
$pk->eid = 0;
$pk->x = $this->forceMovement->x;
$pk->y = $this->forceMovement->y + $this->getEyeHeight();
$pk->z = $this->forceMovement->z;
$pk->bodyYaw = $packet->bodyYaw;
$pk->pitch = $packet->pitch;
$pk->yaw = $packet->yaw;
$pk->teleport = true;
$this->directDataPacket($pk);
} 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::PLAYER_EQUIPMENT_PACKET:
if ($this->spawned === false or $this->dead === true) {
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
}
if ($this->isCreative()) {
//Creative mode match
$item = Item::get($packet->item, $packet->meta, 1);
$slot = $this->getCreativeBlock($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 {
$this->inventory->setHeldItemSlot($packet->slot);
//set Air
}
} elseif (!isset($item) or $slot === -1 or $item->getId() !== $packet->item or $item->getDamage() !== $packet->meta) {
// packet error or not implemented
$this->inventory->sendContents($this);
break;
} elseif ($this->isCreative()) {
$item = Item::get(Block::$creative[$slot][0], Block::$creative[$slot][1], 1);
$this->inventory->setHeldItemIndex($packet->slot);
} else {
$this->inventory->setHeldItemSlot($slot);
}
$this->inventory->sendHeldItem($this->hasSpawned);
if ($this->inAction === true) {
$this->inAction = false;
$this->sendMetadata($this->getViewers());
}
break;
case ProtocolInfo::USE_ITEM_PACKET:
if ($this->spawned === false or $this->dead === true or $this->blocked) {
break;
}
$blockVector = new Vector3($packet->x, $packet->y, $packet->z);
$this->craftingType = 0;
$packet->eid = $this->id;
if ($packet->face >= 0 and $packet->face <= 5) {
//Use Block, place
if ($this->inAction === true) {
$this->inAction = false;
$this->sendMetadata($this->getViewers());
}
if ($blockVector->distance($this) > 10) {
} 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()->getId() !== $packet->item or ($damage = $this->inventory->getItemInHand()->getDamage()) !== $packet->meta and $damage !== null) {
$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) === true) {
if (!$item->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()) {
$this->inventory->setItemInHand($item, $this);
$this->inventory->sendHeldItem($this->hasSpawned);
}
break;
}
}
$target = $this->level->getBlock($blockVector);
$block = $target->getSide($packet->face);
$pk = new UpdateBlockPacket();
$pk->x = $target->x;
$pk->y = $target->y;
$pk->z = $target->z;
$pk->block = $target->getId();
$pk->meta = $target->getDamage();
$this->dataPacket($pk);
$pk = new UpdateBlockPacket();
$pk->x = $block->x;
$pk->y = $block->y;
$pk->z = $block->z;
$pk->block = $block->getId();
$pk->meta = $block->getDamage();
$this->dataPacket($pk);
break;
} elseif ($packet->face === 0xff) {
if ($this->isCreative()) {
$item = $this->inventory->getItemInHand();
} elseif ($this->inventory->getItemInHand()->getId() !== $packet->item or ($damage = $this->inventory->getItemInHand()->getDamage()) !== $packet->meta and $damage !== null) {
$this->inventory->sendHeldItem($this);
break;
} else {
$item = $this->inventory->getItemInHand();
}
$target = $this->level->getBlock($blockVector);
$ev = new PlayerInteractEvent($this, $item, $target, $packet->face);
$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("", -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)])]);
$f = 1.5;
$snowball = Entity::createEntity("Snowball", $this->chunk, $nbt, $this);
$snowball->setMotion($snowball->getMotion()->multiply($f));
if ($this->isSurvival()) {
$this->inventory->removeItem(Item::get(Item::SNOWBALL, 0, 1), $this);
}
if ($snowball instanceof Projectile) {
$this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball));
if ($projectileEv->isCancelled()) {
$snowball->kill();
} else {
$snowball->spawnToAll();
}
} else {
$snowball->spawnToAll();
}
}
$this->inAction = true;
$this->startAction = microtime(true);
$this->sendMetadata($this->getViewers());
}
break;
case ProtocolInfo::PLAYER_ACTION_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
break;
}
$this->craftingType = 0;
$packet->eid = $this->id;
switch ($packet->action) {
case 5:
//Shot arrow
if ($this->inventory->getItemInHand()->getId() === Item::BOW) {
$bow = $this->inventory->getItemInHand();
if ($this->isSurvival()) {
if (!$this->inventory->contains(Item::get(Item::ARROW, 0, 1))) {
$this->inventory->sendContents($this);
return;
}
}
$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)])]);
$f = 1.5;
$ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->chunk, $nbt, $this), $f);
$this->server->getPluginManager()->callEvent($ev);
if ($ev->isCancelled()) {
$ev->getProjectile()->kill();
} else {
$ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce()));
if ($this->isSurvival()) {
$this->inventory->removeItem(Item::get(Item::ARROW, 0, 1), $this);
$bow->setDamage($bow->getDamage() + 1);
$this->inventory->setItemInHand($bow, $this);
if ($bow->getDamage() >= 385) {
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0), $this);
}
}
if ($ev->getProjectile() instanceof Projectile) {
$this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile()));
if ($projectileEv->isCancelled()) {
$ev->getProjectile()->kill();
} else {
$ev->getProjectile()->spawnToAll();
}
} else {
$ev->getProjectile()->spawnToAll();
}
}
}
$this->startAction = false;
$this->inAction = false;
$this->sendMetadata($this->getViewers());
break;
case 6:
//get out of the bed
$this->stopSleep();
break;
}
break;
case ProtocolInfo::REMOVE_BLOCK_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
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->level->useBreakOn($vector, $item, $this) === true) {
if ($this->isSurvival()) {
if (!$item->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()) {
$this->inventory->setItemInHand($item, $this);
$this->inventory->sendHeldItem($this->hasSpawned);
}
}
break;
}
$this->inventory->sendContents($this);
$target = $this->level->getBlock($vector);
$tile = $this->level->getTile($vector);
$pk = new UpdateBlockPacket();
$pk->x = $target->x;
$pk->y = $target->y;
$pk->z = $target->z;
$pk->block = $target->getId();
$pk->meta = $target->getDamage();
$this->dataPacket($pk);
if ($tile instanceof Spawnable) {
$tile->spawnTo($this);
}
break;
case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET:
break;
case ProtocolInfo::INTERACT_PACKET:
if ($this->spawned === false or $this->dead === true 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->dead !== true and $target->dead !== true) {
if ($target instanceof DroppedItem or $target instanceof Arrow) {
$this->kick("Attempting to attack an invalid entity");
$this->server->getLogger()->warning("Player " . $this->getName() . " tried to attack an invalid entity");
return;
}
$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->distance($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), $this);
} else {
$this->inventory->setItemInHand($item, $this);
}
}
}
break;
case ProtocolInfo::ANIMATE_PACKET:
if ($this->spawned === false or $this->dead === true) {
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::RESPAWN_PACKET:
if ($this->spawned === false or $this->dead === false) {
break;
}
$this->craftingType = 0;
$this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn()));
$this->teleport($ev->getRespawnPosition());
$this->fireTicks = 0;
$this->airTicks = 300;
$this->deadTicks = 0;
$this->setHealth(20);
$this->dead = false;
$this->sendMetadata($this->getViewers());
$this->sendMetadata($this);
$this->sendSettings();
$this->inventory->sendContents($this);
$this->inventory->sendArmorContents($this);
$this->blocked = false;
$this->spawnToAll();
$this->scheduleUpdate();
break;
case ProtocolInfo::SET_HEALTH_PACKET:
//Not used
break;
case ProtocolInfo::ENTITY_EVENT_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
break;
}
$this->craftingType = 0;
if ($this->inAction === true) {
$this->inAction = false;
$this->sendMetadata($this->getViewers());
}
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];
$slot = $this->inventory->getItemInHand();
if ($this->getHealth() < 20 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 = 0;
$pk->event = 9;
$this->dataPacket($pk);
$pk->eid = $this->getId();
Server::broadcastPacket($this->getViewers(), $pk);
$amount = $items[$slot->getId()];
$this->server->getPluginManager()->callEvent($ev = new EntityRegainHealthEvent($this, $amount, EntityRegainHealthEvent::CAUSE_EATING));
if (!$ev->isCancelled()) {
$this->heal($ev->getAmount(), $ev);
}
--$slot->count;
$this->inventory->setItemInHand($slot, $this);
if ($slot->getId() === Item::MUSHROOM_STEW or $slot->getId() === Item::BEETROOT_SOUP) {
$this->inventory->addItem(Item::get(Item::BOWL, 0, 1), $this);
}
}
break;
}
break;
case ProtocolInfo::DROP_ITEM_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
break;
}
$packet->eid = $this->id;
$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), $this);
$motion = $this->getDirectionVector()->multiply(0.4);
$this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40);
if ($this->inAction === true) {
$this->inAction = false;
$this->sendMetadata($this->getViewers());
}
break;
case ProtocolInfo::MESSAGE_PACKET:
if ($this->spawned === false or $this->dead === true) {
break;
}
$this->craftingType = 0;
$packet->message = TextFormat::clean($packet->message);
if (trim($packet->message) != "" and strlen($packet->message) <= 255) {
$message = $packet->message;
$this->server->getPluginManager()->callEvent($ev = new PlayerCommandPreprocessEvent($this, $message));
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(sprintf($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::CONTAINER_SET_SLOT_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
break;
}
if ($packet->slot < 0) {
break;
}
if ($packet->windowid === 0) {
//Our inventory
if ($packet->slot >= $this->inventory->getSize()) {
break;
}
if ($this->isCreative()) {
if ($this->getCreativeBlock($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 === 0x78) {
//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()->equals($transaction->getTargetItem(), true) 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) - 0.5) {
if ($this->currentTransaction instanceof SimpleTransactionGroup) {
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()) {
if (!$this->currentTransaction->execute()) {
$this->currentTransaction = null;
break;
}
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:
$this->awardAchievement("acquireIron");
break;
}
}
}
}
$this->currentTransaction = null;
} elseif ($packet->windowid == 0) {
//Try crafting
$craftingGroup = new CraftingTransactionGroup($this->currentTransaction);
if ($craftingGroup->canExecute()) {
//We can craft!
$recipe = $craftingGroup->getMatchingRecipe();
if ($recipe instanceof BigShapelessRecipe and $this->craftingType !== 1) {
break;
} elseif ($recipe instanceof StonecutterShapelessRecipe and $this->craftingType !== 2) {
break;
}
if ($craftingGroup->execute()) {
switch ($craftingGroup->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), $this);
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;
}
}
$this->currentTransaction = null;
}
}
break;
case ProtocolInfo::SEND_INVENTORY_PACKET:
//TODO, Mojang, enable this ยด^_^`
if ($this->spawned === false) {
break;
}
break;
case ProtocolInfo::ENTITY_DATA_PACKET:
if ($this->spawned === false or $this->blocked === true or $this->dead === true) {
break;
}
$this->craftingType = 0;
$t = $this->level->getTile(new Vector3($packet->x, $packet->y, $packet->z));
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, [$nbt["Text1"], $nbt["Text2"], $nbt["Text3"], $nbt["Text4"]]);
if (!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->username) {
$ev->setCancelled(true);
}
$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;
}
}