mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 09:56:06 +00:00
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:
151
src/pocketmine/inventory/ArmorInventory.php
Normal file
151
src/pocketmine/inventory/ArmorInventory.php
Normal 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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user