Inventory: Split up armor and player inventory (#1957)

* Inventory: Split up PlayerInventory and armour handling
* Fixed other players don't see armour changes. This bug also exists on master.
This commit is contained in:
Dylan K. Taylor 2018-01-23 20:01:26 +00:00 committed by GitHub
parent 0bf5ab76fb
commit 6543d96910
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 198 additions and 235 deletions

View File

@ -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);
}
}
}

View File

@ -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]);

View File

@ -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);
}
}

View File

@ -0,0 +1,151 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\entity\Living;
use pocketmine\event\entity\EntityArmorChangeEvent;
use pocketmine\item\Item;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
use pocketmine\Player;
use pocketmine\Server;
class ArmorInventory extends BaseInventory{
public const SLOT_HEAD = 0;
public const SLOT_CHEST = 1;
public const SLOT_LEGS = 2;
public const SLOT_FEET = 3;
/** @var Living */
protected $holder;
public function __construct(Living $holder){
$this->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());
}
}

View File

@ -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();
}
}

View File

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

View File

@ -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);
}
}
}

View File

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