diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 72829a79f..1c40b6904 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -74,7 +74,6 @@ use pocketmine\inventory\BigCraftingGrid; use pocketmine\inventory\CraftingGrid; use pocketmine\inventory\Inventory; use pocketmine\inventory\PlayerCursorInventory; -use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\CraftingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; @@ -2077,7 +2076,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->sendData($this); $this->inventory->sendContents($this); - $this->inventory->sendArmorContents($this); + $this->armorInventory->sendContents($this); $this->inventory->sendCreativeContents(); $this->inventory->sendHeldItem($this); @@ -2669,7 +2668,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->sendSettings(); $this->inventory->sendContents($this); - $this->inventory->sendArmorContents($this); + $this->armorInventory->sendContents($this); $this->spawnToAll(); $this->scheduleUpdate(); @@ -3567,15 +3566,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return false; //never flag players for despawn } - public function getArmorPoints() : int{ - $total = 0; - foreach($this->inventory->getArmorContents() as $item){ - $total += $item->getDefensePoints(); - } - - return $total; - } - protected function applyPostDamageEffects(EntityDamageEvent $source) : void{ parent::applyPostDamageEffects($source); @@ -3675,6 +3665,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected function addDefaultWindows(){ $this->addWindow($this->getInventory(), ContainerIds::INVENTORY, true); + $this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true); + $this->cursorInventory = new PlayerCursorInventory($this); $this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true); @@ -3811,9 +3803,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected function sendAllInventories(){ foreach($this->windowIndex as $id => $inventory){ $inventory->sendContents($this); - if($inventory instanceof PlayerInventory){ - $inventory->sendArmorContents($this); - } } } diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index b94677de3..9111551b3 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -32,7 +32,6 @@ use pocketmine\inventory\EnderChestInventory; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; use pocketmine\item\Consumable; -use pocketmine\item\enchantment\Enchantment; use pocketmine\item\FoodSource; use pocketmine\item\Item as ItemItem; use pocketmine\level\Level; @@ -494,6 +493,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } protected function initEntity(){ + parent::initEntity(); $this->setPlayerFlag(self::DATA_PLAYER_FLAG_SLEEP, false); $this->propertyManager->setBlockPos(self::DATA_PLAYER_BED_POSITION, null); @@ -511,7 +511,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ //Old hotbar saving stuff, remove it (useless now) unset($inventoryTag[$i]); }elseif($slot >= 100 and $slot < 104){ //Armor - $this->inventory->setItem($this->inventory->getSize() + $slot - 100, ItemItem::nbtDeserialize($item)); + $this->armorInventory->setItem($slot - 100, ItemItem::nbtDeserialize($item)); }else{ $this->inventory->setItem($slot - 9, ItemItem::nbtDeserialize($item)); } @@ -528,7 +528,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $this->inventory->setHeldItemIndex($this->namedtag->getInt("SelectedInventorySlot", 0), false); - parent::initEntity(); $this->setFood((float) $this->namedtag->getInt("foodLevel", (int) $this->getFood(), true)); $this->setExhaustion($this->namedtag->getFloat("foodExhaustionLevel", $this->getExhaustion(), true)); @@ -609,14 +608,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } } - protected function doAirSupplyTick(int $tickDiff){ - //TODO: allow this to apply to other mobs - if(($respirationLevel = $this->inventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or - lcg_value() <= (1 / ($respirationLevel + 1))){ - parent::doAirSupplyTick($tickDiff); - } - } - public function getName() : string{ return $this->getNameTag(); } @@ -652,7 +643,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ //Armor for($slot = 100; $slot < 104; ++$slot){ - $item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100); + $item = $this->armorInventory->getItem($slot - 100); if(!$item->isNull()){ $inventoryTag[$slot] = $item->nbtSerialize($slot); } @@ -708,7 +699,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $pk->metadata = $this->propertyManager->getAll(); $player->dataPacket($pk); - $this->inventory->sendArmorContents($player); + $this->armorInventory->sendContents($player); if(!($this instanceof Player)){ $this->sendSkin([$player]); diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index ffd5d89e0..ca1943312 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -32,7 +32,9 @@ use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\Timings; +use pocketmine\inventory\ArmorInventory; use pocketmine\item\Consumable; +use pocketmine\item\enchantment\Enchantment; use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; use pocketmine\math\VoxelRayTrace; @@ -66,11 +68,16 @@ abstract class Living extends Entity implements Damageable{ /** @var Effect[] */ protected $effects = []; + /** @var ArmorInventory */ + protected $armorInventory; + abstract public function getName() : string; protected function initEntity(){ parent::initEntity(); + $this->armorInventory = new ArmorInventory($this); + $health = $this->getMaxHealth(); if($this->namedtag->hasTag("HealF", FloatTag::class)){ @@ -374,7 +381,19 @@ abstract class Living extends Entity implements Damageable{ * @return int */ public function getArmorPoints() : int{ - return 0; + $total = 0; + foreach($this->armorInventory->getContents() as $item){ + $total += $item->getDefensePoints(); + } + + return $total; + } + + /** + * @return ArmorInventory + */ + public function getArmorInventory() : ArmorInventory{ + return $this->armorInventory; } /** @@ -576,13 +595,17 @@ abstract class Living extends Entity implements Damageable{ * @param int $tickDiff */ protected function doAirSupplyTick(int $tickDiff){ - $ticks = $this->getAirSupplyTicks() - $tickDiff; + if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(Enchantment::RESPIRATION)) <= 0 or + lcg_value() <= (1 / ($respirationLevel + 1)) + ){ + $ticks = $this->getAirSupplyTicks() - $tickDiff; - if($ticks <= -20){ - $this->setAirSupplyTicks(0); - $this->onAirExpired(); - }else{ - $this->setAirSupplyTicks($ticks); + if($ticks <= -20){ + $this->setAirSupplyTicks(0); + $this->onAirExpired(); + }else{ + $this->setAirSupplyTicks($ticks); + } } } @@ -750,4 +773,10 @@ abstract class Living extends Entity implements Damageable{ $this->yaw += 360.0; } } + + protected function sendSpawnPacket(Player $player) : void{ + parent::sendSpawnPacket($player); + + $this->armorInventory->sendContents($player); + } } diff --git a/src/pocketmine/inventory/ArmorInventory.php b/src/pocketmine/inventory/ArmorInventory.php new file mode 100644 index 000000000..09a1e7487 --- /dev/null +++ b/src/pocketmine/inventory/ArmorInventory.php @@ -0,0 +1,151 @@ +holder = $holder; + parent::__construct(); + } + + public function getHolder() : Living{ + return $this->holder; + } + + public function getName() : string{ + return "Armor"; + } + + public function getDefaultSize() : int{ + return 4; + } + + public function getHelmet() : Item{ + return $this->getItem(self::SLOT_HEAD); + } + + public function getChestplate() : Item{ + return $this->getItem(self::SLOT_CHEST); + } + + public function getLeggings() : Item{ + return $this->getItem(self::SLOT_LEGS); + } + + public function getBoots() : Item{ + return $this->getItem(self::SLOT_FEET); + } + + public function setHelmet(Item $helmet) : bool{ + return $this->setItem(self::SLOT_HEAD, $helmet); + } + + public function setChestplate(Item $chestplate) : bool{ + return $this->setItem(self::SLOT_CHEST, $chestplate); + } + + public function setLeggings(Item $leggings) : bool{ + return $this->setItem(self::SLOT_LEGS, $leggings); + } + + public function setBoots(Item $boots) : bool{ + return $this->setItem(self::SLOT_FEET, $boots); + } + + protected function doSetItemEvents(int $index, Item $newItem) : ?Item{ + Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $this->getItem($index), $newItem, $index)); + if($ev->isCancelled()){ + return null; + } + + return $ev->getNewItem(); + } + + public function sendSlot(int $index, $target) : void{ + if($target instanceof Player){ + $target = [$target]; + } + + $armor = $this->getContents(true); + + $pk = new MobArmorEquipmentPacket(); + $pk->entityRuntimeId = $this->getHolder()->getId(); + $pk->slots = $armor; + $pk->encode(); + + foreach($target as $player){ + if($player === $this->getHolder()){ + /** @var Player $player */ + + $pk2 = new InventorySlotPacket(); + $pk2->windowId = $player->getWindowId($this); + $pk2->inventorySlot = $index - $this->getSize(); + $pk2->item = $this->getItem($index); + $player->dataPacket($pk2); + }else{ + $player->dataPacket($pk); + } + } + } + + public function sendContents($target) : void{ + if($target instanceof Player){ + $target = [$target]; + } + + $armor = $this->getContents(true); + + $pk = new MobArmorEquipmentPacket(); + $pk->entityRuntimeId = $this->getHolder()->getId(); + $pk->slots = $armor; + $pk->encode(); + + foreach($target as $player){ + $player->dataPacket($pk); + } + } + + /** + * @return Player[] + */ + public function getViewers() : array{ + return array_merge(parent::getViewers(), $this->holder->getViewers()); + } +} diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index 580640e21..018105441 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -426,11 +426,7 @@ abstract class BaseInventory implements Inventory{ } $pk = new InventoryContentPacket(); - - //Using getSize() here allows PlayerInventory to report that it's 4 slots smaller than it actually is (armor hack) - for($i = 0, $size = $this->getSize(); $i < $size; ++$i){ - $pk->items[$i] = $this->getItem($i); - } + $pk->items = $this->getContents(true); foreach($target as $player){ if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ @@ -466,6 +462,6 @@ abstract class BaseInventory implements Inventory{ } public function slotExists(int $slot) : bool{ - return $slot >= 0 and $slot < $this->slots->getSize(); //use actual slots size to allow PlayerInventory to lie + return $slot >= 0 and $slot < $this->slots->getSize(); } } diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index b6e8b2521..1fe62368c 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -24,17 +24,12 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\entity\Human; -use pocketmine\event\entity\EntityArmorChangeEvent; use pocketmine\event\player\PlayerItemHeldEvent; use pocketmine\item\Item; -use pocketmine\item\ItemFactory; use pocketmine\network\mcpe\protocol\InventoryContentPacket; -use pocketmine\network\mcpe\protocol\InventorySlotPacket; -use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; use pocketmine\network\mcpe\protocol\MobEquipmentPacket; use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\Player; -use pocketmine\Server; class PlayerInventory extends EntityInventory{ @@ -56,16 +51,7 @@ class PlayerInventory extends EntityInventory{ } public function getDefaultSize() : int{ - return 40; //36 inventory, 4 armor - } - - public function getSize() : int{ - return parent::getSize() - 4; //Remove armor slots - } - - public function setSize(int $size){ - parent::setSize($size + 4); - $this->sendContents($this->getViewers()); + return 36; } /** @@ -204,23 +190,6 @@ class PlayerInventory extends EntityInventory{ } } - public function onSlotChange(int $index, Item $before, bool $send) : void{ - $holder = $this->getHolder(); - if($holder instanceof Player and !$holder->spawned){ - return; - } - - if($index >= $this->getSize()){ - if($send){ - $this->sendArmorSlot($index, $this->getViewers()); - $this->sendArmorSlot($index, $this->getHolder()->getViewers()); - } - }else{ - //Do not send armor by accident here. - parent::onSlotChange($index, $before, $send); - } - } - /** * Returns the number of slots in the hotbar. * @return int @@ -229,158 +198,6 @@ class PlayerInventory extends EntityInventory{ return 9; } - public function getArmorItem(int $index) : Item{ - return $this->getItem($this->getSize() + $index); - } - - public function setArmorItem(int $index, Item $item) : bool{ - return $this->setItem($this->getSize() + $index, $item); - } - - public function getHelmet() : Item{ - return $this->getItem($this->getSize()); - } - - public function getChestplate() : Item{ - return $this->getItem($this->getSize() + 1); - } - - public function getLeggings() : Item{ - return $this->getItem($this->getSize() + 2); - } - - public function getBoots() : Item{ - return $this->getItem($this->getSize() + 3); - } - - public function setHelmet(Item $helmet) : bool{ - return $this->setItem($this->getSize(), $helmet); - } - - public function setChestplate(Item $chestplate) : bool{ - return $this->setItem($this->getSize() + 1, $chestplate); - } - - public function setLeggings(Item $leggings) : bool{ - return $this->setItem($this->getSize() + 2, $leggings); - } - - public function setBoots(Item $boots) : bool{ - return $this->setItem($this->getSize() + 3, $boots); - } - - protected function doSetItemEvents(int $index, Item $newItem) : ?Item{ - if($index >= $this->getSize()){ - Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $this->getItem($index), $newItem, $index)); - if($ev->isCancelled()){ - return null; - } - - return $ev->getNewItem(); - } - - return parent::doSetItemEvents($index, $newItem); - } - - public function clearAll(bool $send = true) : void{ - parent::clearAll($send); - - for($i = $this->getSize(), $m = $i + 4; $i < $m; ++$i){ - $this->clear($i, false); - } - - if($send){ - $this->sendArmorContents($this->getViewers()); - } - } - - /** - * @return Item[] - */ - public function getArmorContents() : array{ - $armor = []; - - for($i = 0; $i < 4; ++$i){ - $armor[$i] = $this->getItem($this->getSize() + $i); - } - - return $armor; - } - - /** - * @param Player|Player[] $target - */ - public function sendArmorContents($target){ - if($target instanceof Player){ - $target = [$target]; - } - - $armor = $this->getArmorContents(); - - $pk = new MobArmorEquipmentPacket(); - $pk->entityRuntimeId = $this->getHolder()->getId(); - $pk->slots = $armor; - $pk->encode(); - - foreach($target as $player){ - if($player === $this->getHolder()){ - $pk2 = new InventoryContentPacket(); - $pk2->windowId = ContainerIds::ARMOR; - $pk2->items = $armor; - $player->dataPacket($pk2); - }else{ - $player->dataPacket($pk); - } - } - } - - /** - * @param Item[] $items - */ - public function setArmorContents(array $items){ - for($i = 0; $i < 4; ++$i){ - if(!isset($items[$i]) or !($items[$i] instanceof Item)){ - $items[$i] = ItemFactory::get(Item::AIR, 0, 0); - } - - $this->setItem($this->getSize() + $i, $items[$i], false); - } - - $this->sendArmorContents($this->getViewers()); - } - - - /** - * @param int $index - * @param Player|Player[] $target - */ - public function sendArmorSlot(int $index, $target){ - if($target instanceof Player){ - $target = [$target]; - } - - $armor = $this->getArmorContents(); - - $pk = new MobArmorEquipmentPacket(); - $pk->entityRuntimeId = $this->getHolder()->getId(); - $pk->slots = $armor; - $pk->encode(); - - foreach($target as $player){ - if($player === $this->getHolder()){ - /** @var Player $player */ - - $pk2 = new InventorySlotPacket(); - $pk2->windowId = ContainerIds::ARMOR; - $pk2->inventorySlot = $index - $this->getSize(); - $pk2->item = $this->getItem($index); - $player->dataPacket($pk2); - }else{ - $player->dataPacket($pk); - } - } - } - public function sendCreativeContents(){ $pk = new InventoryContentPacket(); $pk->windowId = ContainerIds::CREATIVE; diff --git a/src/pocketmine/inventory/transaction/InventoryTransaction.php b/src/pocketmine/inventory/transaction/InventoryTransaction.php index e6038db83..f708be992 100644 --- a/src/pocketmine/inventory/transaction/InventoryTransaction.php +++ b/src/pocketmine/inventory/transaction/InventoryTransaction.php @@ -25,7 +25,6 @@ namespace pocketmine\inventory\transaction; use pocketmine\event\inventory\InventoryTransactionEvent; use pocketmine\inventory\Inventory; -use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\item\Item; @@ -239,9 +238,6 @@ class InventoryTransaction{ protected function sendInventories() : void{ foreach($this->inventories as $inventory){ $inventory->sendContents($this->source); - if($inventory instanceof PlayerInventory){ - $inventory->sendArmorContents($this->source); - } } } diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index 08fefb82d..fcb820717 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -159,12 +159,6 @@ class NetworkInventoryAction{ public function createInventoryAction(Player $player){ switch($this->sourceType){ case self::SOURCE_CONTAINER: - if($this->windowId === ContainerIds::ARMOR){ - //TODO: HACK! - $this->inventorySlot += 36; - $this->windowId = ContainerIds::INVENTORY; - } - $window = $player->getWindow($this->windowId); if($window !== null){ return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem);