Hotbar/inventory bugfixes (#399)

- Fixed most issues with item equipment in creative
- Added save and restore of currently-held item
- Reset hotbar on death, added API method PlayerInventory->resetHotbar()
- Creative players now have more leeway to get items, alleviates issues with item equipment in desktop GUI
- Fixed creative players wearing armour
- Found unknown field in ContainerSetSlotPacket
- Removed outdated/redundant constants
- Use a case statement in ContainerSetSlotPacket handler, added handling for 0x7a hotbar slot link update
This commit is contained in:
Dylan K. Taylor
2017-03-09 20:31:55 +00:00
committed by GitHub
parent d31e92bbe7
commit 132e04fdbb
6 changed files with 175 additions and 116 deletions

View File

@ -40,7 +40,7 @@ class PlayerInventory extends BaseInventory{
protected $hotbar;
public function __construct(Human $player){
$this->hotbar = range(0, $this->getHotbarSize() - 1, 1);
$this->resetHotbar(false);
parent::__construct($player, InventoryType::get(InventoryType::PLAYER));
}
@ -53,20 +53,93 @@ class PlayerInventory extends BaseInventory{
$this->sendContents($this->getViewers());
}
public function getHotbarSlotIndex($index){
return ($index >= 0 and $index < $this->getHotbarSize()) ? $this->hotbar[$index] : -1;
/**
* Called when a client equips a hotbar slot. This method should not be used by plugins.
* This method will call PlayerItemHeldEvent.
*
* @param int $hotbarSlot Number of the hotbar slot to equip.
* @param int|null $inventorySlot Inventory slot to map to the specified hotbar slot. Supply null to make no change to the link.
*
* @return bool if the equipment change was successful, false if not.
*/
public function equipItem(int $hotbarSlot, $inventorySlot = null) : bool{
if($inventorySlot === null){
$inventorySlot = $this->getHotbarSlotIndex($this->getHeldItemIndex());
}
if($hotbarSlot < 0 or $hotbarSlot >= $this->getHotbarSize() or $inventorySlot < -1 or $inventorySlot >= $this->getSize()){
$this->sendContents($this->getHolder());
return false;
}
$this->getHolder()->getLevel()->getServer()->getPluginManager()->callEvent($ev = new PlayerItemHeldEvent($this->getHolder(), $this->getItem($inventorySlot), $inventorySlot, $hotbarSlot));
if($ev->isCancelled()){
$this->sendContents($this->getHolder());
return false;
}
$this->setHotbarSlotIndex($hotbarSlot, $inventorySlot);
$this->setHeldItemIndex($hotbarSlot, false);
return true;
}
public function setHotbarSlotIndex($index, $slot){
if($index >= 0 and $index < $this->getHotbarSize() and $slot >= -1 and $slot < $this->getSize()){
$this->hotbar[$index] = $slot;
/**
* Returns the index of the inventory slot mapped to the specified hotbar slot, or -1 if the hotbar slot does not exist.
* @param int $index
*
* @return int
*/
public function getHotbarSlotIndex($index){
return $this->hotbar[$index] ?? -1;
}
/**
* @internal This method is intended for use in network interaction with clients only.
* @deprecated Do not change hotbar slot mapping with plugins, this will cause myriad client-sided bugs, especially with desktop GUI clients.
*
* @param int $hotbarSlot
* @param int $inventorySlot
*/
public function setHotbarSlotIndex($hotbarSlot, $inventorySlot){
if($hotbarSlot >= 0 and $hotbarSlot < $this->getHotbarSize() and $inventorySlot >= -1 and $inventorySlot < $this->getSize()){
if($inventorySlot !== -1 and ($alreadyEquippedIndex = array_search($inventorySlot, $this->hotbar)) !== false){
/* Swap the slots
* This assumes that the equipped slot can only be equipped in one other slot
* it will not account for ancient bugs where the same slot ended up linked to several hotbar slots.
* Such bugs will require a hotbar reset to default.
*/
$this->hotbar[$alreadyEquippedIndex] = $this->hotbar[$hotbarSlot];
}
$this->hotbar[$hotbarSlot] = $inventorySlot;
}
}
/**
* Resets hotbar links to their original defaults.
* @param bool $send Whether to send changes to the holder.
*/
public function resetHotbar(bool $send = true){
$this->hotbar = range(0, $this->getHotbarSize() - 1, 1);
if($send){
$this->sendContents($this->getHolder());
}
}
/**
* Returns the hotbar slot number the holder is currently holding.
* @return int
*/
public function getHeldItemIndex(){
return $this->itemInHandIndex;
}
/**
* Sets which hotbar slot the player is currently loading.
*
* @param int $index 0-8 index of the hotbar slot to hold
* @param bool $send Whether to send updates back to the inventory holder. This should usually be true for plugin calls.
* It should only be false to prevent feedback loops of equipment packets between client and server.
*/
public function setHeldItemIndex($index, $send = true){
if($index >= 0 and $index < $this->getHotbarSize()){
$this->itemInHandIndex = $index;
@ -79,6 +152,11 @@ class PlayerInventory extends BaseInventory{
}
}
/**
* Returns the currently-held item.
*
* @return Item
*/
public function getItemInHand(){
$item = $this->getItem($this->getHeldItemSlot());
if($item instanceof Item){
@ -89,6 +167,7 @@ class PlayerInventory extends BaseInventory{
}
/**
* Sets the item in the currently-held slot to the specified item.
* @param Item $item
*
* @return bool
@ -97,29 +176,29 @@ class PlayerInventory extends BaseInventory{
return $this->setItem($this->getHeldItemSlot(), $item);
}
/**
* Returns the hotbar slot number currently held.
*
* @return int
*/
public function getHeldItemSlot(){
return $this->getHotbarSlotIndex($this->itemInHandIndex);
}
/**
* Sets the hotbar slot link of the currently-held hotbar slot.
* @deprecated Do not change hotbar slot mapping with plugins, this will cause myriad client-sided bugs, especially with desktop GUI clients.
*
* @param int $slot
*/
public function setHeldItemSlot($slot){
if($slot >= -1 and $slot < $this->getSize()){
$item = $this->getItem($slot);
$itemIndex = $this->getHeldItemIndex();
if($this->getHolder() instanceof Player){
Server::getInstance()->getPluginManager()->callEvent($ev = new PlayerItemHeldEvent($this->getHolder(), $item, $slot, $itemIndex));
if($ev->isCancelled()){
$this->sendContents($this->getHolder());
return;
}
}
$this->setHotbarSlotIndex($itemIndex, $slot);
$this->setHotbarSlotIndex($this->getHeldItemIndex(), $slot);
}
}
/**
* Sends the currently-held item to specified targets.
* @param Player|Player[] $target
*/
public function sendHeldItem($target){
@ -128,8 +207,8 @@ class PlayerInventory extends BaseInventory{
$pk = new MobEquipmentPacket();
$pk->eid = $this->getHolder()->getId();
$pk->item = $item;
$pk->slot = $this->getHeldItemSlot();
$pk->selectedSlot = $this->getHeldItemIndex();
$pk->inventorySlot = $this->getHeldItemSlot();
$pk->hotbarSlot = $this->getHeldItemIndex();
if(!is_array($target)){
$target->dataPacket($pk);
@ -161,6 +240,10 @@ class PlayerInventory extends BaseInventory{
}
}
/**
* Returns the number of slots in the hotbar.
* @return int
*/
public function getHotbarSize(){
return 9;
}

View File

@ -130,7 +130,21 @@ class SimpleTransactionGroup implements TransactionGroup{
$haveItems = [];
$needItems = [];
return $this->matchItems($haveItems, $needItems) and count($haveItems) === 0 and count($needItems) === 0 and count($this->transactions) > 0;
if($this->matchItems($needItems, $haveItems) and count($this->transactions) > 0){
if(count($haveItems) === 0 and count($needItems) === 0){
return true;
}elseif($this->source->isCreative(true) and count($needItems) > 0){ //Added items from creative inventory
foreach($needItems as $item){
if(Item::getCreativeItemIndex($item) === -1 and $item->getId() !== Item::AIR){
return false;
}
}
return true;
}
}
return false;
}
public function execute(){